aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips/mips-boards/generic
diff options
context:
space:
mode:
authorChris Dearman <chris@mips.com>2007-05-24 17:24:20 -0400
committerRalf Baechle <ralf@linux-mips.org>2007-06-14 13:25:15 -0400
commitffe9ee4709cf513fb80e9b7e04d214dd8b76a10d (patch)
tree07453e5644806b9c755159e5a4c1fe11dacfcab0 /arch/mips/mips-boards/generic
parentb72c05262298cc2ac92edb657f5ea3a97ad5ea3d (diff)
[MIPS] Separate performance counter interrupts
Support for performance counter overflow interrupt that is on a separate interrupt from the timer. Signed-off-by: Chris Dearman <chris@mips.com> Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips/mips-boards/generic')
-rw-r--r--arch/mips/mips-boards/generic/time.c118
1 files changed, 93 insertions, 25 deletions
diff --git a/arch/mips/mips-boards/generic/time.c b/arch/mips/mips-boards/generic/time.c
index b41db9e7ab1f..33432ea188fb 100644
--- a/arch/mips/mips-boards/generic/time.c
+++ b/arch/mips/mips-boards/generic/time.c
@@ -53,9 +53,8 @@
53 53
54unsigned long cpu_khz; 54unsigned long cpu_khz;
55 55
56#define CPUCTR_IMASKBIT (0x100 << MIPSCPU_INT_CPUCTR)
57
58static int mips_cpu_timer_irq; 56static int mips_cpu_timer_irq;
57extern int mipsxx_perfcount_irq;
59extern void smtc_timer_broadcast(int); 58extern void smtc_timer_broadcast(int);
60 59
61static void mips_timer_dispatch(void) 60static void mips_timer_dispatch(void)
@@ -63,6 +62,11 @@ static void mips_timer_dispatch(void)
63 do_IRQ(mips_cpu_timer_irq); 62 do_IRQ(mips_cpu_timer_irq);
64} 63}
65 64
65static void mips_perf_dispatch(void)
66{
67 do_IRQ(mipsxx_perfcount_irq);
68}
69
66/* 70/*
67 * Redeclare until I get around mopping the timer code insanity on MIPS. 71 * Redeclare until I get around mopping the timer code insanity on MIPS.
68 */ 72 */
@@ -70,6 +74,24 @@ extern int null_perf_irq(void);
70 74
71extern int (*perf_irq)(void); 75extern int (*perf_irq)(void);
72 76
77/*
78 * Possibly handle a performance counter interrupt.
79 * Return true if the timer interrupt should not be checked
80 */
81static inline int handle_perf_irq (int r2)
82{
83 /*
84 * The performance counter overflow interrupt may be shared with the
85 * timer interrupt (mipsxx_perfcount_irq < 0). If it is and a
86 * performance counter has overflowed (perf_irq() == IRQ_HANDLED)
87 * and we can't reliably determine if a counter interrupt has also
88 * happened (!r2) then don't check for a timer interrupt.
89 */
90 return (mipsxx_perfcount_irq < 0) &&
91 perf_irq() == IRQ_HANDLED &&
92 !r2;
93}
94
73irqreturn_t mips_timer_interrupt(int irq, void *dev_id) 95irqreturn_t mips_timer_interrupt(int irq, void *dev_id)
74{ 96{
75 int cpu = smp_processor_id(); 97 int cpu = smp_processor_id();
@@ -92,8 +114,7 @@ irqreturn_t mips_timer_interrupt(int irq, void *dev_id)
92 * We could be here due to timer interrupt, 114 * We could be here due to timer interrupt,
93 * perf counter overflow, or both. 115 * perf counter overflow, or both.
94 */ 116 */
95 if (read_c0_cause() & (1 << 26)) 117 (void) handle_perf_irq(1);
96 perf_irq();
97 118
98 if (read_c0_cause() & (1 << 30)) { 119 if (read_c0_cause() & (1 << 30)) {
99 /* 120 /*
@@ -115,19 +136,19 @@ irqreturn_t mips_timer_interrupt(int irq, void *dev_id)
115#else /* CONFIG_MIPS_MT_SMTC */ 136#else /* CONFIG_MIPS_MT_SMTC */
116 int r2 = cpu_has_mips_r2; 137 int r2 = cpu_has_mips_r2;
117 138
139 if (handle_perf_irq(r2))
140 goto out;
141
142 if (r2 && ((read_c0_cause() & (1 << 30)) == 0))
143 goto out;
144
118 if (cpu == 0) { 145 if (cpu == 0) {
119 /* 146 /*
120 * CPU 0 handles the global timer interrupt job and process 147 * CPU 0 handles the global timer interrupt job and process
121 * accounting resets count/compare registers to trigger next 148 * accounting resets count/compare registers to trigger next
122 * timer int. 149 * timer int.
123 */ 150 */
124 if (!r2 || (read_c0_cause() & (1 << 26))) 151 timer_interrupt(irq, NULL);
125 if (perf_irq())
126 goto out;
127
128 /* we keep interrupt disabled all the time */
129 if (!r2 || (read_c0_cause() & (1 << 30)))
130 timer_interrupt(irq, NULL);
131 } else { 152 } else {
132 /* Everyone else needs to reset the timer int here as 153 /* Everyone else needs to reset the timer int here as
133 ll_local_timer_interrupt doesn't */ 154 ll_local_timer_interrupt doesn't */
@@ -225,35 +246,82 @@ void __init mips_time_init(void)
225 mips_scroll_message(); 246 mips_scroll_message();
226} 247}
227 248
228void __init plat_timer_setup(struct irqaction *irq) 249irqreturn_t mips_perf_interrupt(int irq, void *dev_id)
229{ 250{
251 return perf_irq();
252}
253
254static struct irqaction perf_irqaction = {
255 .handler = mips_perf_interrupt,
256 .flags = IRQF_DISABLED | IRQF_PERCPU,
257 .name = "performance",
258};
259
260void __init plat_perf_setup(struct irqaction *irq)
261{
262 int hwint = 0;
263 mipsxx_perfcount_irq = -1;
264
230#ifdef MSC01E_INT_BASE 265#ifdef MSC01E_INT_BASE
231 if (cpu_has_veic) { 266 if (cpu_has_veic) {
232 set_vi_handler (MSC01E_INT_CPUCTR, mips_timer_dispatch); 267 set_vi_handler (MSC01E_INT_PERFCTR, mips_perf_dispatch);
233 mips_cpu_timer_irq = MSC01E_INT_BASE + MSC01E_INT_CPUCTR; 268 mipsxx_perfcount_irq = MSC01E_INT_BASE + MSC01E_INT_PERFCTR;
234 } else 269 } else
235#endif 270#endif
236 { 271 if (cpu_has_mips_r2) {
237 if (cpu_has_vint) 272 /*
238 set_vi_handler (MIPSCPU_INT_CPUCTR, mips_timer_dispatch); 273 * Read IntCtl.IPPCI to determine the performance
239 mips_cpu_timer_irq = MIPSCPU_INT_BASE + MIPSCPU_INT_CPUCTR; 274 * counter interrupt
275 */
276 hwint = (read_c0_intctl () >> 26) & 7;
277 if (hwint != MIPSCPU_INT_CPUCTR) {
278 if (cpu_has_vint)
279 set_vi_handler (hwint, mips_perf_dispatch);
280 mipsxx_perfcount_irq = MIPSCPU_INT_BASE + hwint;
281 }
282 }
283 if (mipsxx_perfcount_irq >= 0) {
284#ifdef CONFIG_MIPS_MT_SMTC
285 setup_irq_smtc(mipsxx_perfcount_irq, irq, 0x100 << hwint);
286#else
287 setup_irq(mipsxx_perfcount_irq, irq);
288#endif /* CONFIG_MIPS_MT_SMTC */
289#ifdef CONFIG_SMP
290 set_irq_handler(mipsxx_perfcount_irq, handle_percpu_irq);
291#endif
240 } 292 }
293}
241 294
295void __init plat_timer_setup(struct irqaction *irq)
296{
297 int hwint = 0;
298 if (cpu_has_veic) {
299 set_vi_handler (MSC01E_INT_CPUCTR, mips_timer_dispatch);
300 mips_cpu_timer_irq = MSC01E_INT_BASE + MSC01E_INT_CPUCTR;
301 }
302 else {
303 if (cpu_has_mips_r2)
304 /*
305 * Read IntCtl.IPTI to determine the timer interrupt
306 */
307 hwint = (read_c0_intctl () >> 29) & 7;
308 else
309 hwint = MIPSCPU_INT_CPUCTR;
310 if (cpu_has_vint)
311 set_vi_handler (hwint, mips_timer_dispatch);
312 mips_cpu_timer_irq = MIPSCPU_INT_BASE + hwint;
313 }
242 314
243 /* we are using the cpu counter for timer interrupts */ 315 /* we are using the cpu counter for timer interrupts */
244 irq->handler = mips_timer_interrupt; /* we use our own handler */ 316 irq->handler = mips_timer_interrupt; /* we use our own handler */
245#ifdef CONFIG_MIPS_MT_SMTC 317#ifdef CONFIG_MIPS_MT_SMTC
246 setup_irq_smtc(mips_cpu_timer_irq, irq, CPUCTR_IMASKBIT); 318 setup_irq_smtc(mips_cpu_timer_irq, irq, 0x100 << hwint);
247#else 319#else
248 setup_irq(mips_cpu_timer_irq, irq); 320 setup_irq(mips_cpu_timer_irq, irq);
249#endif /* CONFIG_MIPS_MT_SMTC */ 321#endif /* CONFIG_MIPS_MT_SMTC */
250
251#ifdef CONFIG_SMP 322#ifdef CONFIG_SMP
252 /* irq_desc(riptor) is a global resource, when the interrupt overlaps
253 on seperate cpu's the first one tries to handle the second interrupt.
254 The effect is that the int remains disabled on the second cpu.
255 Mark the interrupt with IRQ_PER_CPU to avoid any confusion */
256 irq_desc[mips_cpu_timer_irq].status |= IRQ_PER_CPU;
257 set_irq_handler(mips_cpu_timer_irq, handle_percpu_irq); 323 set_irq_handler(mips_cpu_timer_irq, handle_percpu_irq);
258#endif 324#endif
325
326 plat_perf_setup(&perf_irqaction);
259} 327}