| 1 | From edd237c93d564e698e169a89d1b1b35248c5ef4a Mon Sep 17 00:00:00 2001 |
| 2 | From: John Crispin <blogic@openwrt.org> |
| 3 | Date: Mon, 3 Dec 2012 21:44:30 +0100 |
| 4 | Subject: [PATCH 105/123] MIPS: lantiq: rework external irq code |
| 5 | |
| 6 | This code makes the irqs used by the EIU loadable from the DT. Additionally we |
| 7 | add a helper that allows the pinctrl layer to map external irqs to real irq |
| 8 | numbers. |
| 9 | |
| 10 | Signed-off-by: John Crispin <blogic@openwrt.org> |
| 11 | --- |
| 12 | arch/mips/include/asm/mach-lantiq/lantiq.h | 1 + |
| 13 | arch/mips/lantiq/irq.c | 104 +++++++++++++++++++--------- |
| 14 | 2 files changed, 73 insertions(+), 32 deletions(-) |
| 15 | |
| 16 | diff --git a/arch/mips/include/asm/mach-lantiq/lantiq.h b/arch/mips/include/asm/mach-lantiq/lantiq.h |
| 17 | index 76be7a0..f196cce 100644 |
| 18 | --- a/arch/mips/include/asm/mach-lantiq/lantiq.h |
| 19 | +++ b/arch/mips/include/asm/mach-lantiq/lantiq.h |
| 20 | @@ -34,6 +34,7 @@ extern spinlock_t ebu_lock; |
| 21 | extern void ltq_disable_irq(struct irq_data *data); |
| 22 | extern void ltq_mask_and_ack_irq(struct irq_data *data); |
| 23 | extern void ltq_enable_irq(struct irq_data *data); |
| 24 | +extern int ltq_eiu_get_irq(int exin); |
| 25 | |
| 26 | /* clock handling */ |
| 27 | extern int clk_activate(struct clk *clk); |
| 28 | diff --git a/arch/mips/lantiq/irq.c b/arch/mips/lantiq/irq.c |
| 29 | index f36acd1..48407f6 100644 |
| 30 | --- a/arch/mips/lantiq/irq.c |
| 31 | +++ b/arch/mips/lantiq/irq.c |
| 32 | @@ -33,17 +33,10 @@ |
| 33 | /* register definitions - external irqs */ |
| 34 | #define LTQ_EIU_EXIN_C 0x0000 |
| 35 | #define LTQ_EIU_EXIN_INIC 0x0004 |
| 36 | +#define LTQ_EIU_EXIN_INC 0x0008 |
| 37 | #define LTQ_EIU_EXIN_INEN 0x000C |
| 38 | |
| 39 | -/* irq numbers used by the external interrupt unit (EIU) */ |
| 40 | -#define LTQ_EIU_IR0 (INT_NUM_IM4_IRL0 + 30) |
| 41 | -#define LTQ_EIU_IR1 (INT_NUM_IM3_IRL0 + 31) |
| 42 | -#define LTQ_EIU_IR2 (INT_NUM_IM1_IRL0 + 26) |
| 43 | -#define LTQ_EIU_IR3 INT_NUM_IM1_IRL0 |
| 44 | -#define LTQ_EIU_IR4 (INT_NUM_IM1_IRL0 + 1) |
| 45 | -#define LTQ_EIU_IR5 (INT_NUM_IM1_IRL0 + 2) |
| 46 | -#define LTQ_EIU_IR6 (INT_NUM_IM2_IRL0 + 30) |
| 47 | -#define XWAY_EXIN_COUNT 3 |
| 48 | +/* number of external interrupts */ |
| 49 | #define MAX_EIU 6 |
| 50 | |
| 51 | /* the performance counter */ |
| 52 | @@ -72,20 +65,19 @@ |
| 53 | int gic_present; |
| 54 | #endif |
| 55 | |
| 56 | -static unsigned short ltq_eiu_irq[MAX_EIU] = { |
| 57 | - LTQ_EIU_IR0, |
| 58 | - LTQ_EIU_IR1, |
| 59 | - LTQ_EIU_IR2, |
| 60 | - LTQ_EIU_IR3, |
| 61 | - LTQ_EIU_IR4, |
| 62 | - LTQ_EIU_IR5, |
| 63 | -}; |
| 64 | - |
| 65 | static int exin_avail; |
| 66 | +static struct resource ltq_eiu_irq[MAX_EIU]; |
| 67 | static void __iomem *ltq_icu_membase[MAX_IM]; |
| 68 | static void __iomem *ltq_eiu_membase; |
| 69 | static struct irq_domain *ltq_domain; |
| 70 | |
| 71 | +int ltq_eiu_get_irq(int exin) |
| 72 | +{ |
| 73 | + if (exin < exin_avail) |
| 74 | + return ltq_eiu_irq[exin].start; |
| 75 | + return -1; |
| 76 | +} |
| 77 | + |
| 78 | void ltq_disable_irq(struct irq_data *d) |
| 79 | { |
| 80 | u32 ier = LTQ_ICU_IM0_IER; |
| 81 | @@ -128,19 +120,64 @@ void ltq_enable_irq(struct irq_data *d) |
| 82 | ltq_icu_w32(im, ltq_icu_r32(im, ier) | BIT(offset), ier); |
| 83 | } |
| 84 | |
| 85 | +static int ltq_eiu_settype(struct irq_data *d, unsigned int type) |
| 86 | +{ |
| 87 | + int i; |
| 88 | + |
| 89 | + for (i = 0; i < MAX_EIU; i++) { |
| 90 | + if (d->hwirq == ltq_eiu_irq[i].start) { |
| 91 | + int val = 0; |
| 92 | + int edge = 0; |
| 93 | + |
| 94 | + switch (type) { |
| 95 | + case IRQF_TRIGGER_NONE: |
| 96 | + break; |
| 97 | + case IRQF_TRIGGER_RISING: |
| 98 | + val = 1; |
| 99 | + edge = 1; |
| 100 | + break; |
| 101 | + case IRQF_TRIGGER_FALLING: |
| 102 | + val = 2; |
| 103 | + edge = 1; |
| 104 | + break; |
| 105 | + case IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING: |
| 106 | + val = 3; |
| 107 | + edge = 1; |
| 108 | + break; |
| 109 | + case IRQF_TRIGGER_HIGH: |
| 110 | + val = 5; |
| 111 | + break; |
| 112 | + case IRQF_TRIGGER_LOW: |
| 113 | + val = 6; |
| 114 | + break; |
| 115 | + default: |
| 116 | + pr_err("invalid type %d for irq %ld\n", type, d->hwirq); |
| 117 | + return -EINVAL; |
| 118 | + } |
| 119 | + |
| 120 | + if (edge) |
| 121 | + irq_set_handler(d->hwirq, handle_edge_irq); |
| 122 | + |
| 123 | + ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_C) | |
| 124 | + (val << (i * 4)), LTQ_EIU_EXIN_C); |
| 125 | + } |
| 126 | + } |
| 127 | + |
| 128 | + return 0; |
| 129 | +} |
| 130 | + |
| 131 | static unsigned int ltq_startup_eiu_irq(struct irq_data *d) |
| 132 | { |
| 133 | int i; |
| 134 | |
| 135 | ltq_enable_irq(d); |
| 136 | for (i = 0; i < MAX_EIU; i++) { |
| 137 | - if (d->hwirq == ltq_eiu_irq[i]) { |
| 138 | - /* low level - we should really handle set_type */ |
| 139 | - ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_C) | |
| 140 | - (0x6 << (i * 4)), LTQ_EIU_EXIN_C); |
| 141 | + if (d->hwirq == ltq_eiu_irq[i].start) { |
| 142 | + /* by default we are low level triggered */ |
| 143 | + ltq_eiu_settype(d, IRQF_TRIGGER_LOW); |
| 144 | /* clear all pending */ |
| 145 | - ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_INIC) & ~BIT(i), |
| 146 | - LTQ_EIU_EXIN_INIC); |
| 147 | + ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_INC) & ~BIT(i), |
| 148 | + LTQ_EIU_EXIN_INC); |
| 149 | /* enable */ |
| 150 | ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_INEN) | BIT(i), |
| 151 | LTQ_EIU_EXIN_INEN); |
| 152 | @@ -157,7 +194,7 @@ static void ltq_shutdown_eiu_irq(struct irq_data *d) |
| 153 | |
| 154 | ltq_disable_irq(d); |
| 155 | for (i = 0; i < MAX_EIU; i++) { |
| 156 | - if (d->hwirq == ltq_eiu_irq[i]) { |
| 157 | + if (d->hwirq == ltq_eiu_irq[i].start) { |
| 158 | /* disable */ |
| 159 | ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_INEN) & ~BIT(i), |
| 160 | LTQ_EIU_EXIN_INEN); |
| 161 | @@ -186,6 +223,7 @@ static struct irq_chip ltq_eiu_type = { |
| 162 | .irq_ack = ltq_ack_irq, |
| 163 | .irq_mask = ltq_disable_irq, |
| 164 | .irq_mask_ack = ltq_mask_and_ack_irq, |
| 165 | + .irq_set_type = ltq_eiu_settype, |
| 166 | }; |
| 167 | |
| 168 | static void ltq_hw_irqdispatch(int module) |
| 169 | @@ -301,7 +339,7 @@ static int icu_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) |
| 170 | return 0; |
| 171 | |
| 172 | for (i = 0; i < exin_avail; i++) |
| 173 | - if (hw == ltq_eiu_irq[i]) |
| 174 | + if (hw == ltq_eiu_irq[i].start) |
| 175 | chip = <q_eiu_type; |
| 176 | |
| 177 | irq_set_chip_and_handler(hw, chip, handle_level_irq); |
| 178 | @@ -323,7 +361,7 @@ int __init icu_of_init(struct device_node *node, struct device_node *parent) |
| 179 | { |
| 180 | struct device_node *eiu_node; |
| 181 | struct resource res; |
| 182 | - int i; |
| 183 | + int i, ret; |
| 184 | |
| 185 | for (i = 0; i < MAX_IM; i++) { |
| 186 | if (of_address_to_resource(node, i, &res)) |
| 187 | @@ -340,17 +378,19 @@ int __init icu_of_init(struct device_node *node, struct device_node *parent) |
| 188 | } |
| 189 | |
| 190 | /* the external interrupts are optional and xway only */ |
| 191 | - eiu_node = of_find_compatible_node(NULL, NULL, "lantiq,eiu"); |
| 192 | + eiu_node = of_find_compatible_node(NULL, NULL, "lantiq,eiu-xway"); |
| 193 | if (eiu_node && !of_address_to_resource(eiu_node, 0, &res)) { |
| 194 | /* find out how many external irq sources we have */ |
| 195 | - const __be32 *count = of_get_property(node, |
| 196 | - "lantiq,count", NULL); |
| 197 | + exin_avail = of_irq_count(eiu_node); |
| 198 | |
| 199 | - if (count) |
| 200 | - exin_avail = *count; |
| 201 | if (exin_avail > MAX_EIU) |
| 202 | exin_avail = MAX_EIU; |
| 203 | |
| 204 | + ret = of_irq_to_resource_table(eiu_node, |
| 205 | + ltq_eiu_irq, exin_avail); |
| 206 | + if (ret != exin_avail) |
| 207 | + panic("failed to load external irq resources\n"); |
| 208 | + |
| 209 | if (request_mem_region(res.start, resource_size(&res), |
| 210 | res.name) < 0) |
| 211 | pr_err("Failed to request eiu memory"); |
| 212 | -- |
| 213 | 1.7.10.4 |
| 214 | |
| 215 | |