diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2007-10-11 18:46:09 -0400 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2007-10-11 18:46:09 -0400 |
commit | 7bcf7717b6a047c272410d0cd00213185fe6b99d (patch) | |
tree | 81c5d6bbc2130815713e22bb5408ea80b6e1c499 /arch/mips/kernel | |
parent | 91a2fcc88634663e9e13dcdfad0e4a860e64aeee (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')
-rw-r--r-- | arch/mips/kernel/process.c | 3 | ||||
-rw-r--r-- | arch/mips/kernel/smp.c | 2 | ||||
-rw-r--r-- | arch/mips/kernel/smtc.c | 2 | ||||
-rw-r--r-- | arch/mips/kernel/time.c | 256 |
4 files changed, 124 insertions, 139 deletions
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c index aadd2cd5778c..f99bb4085430 100644 --- a/arch/mips/kernel/process.c +++ b/arch/mips/kernel/process.c | |||
@@ -11,6 +11,7 @@ | |||
11 | #include <linux/errno.h> | 11 | #include <linux/errno.h> |
12 | #include <linux/module.h> | 12 | #include <linux/module.h> |
13 | #include <linux/sched.h> | 13 | #include <linux/sched.h> |
14 | #include <linux/tick.h> | ||
14 | #include <linux/kernel.h> | 15 | #include <linux/kernel.h> |
15 | #include <linux/mm.h> | 16 | #include <linux/mm.h> |
16 | #include <linux/stddef.h> | 17 | #include <linux/stddef.h> |
@@ -52,6 +53,7 @@ void __noreturn cpu_idle(void) | |||
52 | { | 53 | { |
53 | /* endless idle loop with no priority at all */ | 54 | /* endless idle loop with no priority at all */ |
54 | while (1) { | 55 | while (1) { |
56 | tick_nohz_stop_sched_tick(); | ||
55 | while (!need_resched()) { | 57 | while (!need_resched()) { |
56 | #ifdef CONFIG_SMTC_IDLE_HOOK_DEBUG | 58 | #ifdef CONFIG_SMTC_IDLE_HOOK_DEBUG |
57 | extern void smtc_idle_loop_hook(void); | 59 | extern void smtc_idle_loop_hook(void); |
@@ -61,6 +63,7 @@ void __noreturn cpu_idle(void) | |||
61 | if (cpu_wait) | 63 | if (cpu_wait) |
62 | (*cpu_wait)(); | 64 | (*cpu_wait)(); |
63 | } | 65 | } |
66 | tick_nohz_restart_sched_tick(); | ||
64 | preempt_enable_no_resched(); | 67 | preempt_enable_no_resched(); |
65 | schedule(); | 68 | schedule(); |
66 | preempt_disable(); | 69 | preempt_disable(); |
diff --git a/arch/mips/kernel/smp.c b/arch/mips/kernel/smp.c index 73b0dab02668..500a7ec2880f 100644 --- a/arch/mips/kernel/smp.c +++ b/arch/mips/kernel/smp.c | |||
@@ -38,6 +38,7 @@ | |||
38 | #include <asm/system.h> | 38 | #include <asm/system.h> |
39 | #include <asm/mmu_context.h> | 39 | #include <asm/mmu_context.h> |
40 | #include <asm/smp.h> | 40 | #include <asm/smp.h> |
41 | #include <asm/time.h> | ||
41 | 42 | ||
42 | #ifdef CONFIG_MIPS_MT_SMTC | 43 | #ifdef CONFIG_MIPS_MT_SMTC |
43 | #include <asm/mipsmtregs.h> | 44 | #include <asm/mipsmtregs.h> |
@@ -70,6 +71,7 @@ asmlinkage __cpuinit void start_secondary(void) | |||
70 | cpu_probe(); | 71 | cpu_probe(); |
71 | cpu_report(); | 72 | cpu_report(); |
72 | per_cpu_trap_init(); | 73 | per_cpu_trap_init(); |
74 | mips_clockevent_init(); | ||
73 | prom_init_secondary(); | 75 | prom_init_secondary(); |
74 | 76 | ||
75 | /* | 77 | /* |
diff --git a/arch/mips/kernel/smtc.c b/arch/mips/kernel/smtc.c index a7afbf2c9710..137183bba54f 100644 --- a/arch/mips/kernel/smtc.c +++ b/arch/mips/kernel/smtc.c | |||
@@ -867,7 +867,7 @@ void ipi_decode(struct smtc_ipi *pipi) | |||
867 | #ifdef CONFIG_SMTC_IDLE_HOOK_DEBUG | 867 | #ifdef CONFIG_SMTC_IDLE_HOOK_DEBUG |
868 | clock_hang_reported[dest_copy] = 0; | 868 | clock_hang_reported[dest_copy] = 0; |
869 | #endif /* CONFIG_SMTC_IDLE_HOOK_DEBUG */ | 869 | #endif /* CONFIG_SMTC_IDLE_HOOK_DEBUG */ |
870 | local_timer_interrupt(0); | 870 | local_timer_interrupt(0, NULL); |
871 | irq_exit(); | 871 | irq_exit(); |
872 | break; | 872 | break; |
873 | case LINUX_SMP_IPI: | 873 | case LINUX_SMP_IPI: |
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 */ |
71 | static unsigned long cycles_per_jiffy __read_mostly; | 74 | static unsigned long cycles_per_jiffy __read_mostly; |
72 | 75 | ||
73 | /* expirelo is the count value for next CPU timer interrupt */ | ||
74 | static 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 | */ |
93 | static void c0_timer_ack(void) | 92 | static 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. */ | ||
118 | static void __init c0_hpt_timer_init(void) | ||
119 | { | ||
120 | expirelo = read_c0_count() + cycles_per_jiffy; | ||
121 | write_c0_compare(expirelo); | ||
122 | } | ||
123 | |||
124 | int (*mips_timer_state)(void); | 105 | int (*mips_timer_state)(void); |
125 | void (*mips_timer_ack)(void); | 106 | void (*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 | */ | ||
147 | static 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 | |||
172 | int null_perf_irq(void) | 124 | int 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 | ||
212 | void 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 | ||
302 | unsigned int mips_hpt_frequency; | 179 | unsigned int mips_hpt_frequency; |
303 | 180 | ||
304 | static struct irqaction timer_irqaction = { | ||
305 | .handler = timer_interrupt, | ||
306 | .flags = IRQF_DISABLED | IRQF_PERCPU, | ||
307 | .name = "timer", | ||
308 | }; | ||
309 | |||
310 | static unsigned int __init calibrate_hpt(void) | 181 | static 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 | ||
229 | static 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 | |||
241 | static void mips_set_mode(enum clock_event_mode mode, | ||
242 | struct clock_event_device *evt) | ||
243 | { | ||
244 | /* Nothing to do ... */ | ||
245 | } | ||
246 | |||
247 | struct clock_event_device mips_clockevent; | ||
248 | |||
249 | static struct clock_event_device *global_cd[NR_CPUS]; | ||
250 | static int cp0_timer_irq_installed; | ||
251 | |||
252 | static 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 | |||
278 | out: | ||
279 | return IRQ_HANDLED; | ||
280 | } | ||
281 | |||
282 | static struct irqaction timer_irqaction = { | ||
283 | .handler = timer_interrupt, | ||
284 | .flags = IRQF_DISABLED | IRQF_PERCPU, | ||
285 | .name = "timer", | ||
286 | }; | ||
287 | |||
358 | static void __init init_mips_clocksource(void) | 288 | static 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 | ||
315 | void __init __weak plat_timer_setup(struct irqaction *irq) | ||
316 | { | ||
317 | } | ||
318 | |||
319 | void __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 | |||
385 | void __init time_init(void) | 365 | void __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 | } |