Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=5aa85c9fc49a6ce44dc10a42e2011bbde9dc445a
Commit:     5aa85c9fc49a6ce44dc10a42e2011bbde9dc445a
Parent:     0f67e90e1caea4a0a14d2c60102547bce29f7f08
Author:     Ralf Baechle <[EMAIL PROTECTED]>
AuthorDate: Wed Nov 21 16:39:44 2007 +0000
Committer:  Ralf Baechle <[EMAIL PROTECTED]>
CommitDate: Mon Nov 26 17:26:14 2007 +0000

    [MIPS] Handle R4000/R4400 mfc0 from count register.
    
    The R4000 and R4400 have an errata where if the cp0 count register is read
    in the exact moment when it matches the compare register no interrupt will
    be generated.
    
    This bug may be triggered if the cp0 count register is being used as
    clocksource and the compare interrupt as clockevent.  So a simple
    workaround is to avoid using the compare for both facilities on the
    affected CPUs.
    
    This is different from the workaround suggested in the old errata documents;
    at some opportunity probably the official version should be implemented
    and tested.  Another thing to find out is which processor versions
    exactly are affected.  I only have errata documents upto R4400 V3.0
    available so for the moment the code treats all R4000 and R4400 as broken.
    
    This is potencially a problem for some machines that have no other decent
    clocksource available; this workaround will cause them to fall back to
    another clocksource, worst case the "jiffies" source.
---
 arch/mips/kernel/cevt-r4k.c |   12 +++--
 arch/mips/kernel/time.c     |   87 ++++++++++++++++--------------------------
 include/asm-mips/time.h     |    5 +-
 3 files changed, 43 insertions(+), 61 deletions(-)

diff --git a/arch/mips/kernel/cevt-r4k.c b/arch/mips/kernel/cevt-r4k.c
index 076f52b..24a2d90 100644
--- a/arch/mips/kernel/cevt-r4k.c
+++ b/arch/mips/kernel/cevt-r4k.c
@@ -219,7 +219,7 @@ 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();
@@ -227,7 +227,7 @@ void __cpuinit mips_clockevent_init(void)
        unsigned int irq;
 
        if (!cpu_has_counter || !mips_hpt_frequency)
-               return;
+               return -ENXIO;
 
 #ifdef CONFIG_MIPS_MT_SMTC
        setup_smtc_dummy_clockevent_device();
@@ -237,11 +237,11 @@ 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.
@@ -277,7 +277,7 @@ void __cpuinit mips_clockevent_init(void)
        clockevents_register_device(cd);
 
        if (cp0_timer_irq_installed)
-               return;
+               return 0;
 
        cp0_timer_irq_installed = 1;
 
@@ -287,4 +287,6 @@ void __cpuinit mips_clockevent_init(void)
 #else
        setup_irq(irq, &c0_compare_irqaction);
 #endif
+
+       return 0;
 }
diff --git a/arch/mips/kernel/time.c b/arch/mips/kernel/time.c
index 3284b9b..d7d52ef 100644
--- a/arch/mips/kernel/time.c
+++ b/arch/mips/kernel/time.c
@@ -91,48 +91,6 @@ static struct clocksource clocksource_mips = {
        .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
 };
 
-static unsigned int __init calibrate_hpt(void)
-{
-       cycle_t frequency, hpt_start, hpt_end, hpt_count, hz;
-
-       const int loops = HZ / 10;
-       int log_2_loops = 0;
-       int i;
-
-       /*
-        * We want to calibrate for 0.1s, but to avoid a 64-bit
-        * division we round the number of loops up to the nearest
-        * power of 2.
-        */
-       while (loops > 1 << log_2_loops)
-               log_2_loops++;
-       i = 1 << log_2_loops;
-
-       /*
-        * Wait for a rising edge of the timer interrupt.
-        */
-       while (mips_timer_state());
-       while (!mips_timer_state());
-
-       /*
-        * Now see how many high precision timer ticks happen
-        * during the calculated number of periods between timer
-        * interrupts.
-        */
-       hpt_start = clocksource_mips.read();
-       do {
-               while (mips_timer_state());
-               while (!mips_timer_state());
-       } while (--i);
-       hpt_end = clocksource_mips.read();
-
-       hpt_count = (hpt_end - hpt_start) & clocksource_mips.mask;
-       hz = HZ;
-       frequency = hpt_count * hz;
-
-       return frequency >> log_2_loops;
-}
-
 void __init clocksource_set_clock(struct clocksource *cs, unsigned int clock)
 {
        u64 temp;
@@ -194,21 +152,42 @@ void __init plat_timer_setup(void)
        BUG();
 }
 
+static __init int cpu_has_mfc0_count_bug(void)
+{
+       switch (current_cpu_type()) {
+       case CPU_R4000PC:
+       case CPU_R4000SC:
+       case CPU_R4000MC:
+               /*
+                * V3.0 is documented as suffering from the mfc0 from count bug.
+                * Afaik this is the last version of the R4000.  Later versions
+                * were marketed as R4400.
+                */
+               return 1;
+
+       case CPU_R4400PC:
+       case CPU_R4400SC:
+       case CPU_R4400MC:
+               /*
+                * The published errata for the R4400 upto 3.0 say the CPU
+                * has the mfc0 from count bug.
+                */
+               if ((current_cpu_data.processor_id & 0xff) <= 0x30)
+                       return 1;
+
+               /*
+                * I don't have erratas for newer R4400 so be paranoid.
+                */
+               return 1;
+       }
+
+       return 0;
+}
+
 void __init time_init(void)
 {
        plat_time_init();
 
-       if (cpu_has_counter && (mips_hpt_frequency || mips_timer_state)) {
-               /* We know counter frequency.  Or we can get it.  */
-               if (!mips_hpt_frequency)
-                       mips_hpt_frequency = calibrate_hpt();
-
-               /* Report the high precision timer rate for a reference.  */
-               printk("Using %u.%03u MHz high precision timer.\n",
-                      ((mips_hpt_frequency + 500) / 1000) / 1000,
-                      ((mips_hpt_frequency + 500) / 1000) % 1000);
+       if (mips_clockevent_init() || !cpu_has_mfc0_count_bug())
                init_mips_clocksource();
-       }
-
-       mips_clockevent_init();
 }
diff --git a/include/asm-mips/time.h b/include/asm-mips/time.h
index ee1663e..1922494 100644
--- a/include/asm-mips/time.h
+++ b/include/asm-mips/time.h
@@ -58,11 +58,12 @@ extern int (*perf_irq)(void);
  * Initialize the calling CPU's compare interrupt as clockevent device
  */
 #ifdef CONFIG_CEVT_R4K
-extern void mips_clockevent_init(void);
+extern int mips_clockevent_init(void);
 extern unsigned int __weak get_c0_compare_int(void);
 #else
-static inline void mips_clockevent_init(void)
+static inline int mips_clockevent_init(void)
 {
+       return -ENXIO;
 }
 #endif
 
-
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to