aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips/kernel/time.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2007-10-11 18:46:09 -0400
committerRalf Baechle <ralf@linux-mips.org>2007-10-11 18:46:09 -0400
commit7bcf7717b6a047c272410d0cd00213185fe6b99d (patch)
tree81c5d6bbc2130815713e22bb5408ea80b6e1c499 /arch/mips/kernel/time.c
parent91a2fcc88634663e9e13dcdfad0e4a860e64aeee (diff)
[MIPS] Implement clockevents for R4000-style cp0 count/compare interrupt
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips/kernel/time.c')
-rw-r--r--arch/mips/kernel/time.c256
1 files changed, 118 insertions, 138 deletions
diff --git a/arch/mips/kernel/time.c b/arch/mips/kernel/time.c
index d23e6825e988..35988847c98a 100644
--- a/arch/mips/kernel/time.c
+++ b/arch/mips/kernel/time.c
@@ -11,6 +11,7 @@
11 * Free Software Foundation; either version 2 of the License, or (at your 11 * Free Software Foundation; either version 2 of the License, or (at your
12 * option) any later version. 12 * option) any later version.
13 */ 13 */
14#include <linux/clockchips.h>
14#include <linux/types.h> 15#include <linux/types.h>
15#include <linux/kernel.h> 16#include <linux/kernel.h>
16#include <linux/init.h> 17#include <linux/init.h>
@@ -34,6 +35,8 @@
34#include <asm/sections.h> 35#include <asm/sections.h>
35#include <asm/time.h> 36#include <asm/time.h>
36 37
38#include <irq.h>
39
37/* 40/*
38 * The integer part of the number of usecs per jiffy is taken from tick, 41 * The integer part of the number of usecs per jiffy is taken from tick,
39 * but the fractional part is not recorded, so we calculate it using the 42 * but the fractional part is not recorded, so we calculate it using the
@@ -70,10 +73,6 @@ int update_persistent_clock(struct timespec now)
70/* how many counter cycles in a jiffy */ 73/* how many counter cycles in a jiffy */
71static unsigned long cycles_per_jiffy __read_mostly; 74static unsigned long cycles_per_jiffy __read_mostly;
72 75
73/* expirelo is the count value for next CPU timer interrupt */
74static unsigned int expirelo;
75
76
77/* 76/*
78 * Null timer ack for systems not needing one (e.g. i8254). 77 * Null timer ack for systems not needing one (e.g. i8254).
79 */ 78 */
@@ -92,18 +91,7 @@ static cycle_t null_hpt_read(void)
92 */ 91 */
93static void c0_timer_ack(void) 92static void c0_timer_ack(void)
94{ 93{
95 unsigned int count; 94 write_c0_compare(read_c0_compare());
96
97 /* Ack this timer interrupt and set the next one. */
98 expirelo += cycles_per_jiffy;
99 write_c0_compare(expirelo);
100
101 /* Check to see if we have missed any timer interrupts. */
102 while (((count = read_c0_count()) - expirelo) < 0x7fffffff) {
103 /* missed_timer_count++; */
104 expirelo = count + cycles_per_jiffy;
105 write_c0_compare(expirelo);
106 }
107} 95}
108 96
109/* 97/*
@@ -114,13 +102,6 @@ static cycle_t c0_hpt_read(void)
114 return read_c0_count(); 102 return read_c0_count();
115} 103}
116 104
117/* For use both as a high precision timer and an interrupt source. */
118static void __init c0_hpt_timer_init(void)
119{
120 expirelo = read_c0_count() + cycles_per_jiffy;
121 write_c0_compare(expirelo);
122}
123
124int (*mips_timer_state)(void); 105int (*mips_timer_state)(void);
125void (*mips_timer_ack)(void); 106void (*mips_timer_ack)(void);
126 107
@@ -140,35 +121,6 @@ void local_timer_interrupt(int irq, void *dev_id)
140 update_process_times(user_mode(get_irq_regs())); 121 update_process_times(user_mode(get_irq_regs()));
141} 122}
142 123
143/*
144 * High-level timer interrupt service routines. This function
145 * is set as irqaction->handler and is invoked through do_IRQ.
146 */
147static irqreturn_t timer_interrupt(int irq, void *dev_id)
148{
149 write_seqlock(&xtime_lock);
150
151 mips_timer_ack();
152
153 /*
154 * call the generic timer interrupt handling
155 */
156 do_timer(1);
157
158 write_sequnlock(&xtime_lock);
159
160 /*
161 * In UP mode, we call local_timer_interrupt() to do profiling
162 * and process accouting.
163 *
164 * In SMP mode, local_timer_interrupt() is invoked by appropriate
165 * low-level local timer interrupt handler.
166 */
167 local_timer_interrupt(irq, dev_id);
168
169 return IRQ_HANDLED;
170}
171
172int null_perf_irq(void) 124int null_perf_irq(void)
173{ 125{
174 return 0; 126 return 0;
@@ -209,81 +161,6 @@ static inline int handle_perf_irq (int r2)
209 !r2; 161 !r2;
210} 162}
211 163
212void ll_timer_interrupt(int irq, void *dev_id)
213{
214 int cpu = smp_processor_id();
215
216#ifdef CONFIG_MIPS_MT_SMTC
217 /*
218 * In an SMTC system, one Count/Compare set exists per VPE.
219 * Which TC within a VPE gets the interrupt is essentially
220 * random - we only know that it shouldn't be one with
221 * IXMT set. Whichever TC gets the interrupt needs to
222 * send special interprocessor interrupts to the other
223 * TCs to make sure that they schedule, etc.
224 *
225 * That code is specific to the SMTC kernel, not to
226 * the a particular platform, so it's invoked from
227 * the general MIPS timer_interrupt routine.
228 */
229
230 /*
231 * We could be here due to timer interrupt,
232 * perf counter overflow, or both.
233 */
234 (void) handle_perf_irq(1);
235
236 if (read_c0_cause() & (1 << 30)) {
237 /*
238 * There are things we only want to do once per tick
239 * in an "MP" system. One TC of each VPE will take
240 * the actual timer interrupt. The others will get
241 * timer broadcast IPIs. We use whoever it is that takes
242 * the tick on VPE 0 to run the full timer_interrupt().
243 */
244 if (cpu_data[cpu].vpe_id == 0) {
245 timer_interrupt(irq, NULL);
246 } else {
247 write_c0_compare(read_c0_count() +
248 (mips_hpt_frequency/HZ));
249 local_timer_interrupt(irq, dev_id);
250 }
251 smtc_timer_broadcast(cpu_data[cpu].vpe_id);
252 }
253#else /* CONFIG_MIPS_MT_SMTC */
254 int r2 = cpu_has_mips_r2;
255
256 if (handle_perf_irq(r2))
257 return;
258
259 if (r2 && ((read_c0_cause() & (1 << 30)) == 0))
260 return;
261
262 if (cpu == 0) {
263 /*
264 * CPU 0 handles the global timer interrupt job and process
265 * accounting resets count/compare registers to trigger next
266 * timer int.
267 */
268 timer_interrupt(irq, NULL);
269 } else {
270 /* Everyone else needs to reset the timer int here as
271 ll_local_timer_interrupt doesn't */
272 /*
273 * FIXME: need to cope with counter underflow.
274 * More support needs to be added to kernel/time for
275 * counter/timer interrupts on multiple CPU's
276 */
277 write_c0_compare(read_c0_count() + (mips_hpt_frequency/HZ));
278
279 /*
280 * Other CPUs should do profiling and process accounting
281 */
282 local_timer_interrupt(irq, dev_id);
283 }
284#endif /* CONFIG_MIPS_MT_SMTC */
285}
286
287/* 164/*
288 * time_init() - it does the following things. 165 * time_init() - it does the following things.
289 * 166 *
@@ -301,12 +178,6 @@ void ll_timer_interrupt(int irq, void *dev_id)
301 178
302unsigned int mips_hpt_frequency; 179unsigned int mips_hpt_frequency;
303 180
304static struct irqaction timer_irqaction = {
305 .handler = timer_interrupt,
306 .flags = IRQF_DISABLED | IRQF_PERCPU,
307 .name = "timer",
308};
309
310static unsigned int __init calibrate_hpt(void) 181static unsigned int __init calibrate_hpt(void)
311{ 182{
312 cycle_t frequency, hpt_start, hpt_end, hpt_count, hz; 183 cycle_t frequency, hpt_start, hpt_end, hpt_count, hz;
@@ -355,6 +226,65 @@ struct clocksource clocksource_mips = {
355 .flags = CLOCK_SOURCE_IS_CONTINUOUS, 226 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
356}; 227};
357 228
229static int mips_next_event(unsigned long delta,
230 struct clock_event_device *evt)
231{
232 unsigned int cnt;
233
234 cnt = read_c0_count();
235 cnt += delta;
236 write_c0_compare(cnt);
237
238 return ((long)(read_c0_count() - cnt ) > 0) ? -ETIME : 0;
239}
240
241static void mips_set_mode(enum clock_event_mode mode,
242 struct clock_event_device *evt)
243{
244 /* Nothing to do ... */
245}
246
247struct clock_event_device mips_clockevent;
248
249static struct clock_event_device *global_cd[NR_CPUS];
250static int cp0_timer_irq_installed;
251
252static irqreturn_t timer_interrupt(int irq, void *dev_id)
253{
254 const int r2 = cpu_has_mips_r2;
255 struct clock_event_device *cd;
256 int cpu = smp_processor_id();
257
258 /*
259 * Suckage alert:
260 * Before R2 of the architecture there was no way to see if a
261 * performance counter interrupt was pending, so we have to run
262 * the performance counter interrupt handler anyway.
263 */
264 if (handle_perf_irq(r2))
265 goto out;
266
267 /*
268 * The same applies to performance counter interrupts. But with the
269 * above we now know that the reason we got here must be a timer
270 * interrupt. Being the paranoiacs we are we check anyway.
271 */
272 if (!r2 || (read_c0_cause() & (1 << 30))) {
273 c0_timer_ack();
274 cd = global_cd[cpu];
275 cd->event_handler(cd);
276 }
277
278out:
279 return IRQ_HANDLED;
280}
281
282static struct irqaction timer_irqaction = {
283 .handler = timer_interrupt,
284 .flags = IRQF_DISABLED | IRQF_PERCPU,
285 .name = "timer",
286};
287
358static void __init init_mips_clocksource(void) 288static void __init init_mips_clocksource(void)
359{ 289{
360 u64 temp; 290 u64 temp;
@@ -382,6 +312,56 @@ void __init __weak plat_time_init(void)
382{ 312{
383} 313}
384 314
315void __init __weak plat_timer_setup(struct irqaction *irq)
316{
317}
318
319void __cpuinit mips_clockevent_init(void)
320{
321 uint64_t mips_freq = mips_hpt_frequency;
322 unsigned int cpu = smp_processor_id();
323 struct clock_event_device *cd;
324 unsigned int irq = MIPS_CPU_IRQ_BASE + 7;
325
326 if (!cpu_has_counter)
327 return;
328
329 if (cpu == 0)
330 cd = &mips_clockevent;
331 else
332 cd = kzalloc(sizeof(*cd), GFP_ATOMIC);
333 if (!cd)
334 return; /* We're probably roadkill ... */
335
336 cd->name = "MIPS";
337 cd->features = CLOCK_EVT_FEAT_ONESHOT;
338
339 /* Calculate the min / max delta */
340 cd->mult = div_sc((unsigned long) mips_freq, NSEC_PER_SEC, 32);
341 cd->shift = 32;
342 cd->max_delta_ns = clockevent_delta2ns(0x7fffffff, cd);
343 cd->min_delta_ns = clockevent_delta2ns(0x30, cd);
344
345 cd->rating = 300;
346 cd->irq = irq;
347 cd->cpumask = cpumask_of_cpu(cpu);
348 cd->set_next_event = mips_next_event;
349 cd->set_mode = mips_set_mode;
350
351 global_cd[cpu] = cd;
352 clockevents_register_device(cd);
353
354 if (!cp0_timer_irq_installed) {
355#ifdef CONFIG_MIPS_MT_SMTC
356#define CPUCTR_IMASKBIT (0x100 << cp0_compare_irq)
357 setup_irq_smtc(irq, &timer_irqaction, CPUCTR_IMASKBIT);
358#else
359 setup_irq(irq, &timer_irqaction);
360#endif /* CONFIG_MIPS_MT_SMTC */
361 cp0_timer_irq_installed = 1;
362 }
363}
364
385void __init time_init(void) 365void __init time_init(void)
386{ 366{
387 plat_time_init(); 367 plat_time_init();
@@ -407,11 +387,6 @@ void __init time_init(void)
407 /* Calculate cache parameters. */ 387 /* Calculate cache parameters. */
408 cycles_per_jiffy = 388 cycles_per_jiffy =
409 (mips_hpt_frequency + HZ / 2) / HZ; 389 (mips_hpt_frequency + HZ / 2) / HZ;
410 /*
411 * This sets up the high precision
412 * timer for the first interrupt.
413 */
414 c0_hpt_timer_init();
415 } 390 }
416 } 391 }
417 if (!mips_hpt_frequency) 392 if (!mips_hpt_frequency)
@@ -421,6 +396,10 @@ void __init time_init(void)
421 printk("Using %u.%03u MHz high precision timer.\n", 396 printk("Using %u.%03u MHz high precision timer.\n",
422 ((mips_hpt_frequency + 500) / 1000) / 1000, 397 ((mips_hpt_frequency + 500) / 1000) / 1000,
423 ((mips_hpt_frequency + 500) / 1000) % 1000); 398 ((mips_hpt_frequency + 500) / 1000) % 1000);
399
400#ifdef CONFIG_IRQ_CPU
401 setup_irq(MIPS_CPU_IRQ_BASE + 7, &timer_irqaction);
402#endif
424 } 403 }
425 404
426 if (!mips_timer_ack) 405 if (!mips_timer_ack)
@@ -441,4 +420,5 @@ void __init time_init(void)
441 plat_timer_setup(&timer_irqaction); 420 plat_timer_setup(&timer_irqaction);
442 421
443 init_mips_clocksource(); 422 init_mips_clocksource();
423 mips_clockevent_init();
444} 424}