diff options
author | John Crispin <blogic@openwrt.org> | 2013-01-19 03:54:27 -0500 |
---|---|---|
committer | John Crispin <blogic@openwrt.org> | 2013-02-16 18:15:18 -0500 |
commit | 26365625947fb7d6501065272a1fd59460c0f4ed (patch) | |
tree | 5966c84966c51d1c45a6f0896ceb04e2980fed53 /arch/mips/lantiq | |
parent | bae696a267d81ea268f4de1e396b8c82154f22ed (diff) |
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/
Diffstat (limited to 'arch/mips/lantiq')
-rw-r--r-- | arch/mips/lantiq/irq.c | 105 |
1 files changed, 73 insertions, 32 deletions
diff --git a/arch/mips/lantiq/irq.c b/arch/mips/lantiq/irq.c index a7935bf0fecb..51194875f158 100644 --- a/arch/mips/lantiq/irq.c +++ b/arch/mips/lantiq/irq.c | |||
@@ -33,17 +33,10 @@ | |||
33 | /* register definitions - external irqs */ | 33 | /* register definitions - external irqs */ |
34 | #define LTQ_EIU_EXIN_C 0x0000 | 34 | #define LTQ_EIU_EXIN_C 0x0000 |
35 | #define LTQ_EIU_EXIN_INIC 0x0004 | 35 | #define LTQ_EIU_EXIN_INIC 0x0004 |
36 | #define LTQ_EIU_EXIN_INC 0x0008 | ||
36 | #define LTQ_EIU_EXIN_INEN 0x000C | 37 | #define LTQ_EIU_EXIN_INEN 0x000C |
37 | 38 | ||
38 | /* irq numbers used by the external interrupt unit (EIU) */ | 39 | /* number of external interrupts */ |
39 | #define LTQ_EIU_IR0 (INT_NUM_IM4_IRL0 + 30) | ||
40 | #define LTQ_EIU_IR1 (INT_NUM_IM3_IRL0 + 31) | ||
41 | #define LTQ_EIU_IR2 (INT_NUM_IM1_IRL0 + 26) | ||
42 | #define LTQ_EIU_IR3 INT_NUM_IM1_IRL0 | ||
43 | #define LTQ_EIU_IR4 (INT_NUM_IM1_IRL0 + 1) | ||
44 | #define LTQ_EIU_IR5 (INT_NUM_IM1_IRL0 + 2) | ||
45 | #define LTQ_EIU_IR6 (INT_NUM_IM2_IRL0 + 30) | ||
46 | #define XWAY_EXIN_COUNT 3 | ||
47 | #define MAX_EIU 6 | 40 | #define MAX_EIU 6 |
48 | 41 | ||
49 | /* the performance counter */ | 42 | /* the performance counter */ |
@@ -72,20 +65,19 @@ | |||
72 | int gic_present; | 65 | int gic_present; |
73 | #endif | 66 | #endif |
74 | 67 | ||
75 | static unsigned short ltq_eiu_irq[MAX_EIU] = { | ||
76 | LTQ_EIU_IR0, | ||
77 | LTQ_EIU_IR1, | ||
78 | LTQ_EIU_IR2, | ||
79 | LTQ_EIU_IR3, | ||
80 | LTQ_EIU_IR4, | ||
81 | LTQ_EIU_IR5, | ||
82 | }; | ||
83 | |||
84 | static int exin_avail; | 68 | static int exin_avail; |
69 | static struct resource ltq_eiu_irq[MAX_EIU]; | ||
85 | static void __iomem *ltq_icu_membase[MAX_IM]; | 70 | static void __iomem *ltq_icu_membase[MAX_IM]; |
86 | static void __iomem *ltq_eiu_membase; | 71 | static void __iomem *ltq_eiu_membase; |
87 | static struct irq_domain *ltq_domain; | 72 | static struct irq_domain *ltq_domain; |
88 | 73 | ||
74 | int ltq_eiu_get_irq(int exin) | ||
75 | { | ||
76 | if (exin < exin_avail) | ||
77 | return ltq_eiu_irq[exin].start; | ||
78 | return -1; | ||
79 | } | ||
80 | |||
89 | void ltq_disable_irq(struct irq_data *d) | 81 | void ltq_disable_irq(struct irq_data *d) |
90 | { | 82 | { |
91 | u32 ier = LTQ_ICU_IM0_IER; | 83 | u32 ier = LTQ_ICU_IM0_IER; |
@@ -128,19 +120,65 @@ void ltq_enable_irq(struct irq_data *d) | |||
128 | ltq_icu_w32(im, ltq_icu_r32(im, ier) | BIT(offset), ier); | 120 | ltq_icu_w32(im, ltq_icu_r32(im, ier) | BIT(offset), ier); |
129 | } | 121 | } |
130 | 122 | ||
123 | static int ltq_eiu_settype(struct irq_data *d, unsigned int type) | ||
124 | { | ||
125 | int i; | ||
126 | |||
127 | for (i = 0; i < MAX_EIU; i++) { | ||
128 | if (d->hwirq == ltq_eiu_irq[i].start) { | ||
129 | int val = 0; | ||
130 | int edge = 0; | ||
131 | |||
132 | switch (type) { | ||
133 | case IRQF_TRIGGER_NONE: | ||
134 | break; | ||
135 | case IRQF_TRIGGER_RISING: | ||
136 | val = 1; | ||
137 | edge = 1; | ||
138 | break; | ||
139 | case IRQF_TRIGGER_FALLING: | ||
140 | val = 2; | ||
141 | edge = 1; | ||
142 | break; | ||
143 | case IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING: | ||
144 | val = 3; | ||
145 | edge = 1; | ||
146 | break; | ||
147 | case IRQF_TRIGGER_HIGH: | ||
148 | val = 5; | ||
149 | break; | ||
150 | case IRQF_TRIGGER_LOW: | ||
151 | val = 6; | ||
152 | break; | ||
153 | default: | ||
154 | pr_err("invalid type %d for irq %ld\n", | ||
155 | type, d->hwirq); | ||
156 | return -EINVAL; | ||
157 | } | ||
158 | |||
159 | if (edge) | ||
160 | irq_set_handler(d->hwirq, handle_edge_irq); | ||
161 | |||
162 | ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_C) | | ||
163 | (val << (i * 4)), LTQ_EIU_EXIN_C); | ||
164 | } | ||
165 | } | ||
166 | |||
167 | return 0; | ||
168 | } | ||
169 | |||
131 | static unsigned int ltq_startup_eiu_irq(struct irq_data *d) | 170 | static unsigned int ltq_startup_eiu_irq(struct irq_data *d) |
132 | { | 171 | { |
133 | int i; | 172 | int i; |
134 | 173 | ||
135 | ltq_enable_irq(d); | 174 | ltq_enable_irq(d); |
136 | for (i = 0; i < MAX_EIU; i++) { | 175 | for (i = 0; i < MAX_EIU; i++) { |
137 | if (d->hwirq == ltq_eiu_irq[i]) { | 176 | if (d->hwirq == ltq_eiu_irq[i].start) { |
138 | /* low level - we should really handle set_type */ | 177 | /* by default we are low level triggered */ |
139 | ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_C) | | 178 | ltq_eiu_settype(d, IRQF_TRIGGER_LOW); |
140 | (0x6 << (i * 4)), LTQ_EIU_EXIN_C); | ||
141 | /* clear all pending */ | 179 | /* clear all pending */ |
142 | ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_INIC) & ~BIT(i), | 180 | ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_INC) & ~BIT(i), |
143 | LTQ_EIU_EXIN_INIC); | 181 | LTQ_EIU_EXIN_INC); |
144 | /* enable */ | 182 | /* enable */ |
145 | ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_INEN) | BIT(i), | 183 | ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_INEN) | BIT(i), |
146 | LTQ_EIU_EXIN_INEN); | 184 | LTQ_EIU_EXIN_INEN); |
@@ -157,7 +195,7 @@ static void ltq_shutdown_eiu_irq(struct irq_data *d) | |||
157 | 195 | ||
158 | ltq_disable_irq(d); | 196 | ltq_disable_irq(d); |
159 | for (i = 0; i < MAX_EIU; i++) { | 197 | for (i = 0; i < MAX_EIU; i++) { |
160 | if (d->hwirq == ltq_eiu_irq[i]) { | 198 | if (d->hwirq == ltq_eiu_irq[i].start) { |
161 | /* disable */ | 199 | /* disable */ |
162 | ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_INEN) & ~BIT(i), | 200 | ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_INEN) & ~BIT(i), |
163 | LTQ_EIU_EXIN_INEN); | 201 | LTQ_EIU_EXIN_INEN); |
@@ -186,6 +224,7 @@ static struct irq_chip ltq_eiu_type = { | |||
186 | .irq_ack = ltq_ack_irq, | 224 | .irq_ack = ltq_ack_irq, |
187 | .irq_mask = ltq_disable_irq, | 225 | .irq_mask = ltq_disable_irq, |
188 | .irq_mask_ack = ltq_mask_and_ack_irq, | 226 | .irq_mask_ack = ltq_mask_and_ack_irq, |
227 | .irq_set_type = ltq_eiu_settype, | ||
189 | }; | 228 | }; |
190 | 229 | ||
191 | static void ltq_hw_irqdispatch(int module) | 230 | 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) | |||
301 | return 0; | 340 | return 0; |
302 | 341 | ||
303 | for (i = 0; i < exin_avail; i++) | 342 | for (i = 0; i < exin_avail; i++) |
304 | if (hw == ltq_eiu_irq[i]) | 343 | if (hw == ltq_eiu_irq[i].start) |
305 | chip = <q_eiu_type; | 344 | chip = <q_eiu_type; |
306 | 345 | ||
307 | irq_set_chip_and_handler(hw, chip, handle_level_irq); | 346 | 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) | |||
323 | { | 362 | { |
324 | struct device_node *eiu_node; | 363 | struct device_node *eiu_node; |
325 | struct resource res; | 364 | struct resource res; |
326 | int i; | 365 | int i, ret; |
327 | 366 | ||
328 | for (i = 0; i < MAX_IM; i++) { | 367 | for (i = 0; i < MAX_IM; i++) { |
329 | if (of_address_to_resource(node, i, &res)) | 368 | 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) | |||
340 | } | 379 | } |
341 | 380 | ||
342 | /* the external interrupts are optional and xway only */ | 381 | /* the external interrupts are optional and xway only */ |
343 | eiu_node = of_find_compatible_node(NULL, NULL, "lantiq,eiu"); | 382 | eiu_node = of_find_compatible_node(NULL, NULL, "lantiq,eiu-xway"); |
344 | if (eiu_node && !of_address_to_resource(eiu_node, 0, &res)) { | 383 | if (eiu_node && !of_address_to_resource(eiu_node, 0, &res)) { |
345 | /* find out how many external irq sources we have */ | 384 | /* find out how many external irq sources we have */ |
346 | const __be32 *count = of_get_property(node, | 385 | exin_avail = of_irq_count(eiu_node); |
347 | "lantiq,count", NULL); | ||
348 | 386 | ||
349 | if (count) | ||
350 | exin_avail = *count; | ||
351 | if (exin_avail > MAX_EIU) | 387 | if (exin_avail > MAX_EIU) |
352 | exin_avail = MAX_EIU; | 388 | exin_avail = MAX_EIU; |
353 | 389 | ||
390 | ret = of_irq_to_resource_table(eiu_node, | ||
391 | ltq_eiu_irq, exin_avail); | ||
392 | if (ret != exin_avail) | ||
393 | panic("failed to load external irq resources\n"); | ||
394 | |||
354 | if (request_mem_region(res.start, resource_size(&res), | 395 | if (request_mem_region(res.start, resource_size(&res), |
355 | res.name) < 0) | 396 | res.name) < 0) |
356 | pr_err("Failed to request eiu memory"); | 397 | pr_err("Failed to request eiu memory"); |