[lantiq] bump to v3.8
[openwrt/svn-archive/archive.git] / target / linux / lantiq / patches-3.8 / 0019-MIPS-lantiq-rework-external-irq-code.patch
diff --git a/target/linux/lantiq/patches-3.8/0019-MIPS-lantiq-rework-external-irq-code.patch b/target/linux/lantiq/patches-3.8/0019-MIPS-lantiq-rework-external-irq-code.patch
new file mode 100644 (file)
index 0000000..f976765
--- /dev/null
@@ -0,0 +1,216 @@
+From d8f6bf3fb606ee8fdd5b7aff4aedb54e30792b84 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Sat, 19 Jan 2013 08:54:27 +0000
+Subject: [PATCH 19/40] MIPS: lantiq: rework external irq code
+
+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 |    1 +
+ arch/mips/lantiq/irq.c                     |  105 +++++++++++++++++++---------
+ 2 files changed, 74 insertions(+), 32 deletions(-)
+
+diff --git a/arch/mips/include/asm/mach-lantiq/lantiq.h b/arch/mips/include/asm/mach-lantiq/lantiq.h
+index 76be7a0..f196cce 100644
+--- a/arch/mips/include/asm/mach-lantiq/lantiq.h
++++ b/arch/mips/include/asm/mach-lantiq/lantiq.h
+@@ -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);
+diff --git a/arch/mips/lantiq/irq.c b/arch/mips/lantiq/irq.c
+index a7935bf..5119487 100644
+--- a/arch/mips/lantiq/irq.c
++++ b/arch/mips/lantiq/irq.c
+@@ -33,17 +33,10 @@
+ /* 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 */
+@@ -72,20 +65,19 @@
+ 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");
+-- 
+1.7.10.4
+