mirror of https://github.com/hak5/openwrt.git
459 lines
14 KiB
Diff
459 lines
14 KiB
Diff
From 9a70f2dcb24a5aab29386373c86ba035acba4891 Mon Sep 17 00:00:00 2001
|
|
From: Axel Gembe <ago@bastart.eu.org>
|
|
Date: Sun, 18 May 2008 12:07:21 +0200
|
|
Subject: [PATCH] bcm963xx: rewrite irq handling code
|
|
|
|
This patch adds interrupt handling as on AR7. The old code was very messy and
|
|
didn't work too well.
|
|
|
|
Signed-off-by: Axel Gembe <ago@bastart.eu.org>
|
|
---
|
|
arch/mips/bcm963xx/irq.c | 308 ++++++++++-------------------
|
|
drivers/serial/bcm63xx_cons.c | 13 +-
|
|
include/asm-mips/mach-bcm963xx/bcm_intr.h | 18 +--
|
|
3 files changed, 119 insertions(+), 220 deletions(-)
|
|
|
|
Index: linux-2.6.25.4/arch/mips/bcm963xx/irq.c
|
|
===================================================================
|
|
--- linux-2.6.25.4.orig/arch/mips/bcm963xx/irq.c
|
|
+++ linux-2.6.25.4/arch/mips/bcm963xx/irq.c
|
|
@@ -1,259 +1,159 @@
|
|
/*
|
|
-<:copyright-gpl
|
|
- Copyright 2002 Broadcom Corp. All Rights Reserved.
|
|
-
|
|
- This program is free software; you can distribute it and/or modify it
|
|
- under the terms of the GNU General Public License (Version 2) as
|
|
- published by the Free Software Foundation.
|
|
-
|
|
- This program is distributed in the hope it will be useful, but WITHOUT
|
|
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
- for more details.
|
|
-
|
|
- You should have received a copy of the GNU General Public License along
|
|
- with this program; if not, write to the Free Software Foundation, Inc.,
|
|
- 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
|
|
-:>
|
|
-*/
|
|
-/*
|
|
- * Interrupt control functions for Broadcom 963xx MIPS boards
|
|
+ * Copyright (C) 2006,2007 Felix Fietkau <nbd@openwrt.org>
|
|
+ * Copyright (C) 2006,2007 Eugene Konev <ejka@openwrt.org>
|
|
+ * Copyright (C) 2008 Axel Gembe <ago@bastart.eu.org>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program; if not, write to the Free Software
|
|
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
-#include <asm/atomic.h>
|
|
-
|
|
-#include <linux/delay.h>
|
|
-#include <linux/init.h>
|
|
-#include <linux/ioport.h>
|
|
-#include <linux/irq.h>
|
|
#include <linux/interrupt.h>
|
|
-#include <linux/kernel.h>
|
|
-#include <linux/slab.h>
|
|
-#include <linux/module.h>
|
|
+#include <linux/io.h>
|
|
|
|
-#include <asm/irq.h>
|
|
+#include <asm/irq_cpu.h>
|
|
#include <asm/mipsregs.h>
|
|
-#include <asm/addrspace.h>
|
|
-#include <asm/signal.h>
|
|
+
|
|
#include <6348_map_part.h>
|
|
#include <6348_intr.h>
|
|
#include <bcm_map_part.h>
|
|
#include <bcm_intr.h>
|
|
|
|
-static void irq_dispatch_int(void)
|
|
-{
|
|
- unsigned int pendingIrqs;
|
|
- static unsigned int irqBit;
|
|
- static unsigned int isrNumber = 31;
|
|
-
|
|
- pendingIrqs = PERF->IrqStatus & PERF->IrqMask;
|
|
- if (!pendingIrqs) {
|
|
- return;
|
|
- }
|
|
+static int bcm963xx_irq_base;
|
|
|
|
- while (1) {
|
|
- irqBit <<= 1;
|
|
- isrNumber++;
|
|
- if (isrNumber == 32) {
|
|
- isrNumber = 0;
|
|
- irqBit = 0x1;
|
|
- }
|
|
- if (pendingIrqs & irqBit) {
|
|
- PERF->IrqMask &= ~irqBit; // mask
|
|
- do_IRQ(isrNumber + INTERNAL_ISR_TABLE_OFFSET);
|
|
- break;
|
|
- }
|
|
- }
|
|
+void bcm963xx_unmask_irq(unsigned int irq)
|
|
+{
|
|
+ PERF->IrqMask |= (1 << (irq - bcm963xx_irq_base));
|
|
}
|
|
|
|
-static void irq_dispatch_ext(uint32 irq)
|
|
+void bcm963xx_mask_irq(unsigned int irq)
|
|
{
|
|
- if (!(PERF->ExtIrqCfg & (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT)))) {
|
|
- printk("**** Ext IRQ mask. Should not dispatch ****\n");
|
|
- }
|
|
- /* disable and clear interrupt in the controller */
|
|
- PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT));
|
|
- PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT));
|
|
- do_IRQ(irq);
|
|
+ PERF->IrqMask &= ~(1 << (irq - bcm963xx_irq_base));
|
|
}
|
|
|
|
-
|
|
-//extern void brcm_timer_interrupt(struct pt_regs *regs);
|
|
-
|
|
-asmlinkage void plat_irq_dispatch(void)
|
|
+void bcm963xx_ack_irq(unsigned int irq)
|
|
{
|
|
- unsigned long cause;
|
|
-
|
|
- cause = read_c0_status() & read_c0_cause() & ST0_IM;
|
|
- if (cause & CAUSEF_IP7)
|
|
- do_IRQ(7);
|
|
- else if (cause & CAUSEF_IP2)
|
|
- irq_dispatch_int();
|
|
- else if (cause & CAUSEF_IP3)
|
|
- irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_0);
|
|
- else if (cause & CAUSEF_IP4)
|
|
- irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_1);
|
|
- else if (cause & CAUSEF_IP5)
|
|
- irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_2);
|
|
- else if (cause & CAUSEF_IP6) {
|
|
- irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_3);
|
|
- local_irq_disable();
|
|
- }
|
|
+ PERF->IrqStatus &= ~(1 << (irq - bcm963xx_irq_base));
|
|
}
|
|
|
|
-
|
|
-void enable_brcm_irq(unsigned int irq)
|
|
+void bcm963xx_unmask_ext_irq(unsigned int irq)
|
|
{
|
|
- unsigned long flags;
|
|
-
|
|
- local_irq_save(flags);
|
|
- if( irq >= INTERNAL_ISR_TABLE_OFFSET ) {
|
|
- PERF->IrqMask |= (1 << (irq - INTERNAL_ISR_TABLE_OFFSET));
|
|
- }
|
|
- else if (irq >= INTERRUPT_ID_EXTERNAL_0 && irq <= INTERRUPT_ID_EXTERNAL_3) {
|
|
- /* enable and clear interrupt in the controller */
|
|
- PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT));
|
|
PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT));
|
|
- }
|
|
- local_irq_restore(flags);
|
|
}
|
|
|
|
-void disable_brcm_irq(unsigned int irq)
|
|
+void bcm963xx_mask_ext_irq(unsigned int irq)
|
|
{
|
|
- unsigned long flags;
|
|
-
|
|
- local_irq_save(flags);
|
|
- if( irq >= INTERNAL_ISR_TABLE_OFFSET ) {
|
|
- PERF->IrqMask &= ~(1 << (irq - INTERNAL_ISR_TABLE_OFFSET));
|
|
- }
|
|
- else if (irq >= INTERRUPT_ID_EXTERNAL_0 && irq <= INTERRUPT_ID_EXTERNAL_3) {
|
|
- /* disable interrupt in the controller */
|
|
PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT));
|
|
- }
|
|
- local_irq_restore(flags);
|
|
}
|
|
|
|
-void ack_brcm_irq(unsigned int irq)
|
|
+void bcm963xx_ack_ext_irq(unsigned int irq)
|
|
{
|
|
- /* Already done in brcm_irq_dispatch */
|
|
+ PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT));
|
|
}
|
|
|
|
-unsigned int startup_brcm_irq(unsigned int irq)
|
|
+static void bcm963xx_dispatch_ext_irq(unsigned int irq)
|
|
{
|
|
- enable_brcm_irq(irq);
|
|
-
|
|
- return 0; /* never anything pending */
|
|
+ bcm963xx_ack_ext_irq(irq);
|
|
+ bcm963xx_mask_ext_irq(irq);
|
|
+ do_IRQ(irq);
|
|
}
|
|
|
|
-unsigned int startup_brcm_none(unsigned int irq)
|
|
+static void bcm963xx_cascade(void)
|
|
{
|
|
- return 0;
|
|
-}
|
|
+ uint32_t pending, bit, irq;
|
|
|
|
-void end_brcm_irq(unsigned int irq)
|
|
-{
|
|
- if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
|
|
- enable_brcm_irq(irq);
|
|
-}
|
|
+ if (!(pending = PERF->IrqStatus & PERF->IrqMask))
|
|
+ return;
|
|
|
|
-void end_brcm_none(unsigned int irq)
|
|
-{
|
|
-}
|
|
+ for (irq = 0, bit = 1; irq < 32; irq++, bit <<= 1) {
|
|
+ if (pending & bit) {
|
|
+ bcm963xx_ack_irq(irq + bcm963xx_irq_base);
|
|
+ bcm963xx_mask_irq(irq + bcm963xx_irq_base);
|
|
+ do_IRQ(irq + bcm963xx_irq_base);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ spurious_interrupt();
|
|
+}
|
|
+
|
|
+static struct irq_chip bcm963xx_irq_type = {
|
|
+ .name = "bcm963xx",
|
|
+ .unmask = bcm963xx_unmask_irq,
|
|
+ .mask = bcm963xx_mask_irq,
|
|
+ .ack = bcm963xx_ack_irq
|
|
+};
|
|
|
|
-static struct hw_interrupt_type brcm_irq_type = {
|
|
- .typename = "MIPS",
|
|
- .startup = startup_brcm_irq,
|
|
- .shutdown = disable_brcm_irq,
|
|
- .enable = enable_brcm_irq,
|
|
- .disable = disable_brcm_irq,
|
|
- .ack = ack_brcm_irq,
|
|
- .end = end_brcm_irq,
|
|
- .set_affinity = NULL
|
|
+static struct irq_chip bcm963xx_ext_irq_type = {
|
|
+ .name = "bcm963xx_ext",
|
|
+ .unmask = bcm963xx_unmask_ext_irq,
|
|
+ .mask = bcm963xx_mask_ext_irq,
|
|
+ .ack = bcm963xx_ack_ext_irq,
|
|
};
|
|
|
|
-static struct hw_interrupt_type brcm_irq_no_end_type = {
|
|
- .typename = "MIPS",
|
|
- .startup = startup_brcm_none,
|
|
- .shutdown = disable_brcm_irq,
|
|
- .enable = enable_brcm_irq,
|
|
- .disable = disable_brcm_irq,
|
|
- .ack = ack_brcm_irq,
|
|
- .end = end_brcm_none,
|
|
- .set_affinity = NULL
|
|
+static struct irqaction bcm963xx_cascade_action = {
|
|
+ .handler = no_action,
|
|
+ .name = "BCM963xx cascade interrupt"
|
|
};
|
|
|
|
-void __init arch_init_irq(void)
|
|
+static void __init bcm963xx_irq_init(int base)
|
|
{
|
|
int i;
|
|
|
|
- clear_c0_status(ST0_BEV);
|
|
- change_c0_status(ST0_IM, (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4));
|
|
+ bcm963xx_irq_base = base;
|
|
|
|
- for (i = 0; i < NR_IRQS; i++) {
|
|
- irq_desc[i].status = IRQ_DISABLED;
|
|
- irq_desc[i].action = 0;
|
|
- irq_desc[i].depth = 1;
|
|
- irq_desc[i].chip = &brcm_irq_type;
|
|
- }
|
|
+ /* External IRQs */
|
|
+ set_irq_chip_and_handler(INTERRUPT_ID_EXTERNAL_0, &bcm963xx_ext_irq_type,
|
|
+ handle_level_irq);
|
|
+ set_irq_chip_and_handler(INTERRUPT_ID_EXTERNAL_1, &bcm963xx_ext_irq_type,
|
|
+ handle_level_irq);
|
|
+ set_irq_chip_and_handler(INTERRUPT_ID_EXTERNAL_2, &bcm963xx_ext_irq_type,
|
|
+ handle_level_irq);
|
|
+ set_irq_chip_and_handler(INTERRUPT_ID_EXTERNAL_3, &bcm963xx_ext_irq_type,
|
|
+ handle_level_irq);
|
|
+
|
|
+ for (i = 0; i < 32; i++) {
|
|
+ set_irq_chip_and_handler(base + i, &bcm963xx_irq_type,
|
|
+ handle_level_irq);
|
|
+ }
|
|
+
|
|
+ setup_irq(2, &bcm963xx_cascade_action);
|
|
+ setup_irq(bcm963xx_irq_base, &bcm963xx_cascade_action);
|
|
+ set_c0_status(IE_IRQ0);
|
|
}
|
|
|
|
-int request_external_irq(unsigned int irq,
|
|
- FN_HANDLER handler,
|
|
- unsigned long irqflags,
|
|
- const char * devname,
|
|
- void *dev_id)
|
|
+asmlinkage void plat_irq_dispatch(void)
|
|
{
|
|
- unsigned long flags;
|
|
-
|
|
- local_irq_save(flags);
|
|
+ unsigned int pending = read_c0_status() & read_c0_cause() & ST0_IM;
|
|
|
|
- PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT)); // Clear
|
|
- PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT)); // Mask
|
|
- PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_INSENS_SHFT)); // Edge insesnsitive
|
|
- PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_LEVEL_SHFT)); // Level triggered
|
|
- PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_SENSE_SHFT)); // Low level
|
|
-
|
|
- local_irq_restore(flags);
|
|
-
|
|
- return( request_irq(irq, handler, irqflags, devname, dev_id) );
|
|
+ if (pending & STATUSF_IP7) /* cpu timer */
|
|
+ do_IRQ(7);
|
|
+ else if (pending & STATUSF_IP2) /* internal interrupt cascade */
|
|
+ bcm963xx_cascade();
|
|
+ else if (pending & STATUSF_IP3)
|
|
+ bcm963xx_dispatch_ext_irq(INTERRUPT_ID_EXTERNAL_0);
|
|
+ else if (pending & STATUSF_IP4)
|
|
+ bcm963xx_dispatch_ext_irq(INTERRUPT_ID_EXTERNAL_1);
|
|
+ else if (pending & STATUSF_IP5)
|
|
+ bcm963xx_dispatch_ext_irq(INTERRUPT_ID_EXTERNAL_2);
|
|
+ else if (pending & STATUSF_IP6)
|
|
+ bcm963xx_dispatch_ext_irq(INTERRUPT_ID_EXTERNAL_3);
|
|
+ else
|
|
+ spurious_interrupt();
|
|
}
|
|
|
|
-/* VxWorks compatibility function(s). */
|
|
-
|
|
-unsigned int BcmHalMapInterrupt(FN_HANDLER pfunc, unsigned int param,
|
|
- unsigned int interruptId)
|
|
+void __init arch_init_irq(void)
|
|
{
|
|
- int nRet = -1;
|
|
- char *devname;
|
|
-
|
|
- devname = kmalloc(16, GFP_KERNEL);
|
|
- if (devname)
|
|
- sprintf( devname, "brcm_%d", interruptId );
|
|
-
|
|
- /* Set the IRQ description to not automatically enable the interrupt at
|
|
- * the end of an ISR. The driver that handles the interrupt must
|
|
- * explicitly call BcmHalInterruptEnable or enable_brcm_irq. This behavior
|
|
- * is consistent with interrupt handling on VxWorks.
|
|
- */
|
|
- irq_desc[interruptId].chip = &brcm_irq_no_end_type;
|
|
-
|
|
- if( interruptId >= INTERNAL_ISR_TABLE_OFFSET )
|
|
- {
|
|
- printk("BcmHalMapInterrupt : internal IRQ\n");
|
|
- nRet = request_irq( interruptId, pfunc, IRQF_DISABLED, devname, (void *) param );
|
|
- }
|
|
- else if (interruptId >= INTERRUPT_ID_EXTERNAL_0 && interruptId <= INTERRUPT_ID_EXTERNAL_3)
|
|
- {
|
|
- printk("BcmHalMapInterrupt : external IRQ\n");
|
|
- nRet = request_external_irq( interruptId, pfunc, IRQF_DISABLED, devname, (void *) param );
|
|
- }
|
|
-
|
|
- return( nRet );
|
|
+ mips_cpu_irq_init();
|
|
+ bcm963xx_irq_init(INTERNAL_ISR_TABLE_OFFSET);
|
|
}
|
|
-
|
|
-
|
|
-EXPORT_SYMBOL(enable_brcm_irq);
|
|
-EXPORT_SYMBOL(disable_brcm_irq);
|
|
-EXPORT_SYMBOL(request_external_irq);
|
|
-EXPORT_SYMBOL(BcmHalMapInterrupt);
|
|
-
|
|
Index: linux-2.6.25.4/drivers/serial/bcm63xx_cons.c
|
|
===================================================================
|
|
--- linux-2.6.25.4.orig/drivers/serial/bcm63xx_cons.c
|
|
+++ linux-2.6.25.4/drivers/serial/bcm63xx_cons.c
|
|
@@ -267,7 +267,7 @@ static void bcm_interrupt(int irq, void
|
|
}
|
|
|
|
// Clear the interrupt
|
|
- enable_brcm_irq(INTERRUPT_ID_UART);
|
|
+// bcm963xx_unmask_irq(INTERRUPT_ID_UART);
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
|
|
return IRQ_HANDLED;
|
|
#endif
|
|
@@ -880,7 +880,7 @@ static int bcm63xx_cons_open(struct tty_
|
|
info->count++;
|
|
tty->driver_data = info;
|
|
info->tty = tty;
|
|
- enable_brcm_irq(INTERRUPT_ID_UART);
|
|
+ bcm963xx_unmask_irq(INTERRUPT_ID_UART);
|
|
|
|
// Start up serial port
|
|
retval = startup(info);
|
|
@@ -927,7 +927,7 @@ static struct tty_operations rs_ops = {
|
|
-------------------------------------------------------------------------- */
|
|
static int __init bcm63xx_serialinit(void)
|
|
{
|
|
- int i, flags;
|
|
+ int i, flags, res;
|
|
struct bcm_serial *info;
|
|
|
|
// Print the driver version information
|
|
@@ -981,7 +981,12 @@ static int __init bcm63xx_serialinit(voi
|
|
*/
|
|
if (!info->port)
|
|
return 0;
|
|
- BcmHalMapInterrupt(bcm_interrupt, 0, INTERRUPT_ID_UART);
|
|
+
|
|
+ res = request_irq(INTERRUPT_ID_UART, bcm_interrupt, 0, "bcm-uart", NULL);
|
|
+ if (res) {
|
|
+ spin_unlock_irqrestore(&bcm963xx_serial_lock, flags);
|
|
+ return res;
|
|
+ }
|
|
}
|
|
|
|
/* order matters here... the trick is that flags
|
|
Index: linux-2.6.25.4/include/asm-mips/mach-bcm963xx/bcm_intr.h
|
|
===================================================================
|
|
--- linux-2.6.25.4.orig/include/asm-mips/mach-bcm963xx/bcm_intr.h
|
|
+++ linux-2.6.25.4/include/asm-mips/mach-bcm963xx/bcm_intr.h
|
|
@@ -39,18 +39,12 @@ struct pt_regs;
|
|
typedef int (*FN_HANDLER) (int, void *);
|
|
|
|
/* prototypes */
|
|
-extern void enable_brcm_irq(unsigned int irq);
|
|
-extern void disable_brcm_irq(unsigned int irq);
|
|
-extern int request_external_irq(unsigned int irq,
|
|
- FN_HANDLER handler, unsigned long irqflags,
|
|
- const char * devname, void *dev_id);
|
|
-extern unsigned int BcmHalMapInterrupt(FN_HANDLER isr, unsigned int param,
|
|
- unsigned int interruptId);
|
|
-extern void dump_intr_regs(void);
|
|
-
|
|
-/* compatibility definitions */
|
|
-#define BcmHalInterruptEnable(irq) enable_brcm_irq( irq )
|
|
-#define BcmHalInterruptDisable(irq) disable_brcm_irq( irq )
|
|
+extern void bcm963xx_unmask_irq(unsigned int irq);
|
|
+extern void bcm963xx_mask_irq(unsigned int irq);
|
|
+extern void bcm963xx_ack_irq(unsigned int irq);
|
|
+extern void bcm963xx_unmask_ext_irq(unsigned int irq);
|
|
+extern void bcm963xx_mask_ext_irq(unsigned int irq);
|
|
+extern void bcm963xx_ack_ext_irq(unsigned int irq);
|
|
|
|
#ifdef __cplusplus
|
|
}
|