MIPS: lantiq: rework external irq code
authorJohn Crispin <blogic@openwrt.org>
Sat, 19 Jan 2013 08:54:27 +0000 (08:54 +0000)
committerJohn Crispin <blogic@openwrt.org>
Sat, 16 Feb 2013 23:15:18 +0000 (00:15 +0100)
This code makes the irqs used by the EIU loadable from the DT. Additionally we
add a helper that allows the pinctrl layer to map external irqs to real irq
numbers.

Signed-off-by: John Crispin <blogic@openwrt.org>
Patchwork: http://patchwork.linux-mips.org/patch/4818/

arch/mips/include/asm/mach-lantiq/lantiq.h
arch/mips/lantiq/irq.c

index 76be7a0..f196cce 100644 (file)
@@ -34,6 +34,7 @@ extern spinlock_t ebu_lock;
 extern void ltq_disable_irq(struct irq_data *data);
 extern void ltq_mask_and_ack_irq(struct irq_data *data);
 extern void ltq_enable_irq(struct irq_data *data);
+extern int ltq_eiu_get_irq(int exin);
 
 /* clock handling */
 extern int clk_activate(struct clk *clk);
index a7935bf..5119487 100644 (file)
 /* register definitions - external irqs */
 #define LTQ_EIU_EXIN_C         0x0000
 #define LTQ_EIU_EXIN_INIC      0x0004
+#define LTQ_EIU_EXIN_INC       0x0008
 #define LTQ_EIU_EXIN_INEN      0x000C
 
-/* irq numbers used by the external interrupt unit (EIU) */
-#define LTQ_EIU_IR0            (INT_NUM_IM4_IRL0 + 30)
-#define LTQ_EIU_IR1            (INT_NUM_IM3_IRL0 + 31)
-#define LTQ_EIU_IR2            (INT_NUM_IM1_IRL0 + 26)
-#define LTQ_EIU_IR3            INT_NUM_IM1_IRL0
-#define LTQ_EIU_IR4            (INT_NUM_IM1_IRL0 + 1)
-#define LTQ_EIU_IR5            (INT_NUM_IM1_IRL0 + 2)
-#define LTQ_EIU_IR6            (INT_NUM_IM2_IRL0 + 30)
-#define XWAY_EXIN_COUNT                3
+/* number of external interrupts */
 #define MAX_EIU                        6
 
 /* the performance counter */
 int gic_present;
 #endif
 
-static unsigned short ltq_eiu_irq[MAX_EIU] = {
-       LTQ_EIU_IR0,
-       LTQ_EIU_IR1,
-       LTQ_EIU_IR2,
-       LTQ_EIU_IR3,
-       LTQ_EIU_IR4,
-       LTQ_EIU_IR5,
-};
-
 static int exin_avail;
+static struct resource ltq_eiu_irq[MAX_EIU];
 static void __iomem *ltq_icu_membase[MAX_IM];
 static void __iomem *ltq_eiu_membase;
 static struct irq_domain *ltq_domain;
 
+int ltq_eiu_get_irq(int exin)
+{
+       if (exin < exin_avail)
+               return ltq_eiu_irq[exin].start;
+       return -1;
+}
+
 void ltq_disable_irq(struct irq_data *d)
 {
        u32 ier = LTQ_ICU_IM0_IER;
@@ -128,19 +120,65 @@ void ltq_enable_irq(struct irq_data *d)
        ltq_icu_w32(im, ltq_icu_r32(im, ier) | BIT(offset), ier);
 }
 
