]> Pileus Git - ~andy/linux/blobdiff - arch/mips/kernel/cevt-r4k.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
[~andy/linux] / arch / mips / kernel / cevt-r4k.c
index 08b84d476c8727c243f3a0bb2cb942eae65190f9..24a2d907aa0de4c022c0d5e89227dceae2a8a572 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/interrupt.h>
 #include <linux/percpu.h>
 
+#include <asm/smtc_ipi.h>
 #include <asm/time.h>
 
 static int mips_next_event(unsigned long delta,
@@ -27,7 +28,7 @@ static int mips_next_event(unsigned long delta,
        cnt = read_c0_count();
        cnt += delta;
        write_c0_compare(cnt);
-       res = ((long)(read_c0_count() - cnt ) > 0) ? -ETIME : 0;
+       res = ((int)(read_c0_count() - cnt) > 0) ? -ETIME : 0;
 #ifdef CONFIG_MIPS_MT_SMTC
        evpe(vpflags);
        local_irq_restore(flags);
@@ -178,30 +179,36 @@ static int c0_compare_int_pending(void)
 
 static int c0_compare_int_usable(void)
 {
-       const unsigned int delta = 0x300000;
+       unsigned int delta;
        unsigned int cnt;
 
        /*
         * IP7 already pending?  Try to clear it by acking the timer.
         */
        if (c0_compare_int_pending()) {
-               write_c0_compare(read_c0_compare());
+               write_c0_compare(read_c0_count());
                irq_disable_hazard();
                if (c0_compare_int_pending())
                        return 0;
        }
 
-       cnt = read_c0_count();
-       cnt += delta;
-       write_c0_compare(cnt);
+       for (delta = 0x10; delta <= 0x400000; delta <<= 1) {
+               cnt = read_c0_count();
+               cnt += delta;
+               write_c0_compare(cnt);
+               irq_disable_hazard();
+               if ((int)(read_c0_count() - cnt) < 0)
+                   break;
+               /* increase delta if the timer was already expired */
+       }
 
-       while ((long)(read_c0_count() - cnt) <= 0)
+       while ((int)(read_c0_count() - cnt) <= 0)
                ;       /* Wait for expiry  */
 
        if (!c0_compare_int_pending())
                return 0;
 
-       write_c0_compare(read_c0_compare());
+       write_c0_compare(read_c0_count());
        irq_disable_hazard();
        if (c0_compare_int_pending())
                return 0;
@@ -212,15 +219,15 @@ static int c0_compare_int_usable(void)
        return 1;
 }
 
-void __cpuinit mips_clockevent_init(void)
+int __cpuinit mips_clockevent_init(void)
 {
        uint64_t mips_freq = mips_hpt_frequency;
        unsigned int cpu = smp_processor_id();
        struct clock_event_device *cd;
-       unsigned int irq = MIPS_CPU_IRQ_BASE + 7;
+       unsigned int irq;
 
-       if (!cpu_has_counter)
-               return;
+       if (!cpu_has_counter || !mips_hpt_frequency)
+               return -ENXIO;
 
 #ifdef CONFIG_MIPS_MT_SMTC
        setup_smtc_dummy_clockevent_device();
@@ -230,11 +237,20 @@ void __cpuinit mips_clockevent_init(void)
         * device.
         */
        if (cpu)
-               return;
+               return 0;
 #endif
 
        if (!c0_compare_int_usable())
-               return;
+               return -ENXIO;
+
+       /*
+        * With vectored interrupts things are getting platform specific.
+        * get_c0_compare_int is a hook to allow a platform to return the
+        * interrupt number of it's liking.
+        */
+       irq = MIPS_CPU_IRQ_BASE + cp0_compare_irq;
+       if (get_c0_compare_int)
+               irq = get_c0_compare_int();
 
        cd = &per_cpu(mips_clockevent_device, cpu);
 
@@ -260,13 +276,17 @@ void __cpuinit mips_clockevent_init(void)
 
        clockevents_register_device(cd);
 
-       if (!cp0_timer_irq_installed) {
+       if (cp0_timer_irq_installed)
+               return 0;
+
+       cp0_timer_irq_installed = 1;
+
 #ifdef CONFIG_MIPS_MT_SMTC
 #define CPUCTR_IMASKBIT (0x100 << cp0_compare_irq)
-               setup_irq_smtc(irq, &c0_compare_irqaction, CPUCTR_IMASKBIT);
+       setup_irq_smtc(irq, &c0_compare_irqaction, CPUCTR_IMASKBIT);
 #else
-               setup_irq(irq, &c0_compare_irqaction);
-#endif /* CONFIG_MIPS_MT_SMTC */
-               cp0_timer_irq_installed = 1;
-       }
+       setup_irq(irq, &c0_compare_irqaction);
+#endif
+
+       return 0;
 }