aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2007-11-21 11:39:44 -0500
committerRalf Baechle <ralf@linux-mips.org>2007-11-26 12:26:14 -0500
commit5aa85c9fc49a6ce44dc10a42e2011bbde9dc445a (patch)
tree14b8d1a014349568be39753f879c152e1e3f2b41
parent0f67e90e1caea4a0a14d2c60102547bce29f7f08 (diff)
[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.
-rw-r--r--arch/mips/kernel/cevt-r4k.c12
-rw-r--r--arch/mips/kernel/time.c87
-rw-r--r--include/asm-mips/time.h5
3 files changed, 43 insertions, 61 deletions
diff --git a/arch/mips/kernel/cevt-r4k.c b/arch/mips/kernel/cevt-r4k.c
index 076f52b9bb79..24a2d907aa0d 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)
219 return 1; 219 return 1;
220} 220}
221 221
222void __cpuinit mips_clockevent_init(void) 222int __cpuinit mips_clockevent_init(void)
223{ 223{
224 uint64_t mips_freq = mips_hpt_frequency; 224 uint64_t mips_freq = mips_hpt_frequency;
225 unsigned int cpu = smp_processor_id(); 225 unsigned int cpu = smp_processor_id();
@@ -227,7 +227,7 @@ void __cpuinit mips_clockevent_init(void)
227 unsigned int irq; 227 unsigned int irq;
228 228
229 if (!cpu_has_counter || !mips_hpt_frequency) 229 if (!cpu_has_counter || !mips_hpt_frequency)
230 return; 230 return -ENXIO;
231 231
232#ifdef CONFIG_MIPS_MT_SMTC 232#ifdef CONFIG_MIPS_MT_SMTC
233 setup_smtc_dummy_clockevent_device(); 233 setup_smtc_dummy_clockevent_device();
@@ -237,11 +237,11 @@ void __cpuinit mips_clockevent_init(void)
237 * device. 237 * device.
238 */ 238 */
239 if (cpu) 239 if (cpu)
240 return; 240 return 0;
241#endif 241#endif
242 242
243 if (!c0_compare_int_usable()) 243 if (!c0_compare_int_usable())
244 return; 244 return -ENXIO;
245 245
246 /* 246 /*
247 * With vectored interrupts things are getting platform specific. 247 * With vectored interrupts things are getting platform specific.
@@ -277,7 +277,7 @@ void __cpuinit mips_clockevent_init(void)
277 clockevents_register_device(cd); 277 clockevents_register_device(cd);
278 278
279 if (cp0_timer_irq_installed) 279 if (cp0_timer_irq_installed)
280 return; 280 return 0;
281 281
282 cp0_timer_irq_installed = 1; 282 cp0_timer_irq_installed = 1;
283 283
@@ -287,4 +287,6 @@ void __cpuinit mips_clockevent_init(void)
287#else 287#else
288 setup_irq(irq, &c0_compare_irqaction); 288 setup_irq(irq, &c0_compare_irqaction);
289#endif 289#endif
290
291 return 0;
290} 292}
diff --git a/arch/mips/kernel/time.c b/arch/mips/kernel/time.c
index 3284b9b4ecac..d7d52efff51f 100644
--- a/arch/mips/kernel/time.c
+++ b/arch/mips/kernel/time.c
@@ -91,48 +91,6 @@ static struct clocksource clocksource_mips = {
91 .flags = CLOCK_SOURCE_IS_CONTINUOUS, 91 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
92}; 92};
93 93
94static unsigned int __init calibrate_hpt(void)
95{
96 cycle_t frequency, hpt_start, hpt_end, hpt_count, hz;
97
98 const int loops = HZ / 10;
99 int log_2_loops = 0;
100 int i;
101
102 /*
103 * We want to calibrate for 0.1s, but to avoid a 64-bit
104 * division we round the number of loops up to the nearest
105 * power of 2.
106 */
107 while (loops > 1 << log_2_loops)
108 log_2_loops++;
109 i = 1 << log_2_loops;
110
111 /*
112 * Wait for a rising edge of the timer interrupt.
113 */
114 while (mips_timer_state());
115 while (!mips_timer_state());
116
117 /*
118 * Now see how many high precision timer ticks happen
119 * during the calculated number of periods between timer
120 * interrupts.
121 */
122 hpt_start = clocksource_mips.read();
123 do {
124 while (mips_timer_state());
125 while (!mips_timer_state());
126 } while (--i);
127 hpt_end = clocksource_mips.read();
128
129 hpt_count = (hpt_end - hpt_start) & clocksource_mips.mask;
130 hz = HZ;
131 frequency = hpt_count * hz;
132
133 return frequency >> log_2_loops;
134}
135
136void __init clocksource_set_clock(struct clocksource *cs, unsigned int clock) 94void __init clocksource_set_clock(struct clocksource *cs, unsigned int clock)
137{ 95{
138 u64 temp; 96 u64 temp;
@@ -194,21 +152,42 @@ void __init plat_timer_setup(void)
194 BUG(); 152 BUG();
195} 153}
196 154
155static __init int cpu_has_mfc0_count_bug(void)
156{
157 switch (current_cpu_type()) {
158 case CPU_R4000PC:
159 case CPU_R4000SC:
160 case CPU_R4000MC:
161 /*
162 * V3.0 is documented as suffering from the mfc0 from count bug.
163 * Afaik this is the last version of the R4000. Later versions
164 * were marketed as R4400.
165 */
166 return 1;
167
168 case CPU_R4400PC:
169 case CPU_R4400SC:
170 case CPU_R4400MC:
171 /*
172 * The published errata for the R4400 upto 3.0 say the CPU
173 * has the mfc0 from count bug.
174 */
175 if ((current_cpu_data.processor_id & 0xff) <= 0x30)
176 return 1;
177
178 /*
179 * I don't have erratas for newer R4400 so be paranoid.
180 */
181 return 1;
182 }
183
184 return 0;
185}
186
197void __init time_init(void) 187void __init time_init(void)
198{ 188{
199 plat_time_init(); 189 plat_time_init();
200 190
201 if (cpu_has_counter && (mips_hpt_frequency || mips_timer_state)) { 191 if (mips_clockevent_init() || !cpu_has_mfc0_count_bug())
202 /* We know counter frequency. Or we can get it. */
203 if (!mips_hpt_frequency)
204 mips_hpt_frequency = calibrate_hpt();
205
206 /* Report the high precision timer rate for a reference. */
207 printk("Using %u.%03u MHz high precision timer.\n",
208 ((mips_hpt_frequency + 500) / 1000) / 1000,
209 ((mips_hpt_frequency + 500) / 1000) % 1000);
210 init_mips_clocksource(); 192 init_mips_clocksource();
211 }
212
213 mips_clockevent_init();
214} 193}
diff --git a/include/asm-mips/time.h b/include/asm-mips/time.h
index ee1663e64da1..1922494a0d9e 100644
--- a/include/asm-mips/time.h
+++ b/include/asm-mips/time.h
@@ -58,11 +58,12 @@ extern int (*perf_irq)(void);
58 * Initialize the calling CPU's compare interrupt as clockevent device 58 * Initialize the calling CPU's compare interrupt as clockevent device
59 */ 59 */
60#ifdef CONFIG_CEVT_R4K 60#ifdef CONFIG_CEVT_R4K
61extern void mips_clockevent_init(void); 61extern int mips_clockevent_init(void);
62extern unsigned int __weak get_c0_compare_int(void); 62extern unsigned int __weak get_c0_compare_int(void);
63#else 63#else
64static inline void mips_clockevent_init(void) 64static inline int mips_clockevent_init(void)
65{ 65{
66 return -ENXIO;
66} 67}
67#endif 68#endif
68 69