+static int ltq_eiu_settype(struct irq_data *d, unsigned int type)
+{
+       int i;
+
+       for (i = 0; i < MAX_EIU; i++) {
+               if (d->hwirq == ltq_eiu_irq[i].start) {
+                       int val = 0;
+                       int edge = 0;
+
+                       switch (type) {
+                       case IRQF_TRIGGER_NONE:
+                               break;
+                       case IRQF_TRIGGER_RISING:
+                               val = 1;
+                               edge = 1;
+                               break;
+                       case IRQF_TRIGGER_FALLING:
+                               val = 2;
+                               edge = 1;
+                               break;
+                       case IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING:
+                               val = 3;
+                               edge = 1;
+                               break;
+                       case IRQF_TRIGGER_HIGH:
+                               val = 5;
+                               break;
+                       case IRQF_TRIGGER_LOW:
+                               val = 6;
+                               break;
+                       default:
+                               pr_err("invalid type %d for irq %ld\n",
+                                       type, d->hwirq);
+                               return -EINVAL;
+                       }
+
+                       if (edge)
+                               irq_set_handler(d->hwirq, handle_edge_irq);
+
+                       ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_C) |
+                               (val << (i * 4)), LTQ_EIU_EXIN_C);
+               }
+       }
+
+       return 0;
+}
+
 static unsigned int ltq_startup_eiu_irq(struct irq_data *d)
 {
        int i;
 
        ltq_enable_irq(d);
        for (i = 0; i < MAX_EIU; i++) {
-               if (d->hwirq == ltq_eiu_irq[i]) {
-                       /* low level - we should really handle set_type */
-                       ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_C) |
-                               (0x6 << (i * 4)), LTQ_EIU_EXIN_C);
+               if (d->hwirq == ltq_eiu_irq[i].start) {
+                       /* by default we are low level triggered */
+                       ltq_eiu_settype(d, IRQF_TRIGGER_LOW);
                        /* clear all pending */
-                       ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_INIC) & ~BIT(i),
-                               LTQ_EIU_EXIN_INIC);
+                       ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_INC) & ~BIT(i),
+                               LTQ_EIU_EXIN_INC);
                        /* enable */
                        ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_INEN) | BIT(i),
                                LTQ_EIU_EXIN_INEN);
@@ -157,7 +195,7 @@ static void ltq_shutdown_eiu_irq(struct irq_data *d)
 
        ltq_disable_irq(d);
        for (i = 0; i < MAX_EIU; i++) {
-               if (d->hwirq == ltq_eiu_irq[i]) {
+               if (d->hwirq == ltq_eiu_irq[i].start) {
                        /* disable */
                        ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_INEN) & ~BIT(i),
                                LTQ_EIU_EXIN_INEN);
@@ -186,6 +224,7 @@ static struct irq_chip ltq_eiu_type = {
        .irq_ack = ltq_ack_irq,
        .irq_mask = ltq_disable_irq,
        .irq_mask_ack = ltq_mask_and_ack_irq,
+       .irq_set_type = ltq_eiu_settype,
 };
 
 static void ltq_hw_irqdispatch(int module)
@@ -301,7 +340,7 @@ static int icu_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
                return 0;
 
        for (i = 0; i < exin_avail; i++)
-               if (hw == ltq_eiu_irq[i])
+               if (hw == ltq_eiu_irq[i].start)
                        chip = &ltq_eiu_type;
 
        irq_set_chip_and_handler(hw, chip, handle_level_irq);
@@ -323,7 +362,7 @@ int __init icu_of_init(struct device_node *node, struct device_node *parent)
 {
        struct device_node *eiu_node;
        struct resource res;
-       int i;
+       int i, ret;
 
        for (i = 0; i < MAX_IM; i++) {
                if (of_address_to_resource(node, i, &res))
@@ -340,17 +379,19 @@ int __init icu_of_init(struct device_node *node, struct device_node *parent)
        }
 
        /* the external interrupts are optional and xway only */
-       eiu_node = of_find_compatible_node(NULL, NULL, "lantiq,eiu");
+       eiu_node = of_find_compatible_node(NULL, NULL, "lantiq,eiu-xway");
        if (eiu_node && !of_address_to_resource(eiu_node, 0, &res)) {
                /* find out how many external irq sources we have */
-               const __be32 *count = of_get_property(node,
-                                                       "lantiq,count", NULL);
+               exin_avail = of_irq_count(eiu_node);
 
-               if (count)
-                       exin_avail = *count;
                if (exin_avail > MAX_EIU)
                        exin_avail = MAX_EIU;
 
+               ret = of_irq_to_resource_table(eiu_node,
+                                               ltq_eiu_irq, exin_avail);
+               if (ret != exin_avail)
+                       panic("failed to load external irq resources\n");
+
                if (request_mem_region(res.start, resource_size(&res),
                                                        res.name) < 0)
                        pr_err("Failed to request eiu memory");