Merge git://git.kernel.org/pub/scm/linux/kernel/git/mason/btrfs-unstable
[pandora-kernel.git] / arch / frv / kernel / irq-mb93091.c
index 1381abc..4dd9ada 100644 (file)
 #include <linux/interrupt.h>
 #include <linux/init.h>
 #include <linux/irq.h>
+#include <linux/bitops.h>
 
 #include <asm/io.h>
 #include <asm/system.h>
-#include <asm/bitops.h>
 #include <asm/delay.h>
 #include <asm/irq.h>
 #include <asm/irc-regs.h>
-#include <asm/irq-routing.h>
 
 #define __reg16(ADDR) (*(volatile unsigned short *)(ADDR))
 
 #define __get_IFR()    ({ __reg16(0xffc0000c); })
 #define __clr_IFR(M)   do { __reg16(0xffc0000c) = ~(M); wmb(); } while(0)
 
-static void frv_fpga_doirq(struct irq_source *source);
-static void frv_fpga_control(struct irq_group *group, int irq, int on);
 
-/*****************************************************************************/
 /*
- * FPGA IRQ multiplexor
+ * on-motherboard FPGA PIC operations
  */
-static struct irq_source frv_fpga[4] = {
-#define __FPGA(X, M)                                   \
-       [X] = {                                         \
-               .muxname        = "fpga."#X,            \
-               .irqmask        = M,                    \
-               .doirq          = frv_fpga_doirq,       \
-       }
+static void frv_fpga_mask(unsigned int irq)
+{
+       uint16_t imr = __get_IMR();
 
-       __FPGA(0, 0x0028),
-       __FPGA(1, 0x0050),
-       __FPGA(2, 0x1c00),
-       __FPGA(3, 0x6386),
-};
+       imr |= 1 << (irq - IRQ_BASE_FPGA);
 
-static struct irq_group frv_fpga_irqs = {
-       .first_irq      = IRQ_BASE_FPGA,
-       .control        = frv_fpga_control,
-       .sources = {
-               [ 1] = &frv_fpga[3],
-               [ 2] = &frv_fpga[3],
-               [ 3] = &frv_fpga[0],
-               [ 4] = &frv_fpga[1],
-               [ 5] = &frv_fpga[0],
-               [ 6] = &frv_fpga[1],
-               [ 7] = &frv_fpga[3],
-               [ 8] = &frv_fpga[3],
-               [ 9] = &frv_fpga[3],
-               [10] = &frv_fpga[2],
-               [11] = &frv_fpga[2],
-               [12] = &frv_fpga[2],
-               [13] = &frv_fpga[3],
-               [14] = &frv_fpga[3],
-       },
-};
+       __set_IMR(imr);
+}
 
+static void frv_fpga_ack(unsigned int irq)
+{
+       __clr_IFR(1 << (irq - IRQ_BASE_FPGA));
+}
 
-static void frv_fpga_control(struct irq_group *group, int index, int on)
+static void frv_fpga_mask_ack(unsigned int irq)
 {
        uint16_t imr = __get_IMR();
 
-       if (on)
-               imr &= ~(1 << index);
-       else
-               imr |= 1 << index;
+       imr |= 1 << (irq - IRQ_BASE_FPGA);
+       __set_IMR(imr);
+
+       __clr_IFR(1 << (irq - IRQ_BASE_FPGA));
+}
+
+static void frv_fpga_unmask(unsigned int irq)
+{
+       uint16_t imr = __get_IMR();
+
+       imr &= ~(1 << (irq - IRQ_BASE_FPGA));
 
        __set_IMR(imr);
 }
 
