No, 0.9.2 is not yet out.
This patch should be committed in r999 so that the 0.9.2 tag in svn will
be r1000.
Patches I absolutely want to get in before 0.9.2:
AT49F002(N)(T) eraseblock fix (needs re-review due to datasheet
contradictions)
http://patchwork.coreboot.org/patch/833/
drkaiser: MEM BAR fixes (needs update to fix gfxnvidia MEM BAR as well)
http://patchwork.coreboot.org/patch/1077/
msg_* conversions (two patches, preferably as one commit, needs review)
http://patchwork.coreboot.org/patch/1167/http://patchwork.coreboot.org/patch/1166/
Fix delay loop (needs review)
http://patchwork.coreboot.org/patch/1172/
This means we can fit 5 additional patches in flashrom to reach the
desired commit number.
Candidates for such patches (desirable, but not absolutely must-have):
flashrom dependencies (needs fixing)
http://patchwork.coreboot.org/patch/1131/
Merge print/print_wiki tables (needs review)
http://patchwork.coreboot.org/patch/1044/
Move Gigabyte it87spi boards to 'no enable' section (conflicts with 1044)
http://patchwork.coreboot.org/patch/1151/
Fix Kontron 986lcd-m (needs cleanup and retesting)
http://patchwork.coreboot.org/patch/871/
Add missing Intel/VIA chipset IDs (needs finishing, Idwer made some
progress)
http://patchwork.coreboot.org/patch/1169/
Suggestions for other patches? If I overlooked any of your patches, I'm
sorry. Feel free to point me to them and/or push them for inclusion.
Signed-off-by: Carl-Daniel Hailfinger <c-d.hailfinger.devel.2006(a)gmx.net>
Index: flashrom-0.9.2/Makefile
===================================================================
--- flashrom-0.9.2/Makefile (Revision 989)
+++ flashrom-0.9.2/Makefile (Arbeitskopie)
@@ -73,7 +73,7 @@
# will not require subversion. The downloadable snapshots are already exported.
SVNVERSION := $(shell LC_ALL=C svnversion -cn . | sed -e "s/.*://" -e "s/\([0-9]*\).*/\1/" | grep "[0-9]" || LC_ALL=C svn info . | grep ^Revision | sed "s/.*[[:blank:]]\+\([0-9]*\)[^0-9]*/\1/" | grep "[0-9]" || echo unknown)
-RELEASE := 0.9.1
+RELEASE := 0.9.2
VERSION := $(RELEASE)-r$(SVNVERSION)
RELEASENAME ?= $(VERSION)
--
http://www.hailfinger.org/
Hi,
[fullquote for flashrom(a)flashrom.org]
On 23.03.2010 22:58, RayeR wrote:
> >That is very strange. Do you have a log?
> >SB600 should work fine. Maybe the SPI chip is not
> >attached to the SB600 but instead to the SuperI/O.
> >Can you try
> >flashrom -p it87spi
>
> Yes here's it:
>
> C:\F>flashrom.exe -p it87spi
> flashrom v0.9.1-r933
> Error: Programmer initialization failed.
OK, so it is not ITE IT87.
> C:\F>flashrom.exe -V -r
> flashrom v0.9.1-r933
> No coreboot table found.
> DMI string system-manufacturer: "Dell Inc. "
> DMI string system-product-name: "OptiPlex 320 "
> DMI string system-version: "Not Specified"
> DMI string baseboard-manufacturer: "Dell Inc. "
> DMI string baseboard-product-name: "0CU395"
> DMI string baseboard-version: " "
> DMI string chassis-type: "Tower"
> Found chipset "AMD SB600", enabling flash write... SPI base address is
> at 0xd8000
> Error accessing SB600 SPI registers, 0x1000 bytes at 0x000d8000
> dpmi mmap failed: No such file or directory (ENOENT)
Ah yes. That's a limitation of the DOS port of flashrom.
Usually the SB600 SPI base address is near the top of the address space
(4 GB). On your board it is below 1 MB, and AFAIK Rudolf said that we
can't map any region below 1 MB as uncached due to CWSDPMI/DJGPP
limitations.
If anyone has an idea how to overcome these limitations, we can fix
flashrom for this special case.
> See PCI device listing attached.
>
> >The W25x40 supports multiple erase commands,
> >and your chipset does not allow flashrom to use
> >the spi_block_erase_20 command. Flashrom notices
> >that this command failed and tries another erase command which works.
>
> OK
>
> >Yes, strange. Maybe that happens as side effect from DJGPP compilation?
>
> I don't know...
>
> >It should try only 2 times. If it tries more often,
> >we have to check the code (bug?). I think Rudolf(?) said that there
> >are problems if we try to run a CWSDPMI app (dmidecode) from another
> >CWSDPMI app (flashrom).
> >Does it work if dmidecode.exe is in the PATH?
> >Hm. DMIDECODE.EXE is a name with 9+3 letters. That can't work on old
> DOS.
>
> Yes it works with dmidecode in path but nobody told me that I need it.
> On my home PC I already have dmidecode for DOS in my utilities
> directory. The file name is not problem because of DJGPP is smart
> and do some file name translation. If there's no LFN support it
> tries to look for "dmidecod.exe" and when run on LFN enabled OS
> it will use "dmidecode.exe". But it's different LFN truncating
> mode than windows use ("dmidec~1.exe"). I don't know why but e.g.
> DJGPP PKUNZIP tool use filename truncate this way without ~.
> But it would be better to check if both version of name exist and
> display non-confusing error message when nothing found.
Michael? Is there a good way to handle this?
Regards,
Carl-Daniel
--
http://www.hailfinger.org/
On 01.04.2010 00:48, Michael Karcher wrote:
> This should also fix broken write for non-uniform sectored FWH-like chips.
> These are:
> Intel 28F001
> Sharp LHF00L04
> ST M50FW002
> ST M50LPW116
>
> Signed-off-by: Michael Karcher <flashrom(a)mkarcher.dialup.fu-berlin.de>
> ---
> 82802ab.c | 85 +++++++++++++++++++++++++++++--------------
> chipdrivers.h | 3 ++
> flash.h | 2 +-
> flashchips.c | 110 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> flashchips.h | 4 ++
> 5 files changed, 175 insertions(+), 29 deletions(-)
>
> diff --git a/82802ab.c b/82802ab.c
> index aa7e45e..27acaa3 100644
> --- a/82802ab.c
> +++ b/82802ab.c
> @@ -48,6 +48,12 @@ int probe_82802ab(struct flashchip *flash)
> chipaddr bios = flash->virtual_memory;
> uint8_t id1, id2;
> uint8_t flashcontent1, flashcontent2;
> + int id2_offset;
> +
> + if (flash->feature_bits & FEATURE_ADDR_SHIFTED)
>
Umm... I'm pretty sure that FEATURE_ADDR_SHIFTED has another meaning
(the 0x555 etc shifted left by one bit).
The id2_offset may be needed anyway, but I propose to use a variable
"shifted" which is 0 for all non-shifted chips and 1 for all shifted
chips. It would be used as left shift for every programmatic access to
special addresses.
Such a change would allow us to kill m29f400bt.c which has buggy write
functions anyway.
Example for probing:
/* Issue JEDEC Product ID Entry command */
chip_writeb(0xAA, bios + (0x5555 & mask) << shifted);
chip_writeb(0x55, bios + (0x2AAA & mask) << shifted);
chip_writeb(0x90, bios + (0x5555 & mask) << shifted);
> + id2_offset = 2;
> + else
> + id2_offset = 1;
>
> /* Reset to get a clean state */
> chip_writeb(0xFF, bios);
> @@ -58,7 +64,7 @@ int probe_82802ab(struct flashchip *flash)
> programmer_delay(10);
>
> id1 = chip_readb(bios);
>
Arguably my "shifted" proposal suggests to use the following line for
consistency:
id1 = chip_readb(bios + 0x00 << shifted);
> - id2 = chip_readb(bios + 0x01);
> + id2 = chip_readb(bios + id2_offset);
>
id2 = chip_readb(bios + 0x01 << shifted);
>
> /* Leave ID mode */
> chip_writeb(0xFF, bios);
> @@ -72,7 +78,7 @@ int probe_82802ab(struct flashchip *flash)
>
> /* Read the product ID location again. We should now see normal flash contents. */
> flashcontent1 = chip_readb(bios);
> - flashcontent2 = chip_readb(bios + 0x01);
> + flashcontent2 = chip_readb(bios + id2_offset);
>
> if (id1 == flashcontent1)
> msg_cdbg(", id1 is normal flash content");
> @@ -177,41 +183,64 @@ void write_page_82802ab(chipaddr bios, uint8_t *src,
>
> int write_82802ab(struct flashchip *flash, uint8_t *buf)
> {
> - int i;
> - int total_size = flash->total_size * 1024;
> + int i, j, blocknum;
> + int blockoffset = 0;
> int page_size = flash->page_size;
> chipaddr bios = flash->virtual_memory;
> uint8_t *tmpbuf = malloc(page_size);
> + struct eraseblock * blocks;
> + int (*erase) (struct flashchip *flash, unsigned int blockaddr,
> + unsigned int blocklen);
> +
> + /* find first erase layout with a working erase function */
> + for (i = 0; flash->block_erasers[i].block_erase == NULL &&
> + i < NUM_ERASEFUNCTIONS; i++);
> +
> + if (i == NUM_ERASEFUNCTIONS)
> + {
> + msg_perr("No working erase function found - can't write\n");
> + return -1;
> + }
> + msg_pdbg("Chosing block layout #%d\n", i);
> + blocks = flash->block_erasers[i].eraseblocks;
> + erase = flash->block_erasers[i].block_erase;
>
That's too clever to be risked for 0.9.2. I can't see obvious bugs, but
this code triggers my "might break if included directly before a
release" fear.
>
> if (!tmpbuf) {
> msg_cerr("Could not allocate memory!\n");
> exit(1);
> }
> - msg_cinfo("Programming page: \n");
> - for (i = 0; i < total_size / page_size; i++) {
> - msg_cinfo("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
> - msg_cinfo("%04d at address: 0x%08x", i, i * page_size);
> -
> - /* Auto Skip Blocks, which already contain the desired data
> - * Faster, because we only write, what has changed
> - * More secure, because blocks, which are excluded
> - * (with the exclude or layout feature)
> - * or not erased and rewritten; their data is retained also in
> - * sudden power off situations
> - */
> - chip_readn(tmpbuf, bios + i * page_size, page_size);
> - if (!memcmp((void *)(buf + i * page_size), tmpbuf, page_size)) {
> - msg_cdbg("SKIPPED\n");
> - continue;
> - }
> -
> - /* erase block by block and write block by block; this is the most secure way */
> - if (erase_block_82802ab(flash, i * page_size, page_size)) {
> - msg_cerr("ERASE FAILED!\n");
> - return -1;
> + msg_cinfo("Programming block: \n");
> + blocknum = 0;
> + for (i = 0; blocks[i].size != 0; i++) {
>
This is guaranteed access random data if a chip has NUM_ERASEREGIONS
erase regions for a particular erase function. Either check for
i<NUM_ERASEREGIONS as well or use the chip size as upper limit. The chip
size check is probably more difficult here.
> + for(j = 0; j < blocks[i].count; j++) {
> + msg_cinfo("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"
> + "\b\b\b\b\b\b\b\b\b\b\b");
> + msg_cinfo("%04d at address: 0x%08x", blocknum,
> + blockoffset);
> +
> + /* Auto Skip Blocks, which already contain the
> + * desired data:
> + * Faster, because we only write, what has changed
> + * More secure, because blocks, which are excluded
> + * (with the exclude or layout feature)
> + * or not erased and rewritten; their data is
> + * retained also in sudden power off situations
> + */
> + chip_readn(tmpbuf, bios + blockoffset, blocks[i].size);
> + if (!memcmp((void *)(buf + i * page_size), tmpbuf,
> + blocks[i].size)) {
> + msg_cdbg("SKIPPED\n");
> + } else {
> + if (erase(flash, blockoffset, page_size)) {
> + msg_cerr("ERASE FAILED!\n");
> + return -1;
> + }
> + write_page_82802ab(bios, buf + blockoffset,
> + bios + blockoffset, blocks[i].size);
> + }
> + blockoffset += blocks[i].size;
> + blocknum++;
> }
> - write_page_82802ab(bios, buf + i * page_size,
> - bios + i * page_size, page_size);
>
To be honest, I think the current code in write_82802ab() is something
that should be ripped out and destroyed. It is essentially
special-casing partial writes for a family of chips instead of doing it
correctly in the generic code. The conversion to partial writes will
happen post 0.9.2 anyway, and then this code will be ripped out completely.
Your changes, on the other hand, beat at least some sanity into the
code, so it would be desirable to have them in generic code post-0.9.2.
Please coordinate with David Hendricks who is working on partial flashing.
> }
> msg_cinfo("DONE!\n");
> free(tmpbuf);
> diff --git a/chipdrivers.h b/chipdrivers.h
> index 6d5cef0..816088a 100644
> --- a/chipdrivers.h
> +++ b/chipdrivers.h
> @@ -53,6 +53,9 @@ int spi_nbyte_read(int addr, uint8_t *bytes, int len);
> int spi_read_chunked(struct flashchip *flash, uint8_t *buf, int start, int len, int chunksize);
> int spi_aai_write(struct flashchip *flash, uint8_t *buf);
>
> +/* i28f00x.c */
> +int write_28f00x(struct flashchip *flash, uint8_t *buf);
>
I don't see that function anywhere. Did you forget to svn add? Or was
this some aborted attempt?
> +
> /* 82802ab.c */
> uint8_t wait_82802ab(chipaddr bios);
> int probe_82802ab(struct flashchip *flash);
> diff --git a/flashchips.c b/flashchips.c
> index 2dbc1e0..f838ab2 100644
> --- a/flashchips.c
> +++ b/flashchips.c
> @@ -2385,6 +2385,116 @@ struct flashchip flashchips[] = {
>
> {
> .vendor = "Intel",
> + .name = "28F004BV/BE-B",
> + .bustype = CHIP_BUSTYPE_PARALLEL,
> + },
> +
> + {
> + .vendor = "Intel",
> + .name = "28F004BV/BE-T",
> + .bustype = CHIP_BUSTYPE_PARALLEL,
> + },
> +
> + {
> + .vendor = "Intel",
> + .name = "28F400BV/CV/CE-B",
> + .bustype = CHIP_BUSTYPE_PARALLEL,
> + .feature_bits = FEATURE_ADDR_SHIFTED,
> + },
> +
> + {
> + .vendor = "Intel",
> + .name = "28F004BV/BE-T",
>
That name is identical to the one two chips above AFAICS.
> + .bustype = CHIP_BUSTYPE_PARALLEL,
> + .feature_bits = FEATURE_ADDR_SHIFTED,
>
So the bottom two chips have the address left-shifted by 1 bit, but use
full address size?
Suggestions:
How about pointing the FEATURE_ADDR_SHIFTED chips to a to-be-fixed
m29f400bt.c for now? It would reduce jedec.c changes a bit.
How about using erase_flash() in write_82802ab() and dropping any
rolling reflash mechanism until post 0.9.2? It should solve all
non-uniform sector stuff. While that's a fear-reaching change as well, I
believe it makes the code more reviewable, and does allow us to kill
more duplicated code. As an added benefit, conversion to partial write
will be easier.
Regards,
Carl-Daniel
--
http://www.hailfinger.org/
Hi,
I was looking through the code, and see that flashrom is using software sequencing. Is there any reason for software sequencing to be chosen instead of hardware sequencing?
Are there any restrictions imposed for hardware sequencing?
Rgds,
John
_________________________________________________________________
Hotmail: Trusted email with powerful SPAM protection.
http://clk.atdmt.com/GBL/go/210850553/direct/01/
[Side note: This is not the big timing code rewrite I originally wanted
to post. I decided to focus on readable code instead of clever code. My
other rewrite is available on request if anyone is interested.]
The current delay loop calculation is still from revision 1 of flashrom,
and since then it had a logic bug which caused all delays to be twice as
long as intended. Fix the delay duration.
Protect against delay loop overflows.
Detect a non-working delay loop.
Change the delay loop itself to ensure clever compiler optimizers won't
eliminate it (as happens with clang/llvm in the current code). Some
people suggested machine-specific asm, but the empty asm statement with
the loop counter as register/memory input has the benefit of being
perfectly cross-platform and working in gcc and clang.
If time goes backwards (catastrophical NTP time difference, manual time
change), timing measurements were shot because the new-old time
subtraction yielded negative numbers which weren't handled correctly
because the variable is unsigned. Simply return 1 microsecond timing in
that case.
If time goes forward too fast, pick the biggest possible timing
measurement with a guaranteed overflow avoidance for all timing
calculations.
Check four times if the calculated timing is at most 10% too fast. This
addresses OS scheduler interactions, e.g. being scheduled out during
measurement which inflates measurements.
If the timing is off, recalculate the timer values up to four times
before giving up.
Avoid division by zero in rare cases where timing measurements for a 250
ms delay return 0 us elapsed.
Signed-off-by: Carl-Daniel Hailfinger <c-d.hailfinger.devel.2006(a)gmx.net>
Index: flashrom-delayloop_portable_fix/udelay.c
===================================================================
--- flashrom-delayloop_portable_fix/udelay.c (Revision 988)
+++ flashrom-delayloop_portable_fix/udelay.c (Arbeitskopie)
@@ -24,13 +24,16 @@
#include <limits.h>
#include "flash.h"
-// count to a billion. Time it. If it's < 1 sec, count to 10B, etc.
+/* loops per microsecond */
unsigned long micro = 1;
-void myusec_delay(int usecs)
+__attribute__ ((noinline)) void myusec_delay(int usecs)
{
- volatile unsigned long i;
- for (i = 0; i < usecs * micro; i++) ;
+ unsigned long i;
+ for (i = 0; i < usecs * micro; i++) {
+ /* Make sure the compiler doesn't optimize the loop away. */
+ asm volatile ("" : : "rm" (i) );
+ }
}
unsigned long measure_delay(int usecs)
@@ -43,30 +46,62 @@
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 = LONG_MAX;
+ /* Protect against time going backwards during leap seconds. */
+ if ((end.tv_sec < start.tv_sec) || (timeusec > LONG_MAX))
+ timeusec = 1;
return timeusec;
}
void myusec_calibrate_delay(void)
{
- int count = 1000;
+ unsigned long count = 1000;
unsigned long timeusec;
- int ok = 0;
+ int i, tries = 0;
printf("Calibrating delay loop... ");
- while (!ok) {
+recalibrate:
+ while (1) {
timeusec = measure_delay(count);
+ if (timeusec > 1000000 / 4)
+ break;
+ if (count >= ULONG_MAX / 2) {
+ msg_pinfo("timer loop overflow, reduced precision. ");
+ break;
+ }
count *= 2;
- if (timeusec < 1000000 / 4)
- continue;
- ok = 1;
}
+ tries ++;
- // compute one microsecond. That will be count / time
- micro = count / timeusec;
- msg_pdbg("%ldM loops per second, ", micro);
+ /* Avoid division by zero, but in that case the loop is shot anyway. */
+ if (!timeusec)
+ timeusec = 1;
+
+ /* Compute rounded up number of loops per microsecond. */
+ micro = (count * micro) / timeusec + 1;
+ msg_pdbg("%luM loops per second, ", micro);
+ /* Did we try to recalibrate less than 5 times? */
+ if (tries < 5) {
+ /* Recheck our timing to make sure we weren't just hitting
+ * 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... ");
+ goto recalibrate;
+ }
+ }
+ } else {
+ msg_perr("delay loop is unreliable, trying to continue ");
+ }
+
/* We're interested in the actual precision. */
timeusec = measure_delay(10);
msg_pdbg("10 myus = %ld us, ", timeusec);
--
http://www.hailfinger.org/
Author: hailfinger
Date: Thu Apr 1 01:55:06 2010
New Revision: 990
URL: http://flashrom.org/trac/coreboot/changeset/990
Log:
The current delay loop calculation is still from revision 1 of flashrom,
and since then it had a logic bug which caused all delays to be twice as
long as intended. Fix the delay duration.
Protect against delay loop overflows.
Detect a non-working delay loop.
Change the delay loop itself to ensure clever compiler optimizers won't
eliminate it (as happens with clang/llvm in the current code). Some
people suggested machine-specific asm, but the empty asm statement with
the loop counter as register/memory input has the benefit of being
perfectly cross-platform and working in gcc and clang.
If time goes backwards (catastrophical NTP time difference, manual time
change), timing measurements were shot because the new-old time
subtraction yielded negative numbers which weren't handled correctly
because the variable is unsigned. Work around that issue (a fix is
mathematically impossible).
If time goes forward too fast, pick the biggest possible timing
measurement with a guaranteed overflow avoidance for all timing
calculations.
Check four times if the calculated timing is at most 10% too fast. This
addresses OS scheduler interactions, e.g. being scheduled out during
measurement which inflates measurements.
If the timing looks like garbage, recalculate the timer values up to
four times before giving up.
Avoid division by zero in rare cases where timing measurements for a 250
ms delay returned 0 us elapsed.
Signed-off-by: Carl-Daniel Hailfinger <c-d.hailfinger.devel.2006(a)gmx.net>
Acked-by: Maciej Pijanka <maciej.pijanka(a)gmail.com>
Modified:
trunk/udelay.c
Modified: trunk/udelay.c
==============================================================================
--- trunk/udelay.c Tue Mar 30 04:45:18 2010 (r989)
+++ trunk/udelay.c Thu Apr 1 01:55:06 2010 (r990)
@@ -24,13 +24,16 @@
#include <limits.h>
#include "flash.h"
-// count to a billion. Time it. If it's < 1 sec, count to 10B, etc.
+/* loops per microsecond */
unsigned long micro = 1;
-void myusec_delay(int usecs)
+__attribute__ ((noinline)) void myusec_delay(int usecs)
{
- volatile unsigned long i;
- for (i = 0; i < usecs * micro; i++) ;
+ unsigned long i;
+ for (i = 0; i < usecs * micro; i++) {
+ /* Make sure the compiler doesn't optimize the loop away. */
+ asm volatile ("" : : "rm" (i) );
+ }
}
unsigned long measure_delay(int usecs)
@@ -43,29 +46,61 @@
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 = LONG_MAX;
+ /* Protect against time going backwards during leap seconds. */
+ if ((end.tv_sec < start.tv_sec) || (timeusec > LONG_MAX))
+ timeusec = 1;
return timeusec;
}
void myusec_calibrate_delay(void)
{
- int count = 1000;
+ unsigned long count = 1000;
unsigned long timeusec;
- int ok = 0;
+ int i, tries = 0;
printf("Calibrating delay loop... ");
- while (!ok) {
+recalibrate:
+ while (1) {
timeusec = measure_delay(count);
+ if (timeusec > 1000000 / 4)
+ break;
+ if (count >= ULONG_MAX / 2) {
+ msg_pinfo("timer loop overflow, reduced precision. ");
+ break;
+ }
count *= 2;
- if (timeusec < 1000000 / 4)
- continue;
- ok = 1;
}
+ tries ++;
- // compute one microsecond. That will be count / time
- micro = count / timeusec;
- msg_pdbg("%ldM loops per second, ", micro);
+ /* Avoid division by zero, but in that case the loop is shot anyway. */
+ if (!timeusec)
+ timeusec = 1;
+
+ /* Compute rounded up number of loops per microsecond. */
+ micro = (count * micro) / timeusec + 1;
+ msg_pdbg("%luM loops per second, ", micro);
+
+ /* Did we try to recalibrate less than 5 times? */
+ if (tries < 5) {
+ /* Recheck our timing to make sure we weren't just hitting
+ * 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... ");
+ goto recalibrate;
+ }
+ }
+ } else {
+ msg_perr("delay loop is unreliable, trying to continue ");
+ }
/* We're interested in the actual precision. */
timeusec = measure_delay(10);