diff options
author | Aurelien Jacquiot <a-jacquiot@ti.com> | 2011-10-04 11:06:27 -0400 |
---|---|---|
committer | Mark Salter <msalter@redhat.com> | 2011-10-06 19:47:54 -0400 |
commit | ec500af3059b474df35418c41c684c1cde830c81 (patch) | |
tree | fca5ee52137efe4fc9d9c07ddce4f4e4ea52ba16 /arch/c6x/platforms | |
parent | 546a39546c64ad7e73796c5508ef5487af42cae2 (diff) |
C6X: interrupt handling
Original port to early 2.6 kernel using TI COFF toolchain.
Brought up to date by Mark Salter <msalter@redhat.com>
Signed-off-by: Aurelien Jacquiot <a-jacquiot@ti.com>
Signed-off-by: Mark Salter <msalter@redhat.com>
Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Diffstat (limited to 'arch/c6x/platforms')
-rw-r--r-- | arch/c6x/platforms/megamod-pic.c | 349 |
1 files changed, 349 insertions, 0 deletions
diff --git a/arch/c6x/platforms/megamod-pic.c b/arch/c6x/platforms/megamod-pic.c new file mode 100644 index 000000000000..7c37a947fb1c --- /dev/null +++ b/arch/c6x/platforms/megamod-pic.c | |||
@@ -0,0 +1,349 @@ | |||
1 | /* | ||
2 | * Support for C64x+ Megamodule Interrupt Controller | ||
3 | * | ||
4 | * Copyright (C) 2010, 2011 Texas Instruments Incorporated | ||
5 | * Contributed by: Mark Salter <msalter@redhat.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/interrupt.h> | ||
13 | #include <linux/io.h> | ||
14 | #include <linux/of.h> | ||
15 | #include <linux/of_irq.h> | ||
16 | #include <linux/of_address.h> | ||
17 | #include <linux/slab.h> | ||
18 | #include <asm/soc.h> | ||
19 | #include <asm/megamod-pic.h> | ||
20 | |||
21 | #define NR_COMBINERS 4 | ||
22 | #define NR_MUX_OUTPUTS 12 | ||
23 | |||
24 | #define IRQ_UNMAPPED 0xffff | ||
25 | |||
26 | /* | ||
27 | * Megamodule Interrupt Controller register layout | ||
28 | */ | ||
29 | struct megamod_regs { | ||
30 | u32 evtflag[8]; | ||
31 | u32 evtset[8]; | ||
32 | u32 evtclr[8]; | ||
33 | u32 reserved0[8]; | ||
34 | u32 evtmask[8]; | ||
35 | u32 mevtflag[8]; | ||
36 | u32 expmask[8]; | ||
37 | u32 mexpflag[8]; | ||
38 | u32 intmux_unused; | ||
39 | u32 intmux[7]; | ||
40 | u32 reserved1[8]; | ||
41 | u32 aegmux[2]; | ||
42 | u32 reserved2[14]; | ||
43 | u32 intxstat; | ||
44 | u32 intxclr; | ||
45 | u32 intdmask; | ||
46 | u32 reserved3[13]; | ||
47 | u32 evtasrt; | ||
48 | }; | ||
49 | |||
50 | struct megamod_pic { | ||
51 | struct irq_host *irqhost; | ||
52 | struct megamod_regs __iomem *regs; | ||
53 | raw_spinlock_t lock; | ||
54 | |||
55 | /* hw mux mapping */ | ||
56 | unsigned int output_to_irq[NR_MUX_OUTPUTS]; | ||
57 | }; | ||
58 | |||
59 | static struct megamod_pic *mm_pic; | ||
60 | |||
61 | struct megamod_cascade_data { | ||
62 | struct megamod_pic *pic; | ||
63 | int index; | ||
64 | }; | ||
65 | |||
66 | static struct megamod_cascade_data cascade_data[NR_COMBINERS]; | ||
67 | |||
68 | static void mask_megamod(struct irq_data *data) | ||
69 | { | ||
70 | struct megamod_pic *pic = irq_data_get_irq_chip_data(data); | ||
71 | irq_hw_number_t src = irqd_to_hwirq(data); | ||
72 | u32 __iomem *evtmask = &pic->regs->evtmask[src / 32]; | ||
73 | |||
74 | raw_spin_lock(&pic->lock); | ||
75 | soc_writel(soc_readl(evtmask) | (1 << (src & 31)), evtmask); | ||
76 | raw_spin_unlock(&pic->lock); | ||
77 | } | ||
78 | |||
79 | static void unmask_megamod(struct irq_data *data) | ||
80 | { | ||
81 | struct megamod_pic *pic = irq_data_get_irq_chip_data(data); | ||
82 | irq_hw_number_t src = irqd_to_hwirq(data); | ||
83 | u32 __iomem *evtmask = &pic->regs->evtmask[src / 32]; | ||
84 | |||
85 | raw_spin_lock(&pic->lock); | ||
86 | soc_writel(soc_readl(evtmask) & ~(1 << (src & 31)), evtmask); | ||
87 | raw_spin_unlock(&pic->lock); | ||
88 | } | ||
89 | |||
90 | static struct irq_chip megamod_chip = { | ||
91 | .name = "megamod", | ||
92 | .irq_mask = mask_megamod, | ||
93 | .irq_unmask = unmask_megamod, | ||
94 | }; | ||
95 | |||
96 | static void megamod_irq_cascade(unsigned int irq, struct irq_desc *desc) | ||
97 | { | ||
98 | struct megamod_cascade_data *cascade; | ||
99 | struct megamod_pic *pic; | ||
100 | u32 events; | ||
101 | int n, idx; | ||
102 | |||
103 | cascade = irq_desc_get_handler_data(desc); | ||
104 | |||
105 | pic = cascade->pic; | ||
106 | idx = cascade->index; | ||
107 | |||
108 | while ((events = soc_readl(&pic->regs->mevtflag[idx])) != 0) { | ||
109 | n = __ffs(events); | ||
110 | |||
111 | irq = irq_linear_revmap(pic->irqhost, idx * 32 + n); | ||
112 | |||
113 | soc_writel(1 << n, &pic->regs->evtclr[idx]); | ||
114 | |||
115 | generic_handle_irq(irq); | ||
116 | } | ||
117 | } | ||
118 | |||
119 | static int megamod_map(struct irq_host *h, unsigned int virq, | ||
120 | irq_hw_number_t hw) | ||
121 | { | ||
122 | struct megamod_pic *pic = h->host_data; | ||
123 | int i; | ||
124 | |||
125 | /* We shouldn't see a hwirq which is muxed to core controller */ | ||
126 | for (i = 0; i < NR_MUX_OUTPUTS; i++) | ||
127 | if (pic->output_to_irq[i] == hw) | ||
128 | return -1; | ||
129 | |||
130 | irq_set_chip_data(virq, pic); | ||
131 | irq_set_chip_and_handler(virq, &megamod_chip, handle_level_irq); | ||
132 | |||
133 | /* Set default irq type */ | ||
134 | irq_set_irq_type(virq, IRQ_TYPE_NONE); | ||
135 | |||
136 | return 0; | ||
137 | } | ||
138 | |||
139 | static int megamod_xlate(struct irq_host *h, struct device_node *ct, | ||
140 | const u32 *intspec, unsigned int intsize, | ||
141 | irq_hw_number_t *out_hwirq, unsigned int *out_type) | ||
142 | |||
143 | { | ||
144 | /* megamod intspecs must have 1 cell */ | ||
145 | BUG_ON(intsize != 1); | ||
146 | *out_hwirq = intspec[0]; | ||
147 | *out_type = IRQ_TYPE_NONE; | ||
148 | return 0; | ||
149 | } | ||
150 | |||
151 | static struct irq_host_ops megamod_host_ops = { | ||
152 | .map = megamod_map, | ||
153 | .xlate = megamod_xlate, | ||
154 | }; | ||
155 | |||
156 | static void __init set_megamod_mux(struct megamod_pic *pic, int src, int output) | ||
157 | { | ||
158 | int index, offset; | ||
159 | u32 val; | ||
160 | |||
161 | if (src < 0 || src >= (NR_COMBINERS * 32)) { | ||
162 | pic->output_to_irq[output] = IRQ_UNMAPPED; | ||
163 | return; | ||
164 | } | ||
165 | |||
166 | /* four mappings per mux register */ | ||
167 | index = output / 4; | ||
168 | offset = (output & 3) * 8; | ||
169 | |||
170 | val = soc_readl(&pic->regs->intmux[index]); | ||
171 | val &= ~(0xff << offset); | ||
172 | val |= src << offset; | ||
173 | soc_writel(val, &pic->regs->intmux[index]); | ||
174 | } | ||
175 | |||
176 | /* | ||
177 | * Parse the MUX mapping, if one exists. | ||
178 | * | ||
179 | * The MUX map is an array of up to 12 cells; one for each usable core priority | ||
180 | * interrupt. The value of a given cell is the megamodule interrupt source | ||
181 | * which is to me MUXed to the output corresponding to the cell position | ||
182 | * withing the array. The first cell in the array corresponds to priority | ||
183 | * 4 and the last (12th) cell corresponds to priority 15. The allowed | ||
184 | * values are 4 - ((NR_COMBINERS * 32) - 1). Note that the combined interrupt | ||
185 | * sources (0 - 3) are not allowed to be mapped through this property. They | ||
186 | * are handled through the "interrupts" property. This allows us to use a | ||
187 | * value of zero as a "do not map" placeholder. | ||
188 | */ | ||
189 | static void __init parse_priority_map(struct megamod_pic *pic, | ||
190 | int *mapping, int size) | ||
191 | { | ||
192 | struct device_node *np = pic->irqhost->of_node; | ||
193 | const __be32 *map; | ||
194 | int i, maplen; | ||
195 | u32 val; | ||
196 | |||
197 | map = of_get_property(np, "ti,c64x+megamod-pic-mux", &maplen); | ||
198 | if (map) { | ||
199 | maplen /= 4; | ||
200 | if (maplen > size) | ||
201 | maplen = size; | ||
202 | |||
203 | for (i = 0; i < maplen; i++) { | ||
204 | val = be32_to_cpup(map); | ||
205 | if (val && val >= 4) | ||
206 | mapping[i] = val; | ||
207 | ++map; | ||
208 | } | ||
209 | } | ||
210 | } | ||
211 | |||
212 | static struct megamod_pic * __init init_megamod_pic(struct device_node *np) | ||
213 | { | ||
214 | struct megamod_pic *pic; | ||
215 | int i, irq; | ||
216 | int mapping[NR_MUX_OUTPUTS]; | ||
217 | |||
218 | pr_info("Initializing C64x+ Megamodule PIC\n"); | ||
219 | |||
220 | pic = kzalloc(sizeof(struct megamod_pic), GFP_KERNEL); | ||
221 | if (!pic) { | ||
222 | pr_err("%s: Could not alloc PIC structure.\n", np->full_name); | ||
223 | return NULL; | ||
224 | } | ||
225 | |||
226 | pic->irqhost = irq_alloc_host(np, IRQ_HOST_MAP_LINEAR, | ||
227 | NR_COMBINERS * 32, &megamod_host_ops, | ||
228 | IRQ_UNMAPPED); | ||
229 | if (!pic->irqhost) { | ||
230 | pr_err("%s: Could not alloc host.\n", np->full_name); | ||
231 | goto error_free; | ||
232 | } | ||
233 | |||
234 | pic->irqhost->host_data = pic; | ||
235 | |||
236 | raw_spin_lock_init(&pic->lock); | ||
237 | |||
238 | pic->regs = of_iomap(np, 0); | ||
239 | if (!pic->regs) { | ||
240 | pr_err("%s: Could not map registers.\n", np->full_name); | ||
241 | goto error_free; | ||
242 | } | ||
243 | |||
244 | /* Initialize MUX map */ | ||
245 | for (i = 0; i < ARRAY_SIZE(mapping); i++) | ||
246 | mapping[i] = IRQ_UNMAPPED; | ||
247 | |||
248 | parse_priority_map(pic, mapping, ARRAY_SIZE(mapping)); | ||
249 | |||
250 | /* | ||
251 | * We can have up to 12 interrupts cascading to the core controller. | ||
252 | * These cascades can be from the combined interrupt sources or for | ||
253 | * individual interrupt sources. The "interrupts" property only | ||
254 | * deals with the cascaded combined interrupts. The individual | ||
255 | * interrupts muxed to the core controller use the core controller | ||
256 | * as their interrupt parent. | ||
257 | */ | ||
258 | for (i = 0; i < NR_COMBINERS; i++) { | ||
259 | |||
260 | irq = irq_of_parse_and_map(np, i); | ||
261 | if (irq == NO_IRQ) | ||
262 | continue; | ||
263 | |||
264 | /* | ||
265 | * We count on the core priority interrupts (4 - 15) being | ||
266 | * direct mapped. Check that device tree provided something | ||
267 | * in that range. | ||
268 | */ | ||
269 | if (irq < 4 || irq >= NR_PRIORITY_IRQS) { | ||
270 | pr_err("%s: combiner-%d virq %d out of range!\n", | ||
271 | np->full_name, i, irq); | ||
272 | continue; | ||
273 | } | ||
274 | |||
275 | /* record the mapping */ | ||
276 | mapping[irq - 4] = i; | ||
277 | |||
278 | pr_debug("%s: combiner-%d cascading to virq %d\n", | ||
279 | np->full_name, i, irq); | ||
280 | |||
281 | cascade_data[i].pic = pic; | ||
282 | cascade_data[i].index = i; | ||
283 | |||
284 | /* mask and clear all events in combiner */ | ||
285 | soc_writel(~0, &pic->regs->evtmask[i]); | ||
286 | soc_writel(~0, &pic->regs->evtclr[i]); | ||
287 | |||
288 | irq_set_handler_data(irq, &cascade_data[i]); | ||
289 | irq_set_chained_handler(irq, megamod_irq_cascade); | ||
290 | } | ||
291 | |||
292 | /* Finally, set up the MUX registers */ | ||
293 | for (i = 0; i < NR_MUX_OUTPUTS; i++) { | ||
294 | if (mapping[i] != IRQ_UNMAPPED) { | ||
295 | pr_debug("%s: setting mux %d to priority %d\n", | ||
296 | np->full_name, mapping[i], i + 4); | ||
297 | set_megamod_mux(pic, mapping[i], i); | ||
298 | } | ||
299 | } | ||
300 | |||
301 | return pic; | ||
302 | |||
303 | error_free: | ||
304 | kfree(pic); | ||
305 | |||
306 | return NULL; | ||
307 | } | ||
308 | |||
309 | /* | ||
310 | * Return next active event after ACK'ing it. | ||
311 | * Return -1 if no events active. | ||
312 | */ | ||
313 | static int get_exception(void) | ||
314 | { | ||
315 | int i, bit; | ||
316 | u32 mask; | ||
317 | |||
318 | for (i = 0; i < NR_COMBINERS; i++) { | ||
319 | mask = soc_readl(&mm_pic->regs->mexpflag[i]); | ||
320 | if (mask) { | ||
321 | bit = __ffs(mask); | ||
322 | soc_writel(1 << bit, &mm_pic->regs->evtclr[i]); | ||
323 | return (i * 32) + bit; | ||
324 | } | ||
325 | } | ||
326 | return -1; | ||
327 | } | ||
328 | |||
329 | static void assert_event(unsigned int val) | ||
330 | { | ||
331 | soc_writel(val, &mm_pic->regs->evtasrt); | ||
332 | } | ||
333 | |||
334 | void __init megamod_pic_init(void) | ||
335 | { | ||
336 | struct device_node *np; | ||
337 | |||
338 | np = of_find_compatible_node(NULL, NULL, "ti,c64x+megamod-pic"); | ||
339 | if (!np) | ||
340 | return; | ||
341 | |||
342 | mm_pic = init_megamod_pic(np); | ||
343 | of_node_put(np); | ||
344 | |||
345 | soc_ops.get_exception = get_exception; | ||
346 | soc_ops.assert_event = assert_event; | ||
347 | |||
348 | return; | ||
349 | } | ||