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
6 This patch adds interrupt handling as on AR7. The old code was very messy and
9 Signed-off-by: Axel Gembe <ago@bastart.eu.org>
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(-)
16 --- a/arch/mips/bcm963xx/irq.c
17 +++ b/arch/mips/bcm963xx/irq.c
21 - Copyright 2002 Broadcom Corp. All Rights Reserved.
23 - This program is free software; you can distribute it and/or modify it
24 - under the terms of the GNU General Public License (Version 2) as
25 - published by the Free Software Foundation.
27 - This program is distributed in the hope it will be useful, but WITHOUT
28 - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
29 - FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
32 - You should have received a copy of the GNU General Public License along
33 - with this program; if not, write to the Free Software Foundation, Inc.,
34 - 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
38 - * Interrupt control functions for Broadcom 963xx MIPS boards
39 + * Copyright (C) 2006,2007 Felix Fietkau <nbd@openwrt.org>
40 + * Copyright (C) 2006,2007 Eugene Konev <ejka@openwrt.org>
41 + * Copyright (C) 2008 Axel Gembe <ago@bastart.eu.org>
43 + * This program is free software; you can redistribute it and/or modify
44 + * it under the terms of the GNU General Public License as published by
45 + * the Free Software Foundation; either version 2 of the License, or
46 + * (at your option) any later version.
48 + * This program is distributed in the hope that it will be useful,
49 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
50 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
51 + * GNU General Public License for more details.
53 + * You should have received a copy of the GNU General Public License
54 + * along with this program; if not, write to the Free Software
55 + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
58 -#include <asm/atomic.h>
60 -#include <linux/delay.h>
61 -#include <linux/init.h>
62 -#include <linux/ioport.h>
63 -#include <linux/irq.h>
64 #include <linux/interrupt.h>
65 -#include <linux/kernel.h>
66 -#include <linux/slab.h>
67 -#include <linux/module.h>
68 +#include <linux/io.h>
71 +#include <asm/irq_cpu.h>
72 #include <asm/mipsregs.h>
73 -#include <asm/addrspace.h>
74 -#include <asm/signal.h>
76 #include <6348_map_part.h>
77 #include <6348_intr.h>
78 #include <bcm_map_part.h>
81 -static void irq_dispatch_int(void)
83 - unsigned int pendingIrqs;
84 - static unsigned int irqBit;
85 - static unsigned int isrNumber = 31;
87 - pendingIrqs = PERF->IrqStatus & PERF->IrqMask;
91 +static int bcm963xx_irq_base;
96 - if (isrNumber == 32) {
100 - if (pendingIrqs & irqBit) {
101 - PERF->IrqMask &= ~irqBit; // mask
102 - do_IRQ(isrNumber + INTERNAL_ISR_TABLE_OFFSET);
106 +void bcm963xx_unmask_irq(unsigned int irq)
108 + PERF->IrqMask |= (1 << (irq - bcm963xx_irq_base));
111 -static void irq_dispatch_ext(uint32 irq)
112 +void bcm963xx_mask_irq(unsigned int irq)
114 - if (!(PERF->ExtIrqCfg & (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT)))) {
115 - printk("**** Ext IRQ mask. Should not dispatch ****\n");
117 - /* disable and clear interrupt in the controller */
118 - PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT));
119 - PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT));
121 + PERF->IrqMask &= ~(1 << (irq - bcm963xx_irq_base));
125 -//extern void brcm_timer_interrupt(struct pt_regs *regs);
127 -asmlinkage void plat_irq_dispatch(void)
128 +void bcm963xx_ack_irq(unsigned int irq)
130 - unsigned long cause;
132 - cause = read_c0_status() & read_c0_cause() & ST0_IM;
133 - if (cause & CAUSEF_IP7)
135 - else if (cause & CAUSEF_IP2)
136 - irq_dispatch_int();
137 - else if (cause & CAUSEF_IP3)
138 - irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_0);
139 - else if (cause & CAUSEF_IP4)
140 - irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_1);
141 - else if (cause & CAUSEF_IP5)
142 - irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_2);
143 - else if (cause & CAUSEF_IP6) {
144 - irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_3);
145 - local_irq_disable();
147 + PERF->IrqStatus &= ~(1 << (irq - bcm963xx_irq_base));
151 -void enable_brcm_irq(unsigned int irq)
152 +void bcm963xx_unmask_ext_irq(unsigned int irq)
154 - unsigned long flags;
156 - local_irq_save(flags);
157 - if( irq >= INTERNAL_ISR_TABLE_OFFSET ) {
158 - PERF->IrqMask |= (1 << (irq - INTERNAL_ISR_TABLE_OFFSET));
160 - else if (irq >= INTERRUPT_ID_EXTERNAL_0 && irq <= INTERRUPT_ID_EXTERNAL_3) {
161 - /* enable and clear interrupt in the controller */
162 - PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT));
163 PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT));
165 - local_irq_restore(flags);
168 -void disable_brcm_irq(unsigned int irq)
169 +void bcm963xx_mask_ext_irq(unsigned int irq)
171 - unsigned long flags;
173 - local_irq_save(flags);
174 - if( irq >= INTERNAL_ISR_TABLE_OFFSET ) {
175 - PERF->IrqMask &= ~(1 << (irq - INTERNAL_ISR_TABLE_OFFSET));
177 - else if (irq >= INTERRUPT_ID_EXTERNAL_0 && irq <= INTERRUPT_ID_EXTERNAL_3) {
178 - /* disable interrupt in the controller */
179 PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT));
181 - local_irq_restore(flags);
184 -void ack_brcm_irq(unsigned int irq)
185 +void bcm963xx_ack_ext_irq(unsigned int irq)
187 - /* Already done in brcm_irq_dispatch */
188 + PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT));
191 -unsigned int startup_brcm_irq(unsigned int irq)
192 +static void bcm963xx_dispatch_ext_irq(unsigned int irq)
194 - enable_brcm_irq(irq);
196 - return 0; /* never anything pending */
197 + bcm963xx_ack_ext_irq(irq);
198 + bcm963xx_mask_ext_irq(irq);
202 -unsigned int startup_brcm_none(unsigned int irq)
203 +static void bcm963xx_cascade(void)
207 + uint32_t pending, bit, irq;
209 -void end_brcm_irq(unsigned int irq)
211 - if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
212 - enable_brcm_irq(irq);
214 + if (!(pending = PERF->IrqStatus & PERF->IrqMask))
217 -void end_brcm_none(unsigned int irq)
220 + for (irq = 0, bit = 1; irq < 32; irq++, bit <<= 1) {
221 + if (pending & bit) {
222 + bcm963xx_ack_irq(irq + bcm963xx_irq_base);
223 + bcm963xx_mask_irq(irq + bcm963xx_irq_base);
224 + do_IRQ(irq + bcm963xx_irq_base);
229 + spurious_interrupt();
232 +static struct irq_chip bcm963xx_irq_type = {
233 + .name = "bcm963xx",
234 + .unmask = bcm963xx_unmask_irq,
235 + .mask = bcm963xx_mask_irq,
236 + .ack = bcm963xx_ack_irq
239 -static struct hw_interrupt_type brcm_irq_type = {
240 - .typename = "MIPS",
241 - .startup = startup_brcm_irq,
242 - .shutdown = disable_brcm_irq,
243 - .enable = enable_brcm_irq,
244 - .disable = disable_brcm_irq,
245 - .ack = ack_brcm_irq,
246 - .end = end_brcm_irq,
247 - .set_affinity = NULL
248 +static struct irq_chip bcm963xx_ext_irq_type = {
249 + .name = "bcm963xx_ext",
250 + .unmask = bcm963xx_unmask_ext_irq,
251 + .mask = bcm963xx_mask_ext_irq,
252 + .ack = bcm963xx_ack_ext_irq,
255 -static struct hw_interrupt_type brcm_irq_no_end_type = {
256 - .typename = "MIPS",
257 - .startup = startup_brcm_none,
258 - .shutdown = disable_brcm_irq,
259 - .enable = enable_brcm_irq,
260 - .disable = disable_brcm_irq,
261 - .ack = ack_brcm_irq,
262 - .end = end_brcm_none,
263 - .set_affinity = NULL
264 +static struct irqaction bcm963xx_cascade_action = {
265 + .handler = no_action,
266 + .name = "BCM963xx cascade interrupt"
269 -void __init arch_init_irq(void)
270 +static void __init bcm963xx_irq_init(int base)
274 - clear_c0_status(ST0_BEV);
275 - change_c0_status(ST0_IM, (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4));
276 + bcm963xx_irq_base = base;
278 - for (i = 0; i < NR_IRQS; i++) {
279 - irq_desc[i].status = IRQ_DISABLED;
280 - irq_desc[i].action = 0;
281 - irq_desc[i].depth = 1;
282 - irq_desc[i].chip = &brcm_irq_type;
284 + /* External IRQs */
285 + set_irq_chip_and_handler(INTERRUPT_ID_EXTERNAL_0, &bcm963xx_ext_irq_type,
287 + set_irq_chip_and_handler(INTERRUPT_ID_EXTERNAL_1, &bcm963xx_ext_irq_type,
289 + set_irq_chip_and_handler(INTERRUPT_ID_EXTERNAL_2, &bcm963xx_ext_irq_type,
291 + set_irq_chip_and_handler(INTERRUPT_ID_EXTERNAL_3, &bcm963xx_ext_irq_type,
294 + for (i = 0; i < 32; i++) {
295 + set_irq_chip_and_handler(base + i, &bcm963xx_irq_type,
299 + setup_irq(2, &bcm963xx_cascade_action);
300 + setup_irq(bcm963xx_irq_base, &bcm963xx_cascade_action);
301 + set_c0_status(IE_IRQ0);
304 -int request_external_irq(unsigned int irq,
305 - FN_HANDLER handler,
306 - unsigned long irqflags,
307 - const char * devname,
309 +asmlinkage void plat_irq_dispatch(void)
311 - unsigned long flags;
313 - local_irq_save(flags);
314 + unsigned int pending = read_c0_status() & read_c0_cause() & ST0_IM;
316 - PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT)); // Clear
317 - PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT)); // Mask
318 - PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_INSENS_SHFT)); // Edge insesnsitive
319 - PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_LEVEL_SHFT)); // Level triggered
320 - PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_SENSE_SHFT)); // Low level
322 - local_irq_restore(flags);
324 - return( request_irq(irq, handler, irqflags, devname, dev_id) );
325 + if (pending & STATUSF_IP7) /* cpu timer */
327 + else if (pending & STATUSF_IP2) /* internal interrupt cascade */
328 + bcm963xx_cascade();
329 + else if (pending & STATUSF_IP3)
330 + bcm963xx_dispatch_ext_irq(INTERRUPT_ID_EXTERNAL_0);
331 + else if (pending & STATUSF_IP4)
332 + bcm963xx_dispatch_ext_irq(INTERRUPT_ID_EXTERNAL_1);
333 + else if (pending & STATUSF_IP5)
334 + bcm963xx_dispatch_ext_irq(INTERRUPT_ID_EXTERNAL_2);
335 + else if (pending & STATUSF_IP6)
336 + bcm963xx_dispatch_ext_irq(INTERRUPT_ID_EXTERNAL_3);
338 + spurious_interrupt();
341 -/* VxWorks compatibility function(s). */
343 -unsigned int BcmHalMapInterrupt(FN_HANDLER pfunc, unsigned int param,
344 - unsigned int interruptId)
345 +void __init arch_init_irq(void)
350 - devname = kmalloc(16, GFP_KERNEL);
352 - sprintf( devname, "brcm_%d", interruptId );
354 - /* Set the IRQ description to not automatically enable the interrupt at
355 - * the end of an ISR. The driver that handles the interrupt must
356 - * explicitly call BcmHalInterruptEnable or enable_brcm_irq. This behavior
357 - * is consistent with interrupt handling on VxWorks.
359 - irq_desc[interruptId].chip = &brcm_irq_no_end_type;
361 - if( interruptId >= INTERNAL_ISR_TABLE_OFFSET )
363 - printk("BcmHalMapInterrupt : internal IRQ\n");
364 - nRet = request_irq( interruptId, pfunc, IRQF_DISABLED, devname, (void *) param );
366 - else if (interruptId >= INTERRUPT_ID_EXTERNAL_0 && interruptId <= INTERRUPT_ID_EXTERNAL_3)
368 - printk("BcmHalMapInterrupt : external IRQ\n");
369 - nRet = request_external_irq( interruptId, pfunc, IRQF_DISABLED, devname, (void *) param );
373 + mips_cpu_irq_init();
374 + bcm963xx_irq_init(INTERNAL_ISR_TABLE_OFFSET);
378 -EXPORT_SYMBOL(enable_brcm_irq);
379 -EXPORT_SYMBOL(disable_brcm_irq);
380 -EXPORT_SYMBOL(request_external_irq);
381 -EXPORT_SYMBOL(BcmHalMapInterrupt);
383 --- a/drivers/serial/bcm63xx_cons.c
384 +++ b/drivers/serial/bcm63xx_cons.c
388 // Clear the interrupt
389 - enable_brcm_irq(INTERRUPT_ID_UART);
390 +// bcm963xx_unmask_irq(INTERRUPT_ID_UART);
391 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
396 tty->driver_data = info;
398 - enable_brcm_irq(INTERRUPT_ID_UART);
399 + bcm963xx_unmask_irq(INTERRUPT_ID_UART);
401 // Start up serial port
402 retval = startup(info);
404 -------------------------------------------------------------------------- */
405 static int __init bcm63xx_serialinit(void)
409 struct bcm_serial *info;
411 // Print the driver version information
416 - BcmHalMapInterrupt(bcm_interrupt, 0, INTERRUPT_ID_UART);
418 + res = request_irq(INTERRUPT_ID_UART, bcm_interrupt, 0, "bcm-uart", NULL);
420 + spin_unlock_irqrestore(&bcm963xx_serial_lock, flags);
425 /* order matters here... the trick is that flags
426 --- a/include/asm-mips/mach-bcm963xx/bcm_intr.h
427 +++ b/include/asm-mips/mach-bcm963xx/bcm_intr.h
429 typedef int (*FN_HANDLER) (int, void *);
432 -extern void enable_brcm_irq(unsigned int irq);
433 -extern void disable_brcm_irq(unsigned int irq);
434 -extern int request_external_irq(unsigned int irq,
435 - FN_HANDLER handler, unsigned long irqflags,
436 - const char * devname, void *dev_id);
437 -extern unsigned int BcmHalMapInterrupt(FN_HANDLER isr, unsigned int param,
438 - unsigned int interruptId);
439 -extern void dump_intr_regs(void);
441 -/* compatibility definitions */
442 -#define BcmHalInterruptEnable(irq) enable_brcm_irq( irq )
443 -#define BcmHalInterruptDisable(irq) disable_brcm_irq( irq )
444 +extern void bcm963xx_unmask_irq(unsigned int irq);
445 +extern void bcm963xx_mask_irq(unsigned int irq);
446 +extern void bcm963xx_ack_irq(unsigned int irq);
447 +extern void bcm963xx_unmask_ext_irq(unsigned int irq);
448 +extern void bcm963xx_mask_ext_irq(unsigned int irq);
449 +extern void bcm963xx_ack_ext_irq(unsigned int irq);