From 7b275523aba522aa76891861ee32ba2456e5f146 Mon Sep 17 00:00:00 2001 From: Jesper Nilsson Date: Wed, 14 Nov 2007 17:00:59 -0800 Subject: cris build fixes: corrected and improved NMI and IRQ handling Corrects compile errors and the following: - Remove oldset parameter from do_signal and do_notify_resume. - Modified to fit new consolidated IRQ handling code. - Reverse check order between external nmi and watchdog nmi to avoid false watchdog oops in case of a glitch on the nmi pin. - Return from an pin-generated NMI the same way as for other interrupts. - Moved blocking of ethernet rx/tx irq from ethernet interrupt handler to low-level asm interrupt handlers. Fixed in the multiple interrupt handler also. - Add space for thread local storage in thread_info struct. - Add NO_DMA to Kconfig, and include arch specific Kconfig using arch independent path. Include subsystem Kconfigs for pcmcia, usb, i2c, rtc and pci. Signed-off-by: Jesper Nilsson Acked-by: Mikael Starvik Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/cris/arch-v10/drivers/Kconfig | 1 + arch/cris/arch-v10/kernel/entry.S | 41 +++++++++++++------------- arch/cris/arch-v10/kernel/irq.c | 59 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 78 insertions(+), 23 deletions(-) (limited to 'arch/cris/arch-v10') diff --git a/arch/cris/arch-v10/drivers/Kconfig b/arch/cris/arch-v10/drivers/Kconfig index 03e2e68f947..e6fc8455a37 100644 --- a/arch/cris/arch-v10/drivers/Kconfig +++ b/arch/cris/arch-v10/drivers/Kconfig @@ -2,6 +2,7 @@ config ETRAX_ETHERNET bool "Ethernet support" depends on ETRAX_ARCH_V10 select NET_ETHERNET + select MII help This option enables the ETRAX 100LX built-in 10/100Mbit Ethernet controller. diff --git a/arch/cris/arch-v10/kernel/entry.S b/arch/cris/arch-v10/kernel/entry.S index bc9bed97f22..ec62c951fa3 100644 --- a/arch/cris/arch-v10/kernel/entry.S +++ b/arch/cris/arch-v10/kernel/entry.S @@ -500,9 +500,8 @@ _work_notifysig: ;; deal with pending signals and notify-resume requests move.d $r9, $r10 ; do_notify_resume syscall/irq param - moveq 0, $r11 ; oldset param - 0 in this case - move.d $sp, $r12 ; the regs param - move.d $r1, $r13 ; the thread_info_flags parameter + move.d $sp, $r11 ; the regs param + move.d $r1, $r12 ; the thread_info_flags parameter jsr do_notify_resume ba _Rexit @@ -678,13 +677,19 @@ IRQ1_interrupt: push $r10 ; push orig_r10 clear.d [$sp=$sp-4] ; frametype == 0, normal frame + ;; If there is a glitch on the NMI pin shorter than ~100ns + ;; (i.e. non-active by the time we get here) then the nmi_pin bit + ;; in R_IRQ_MASK0_RD will already be cleared. The watchdog_nmi bit + ;; is cleared by us however (when feeding the watchdog), which is why + ;; we use that bit to determine what brought us here. + move.d [R_IRQ_MASK0_RD], $r1 ; External NMI or watchdog? - and.d 0x80000000, $r1 - beq wdog + and.d (1<<30), $r1 + bne wdog move.d $sp, $r10 jsr handle_nmi setf m ; Enable NMI again - retb ; Return from NMI + ba _Rexit ; Return the standard way nop wdog: #if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM) @@ -775,22 +780,9 @@ multiple_interrupt: push $r10 ; push orig_r10 clear.d [$sp=$sp-4] ; frametype == 0, normal frame - moveq 2, $r2 ; first bit we care about is the timer0 irq - move.d [R_VECT_MASK_RD], $r0; read the irq bits that triggered the multiple irq - move.d $r0, [R_VECT_MASK_CLR] ; Block all active IRQs -1: - btst $r2, $r0 ; check for the irq given by bit r2 - bpl 2f - move.d $r2, $r10 ; First argument to do_IRQ - move.d $sp, $r11 ; second argument to do_IRQ - jsr do_IRQ -2: - addq 1, $r2 ; next vector bit - cmp.b 32, $r2 - bne 1b ; process all irq's up to and including number 31 - moveq 0, $r9 ; make ret_from_intr realise we came from an ir + move.d $sp, $r10 + jsr do_multiple_IRQ - move.d $r0, [R_VECT_MASK_SET] ; Unblock all the IRQs jump ret_from_intr do_sigtrap: @@ -837,6 +829,13 @@ _ugdb_handle_breakpoint: ba do_sigtrap ; SIGTRAP the offending process. pop $dccr ; Restore dccr in delay slot. + .global kernel_execve +kernel_execve: + move.d __NR_execve, $r9 + break 13 + ret + nop + .data hw_bp_trigs: diff --git a/arch/cris/arch-v10/kernel/irq.c b/arch/cris/arch-v10/kernel/irq.c index 845c95f6e87..e06ab0050d3 100644 --- a/arch/cris/arch-v10/kernel/irq.c +++ b/arch/cris/arch-v10/kernel/irq.c @@ -12,10 +12,16 @@ */ #include +#include #include +#include #include #include +/* From kgdb.c. */ +extern void kgdb_init(void); +extern void breakpoint(void); + #define mask_irq(irq_nr) (*R_VECT_MASK_CLR = 1 << (irq_nr)); #define unmask_irq(irq_nr) (*R_VECT_MASK_SET = 1 << (irq_nr)); @@ -75,8 +81,8 @@ BUILD_IRQ(12, 0x1000) BUILD_IRQ(13, 0x2000) void mmu_bus_fault(void); /* IRQ 14 is the bus fault interrupt */ void multiple_interrupt(void); /* IRQ 15 is the multiple IRQ interrupt */ -BUILD_IRQ(16, 0x10000) -BUILD_IRQ(17, 0x20000) +BUILD_IRQ(16, 0x10000 | 0x20000) /* ethernet tx interrupt needs to block rx */ +BUILD_IRQ(17, 0x20000 | 0x10000) /* ...and vice versa */ BUILD_IRQ(18, 0x40000) BUILD_IRQ(19, 0x80000) BUILD_IRQ(20, 0x100000) @@ -147,6 +153,55 @@ void system_call(void); /* from entry.S */ void do_sigtrap(void); /* from entry.S */ void gdb_handle_breakpoint(void); /* from entry.S */ +extern void do_IRQ(int irq, struct pt_regs * regs); + +/* Handle multiple IRQs */ +void do_multiple_IRQ(struct pt_regs* regs) +{ + int bit; + unsigned masked; + unsigned mask; + unsigned ethmask = 0; + + /* Get interrupts to mask and handle */ + mask = masked = *R_VECT_MASK_RD; + + /* Never mask timer IRQ */ + mask &= ~(IO_MASK(R_VECT_MASK_RD, timer0)); + + /* + * If either ethernet interrupt (rx or tx) is active then block + * the other one too. Unblock afterwards also. + */ + if (mask & + (IO_STATE(R_VECT_MASK_RD, dma0, active) | + IO_STATE(R_VECT_MASK_RD, dma1, active))) { + ethmask = (IO_MASK(R_VECT_MASK_RD, dma0) | + IO_MASK(R_VECT_MASK_RD, dma1)); + } + + /* Block them */ + *R_VECT_MASK_CLR = (mask | ethmask); + + /* An extra irq_enter here to prevent softIRQs to run after + * each do_IRQ. This will decrease the interrupt latency. + */ + irq_enter(); + + /* Handle all IRQs */ + for (bit = 2; bit < 32; bit++) { + if (masked & (1 << bit)) { + do_IRQ(bit, regs); + } + } + + /* This irq_exit() will trigger the soft IRQs. */ + irq_exit(); + + /* Unblock the IRQs again */ + *R_VECT_MASK_SET = (masked | ethmask); +} + /* init_IRQ() is called by start_kernel and is responsible for fixing IRQ masks and setting the irq vector table. */ -- cgit v1.2.3