diff options
author | florian <florian@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2007-06-02 23:13:51 +0000 |
---|---|---|
committer | florian <florian@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2007-06-02 23:13:51 +0000 |
commit | 8366dfac71c976acb6d772ec9014026cafab8182 (patch) | |
tree | 7e6839d0a0b426439a52199cbc1ecdd54efd8a7a /target/linux/adm5120-2.6/files/arch/mips/adm5120 | |
parent | c30dde79405b135b38cea530745b66516896b0ea (diff) |
IRQ handler rewrite by Gabor Juhos, uses C no longer assembly
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@7464 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'target/linux/adm5120-2.6/files/arch/mips/adm5120')
4 files changed, 180 insertions, 286 deletions
diff --git a/target/linux/adm5120-2.6/files/arch/mips/adm5120/Makefile b/target/linux/adm5120-2.6/files/arch/mips/adm5120/Makefile index f3e0e7f98..deb1adf7e 100644 --- a/target/linux/adm5120-2.6/files/arch/mips/adm5120/Makefile +++ b/target/linux/adm5120-2.6/files/arch/mips/adm5120/Makefile @@ -2,7 +2,8 @@ # Makefile for the ADMtek ADM5120 SoC specific parts of the kernel # -obj-y := setup.o prom.o irq.o memory.o int-handler.o adm5120_info.o -obj-y += gpio.o +obj-y := setup.o prom.o irq.o memory.o adm5120_info.o +obj-y += gpio.o +obj-y += time.o EXTRA_AFLAGS := $(CFLAGS) diff --git a/target/linux/adm5120-2.6/files/arch/mips/adm5120/int-handler.S b/target/linux/adm5120-2.6/files/arch/mips/adm5120/int-handler.S deleted file mode 100644 index f118fb402..000000000 --- a/target/linux/adm5120-2.6/files/arch/mips/adm5120/int-handler.S +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Carsten Langgaard, carstenl@mips.com - * Copyright (C) 1999, 2000 MIPS Technologies, Inc. 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 exception dispatch code. - * - */ -#include <linux/autoconf.h> - -#include <asm/asm.h> -#include <asm/mipsregs.h> -#include <asm/regdef.h> -#include <asm/stackframe.h> - -#define STATUS_IE 0x00000001 - -/* A lot of complication here is taken away because: - * - * 1) We handle one interrupt and return, sitting in a loop and moving across - * all the pending IRQ bits in the cause register is _NOT_ the answer, the - * common case is one pending IRQ so optimize in that direction. - * - * 2) We need not check against bits in the status register IRQ mask, that - * would make this routine slow as hell. - * - * 3) Linux only thinks in terms of all IRQs on or all IRQs off, nothing in - * between like BSD spl() brain-damage. - * - * Furthermore, the IRQs on the MIPS board look basically (barring software - * IRQs which we don't use at all and all external interrupt sources are - * combined together on hardware interrupt 0 (MIPS IRQ 2)) like: - * - * MIPS IRQ Source - * -------- ------ - * 0 Software (ignored) - * 1 Software (ignored) - * 2 Combined hardware interrupt (hw0) - * 3 Hardware (ignored) - * 4 Hardware (ignored) - * 5 Hardware (ignored) - * 6 Hardware (ignored) - * 7 R4k timer (what we use) - * - * Note: On the SEAD board thing are a little bit different. - * Here IRQ 2 (hw0) is wired to the UART0 and IRQ 3 (hw1) is wired - * wired to UART1. - * - * We handle the IRQ according to _our_ priority which is: - * - * Highest ---- R4k Timer - * Lowest ---- Combined hardware interrupt - * - * then we just return, if multiple IRQs are pending then we will just take - * another exception, big deal. - */ - - .text - .set noreorder - .set noat - .align 5 - -NESTED(mipsIRQ, PT_SIZE, sp) - SAVE_ALL - CLI - .set at - - mfc0 s0, CP0_CAUSE - mfc0 s1, CP0_STATUS - and s0, s0, s1 - - /* First we check for r4k counter/timer IRQ. */ - andi a0, s0, CAUSEF_IP7 - beq a0, zero, 1f - nop - - move a0, sp - jal mips_timer_interrupt - nop - - j ret_from_irq - nop - -1: - andi a0, s0, CAUSEF_IP2 - beq a0, zero, 1f - nop - - move a0, sp - jal adm5120_hw0_irqdispatch - nop -1: - j ret_from_irq - nop - -END(mipsIRQ) - - -LEAF(mips_int_lock) - .set noreorder - mfc0 v0, CP0_STATUS - li v1, ~STATUS_IE - and v1, v1, v0 - mtc0 v1, CP0_STATUS - j ra - and v0, v0, STATUS_IE - .set reorder -END(mips_int_lock) - - -LEAF(mips_int_unlock) - mfc0 v0, CP0_STATUS - and a0, a0, STATUS_IE - or v0, v0, a0 - mtc0 v0, CP0_STATUS - j ra - nop -END(mips_int_unlock) - diff --git a/target/linux/adm5120-2.6/files/arch/mips/adm5120/irq.c b/target/linux/adm5120-2.6/files/arch/mips/adm5120/irq.c index c53272e1e..3e14c92f1 100644 --- a/target/linux/adm5120-2.6/files/arch/mips/adm5120/irq.c +++ b/target/linux/adm5120-2.6/files/arch/mips/adm5120/irq.c @@ -1,157 +1,203 @@ /* - * Copyright (C) ADMtek Incorporated. - * Creator : daniell@admtek.com.tw - * Carsten Langgaard, carstenl@mips.com - * Copyright (C) 2000, 2001 MIPS Technologies, Inc. - * Copyright (C) 2001 Ralf Baechle - * Copyright (C) 2005 Jeroen Vreeken (pe1rxq@amsat.org) + * $Id$ + * + * ADM5120 specific interrupt handlers + * + * Copyright (C) 2007 Gabor Juhos <juhosg@freemail.hu> + * Copyright (C) 2007 OpenWrt.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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * */ -#include <linux/autoconf.h> #include <linux/init.h> -#include <linux/kernel_stat.h> -#include <linux/signal.h> -#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/version.h> #include <linux/interrupt.h> -#include <linux/slab.h> -#include <linux/random.h> -#include <linux/pm.h> +#include <linux/ioport.h> #include <asm/irq.h> -#include <asm/time.h> -#include <asm/mipsregs.h> -#include <asm/gdb-stub.h> #include <asm/irq_cpu.h> +#include <asm/mipsregs.h> +#include <asm/bitops.h> + +#include <asm/mach-adm5120/adm5120_defs.h> +#include <asm/mach-adm5120/adm5120_irq.h> + +#define INTC_REG(r) (*(volatile u32 *)(KSEG1ADDR(ADM5120_INTC_BASE) + r)) + +static void adm5120_intc_irq_unmask(unsigned int irq); +static void adm5120_intc_irq_mask(unsigned int irq); +static int adm5120_intc_irq_set_type(unsigned int irq, unsigned int flow_type); + +static struct irq_chip adm5120_intc_irq_chip = { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) + .name = "INTC", +#else + .typename = "INTC", +#endif + .unmask = adm5120_intc_irq_unmask, + .mask = adm5120_intc_irq_mask, + .mask_ack = adm5120_intc_irq_mask, + .set_type = adm5120_intc_irq_set_type +}; -#define MIPS_CPU_TIMER_IRQ 7 - -extern int setup_irq(unsigned int irq, struct irqaction *irqaction); -extern irq_desc_t irq_desc[]; -extern asmlinkage void mipsIRQ(void); - -int mips_int_lock(void); -void mips_int_unlock(int); - -unsigned int mips_counter_frequency; - -#define ADM5120_INTC_REG(reg) (*(volatile u32 *)(KSEG1ADDR(0x12200000+(reg)))) -#define ADM5120_INTC_STATUS ADM5120_INTC_REG(0x00) -#define ADM5120_INTC_ENABLE ADM5120_INTC_REG(0x08) -#define ADM5120_INTC_DISABLE ADM5120_INTC_REG(0x0c) -#define ADM5120_IRQ_MAX 9 -#define ADM5120_IRQ_MASK 0x3ff +static struct irqaction adm5120_intc_irq_action = { + .handler = no_action, + .name = "cascade [INTC]" +}; -void adm5120_hw0_irqdispatch(struct pt_regs *regs) +static void adm5120_intc_irq_unmask(unsigned int irq) { - unsigned long intsrc; - int i; - - intsrc = ADM5120_INTC_STATUS & ADM5120_IRQ_MASK; - - if (intsrc) { - for (i = 0; intsrc; intsrc >>= 1, i++) - if (intsrc & 0x1) - do_IRQ(i); - } else - spurious_interrupt(); -} + unsigned long flags; -void mips_timer_interrupt(struct pt_regs *regs) -{ - write_c0_compare(read_c0_count()+ mips_counter_frequency/HZ); - ll_timer_interrupt(MIPS_CPU_TIMER_IRQ); + irq -= ADM5120_INTC_IRQ_BASE; + local_irq_save(flags); + INTC_REG(INTC_REG_IRQ_ENABLE) = (1 << irq); + local_irq_restore(flags); } -/* Main interrupt dispatcher */ -asmlinkage void plat_irq_dispatch(struct pt_regs *regs) +static void adm5120_intc_irq_mask(unsigned int irq) { - unsigned int cp0_cause = read_c0_cause() & read_c0_status(); + unsigned long flags; - if (cp0_cause & CAUSEF_IP7) { - mips_timer_interrupt( regs); - } else if (cp0_cause & CAUSEF_IP2) { - adm5120_hw0_irqdispatch( regs); - } + irq -= ADM5120_INTC_IRQ_BASE; + local_irq_save(flags); + INTC_REG(INTC_REG_IRQ_DISABLE) = (1 << irq); + local_irq_restore(flags); } -void enable_adm5120_irq(unsigned int irq) +static int adm5120_intc_irq_set_type(unsigned int irq, unsigned int flow_type) { - int s; - - /* Disable all interrupts (FIQ/IRQ) */ - s = mips_int_lock(); - - if ((irq < 0) || (irq > ADM5120_IRQ_MAX)) - goto err_exit; - - ADM5120_INTC_ENABLE = (1<<irq); - -err_exit: - - /* Restore the interrupts states */ - mips_int_unlock(s); + /* TODO: not yet tested */ +#if 1 + unsigned int sense; + unsigned long mode; + int err; + + err = 0; + sense = flow_type & (IRQ_TYPE_SENSE_MASK); + switch (sense) { + case IRQ_TYPE_NONE: + case IRQ_TYPE_LEVEL_HIGH: + break; + case IRQ_TYPE_LEVEL_LOW: + switch (irq) { + case ADM5120_IRQ_GPIO2: + case ADM5120_IRQ_GPIO4: + break; + default: + err = -EINVAL; + break; + } + break; + default: + err = -EINVAL; + break; + } + + if (err) + return err; + + switch (irq) { + case ADM5120_IRQ_GPIO2: + case ADM5120_IRQ_GPIO4: + mode = INTC_REG(INTC_REG_INT_MODE); + if (sense == IRQ_TYPE_LEVEL_LOW) + mode |= (1 << (irq-ADM5120_INTC_IRQ_BASE)); + else + mode &= (1 << (irq-ADM5120_INTC_IRQ_BASE)); + + INTC_REG(INTC_REG_INT_MODE) = mode; + /* fallthrogh */ + default: + irq_desc[irq].status &= ~IRQ_TYPE_SENSE_MASK; + irq_desc[irq].status |= sense; + break; + } +#endif + return 0; } - -void disable_adm5120_irq(unsigned int irq) +static void adm5120_intc_irq_dispatch(void) { - int s; - - /* Disable all interrupts (FIQ/IRQ) */ - s = mips_int_lock(); + unsigned long status; + int irq; - if ((irq < 0) || (irq > ADM5120_IRQ_MAX)) - goto err_exit; +#if 1 + /* dispatch only one IRQ at a time */ + status = INTC_REG(INTC_REG_IRQ_STATUS) & INTC_INT_ALL; - ADM5120_INTC_DISABLE = (1<<irq); - -err_exit: - /* Restore the interrupts states */ - mips_int_unlock(s); + if (status) { + irq = ADM5120_INTC_IRQ_BASE+fls(status)-1; + do_IRQ(irq); + } else + spurious_interrupt(); +#else + status = INTC_REG(INTC_REG_IRQ_STATUS) & INTC_INT_ALL; + if (status) { + for (irq=ADM5120_INTC_IRQ_BASE; irq <= ADM5120_INTC_IRQ_BASE + + INTC_IRQ_LAST; irq++, status >>=1) { + if ((status & 1) == 1) + do_IRQ(irq); + } + } else + spurious_interrupt(); +#endif } -unsigned int startup_adm5120_irq(unsigned int irq) +asmlinkage void plat_irq_dispatch(void) { - enable_adm5120_irq(irq); - return 0; -} + unsigned long pending; -void shutdown_adm5120_irq(unsigned int irq) -{ - disable_adm5120_irq(irq); -} + pending = read_c0_status() & read_c0_cause(); -static inline void ack_adm5120_irq(unsigned int irq_nr) -{ - ADM5120_INTC_DISABLE = (1 << irq_nr); + if (pending & STATUSF_IP7) + do_IRQ(ADM5120_IRQ_COUNTER); + else if (pending & STATUSF_IP2) + adm5120_intc_irq_dispatch(); + else + spurious_interrupt(); } - -static void end_adm5120_irq(unsigned int irq_nr) +#define INTC_IRQ_STATUS (IRQ_LEVEL | IRQ_TYPE_LEVEL_HIGH | IRQ_DISABLED) +static void __init adm5120_intc_irq_init(int base) { - ADM5120_INTC_ENABLE = (1 << irq_nr); -} + int i; -static hw_irq_controller adm5120_irq_type = { - .typename = "MIPS", - .startup = startup_adm5120_irq, - .shutdown = shutdown_adm5120_irq, - .enable = enable_adm5120_irq, - .disable = disable_adm5120_irq, - .ack = ack_adm5120_irq, - .end = end_adm5120_irq, - .set_affinity = NULL, -}; + /* disable all interrupts */ + INTC_REG(INTC_REG_IRQ_DISABLE) = INTC_INT_ALL; + /* setup all interrupts to generate IRQ instead of FIQ */ + INTC_REG(INTC_REG_INT_MODE) = 0; + /* set active level for all external interrupts to HIGH */ + INTC_REG(INTC_REG_INT_LEVEL) = 0; + /* disable usage of the TEST_SOURCE register */ + INTC_REG(INTC_REG_IRQ_SOURCE_SELECT) = 0; + + for(i=ADM5120_INTC_IRQ_BASE; i <= ADM5120_INTC_IRQ_BASE+INTC_IRQ_LAST; + i++) { + irq_desc[i].status = INTC_IRQ_STATUS; + set_irq_chip_and_handler(i, &adm5120_intc_irq_chip, + handle_level_irq); + } + setup_irq(ADM5120_IRQ_INTC, &adm5120_intc_irq_action); +} -void __init arch_init_irq(void) -{ - int i; - - for (i = 0; i <= ADM5120_IRQ_MAX; i++) { - irq_desc[i].status = IRQ_DISABLED; - irq_desc[i].action = 0; - irq_desc[i].depth = 1; - irq_desc[i].chip = &adm5120_irq_type; - } +void __init arch_init_irq(void) { + mips_cpu_irq_init(); + adm5120_intc_irq_init(ADM5120_INTC_IRQ_BASE); } diff --git a/target/linux/adm5120-2.6/files/arch/mips/adm5120/setup.c b/target/linux/adm5120-2.6/files/arch/mips/adm5120/setup.c index 5767df8b2..aa30dc5c8 100644 --- a/target/linux/adm5120-2.6/files/arch/mips/adm5120/setup.c +++ b/target/linux/adm5120-2.6/files/arch/mips/adm5120/setup.c @@ -15,15 +15,13 @@ #include <asm/io.h> #include <asm/time.h> -#include <adm5120_info.h> +#include <asm/mach-adm5120/adm5120_info.h> +#include <asm/mach-adm5120/adm5120_defs.h> +#include <asm/mach-adm5120/adm5120_irq.h> -#define ADM5120_SOFTRESET 0x12000004 -#define STATUS_IE 0x00000001 -#define ALLINTS (IE_IRQ0 | IE_IRQ5 | STATUS_IE) - -void mips_time_init(void); +extern void adm5120_time_init(void) __init; -extern unsigned int mips_counter_frequency; +#define ADM5120_SOFTRESET 0x12000004 void adm5120_restart(char *command) { @@ -43,25 +41,11 @@ void adm5120_power_off(void) adm5120_halt(); } -void __init adm5120_time_init(void) -{ - mips_counter_frequency = adm5120_speed >> 1; -} - -void __init plat_timer_setup(struct irqaction *irq) -{ - /* to generate the first timer interrupt */ - write_c0_compare(read_c0_count()+ mips_counter_frequency/HZ); - clear_c0_status(ST0_BEV); - set_c0_status(ALLINTS); -} - void __init plat_mem_setup(void) { printk(KERN_INFO "ADM5120 board setup\n"); board_time_init = adm5120_time_init; - //board_timer_setup = mips_timer_setup; _machine_restart = adm5120_restart; _machine_halt = adm5120_halt; @@ -75,16 +59,15 @@ const char *get_system_type(void) return adm5120_board_name(); } -#ifdef CONFIG_USB static struct resource adm5120_hcd_resources[] = { [0] = { - .start = 0x11200000, - .end = 0x11200084, + .start = ADM5120_USBC_BASE, + .end = ADM5120_USBC_BASE+ADM5120_USBC_SIZE-1, .flags = IORESOURCE_MEM, }, [1] = { - .start = 0x3, - .end = 0x3, + .start = ADM5120_IRQ_USBC, + .end = ADM5120_IRQ_USBC, .flags = IORESOURCE_IRQ, }, }; @@ -105,5 +88,4 @@ static int __init adm5120_init(void) return platform_add_devices(devices, ARRAY_SIZE(devices)); } -arch_initcall(adm5120_init); -#endif +subsys_initcall(adm5120_init); |