-static void frv_fpga_doirq(struct irq_source *source)
+static struct irq_chip frv_fpga_pic = {
+       .name           = "mb93091",
+       .ack            = frv_fpga_ack,
+       .mask           = frv_fpga_mask,
+       .mask_ack       = frv_fpga_mask_ack,
+       .unmask         = frv_fpga_unmask,
+};
+
+/*
+ * FPGA PIC interrupt handler
+ */
+static irqreturn_t fpga_interrupt(int irq, void *_mask)
 {
-       uint16_t mask, imr;
+       uint16_t imr, mask = (unsigned long) _mask;
 
        imr = __get_IMR();
-       mask = source->irqmask & ~imr & __get_IFR();
-       if (mask) {
-               __set_IMR(imr | mask);
-               __clr_IFR(mask);
-               distribute_irqs(&frv_fpga_irqs, mask);
-               __set_IMR(imr);
+       mask = mask & ~imr & __get_IFR();
+
+       /* poll all the triggered IRQs */
+       while (mask) {
+               int irq;
+
+               asm("scan %1,gr0,%0" : "=r"(irq) : "r"(mask));
+               irq = 31 - irq;
+               mask &= ~(1 << irq);
+
+               generic_handle_irq(IRQ_BASE_FPGA + irq);
        }
+
+       return IRQ_HANDLED;
 }
 
+/*
+ * define an interrupt action for each FPGA PIC output
+ * - use dev_id to indicate the FPGA PIC input to output mappings
+ */
+static struct irqaction fpga_irq[4]  = {
+       [0] = {
+               .handler        = fpga_interrupt,
+               .flags          = IRQF_DISABLED | IRQF_SHARED,
+               .name           = "fpga.0",
+               .dev_id         = (void *) 0x0028UL,
+       },
+       [1] = {
+               .handler        = fpga_interrupt,
+               .flags          = IRQF_DISABLED | IRQF_SHARED,
+               .name           = "fpga.1",
+               .dev_id         = (void *) 0x0050UL,
+       },
+       [2] = {
+               .handler        = fpga_interrupt,
+               .flags          = IRQF_DISABLED | IRQF_SHARED,
+               .name           = "fpga.2",
+               .dev_id         = (void *) 0x1c00UL,
+       },
+       [3] = {
+               .handler        = fpga_interrupt,
+               .flags          = IRQF_DISABLED | IRQF_SHARED,
+               .name           = "fpga.3",
+               .dev_id         = (void *) 0x6386UL,
+       }
+};
+
+/*
+ * initialise the motherboard FPGA's PIC
+ */
 void __init fpga_init(void)
 {
+       int irq;
+
+       /* all PIC inputs are all set to be low-level driven, apart from the
+        * NMI button (15) which is fixed at falling-edge
+        */
        __set_IMR(0x7ffe);
        __clr_IFR(0x0000);
 
-       frv_irq_route_external(&frv_fpga[0], IRQ_CPU_EXTERNAL0);
-       frv_irq_route_external(&frv_fpga[1], IRQ_CPU_EXTERNAL1);
-       frv_irq_route_external(&frv_fpga[2], IRQ_CPU_EXTERNAL2);
-       frv_irq_route_external(&frv_fpga[3], IRQ_CPU_EXTERNAL3);
-       frv_irq_set_group(&frv_fpga_irqs);
+       for (irq = IRQ_BASE_FPGA + 1; irq <= IRQ_BASE_FPGA + 14; irq++)
+               set_irq_chip_and_handler(irq, &frv_fpga_pic, handle_level_irq);
+
+       set_irq_chip_and_handler(IRQ_FPGA_NMI, &frv_fpga_pic, handle_edge_irq);
+
+       /* the FPGA drives the first four external IRQ inputs on the CPU PIC */
+       setup_irq(IRQ_CPU_EXTERNAL0, &fpga_irq[0]);
+       setup_irq(IRQ_CPU_EXTERNAL1, &fpga_irq[1]);
+       setup_irq(IRQ_CPU_EXTERNAL2, &fpga_irq[2]);
+       setup_irq(IRQ_CPU_EXTERNAL3, &fpga_irq[3]);
 }