e3cff528c5ea96d109a3187e017e1840cccdf32c
[openwrt/staging/dedeckeh.git] / target / linux / brcm63xx / patches-2.6.25 / 060-bcm963xx_rewrite_irq_handling_code.patch
1 From 9a70f2dcb24a5aab29386373c86ba035acba4891 Mon Sep 17 00:00:00 2001
2 From: Axel Gembe <ago@bastart.eu.org>
3 Date: Sun, 18 May 2008 12:07:21 +0200
4 Subject: [PATCH] bcm963xx: rewrite irq handling code
5
6 This patch adds interrupt handling as on AR7. The old code was very messy and
7 didn't work too well.
8
9 Signed-off-by: Axel Gembe <ago@bastart.eu.org>
10 ---
11 arch/mips/bcm963xx/irq.c | 308 ++++++++++-------------------
12 drivers/serial/bcm63xx_cons.c | 13 +-
13 include/asm-mips/mach-bcm963xx/bcm_intr.h | 18 +--
14 3 files changed, 119 insertions(+), 220 deletions(-)
15
16 Index: linux-2.6.25.4/arch/mips/bcm963xx/irq.c
17 ===================================================================
18 --- linux-2.6.25.4.orig/arch/mips/bcm963xx/irq.c
19 +++ linux-2.6.25.4/arch/mips/bcm963xx/irq.c
20 @@ -1,259 +1,159 @@
21 /*
22 -<:copyright-gpl
23 - Copyright 2002 Broadcom Corp. All Rights Reserved.
24 -
25 - This program is free software; you can distribute it and/or modify it
26 - under the terms of the GNU General Public License (Version 2) as
27 - published by the Free Software Foundation.
28 -
29 - This program is distributed in the hope it will be useful, but WITHOUT
30 - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
31 - FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
32 - for more details.
33 -
34 - You should have received a copy of the GNU General Public License along
35 - with this program; if not, write to the Free Software Foundation, Inc.,
36 - 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
37 -:>
38 -*/
39 -/*
40 - * Interrupt control functions for Broadcom 963xx MIPS boards
41 + * Copyright (C) 2006,2007 Felix Fietkau <nbd@openwrt.org>
42 + * Copyright (C) 2006,2007 Eugene Konev <ejka@openwrt.org>
43 + * Copyright (C) 2008 Axel Gembe <ago@bastart.eu.org>
44 + *
45 + * This program is free software; you can redistribute it and/or modify
46 + * it under the terms of the GNU General Public License as published by
47 + * the Free Software Foundation; either version 2 of the License, or
48 + * (at your option) any later version.
49 + *
50 + * This program is distributed in the hope that it will be useful,
51 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
52 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
53 + * GNU General Public License for more details.
54 + *
55 + * You should have received a copy of the GNU General Public License
56 + * along with this program; if not, write to the Free Software
57 + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
58 */
59
60 -#include <asm/atomic.h>
61 -
62 -#include <linux/delay.h>
63 -#include <linux/init.h>
64 -#include <linux/ioport.h>
65 -#include <linux/irq.h>
66 #include <linux/interrupt.h>
67 -#include <linux/kernel.h>
68 -#include <linux/slab.h>
69 -#include <linux/module.h>
70 +#include <linux/io.h>
71
72 -#include <asm/irq.h>
73 +#include <asm/irq_cpu.h>
74 #include <asm/mipsregs.h>
75 -#include <asm/addrspace.h>
76 -#include <asm/signal.h>
77 +
78 #include <6348_map_part.h>
79 #include <6348_intr.h>
80 #include <bcm_map_part.h>
81 #include <bcm_intr.h>
82
83 -static void irq_dispatch_int(void)
84 -{
85 - unsigned int pendingIrqs;
86 - static unsigned int irqBit;
87 - static unsigned int isrNumber = 31;
88 -
89 - pendingIrqs = PERF->IrqStatus & PERF->IrqMask;
90 - if (!pendingIrqs) {
91 - return;
92 - }
93 +static int bcm963xx_irq_base;
94
95 - while (1) {
96 - irqBit <<= 1;
97 - isrNumber++;
98 - if (isrNumber == 32) {
99 - isrNumber = 0;
100 - irqBit = 0x1;
101 - }
102 - if (pendingIrqs & irqBit) {
103 - PERF->IrqMask &= ~irqBit; // mask
104 - do_IRQ(isrNumber + INTERNAL_ISR_TABLE_OFFSET);
105 - break;
106 - }
107 - }
108 +void bcm963xx_unmask_irq(unsigned int irq)
109 +{
110 + PERF->IrqMask |= (1 << (irq - bcm963xx_irq_base));
111 }
112
113 -static void irq_dispatch_ext(uint32 irq)
114 +void bcm963xx_mask_irq(unsigned int irq)
115 {
116 - if (!(PERF->ExtIrqCfg & (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT)))) {
117 - printk("**** Ext IRQ mask. Should not dispatch ****\n");
118 - }
119 - /* disable and clear interrupt in the controller */
120 - PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT));
121 - PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT));
122 - do_IRQ(irq);
123 + PERF->IrqMask &= ~(1 << (irq - bcm963xx_irq_base));
124 }
125
126 -
127 -//extern void brcm_timer_interrupt(struct pt_regs *regs);
128 -
129 -asmlinkage void plat_irq_dispatch(void)
130 +void bcm963xx_ack_irq(unsigned int irq)
131 {
132 - unsigned long cause;
133 -
134 - cause = read_c0_status() & read_c0_cause() & ST0_IM;
135 - if (cause & CAUSEF_IP7)
136 - do_IRQ(7);
137 - else if (cause & CAUSEF_IP2)
138 - irq_dispatch_int();
139 - else if (cause & CAUSEF_IP3)
140 - irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_0);
141 - else if (cause & CAUSEF_IP4)
142 - irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_1);
143 - else if (cause & CAUSEF_IP5)
144 - irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_2);
145 - else if (cause & CAUSEF_IP6) {
146 - irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_3);
147 - local_irq_disable();
148 - }
149 + PERF->IrqStatus &= ~(1 << (irq - bcm963xx_irq_base));
150 }
151
152 -
153 -void enable_brcm_irq(unsigned int irq)
154 +void bcm963xx_unmask_ext_irq(unsigned int irq)
155 {
156 - unsigned long flags;
157 -
158 - local_irq_save(flags);
159 - if( irq >= INTERNAL_ISR_TABLE_OFFSET ) {
160 - PERF->IrqMask |= (1 << (irq - INTERNAL_ISR_TABLE_OFFSET));
161 - }
162 - else if (irq >= INTERRUPT_ID_EXTERNAL_0 && irq <= INTERRUPT_ID_EXTERNAL_3) {
163 - /* enable and clear interrupt in the controller */
164 - PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT));
165 PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT));
166 - }
167 - local_irq_restore(flags);
168 }
169
170 -void disable_brcm_irq(unsigned int irq)
171 +void bcm963xx_mask_ext_irq(unsigned int irq)
172 {
173 - unsigned long flags;
174 -
175 - local_irq_save(flags);
176 - if( irq >= INTERNAL_ISR_TABLE_OFFSET ) {
177 - PERF->IrqMask &= ~(1 << (irq - INTERNAL_ISR_TABLE_OFFSET));
178 - }
179 - else if (irq >= INTERRUPT_ID_EXTERNAL_0 && irq <= INTERRUPT_ID_EXTERNAL_3) {
180 - /* disable interrupt in the controller */
181 PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT));
182 - }
183 - local_irq_restore(flags);
184 }
185
186 -void ack_brcm_irq(unsigned int irq)
187 +void bcm963xx_ack_ext_irq(unsigned int irq)
188 {
189 - /* Already done in brcm_irq_dispatch */
190 + PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT));
191 }
192
193 -unsigned int startup_brcm_irq(unsigned int irq)
194 +static void bcm963xx_dispatch_ext_irq(unsigned int irq)
195 {
196 - enable_brcm_irq(irq);
197 -
198 - return 0; /* never anything pending */
199 + bcm963xx_ack_ext_irq(irq);
200 + bcm963xx_mask_ext_irq(irq);
201 + do_IRQ(irq);
202 }
203
204 -unsigned int startup_brcm_none(unsigned int irq)
205 +static void bcm963xx_cascade(void)
206 {
207 - return 0;
208 -}
209 + uint32_t pending, bit, irq;
210
211 -void end_brcm_irq(unsigned int irq)
212 -{
213 - if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
214 - enable_brcm_irq(irq);
215 -}
216 + if (!(pending = PERF->IrqStatus & PERF->IrqMask))
217 + return;
218
219 -void end_brcm_none(unsigned int irq)
220 -{
221 -}
222 + for (irq = 0, bit = 1; irq < 32; irq++, bit <<= 1) {
223 + if (pending & bit) {
224 + bcm963xx_ack_irq(irq + bcm963xx_irq_base);
225 + bcm963xx_mask_irq(irq + bcm963xx_irq_base);
226 + do_IRQ(irq + bcm963xx_irq_base);
227 + return;
228 + }
229 + }
230 +
231 + spurious_interrupt();
232 +}
233 +
234 +static struct irq_chip bcm963xx_irq_type = {
235 + .name = "bcm963xx",
236 + .unmask = bcm963xx_unmask_irq,
237 + .mask = bcm963xx_mask_irq,
238 + .ack = bcm963xx_ack_irq
239 +};
240
241 -static struct hw_interrupt_type brcm_irq_type = {
242 - .typename = "MIPS",
243 - .startup = startup_brcm_irq,
244 - .shutdown = disable_brcm_irq,
245 - .enable = enable_brcm_irq,
246 - .disable = disable_brcm_irq,
247 - .ack = ack_brcm_irq,
248 - .end = end_brcm_irq,
249 - .set_affinity = NULL
250 +static struct irq_chip bcm963xx_ext_irq_type = {
251 + .name = "bcm963xx_ext",
252 + .unmask = bcm963xx_unmask_ext_irq,
253 + .mask = bcm963xx_mask_ext_irq,
254 + .ack = bcm963xx_ack_ext_irq,
255 };
256
257 -static struct hw_interrupt_type brcm_irq_no_end_type = {
258 - .typename = "MIPS",
259 - .startup = startup_brcm_none,
260 - .shutdown = disable_brcm_irq,
261 - .enable = enable_brcm_irq,
262 - .disable = disable_brcm_irq,
263 - .ack = ack_brcm_irq,
264 - .end = end_brcm_none,
265 - .set_affinity = NULL
266 +static struct irqaction bcm963xx_cascade_action = {
267 + .handler = no_action,
268 + .name = "BCM963xx cascade interrupt"
269 };
270
271 -void __init arch_init_irq(void)
272 +static void __init bcm963xx_irq_init(int base)
273 {
274 int i;
275
276 - clear_c0_status(ST0_BEV);
277 - change_c0_status(ST0_IM, (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4));
278 + bcm963xx_irq_base = base;
279
280 - for (i = 0; i < NR_IRQS; i++) {
281 - irq_desc[i].status = IRQ_DISABLED;
282 - irq_desc[i].action = 0;
283 - irq_desc[i].depth = 1;
284 - irq_desc[i].chip = &brcm_irq_type;
285 - }
286 + /* External IRQs */
287 + set_irq_chip_and_handler(INTERRUPT_ID_EXTERNAL_0, &bcm963xx_ext_irq_type,
288 + handle_level_irq);
289 + set_irq_chip_and_handler(INTERRUPT_ID_EXTERNAL_1, &bcm963xx_ext_irq_type,
290 + handle_level_irq);
291 + set_irq_chip_and_handler(INTERRUPT_ID_EXTERNAL_2, &bcm963xx_ext_irq_type,
292 + handle_level_irq);
293 + set_irq_chip_and_handler(INTERRUPT_ID_EXTERNAL_3, &bcm963xx_ext_irq_type,
294 + handle_level_irq);
295 +
296 + for (i = 0; i < 32; i++) {
297 + set_irq_chip_and_handler(base + i, &bcm963xx_irq_type,
298 + handle_level_irq);
299 + }
300 +
301 + setup_irq(2, &bcm963xx_cascade_action);
302 + setup_irq(bcm963xx_irq_base, &bcm963xx_cascade_action);
303 + set_c0_status(IE_IRQ0);
304 }
305
306 -int request_external_irq(unsigned int irq,
307 - FN_HANDLER handler,
308 - unsigned long irqflags,
309 - const char * devname,
310 - void *dev_id)
311 +asmlinkage void plat_irq_dispatch(void)
312 {
313 - unsigned long flags;
314 -
315 - local_irq_save(flags);
316 + unsigned int pending = read_c0_status() & read_c0_cause() & ST0_IM;
317
318 - PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT)); // Clear
319 - PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT)); // Mask
320 - PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_INSENS_SHFT)); // Edge insesnsitive
321 - PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_LEVEL_SHFT)); // Level triggered
322 - PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_SENSE_SHFT)); // Low level
323 -
324 - local_irq_restore(flags);
325 -
326 - return( request_irq(irq, handler, irqflags, devname, dev_id) );
327 + if (pending & STATUSF_IP7) /* cpu timer */
328 + do_IRQ(7);
329 + else if (pending & STATUSF_IP2) /* internal interrupt cascade */
330 + bcm963xx_cascade();
331 + else if (pending & STATUSF_IP3)
332 + bcm963xx_dispatch_ext_irq(INTERRUPT_ID_EXTERNAL_0);
333 + else if (pending & STATUSF_IP4)
334 + bcm963xx_dispatch_ext_irq(INTERRUPT_ID_EXTERNAL_1);
335 + else if (pending & STATUSF_IP5)
336 + bcm963xx_dispatch_ext_irq(INTERRUPT_ID_EXTERNAL_2);
337 + else if (pending & STATUSF_IP6)
338 + bcm963xx_dispatch_ext_irq(INTERRUPT_ID_EXTERNAL_3);
339 + else
340 + spurious_interrupt();
341 }
342
343 -/* VxWorks compatibility function(s). */
344 -
345 -unsigned int BcmHalMapInterrupt(FN_HANDLER pfunc, unsigned int param,
346 - unsigned int interruptId)
347 +void __init arch_init_irq(void)
348 {
349 - int nRet = -1;
350 - char *devname;
351 -
352 - devname = kmalloc(16, GFP_KERNEL);
353 - if (devname)
354 - sprintf( devname, "brcm_%d", interruptId );
355 -
356 - /* Set the IRQ description to not automatically enable the interrupt at
357 - * the end of an ISR. The driver that handles the interrupt must
358 - * explicitly call BcmHalInterruptEnable or enable_brcm_irq. This behavior
359 - * is consistent with interrupt handling on VxWorks.
360 - */
361 - irq_desc[interruptId].chip = &brcm_irq_no_end_type;
362 -
363 - if( interruptId >= INTERNAL_ISR_TABLE_OFFSET )
364 - {
365 - printk("BcmHalMapInterrupt : internal IRQ\n");
366 - nRet = request_irq( interruptId, pfunc, IRQF_DISABLED, devname, (void *) param );
367 - }
368 - else if (interruptId >= INTERRUPT_ID_EXTERNAL_0 && interruptId <= INTERRUPT_ID_EXTERNAL_3)
369 - {
370 - printk("BcmHalMapInterrupt : external IRQ\n");
371 - nRet = request_external_irq( interruptId, pfunc, IRQF_DISABLED, devname, (void *) param );
372 - }
373 -
374 - return( nRet );
375 + mips_cpu_irq_init();
376 + bcm963xx_irq_init(INTERNAL_ISR_TABLE_OFFSET);
377 }
378 -
379 -
380 -EXPORT_SYMBOL(enable_brcm_irq);
381 -EXPORT_SYMBOL(disable_brcm_irq);
382 -EXPORT_SYMBOL(request_external_irq);
383 -EXPORT_SYMBOL(BcmHalMapInterrupt);
384 -
385 Index: linux-2.6.25.4/drivers/serial/bcm63xx_cons.c
386 ===================================================================
387 --- linux-2.6.25.4.orig/drivers/serial/bcm63xx_cons.c
388 +++ linux-2.6.25.4/drivers/serial/bcm63xx_cons.c
389 @@ -267,7 +267,7 @@ static void bcm_interrupt(int irq, void
390 }
391
392 // Clear the interrupt
393 - enable_brcm_irq(INTERRUPT_ID_UART);
394 +// bcm963xx_unmask_irq(INTERRUPT_ID_UART);
395 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
396 return IRQ_HANDLED;
397 #endif
398 @@ -880,7 +880,7 @@ static int bcm63xx_cons_open(struct tty_
399 info->count++;
400 tty->driver_data = info;
401 info->tty = tty;
402 - enable_brcm_irq(INTERRUPT_ID_UART);
403 + bcm963xx_unmask_irq(INTERRUPT_ID_UART);
404
405 // Start up serial port
406 retval = startup(info);
407 @@ -927,7 +927,7 @@ static struct tty_operations rs_ops = {
408 -------------------------------------------------------------------------- */
409 static int __init bcm63xx_serialinit(void)
410 {
411 - int i, flags;
412 + int i, flags, res;
413 struct bcm_serial *info;
414
415 // Print the driver version information
416 @@ -981,7 +981,12 @@ static int __init bcm63xx_serialinit(voi
417 */
418 if (!info->port)
419 return 0;
420 - BcmHalMapInterrupt(bcm_interrupt, 0, INTERRUPT_ID_UART);
421 +
422 + res = request_irq(INTERRUPT_ID_UART, bcm_interrupt, 0, "bcm-uart", NULL);
423 + if (res) {
424 + spin_unlock_irqrestore(&bcm963xx_serial_lock, flags);
425 + return res;
426 + }
427 }
428
429 /* order matters here... the trick is that flags
430 Index: linux-2.6.25.4/include/asm-mips/mach-bcm963xx/bcm_intr.h
431 ===================================================================
432 --- linux-2.6.25.4.orig/include/asm-mips/mach-bcm963xx/bcm_intr.h
433 +++ linux-2.6.25.4/include/asm-mips/mach-bcm963xx/bcm_intr.h
434 @@ -39,18 +39,12 @@ struct pt_regs;
435 typedef int (*FN_HANDLER) (int, void *);
436
437 /* prototypes */
438 -extern void enable_brcm_irq(unsigned int irq);
439 -extern void disable_brcm_irq(unsigned int irq);
440 -extern int request_external_irq(unsigned int irq,
441 - FN_HANDLER handler, unsigned long irqflags,
442 - const char * devname, void *dev_id);
443 -extern unsigned int BcmHalMapInterrupt(FN_HANDLER isr, unsigned int param,
444 - unsigned int interruptId);
445 -extern void dump_intr_regs(void);
446 -
447 -/* compatibility definitions */
448 -#define BcmHalInterruptEnable(irq) enable_brcm_irq( irq )
449 -#define BcmHalInterruptDisable(irq) disable_brcm_irq( irq )
450 +extern void bcm963xx_unmask_irq(unsigned int irq);
451 +extern void bcm963xx_mask_irq(unsigned int irq);
452 +extern void bcm963xx_ack_irq(unsigned int irq);
453 +extern void bcm963xx_unmask_ext_irq(unsigned int irq);
454 +extern void bcm963xx_mask_ext_irq(unsigned int irq);
455 +extern void bcm963xx_ack_ext_irq(unsigned int irq);
456
457 #ifdef __cplusplus
458 }