avr32: Fix theoretical race in udelay()
[platform/kernel/u-boot.git] / cpu / at32ap / interrupts.c
index d720cfa..160838e 100644 (file)
  * MA 02111-1307 USA
  */
 #include <common.h>
+#include <div64.h>
 
-#include <asm/div64.h>
 #include <asm/errno.h>
 #include <asm/io.h>
 #include <asm/processor.h>
 #include <asm/sysreg.h>
 
-#include <asm/arch/platform.h>
+#include <asm/arch/memory-map.h>
 
 #define HANDLER_MASK   0x00ffffff
 #define INTLEV_SHIFT   30
@@ -44,8 +44,6 @@ volatile unsigned long timer_overflow;
  */
 static unsigned long tb_factor;
 
-static const struct device *intc_dev;
-
 unsigned long get_tbclk(void)
 {
        return gd->cpu_hz;
@@ -100,33 +98,34 @@ void set_timer(unsigned long t)
  */
 void udelay(unsigned long usec)
 {
-       unsigned long now, end;
-
-       now = sysreg_read(COUNT);
+       unsigned long cycles;
+       unsigned long base;
+       unsigned long now;
 
-       end = ((usec * (get_tbclk() / 10000)) + 50) / 100;
-       end += now;
-
-       while (now > end)
-               now = sysreg_read(COUNT);
+       base = sysreg_read(COUNT);
+       cycles = ((usec * (get_tbclk() / 10000)) + 50) / 100;
 
-       while (now < end)
+       do {
                now = sysreg_read(COUNT);
+       } while ((now - base) < cycles);
 }
 
 static int set_interrupt_handler(unsigned int nr, void (*handler)(void),
                                 unsigned int priority)
 {
+       extern void _evba(void);
        unsigned long intpr;
        unsigned long handler_addr = (unsigned long)handler;
 
+       handler_addr -= (unsigned long)&_evba;
+
        if ((handler_addr & HANDLER_MASK) != handler_addr
            || (priority & INTLEV_MASK) != priority)
                return -EINVAL;
 
        intpr = (handler_addr & HANDLER_MASK);
        intpr |= (priority & INTLEV_MASK) << INTLEV_SHIFT;
-       writel(intpr, intc_dev->regs + 4 * nr);
+       writel(intpr, (void *)INTC_BASE + 4 * nr);
 
        return 0;
 }
@@ -143,10 +142,7 @@ void timer_init(void)
        do_div(tmp, gd->cpu_hz);
        tb_factor = (u32)tmp;
 
-       intc_dev = get_device(DEVICE_INTC);
-
-       if (!intc_dev
-           || set_interrupt_handler(0, &timer_interrupt_handler, 3))
+       if (set_interrupt_handler(0, &timer_interrupt_handler, 3))
                return;
 
        /* For all practical purposes, this gives us an overflow interrupt */