This patch is essentially a rewrite of the irq handling code to make proper use of the genirq framework.
Based on patch by Michael Walle. Signed-off-by: Lars-Peter Clausen <[email protected]> --- arch/lm32/Kconfig | 10 +-- arch/lm32/include/asm/hardirq.h | 35 ++----- arch/lm32/include/asm/irq.h | 37 ++---- arch/lm32/include/asm/thread_info.h | 2 - arch/lm32/kernel/entry.S | 49 ++++++-- arch/lm32/kernel/init_task.c | 1 + arch/lm32/kernel/irq.c | 213 +++++++++-------------------------- drivers/input/keyboard/softusb.c | 2 - drivers/net/milkymist_minimac.c | 12 +-- drivers/serial/milkymist_uart.c | 12 ++- 10 files changed, 125 insertions(+), 248 deletions(-) diff --git a/arch/lm32/Kconfig b/arch/lm32/Kconfig index 66c789b..0563647 100644 --- a/arch/lm32/Kconfig +++ b/arch/lm32/Kconfig @@ -2,6 +2,8 @@ config LM32 bool default y select CPU_MICO32 + select HAVE_GENERIC_HARDIRQS + select GENERIC_HARDIRQS_NO_DEPRECATED config MMU bool @@ -43,10 +45,6 @@ config GENERIC_BUG bool default y -config GENERIC_HARDIRQS - bool - default y - config GENERIC_CLOCKEVENTS bool default y @@ -67,10 +65,6 @@ config NO_IOPORT bool default y -config GENERIC_HARDIRQS_NO__DO_IRQ - bool - default y - config APM_EMULATION bool default n diff --git a/arch/lm32/include/asm/hardirq.h b/arch/lm32/include/asm/hardirq.h index fb3b938..05abd08 100644 --- a/arch/lm32/include/asm/hardirq.h +++ b/arch/lm32/include/asm/hardirq.h @@ -1,30 +1,13 @@ -/* - * Based on: - * include/asm-m68knommu/hardirq.h - */ +#ifndef _ASM_HARDIRQ_H_ +#define _ASM_HARDIRQ_H_ -#ifndef _LM32_ASM_HARDIRQ_H -#define _LM32_ASM_HARDIRQ_H +#define ack_bad_irq ack_bad_irq +#include <asm-generic/hardirq.h> -#include <linux/cache.h> -#include <linux/threads.h> -#include <asm/irq.h> +extern atomic_t irq_err_count; +static inline void ack_bad_irq(int irq) +{ + atomic_inc(&irq_err_count); +} -typedef struct { - unsigned int __softirq_pending; -} ____cacheline_aligned irq_cpustat_t; - -#include <linux/irq_cpustat.h> /* Standard mappings for irq_cpustat_t above */ - -#define HARDIRQ_BITS 8 - -/* - * The hardirq mask has to be large enough to have - * space for potentially all IRQ sources in the system - * nesting on a single CPU: - */ -#if (1 << HARDIRQ_BITS) < NR_IRQS -# error HARDIRQ_BITS is too low! #endif - -#endif /* _LM32_ASM_HARDIRQ_H */ diff --git a/arch/lm32/include/asm/irq.h b/arch/lm32/include/asm/irq.h index b8161dc..32b8d8c 100644 --- a/arch/lm32/include/asm/irq.h +++ b/arch/lm32/include/asm/irq.h @@ -24,35 +24,24 @@ #ifndef _LM32_ASM_IRQ_H #define _LM32_ASM_IRQ_H -#include <asm/hw/interrupts.h> +#include <asm/atomic.h> -/* # of lm32 interrupts */ -#define NR_IRQS (32) +#define NR_IRQS 32 +#include <asm-generic/irq.h> -/* # of lm32 irq levels */ -#define NR_IRQLVL 1 +#define NO_IRQ 0 -#define NO_IRQ (-1) - -#define IRQ_SYSTMR (IRQ_TIMER0) - -#include <linux/irq.h> - -#define irq_canonicalize(i) (i) - -extern unsigned long irq_err_count; -static inline void ack_bad_irq(int irq) +static inline uint32_t lm32_irq_pending(void) { - irq_err_count++; + uint32_t ip; + __asm__ __volatile__("rcsr %0, IP" : "=r"(ip) : ); + return ip; } -/* in arch/lm32/kernel/irq.c */ -void lm32_irq_mask(unsigned int irq); -void lm32_irq_multimask(unsigned long mask); -void lm32_irq_unmask(unsigned int irq); -void lm32_irq_ack(unsigned int irq); -unsigned long lm32_irq_pending(void); -void lm32_irq_disable(unsigned int irq); -void lm32_irq_enable(unsigned int irq); +static inline void lm32_irq_ack(unsigned int irq) +{ + uint32_t mask = (1 << irq); + __asm__ __volatile__("wcsr IP, %0" : : "r"(mask) ); +} #endif /* _LM32_ASM_IRQ_H_ */ diff --git a/arch/lm32/include/asm/thread_info.h b/arch/lm32/include/asm/thread_info.h index 221f603..9cbfe45 100644 --- a/arch/lm32/include/asm/thread_info.h +++ b/arch/lm32/include/asm/thread_info.h @@ -77,8 +77,6 @@ static inline struct thread_info *current_thread_info(void) #define TI_CPU 12 #define TI_PRE_COUNT 16 -#define PREEMPT_ACTIVE 0x4000000 - /* * thread information flag bit numbers */ diff --git a/arch/lm32/kernel/entry.S b/arch/lm32/kernel/entry.S index aec9e65..24c1740 100644 --- a/arch/lm32/kernel/entry.S +++ b/arch/lm32/kernel/entry.S @@ -363,22 +363,49 @@ _long_interrupt_handler: sw (sp+120), ra calli _save_irq_frame - rcsr r2, IP - rcsr r3, IM + /* Workaround hardware hazard. Sometimes the interrupt handler is entered + * although interrupts are disabled */ + rcsr r1, IE + andi r1, r1, 0x2 + be r1, r0, 5f + + rcsr r3, IP + rcsr r4, IM mvi r1, 0 - and r2, r2, r3 - mvi r3, 1 - be r2, r0, 3f + and r3, r3, r4 + be r3, r0, 5f + + andi r4, r3, 0xffff + bne r4, r0, 1f + sri r3, r3, 16 + addi r1, r1, 16 1: - and r4, r2, r3 - bne r4, r0, 2f - sli r3, r3, 1 - addi r1, r1, 1 - bi 1b + andi r4, r3, 0xff + bne r4, r0, 2f + sri r3, r3, 8 + addi r1, r1, 8 2: + andi r4, r3, 0xf + bne r4, r0, 3f + sri r3, r3, 4 + addi r1, r1, 4 +3: + andi r4, r3, 0x3 + bne r4, r0, 4f + sri r3, r3, 2 + addi r1, r1, 2 +4: + andi r4, r3, 0x1 + bne r4, r0, 5f + sri r3, r3, 1 + addi r1, r1, 1 +5: + addi r2, sp, 4 calli asm_do_IRQ -3: + addi r1, sp, 4 + calli manage_signals_irq +5: bi _restore_irq_frame_and_return _save_irq_frame: diff --git a/arch/lm32/kernel/init_task.c b/arch/lm32/kernel/init_task.c index 6f8c357..c46a26a 100644 --- a/arch/lm32/kernel/init_task.c +++ b/arch/lm32/kernel/init_task.c @@ -33,6 +33,7 @@ #include <linux/init.h> #include <linux/init_task.h> #include <linux/mqueue.h> +#include <linux/hardirq.h> #include <asm/uaccess.h> #include <asm/pgtable.h> diff --git a/arch/lm32/kernel/irq.c b/arch/lm32/kernel/irq.c index 08d0524..2a695a3 100644 --- a/arch/lm32/kernel/irq.c +++ b/arch/lm32/kernel/irq.c @@ -1,183 +1,85 @@ -/* - * (C) Copyright 2007 - * Theobroma Systems <www.theobroma-systems.com> - * - * See file CREDITS for list of people who contributed to this - * project. - * - * 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., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA - */ - -#include <linux/module.h> -#include <linux/types.h> +#include <linux/atomic.h> #include <linux/init.h> -#include <linux/sched.h> -#include <linux/interrupt.h> #include <linux/irq.h> #include <linux/kernel_stat.h> -#include <linux/errno.h> #include <linux/seq_file.h> +#include <linux/types.h> -unsigned long irq_err_count; -/* this is the current mask in IM */ -static unsigned long lm32_current_irq_mask = 0; -static unsigned long lm32_current_irq_disable = 0; - -/* - * NOP IRQ functions - */ -static void noop(unsigned int irq) -{ -} - -static unsigned int noop_ret(unsigned int irq) -{ - return 0; -} - -void lm32_irq_mask(unsigned int irq) -{ - unsigned long flags; - unsigned long mask = ~(1 << irq); - - local_irq_save(flags); - - mask &= lm32_current_irq_mask; - lm32_current_irq_mask = mask; - - asm volatile ("wcsr IM, %0" : : "r"(mask)); - - local_irq_restore(flags); -} - -void lm32_irq_multimask(unsigned long mask) -{ - unsigned long flags; - - local_irq_save(flags); - - lm32_current_irq_mask &= ~mask; - - asm volatile ("wcsr IM, %0" : : "r"(lm32_current_irq_mask)); - - local_irq_restore(flags); -} - -void lm32_irq_unmask(unsigned int irq) -{ - unsigned long flags; - unsigned long mask = (1 << irq); - - if( !(lm32_current_irq_mask & mask) && !(lm32_current_irq_disable & mask)) { - local_irq_save(flags); - - mask |= lm32_current_irq_mask; - lm32_current_irq_mask = mask; - - asm volatile ("wcsr IM, %0" : : "r"(mask)); - - local_irq_restore(flags); - } -} +atomic_t irq_err_count; -void lm32_irq_disable(unsigned int irq) +static inline uint32_t lm32_pic_get_irq_mask(struct irq_data *data) { - unsigned long mask = (1 << irq); - - mask |= lm32_current_irq_disable; - lm32_current_irq_disable = mask; - - lm32_irq_mask(irq); + return (uint32_t)irq_data_get_irq_chip_data(data); } -void lm32_irq_enable(unsigned int irq) +static void lm32_pic_irq_mask(struct irq_data *data) { - unsigned long mask = ~(1 << irq); - - mask &= lm32_current_irq_disable; - lm32_current_irq_disable = mask; - - lm32_irq_unmask(irq); + uint32_t mask = lm32_pic_get_irq_mask(data); + uint32_t im; + + __asm__ __volatile__("rcsr %0, IM\n" + "not %0, %0\n" + "nor %0, %0, %1\n" + "wcsr IM, %0" + : "=&r"(im) : "r"(mask)); } -void lm32_irq_ack(unsigned int irq) +static void lm32_pic_irq_unmask(struct irq_data *data) { - unsigned long mask = 1 << irq; + uint32_t mask = lm32_pic_get_irq_mask(data); + uint32_t im; - asm volatile ("wcsr IP, %0" : : "r"(mask)); + __asm__ __volatile__("rcsr %0, IM\n" + "or %0, %0, %1\n" + "wcsr IM, %0\n" + : "=&r"(im) : "r"(mask)); } -void lm32_irq_mask_ack(unsigned int irq) +static void lm32_pic_irq_ack(struct irq_data *data) { - unsigned long flags; - unsigned long mask = ~(1 << irq); - unsigned long ack = 1 << irq; - - local_irq_save(flags); - - mask &= lm32_current_irq_mask; - lm32_current_irq_mask = mask; - - asm volatile ("wcsr IM, %0" : : "r"(mask)); - - asm volatile ("wcsr IP, %0" : : "r"(ack)); + uint32_t mask = lm32_pic_get_irq_mask(data); - local_irq_restore(flags); + __asm__ __volatile__("wcsr IP, %0" : : "r"(mask)); } -unsigned long lm32_irq_pending() +static void lm32_pic_irq_mask_ack(struct irq_data *data) { - unsigned long ret; - - asm volatile ("rcsr %0, IP" : "=r"(ret) : ); - - return ret; + uint32_t mask = lm32_pic_get_irq_mask(data); + uint32_t im; + + __asm__ __volatile__("rcsr %0, IM\n" + "not %0, %0\n" + "nor %0, %0, %1\n" + "wcsr IM, %0\n" + "wcsr IP, %1\n" + : "=&r"(im) : "r"(mask) ); } -/* - * LM32 IRQs implementation - */ -struct irq_chip lm32_internal_irq_chip = { - .name = "LM32", - .startup = noop_ret, - .shutdown = noop, - .enable = noop, - .disable = lm32_irq_mask, - .ack = lm32_irq_ack, - .mask = lm32_irq_mask, - .unmask = lm32_irq_unmask, - .mask_ack = lm32_irq_mask_ack, - .end = noop, +static struct irq_chip lm32_irq_chip = { + .name = "LM32 PIC", + .irq_ack = lm32_pic_irq_ack, + .irq_mask = lm32_pic_irq_mask, + .irq_mask_ack = lm32_pic_irq_mask_ack, + .irq_unmask = lm32_pic_irq_unmask, }; void __init init_IRQ(void) { - int irq; + unsigned int irq; local_irq_disable(); + __asm__ __volatile__("wcsr IM, r0"); for (irq = 0; irq < NR_IRQS; irq++) { - set_irq_chip(irq, &lm32_internal_irq_chip); - set_irq_handler(irq, handle_simple_irq); + set_irq_chip(irq, &lm32_irq_chip); + set_irq_chip_data(irq, (void *)(1 << irq)); + set_irq_handler(irq, handle_level_irq); } } int show_interrupts(struct seq_file *p, void *v) { - int i = *(loff_t *) v; + int i = *(loff_t *)v; struct irqaction * action; unsigned long flags; @@ -185,10 +87,11 @@ int show_interrupts(struct seq_file *p, void *v) { raw_spin_lock_irqsave(&irq_desc[i].lock, flags); action = irq_desc[i].action; - if( action ) + if (action) { seq_printf(p, "%3d: ", i); - seq_printf(p, "%10s ", irq_desc[i].chip->name ? : "-"); + seq_printf(p, "%10u", kstat_irqs(i)); + seq_printf(p, "%14s ", get_irq_chip(i)->name ? : "-"); seq_printf(p, " %s", action->name); for (action = action->next; action; action = action->next) seq_printf(p, ", %s", action->name); @@ -196,33 +99,19 @@ int show_interrupts(struct seq_file *p, void *v) } raw_spin_unlock_irqrestore(&irq_desc[i].lock, flags); } else if (i == NR_IRQS) { - seq_printf(p, "Errors: %lu\n", irq_err_count); + seq_printf(p, "\nErrors: %u\n", atomic_read(&irq_err_count)); } return 0; } -asmlinkage void manage_signals_irq(struct pt_regs* regs); - -/* - * do_IRQ handles all hardware IRQ's. Decoded IRQs should not - * come via this function. Instead, they should provide their - * own 'handler' - */ asmlinkage void asm_do_IRQ(unsigned int irq, struct pt_regs *regs) { struct pt_regs *old_regs = set_irq_regs(regs); irq_enter(); - - lm32_irq_mask(irq); - generic_handle_irq(irq); /* < this (re)enables interrupts globally */ - lm32_irq_ack(irq); - lm32_irq_unmask(irq); - + generic_handle_irq(irq); irq_exit(); set_irq_regs(old_regs); - - manage_signals_irq(regs); } diff --git a/drivers/input/keyboard/softusb.c b/drivers/input/keyboard/softusb.c index 3c6abfc..523a8f31 100644 --- a/drivers/input/keyboard/softusb.c +++ b/drivers/input/keyboard/softusb.c @@ -178,8 +178,6 @@ static int __devinit softusb_probe(struct platform_device *pdev) return -EBUSY; } - lm32_irq_unmask(IRQ_USB); - printk(KERN_INFO "milkymist_softusb: softusb at 0x%08x irq %d\n", SOFTUSB_PMEM_BASE, IRQ_USB); diff --git a/drivers/net/milkymist_minimac.c b/drivers/net/milkymist_minimac.c index d037026..06b5d61 100644 --- a/drivers/net/milkymist_minimac.c +++ b/drivers/net/milkymist_minimac.c @@ -257,7 +257,7 @@ static irqreturn_t minimac_interrupt_rx(int irq, void *dev_id) out_be32(CSR_MINIMAC_SETUP, 0); } - lm32_irq_disable(dev->irq); + disable_irq(dev->irq); napi_schedule(&tp->napi); @@ -291,7 +291,7 @@ static int minimac_poll(struct napi_struct *napi, int budget) if (work_done < budget) { napi_complete(napi); - lm32_irq_enable(dev->irq); + enable_irq(dev->irq); } return work_done; @@ -302,7 +302,7 @@ static int minimac_open(struct net_device *dev) struct minimac *tp = netdev_priv(dev); int ret; - ret = request_irq(dev->irq, minimac_interrupt_rx, IRQF_DISABLED, "milkymist_minimac RX", dev); + ret = request_irq(dev->irq, minimac_interrupt_rx, 0, "milkymist_minimac RX", dev); if (ret) return -1; @@ -314,9 +314,6 @@ static int minimac_open(struct net_device *dev) netif_start_queue(dev); napi_enable(&tp->napi); - lm32_irq_unmask(dev->irq); - lm32_irq_unmask((dev->irq)+1); - return 0; } @@ -329,9 +326,6 @@ static int minimac_stop(struct net_device *dev) netif_stop_queue(dev); - lm32_irq_mask(dev->irq); - lm32_irq_mask(dev->irq+1); - free_irq(dev->irq, dev); free_irq(dev->irq+1, dev); diff --git a/drivers/serial/milkymist_uart.c b/drivers/serial/milkymist_uart.c index ec505db..f7bc945 100644 --- a/drivers/serial/milkymist_uart.c +++ b/drivers/serial/milkymist_uart.c @@ -44,6 +44,7 @@ #define MILKYMISTUART_MINOR 64 static volatile int tx_cts; +static bool milkymist_uart_irqs_enabled; /* these two will be initialized by milkymistuart_init */ static struct uart_port milkymistuart_ports[1]; @@ -240,16 +241,15 @@ static int milkymistuart_startup(struct uart_port *port) return -EBUSY; } - lm32_irq_unmask(IRQ_UARTRX); - lm32_irq_unmask(IRQ_UARTTX); + milkymist_uart_irqs_enabled = true; return 0; } static void milkymistuart_shutdown(struct uart_port *port) { - lm32_irq_mask(IRQ_UARTRX); - lm32_irq_mask(IRQ_UARTTX); + milkymist_uart_irqs_enabled = false; + free_irq(IRQ_UARTRX, port); free_irq(IRQ_UARTTX, port); } @@ -309,9 +309,13 @@ static int milkymistuart_verify_port(struct uart_port *port, struct serial_struc #ifdef CONFIG_SERIAL_MILKYMIST_CONSOLE static void milkymist_console_putchar(struct uart_port *port, int ch) { + if (milkymist_uart_irqs_enabled) + disable_irq(IRQ_UARTTX); out_be32(CSR_UART_RXTX,ch); while(!(lm32_irq_pending() & (1 << IRQ_UARTTX))); lm32_irq_ack(IRQ_UARTTX); + if (milkymist_uart_irqs_enabled) + enable_irq(IRQ_UARTTX); } /* -- 1.7.2.3 _______________________________________________ http://lists.milkymist.org/listinfo.cgi/devel-milkymist.org IRC: #milkymist@Freenode Twitter: www.twitter.com/milkymistvj Ideas? http://milkymist.uservoice.com
