diff --git a/udelay.c b/udelay.c index 9c7a673a3..b39597135 100644 --- a/udelay.c +++ b/udelay.c @@ -37,6 +37,30 @@ __attribute__ ((noinline)) void myusec_delay(int usecs) } } +unsigned long measure_os_delay_resolution(void) +{ + unsigned long timeusec; + struct timeval start, end; + unsigned long counter = 0; + + gettimeofday(&start, 0); + timeusec = 0; + + while (!timeusec && (++counter < 1000000000)) { + gettimeofday(&end, 0); + timeusec = 1000000 * (end.tv_sec - start.tv_sec) + + (end.tv_usec - start.tv_usec); + /* Protect against time going forward too much. */ + if ((end.tv_sec > start.tv_sec) && + ((end.tv_sec - start.tv_sec) >= LONG_MAX / 1000000 - 1)) + timeusec = 0; + /* Protect against time going backwards during leap seconds. */ + if ((end.tv_sec < start.tv_sec) || (timeusec > LONG_MAX)) + timeusec = 0; + } + return timeusec; +} + unsigned long measure_delay(int usecs) { unsigned long timeusec; @@ -61,10 +85,16 @@ unsigned long measure_delay(int usecs) void myusec_calibrate_delay(void) { unsigned long count = 1000; - unsigned long timeusec; + unsigned long timeusec, resolution; int i, tries = 0; msg_pinfo("Calibrating delay loop... "); + resolution = measure_os_delay_resolution(); + if (resolution) { + msg_pdbg("OS timer resolution is %u usecs, ", resolution); + } else { + msg_pinfo("OS timer resolution is unusable. "); + } recalibrate: count = 1000; @@ -94,9 +124,27 @@ recalibrate: * a scheduler delay or something similar. */ for (i = 0; i < 4; i++) { - if (measure_delay(100) < 90) { - msg_pdbg("delay more than 10%% too short, " - "recalculating... "); + if (resolution && (resolution < 10)) { + timeusec = measure_delay(100); + } else if (resolution && + (resolution < ULONG_MAX / 200)) { + timeusec = measure_delay(resolution * 10) * + 100 / (resolution * 10); + } else { + /* This workaround should be active for broken + * OS and maybe libpayload. The criterion + * here is horrible or non-measurable OS timer + * resolution which will result in + * measure_delay(100)=0 whereas a longer delay + * (1000 ms) may be sufficient + * to get a nonzero time measurement. + */ + timeusec = measure_delay(1000000) / 10000; + } + if (timeusec < 90) { + msg_pdbg("delay more than 10%% too short (got " + "%lu%% of expected delay), " + "recalculating... ", timeusec); goto recalibrate; } } @@ -113,6 +161,8 @@ recalibrate: msg_pdbg("1000 myus = %ld us, ", timeusec); timeusec = measure_delay(10000); msg_pdbg("10000 myus = %ld us, ", timeusec); + timeusec = measure_delay(resolution * 4); + msg_pdbg("%ld myus = %ld us, ", resolution * 4, timeusec); msg_pinfo("OK.\n"); }