avr32: Fix theoretical race in udelay()
authorHaavard Skinnemoen <haavard.skinnemoen@atmel.com>
Mon, 26 May 2008 10:19:10 +0000 (12:19 +0200)
committerHaavard Skinnemoen <haavard.skinnemoen@atmel.com>
Tue, 27 May 2008 13:27:31 +0000 (15:27 +0200)
If the specified delay is very short, the cycle counter may go past the
"end" time we are waiting for before we get around to reading it.

Fix it by checking the different between the cycle count "now" and the
cycle count at the beginning. This will work as long as the delay
measured in number of cycles is below 2^31.

Signed-off-by: Haavard Skinnemoen <haavard.skinnemoen@atmel.com>
cpu/at32ap/interrupts.c

index bef1f30d79d3414cd7e53b8f8e126ff57d80ec43..160838eeeb3b1d3d12e36fb67f911b3ae1b95547 100644 (file)
@@ -98,18 +98,16 @@ void set_timer(unsigned long t)
  */
 void udelay(unsigned long usec)
 {
-       unsigned long now, end;
+       unsigned long cycles;
+       unsigned long base;
+       unsigned long now;
 
-       now = sysreg_read(COUNT);
+       base = sysreg_read(COUNT);
+       cycles = ((usec * (get_tbclk() / 10000)) + 50) / 100;
 
-       end = ((usec * (get_tbclk() / 10000)) + 50) / 100;
-       end += now;
-
-       while (now > end)
-               now = sysreg_read(COUNT);
-
-       while (now < end)
+       do {
                now = sysreg_read(COUNT);
+       } while ((now - base) < cycles);
 }
 
 static int set_interrupt_handler(unsigned int nr, void (*handler)(void),