X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Farm%2Fmach-s3c2410%2Firq.c;h=1951a0323f178b6a66effef6fedb80f93177588f;hb=c7b5ebbddf7bcd3651947760f423e3783bbe6573;hp=878b9740d63e12e62f4b9c39c0d61e47a57b8fd1;hpb=a2c21200f1c81b08cb55e417b68150bba439b646;p=linux-2.6.git diff --git a/arch/arm/mach-s3c2410/irq.c b/arch/arm/mach-s3c2410/irq.c index 878b9740d..1951a0323 100644 --- a/arch/arm/mach-s3c2410/irq.c +++ b/arch/arm/mach-s3c2410/irq.c @@ -1,7 +1,7 @@ /* linux/arch/arm/mach-s3c2410/irq.c * - * Copyright (c) 2003 Simtec Electronics - * Ben Dooks + * Copyright (c) 2003,2004 Simtec Electronics + * Ben Dooks * * 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 @@ -17,7 +17,23 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * - */ + * Changelog: + * + * 22-Jul-2004 Ben Dooks + * Fixed compile warnings + * + * 22-Jul-2004 Roc Wu + * Fixed s3c_extirq_type + * + * 21-Jul-2004 Arnaud Patard (Rtp) + * Addition of ADC/TC demux + * + * 04-Oct-2004 Klaus Fetscher + * Fix for set_irq_type() on low EINT numbers + * + * 05-Oct-2004 Ben Dooks + * Tidy up KF's patch and sort out new release +*/ #include @@ -34,11 +50,8 @@ #include #include -#include +#include -#if 0 -#include -#endif #define irqdbf(x...) #define irqdbf2(x...) @@ -177,12 +190,84 @@ s3c_irqext_unmask(unsigned int irqno) s3c_irq_unmask((irqno <= (IRQ_EINT7 - EXTINT_OFF)) ? IRQ_EINT4t7 : IRQ_EINT8t23); } -/* todo - put type handler in here */ - static int s3c_irqext_type(unsigned int irq, unsigned int type) { - irqdbf("s3c_irqext_type: called for irq %d, type %d\n", irq, type); + unsigned long extint_reg; + unsigned long gpcon_reg; + unsigned long gpcon_offset, extint_offset; + unsigned long newvalue = 0, value; + + if ((irq >= IRQ_EINT0) && (irq <= IRQ_EINT3)) + { + gpcon_reg = S3C2410_GPFCON; + extint_reg = S3C2410_EXTINT0; + gpcon_offset = (irq - IRQ_EINT0) * 2; + extint_offset = (irq - IRQ_EINT0) * 4; + } + else if ((irq >= IRQ_EINT4) && (irq <= IRQ_EINT7)) + { + gpcon_reg = S3C2410_GPFCON; + extint_reg = S3C2410_EXTINT0; + gpcon_offset = (irq - (EXTINT_OFF)) * 2; + extint_offset = (irq - (EXTINT_OFF)) * 4; + } + else if ((irq >= IRQ_EINT8) && (irq <= IRQ_EINT15)) + { + gpcon_reg = S3C2410_GPGCON; + extint_reg = S3C2410_EXTINT1; + gpcon_offset = (irq - IRQ_EINT8) * 2; + extint_offset = (irq - IRQ_EINT8) * 4; + } + else if ((irq >= IRQ_EINT16) && (irq <= IRQ_EINT23)) + { + gpcon_reg = S3C2410_GPGCON; + extint_reg = S3C2410_EXTINT2; + gpcon_offset = (irq - IRQ_EINT8) * 2; + extint_offset = (irq - IRQ_EINT16) * 4; + } else + return -1; + + /* Set the GPIO to external interrupt mode */ + value = __raw_readl(gpcon_reg); + value = (value & ~(3 << gpcon_offset)) | (0x02 << gpcon_offset); + __raw_writel(value, gpcon_reg); + + /* Set the external interrupt to pointed trigger type */ + switch (type) + { + case IRQT_NOEDGE: + printk(KERN_WARNING "No edge setting!\n"); + break; + + case IRQT_RISING: + newvalue = S3C2410_EXTINT_RISEEDGE; + break; + + case IRQT_FALLING: + newvalue = S3C2410_EXTINT_FALLEDGE; + break; + + case IRQT_BOTHEDGE: + newvalue = S3C2410_EXTINT_BOTHEDGE; + break; + + case IRQT_LOW: + newvalue = S3C2410_EXTINT_LOWLEV; + break; + + case IRQT_HIGH: + newvalue = S3C2410_EXTINT_HILEV; + break; + + default: + printk(KERN_ERR "No such irq type %d", type); + return -1; + } + + value = __raw_readl(extint_reg); + value = (value & ~(7 << extint_offset)) | (newvalue << extint_offset); + __raw_writel(value, extint_reg); return 0; } @@ -194,6 +279,13 @@ static struct irqchip s3c_irqext_chip = { .type = s3c_irqext_type }; +static struct irqchip s3c_irq_eint0t4 = { + .ack = s3c_irq_ack, + .mask = s3c_irq_mask, + .unmask = s3c_irq_unmask, + .type = s3c_irqext_type +}; + /* mask values for the parent registers for each of the interrupt types */ #define INTMSK_UART0 (1UL << (IRQ_UART0 - IRQ_EINT0)) @@ -368,37 +460,37 @@ static struct irqchip s3c_irq_adc = { .ack = s3c_irq_adc_ack, }; -#if 0 -/* LCD (todo) */ - -static void -s3c_irq_lcd_mask(unsigned int irqno) +/* irq demux for adc */ +static void s3c_irq_demux_adc(unsigned int irq, + struct irqdesc *desc, + struct pt_regs *regs) { + unsigned int subsrc, submsk; + unsigned int offset = 9; + struct irqdesc *mydesc; -} - -static void -s3c_irq_lcd_unmask(unsigned int irqno) -{ + /* read the current pending interrupts, and the mask + * for what it is available */ -} + subsrc = __raw_readl(S3C2410_SUBSRCPND); + submsk = __raw_readl(S3C2410_INTSUBMSK); -static void -s3c_irq_lcd_ack(unsigned int irqno) -{ + subsrc &= ~submsk; + subsrc >>= offset; + subsrc &= 3; + if (subsrc != 0) { + if (subsrc & 1) { + mydesc = irq_desc + IRQ_TC; + mydesc->handle( IRQ_TC, mydesc, regs); + } + if (subsrc & 2) { + mydesc = irq_desc + IRQ_ADC; + mydesc->handle(IRQ_ADC, mydesc, regs); + } + } } -static struct irqchip s3c_irq_lcd = { - .mask = s3c_irq_lcd_mask, - .unmask = s3c_irq_lcd_unmask, - .ack = s3c_irq_lcd_ack, -}; -#endif - -/* irq demux */ - - static void s3c_irq_demux_uart(unsigned int start, struct pt_regs *regs) { @@ -466,11 +558,15 @@ s3c_irq_demux_uart2(unsigned int irq, s3c_irq_demux_uart(IRQ_S3CUART_RX2, regs); } - +/* s3c2410_init_irq + * + * Initialise S3C2410 IRQ system +*/ void __init s3c2410_init_irq(void) { unsigned long pend; + unsigned long last; int irqno; int i; @@ -478,48 +574,51 @@ void __init s3c2410_init_irq(void) /* first, clear all interrupts pending... */ + last = 0; for (i = 0; i < 4; i++) { pend = __raw_readl(S3C2410_EINTPEND); - if (pend == 0) + + if (pend == 0 || pend == last) break; + __raw_writel(pend, S3C2410_EINTPEND); printk("irq: clearing pending ext status %08x\n", (int)pend); + last = pend; } + last = 0; for (i = 0; i < 4; i++) { pend = __raw_readl(S3C2410_INTPND); - if (pend == 0) + + if (pend == 0 || pend == last) break; + __raw_writel(pend, S3C2410_SRCPND); __raw_writel(pend, S3C2410_INTPND); printk("irq: clearing pending status %08x\n", (int)pend); + last = pend; } + last = 0; for (i = 0; i < 4; i++) { pend = __raw_readl(S3C2410_SUBSRCPND); - if (pend == 0) + if (pend == 0 || pend == last) break; printk("irq: clearing subpending status %08x\n", (int)pend); __raw_writel(pend, S3C2410_SUBSRCPND); + last = pend; } /* register the main interrupts */ irqdbf("s3c2410_init_irq: registering s3c2410 interrupt handlers\n"); - for (irqno = IRQ_EINT0; irqno < IRQ_ADCPARENT; irqno++) { + for (irqno = IRQ_BATT_FLT; irqno <= IRQ_ADCPARENT; irqno++) { /* set all the s3c2410 internal irqs */ switch (irqno) { - - case IRQ_EINT4t7: - case IRQ_EINT8t23: - /* these are already dealt with, so should never - * appear */ - break; - /* deal with the special IRQs (cascaded) */ case IRQ_UART0: @@ -549,12 +648,18 @@ void __init s3c2410_init_irq(void) set_irq_chained_handler(IRQ_UART0, s3c_irq_demux_uart0); set_irq_chained_handler(IRQ_UART1, s3c_irq_demux_uart1); set_irq_chained_handler(IRQ_UART2, s3c_irq_demux_uart2); - //set_irq_chained_handler(IRQ_LCD, s3c_irq_demux_); - //set_irq_chained_handler(IRQ_ADCPARENT, s3c_irq_demux_); + set_irq_chained_handler(IRQ_ADCPARENT, s3c_irq_demux_adc); /* external interrupts */ + for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++) { + irqdbf("registering irq %d (ext int)\n", irqno); + set_irq_chip(irqno, &s3c_irq_eint0t4); + set_irq_handler(irqno, do_edge_IRQ); + set_irq_flags(irqno, IRQF_VALID); + } + for (irqno = IRQ_EINT4; irqno <= IRQ_EINT23; irqno++) { irqdbf("registering irq %d (extended s3c irq)\n", irqno); set_irq_chip(irqno, &s3c_irqext_chip);