Commit | Line | Data |
---|---|---|
e58923ed DG |
1 | /* |
2 | * arch/powerpc/sysdev/uic.c | |
3 | * | |
4 | * IBM PowerPC 4xx Universal Interrupt Controller | |
5 | * | |
6 | * Copyright 2007 David Gibson <dwg@au1.ibm.com>, IBM Corporation. | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify it | |
9 | * under the terms of the GNU General Public License as published by the | |
10 | * Free Software Foundation; either version 2 of the License, or (at your | |
11 | * option) any later version. | |
12 | */ | |
13 | #include <linux/kernel.h> | |
14 | #include <linux/init.h> | |
15 | #include <linux/errno.h> | |
16 | #include <linux/reboot.h> | |
17 | #include <linux/slab.h> | |
18 | #include <linux/stddef.h> | |
19 | #include <linux/sched.h> | |
20 | #include <linux/signal.h> | |
21 | #include <linux/sysdev.h> | |
22 | #include <linux/device.h> | |
23 | #include <linux/bootmem.h> | |
24 | #include <linux/spinlock.h> | |
25 | #include <linux/irq.h> | |
26 | #include <linux/interrupt.h> | |
868afce2 | 27 | #include <linux/kernel_stat.h> |
e58923ed DG |
28 | #include <asm/irq.h> |
29 | #include <asm/io.h> | |
30 | #include <asm/prom.h> | |
31 | #include <asm/dcr.h> | |
32 | ||
33 | #define NR_UIC_INTS 32 | |
34 | ||
35 | #define UIC_SR 0x0 | |
36 | #define UIC_ER 0x2 | |
37 | #define UIC_CR 0x3 | |
38 | #define UIC_PR 0x4 | |
39 | #define UIC_TR 0x5 | |
40 | #define UIC_MSR 0x6 | |
41 | #define UIC_VR 0x7 | |
42 | #define UIC_VCR 0x8 | |
43 | ||
44 | #define uic_irq_to_hw(virq) (irq_map[virq].hwirq) | |
45 | ||
46 | struct uic *primary_uic; | |
47 | ||
48 | struct uic { | |
49 | int index; | |
50 | int dcrbase; | |
51 | ||
52 | spinlock_t lock; | |
53 | ||
54 | /* The remapper for this UIC */ | |
55 | struct irq_host *irqhost; | |
e58923ed DG |
56 | }; |
57 | ||
58 | static void uic_unmask_irq(unsigned int virq) | |
59 | { | |
6cff46f4 | 60 | struct irq_desc *desc = irq_to_desc(virq); |
e58923ed DG |
61 | struct uic *uic = get_irq_chip_data(virq); |
62 | unsigned int src = uic_irq_to_hw(virq); | |
63 | unsigned long flags; | |
c8090563 | 64 | u32 er, sr; |
e58923ed | 65 | |
c8090563 | 66 | sr = 1 << (31-src); |
e58923ed | 67 | spin_lock_irqsave(&uic->lock, flags); |
c8090563 VB |
68 | /* ack level-triggered interrupts here */ |
69 | if (desc->status & IRQ_LEVEL) | |
70 | mtdcr(uic->dcrbase + UIC_SR, sr); | |
e58923ed | 71 | er = mfdcr(uic->dcrbase + UIC_ER); |
c8090563 | 72 | er |= sr; |
e58923ed DG |
73 | mtdcr(uic->dcrbase + UIC_ER, er); |
74 | spin_unlock_irqrestore(&uic->lock, flags); | |
75 | } | |
76 | ||
77 | static void uic_mask_irq(unsigned int virq) | |
78 | { | |
79 | struct uic *uic = get_irq_chip_data(virq); | |
80 | unsigned int src = uic_irq_to_hw(virq); | |
81 | unsigned long flags; | |
82 | u32 er; | |
83 | ||
84 | spin_lock_irqsave(&uic->lock, flags); | |
85 | er = mfdcr(uic->dcrbase + UIC_ER); | |
86 | er &= ~(1 << (31 - src)); | |
87 | mtdcr(uic->dcrbase + UIC_ER, er); | |
88 | spin_unlock_irqrestore(&uic->lock, flags); | |
89 | } | |
90 | ||
91 | static void uic_ack_irq(unsigned int virq) | |
92 | { | |
93 | struct uic *uic = get_irq_chip_data(virq); | |
94 | unsigned int src = uic_irq_to_hw(virq); | |
95 | unsigned long flags; | |
96 | ||
97 | spin_lock_irqsave(&uic->lock, flags); | |
98 | mtdcr(uic->dcrbase + UIC_SR, 1 << (31-src)); | |
99 | spin_unlock_irqrestore(&uic->lock, flags); | |
100 | } | |
101 | ||
b8b799a4 VB |
102 | static void uic_mask_ack_irq(unsigned int virq) |
103 | { | |
6cff46f4 | 104 | struct irq_desc *desc = irq_to_desc(virq); |
b8b799a4 VB |
105 | struct uic *uic = get_irq_chip_data(virq); |
106 | unsigned int src = uic_irq_to_hw(virq); | |
107 | unsigned long flags; | |
108 | u32 er, sr; | |
109 | ||
110 | sr = 1 << (31-src); | |
111 | spin_lock_irqsave(&uic->lock, flags); | |
112 | er = mfdcr(uic->dcrbase + UIC_ER); | |
113 | er &= ~sr; | |
114 | mtdcr(uic->dcrbase + UIC_ER, er); | |
c8090563 VB |
115 | /* On the UIC, acking (i.e. clearing the SR bit) |
116 | * a level irq will have no effect if the interrupt | |
117 | * is still asserted by the device, even if | |
118 | * the interrupt is already masked. Therefore | |
119 | * we only ack the egde interrupts here, while | |
120 | * level interrupts are ack'ed after the actual | |
121 | * isr call in the uic_unmask_irq() | |
122 | */ | |
123 | if (!(desc->status & IRQ_LEVEL)) | |
124 | mtdcr(uic->dcrbase + UIC_SR, sr); | |
b8b799a4 VB |
125 | spin_unlock_irqrestore(&uic->lock, flags); |
126 | } | |
127 | ||
e58923ed DG |
128 | static int uic_set_irq_type(unsigned int virq, unsigned int flow_type) |
129 | { | |
130 | struct uic *uic = get_irq_chip_data(virq); | |
131 | unsigned int src = uic_irq_to_hw(virq); | |
6cff46f4 | 132 | struct irq_desc *desc = irq_to_desc(virq); |
e58923ed DG |
133 | unsigned long flags; |
134 | int trigger, polarity; | |
135 | u32 tr, pr, mask; | |
136 | ||
137 | switch (flow_type & IRQ_TYPE_SENSE_MASK) { | |
138 | case IRQ_TYPE_NONE: | |
139 | uic_mask_irq(virq); | |
140 | return 0; | |
141 | ||
142 | case IRQ_TYPE_EDGE_RISING: | |
143 | trigger = 1; polarity = 1; | |
144 | break; | |
145 | case IRQ_TYPE_EDGE_FALLING: | |
146 | trigger = 1; polarity = 0; | |
147 | break; | |
148 | case IRQ_TYPE_LEVEL_HIGH: | |
149 | trigger = 0; polarity = 1; | |
150 | break; | |
151 | case IRQ_TYPE_LEVEL_LOW: | |
152 | trigger = 0; polarity = 0; | |
153 | break; | |
154 | default: | |
155 | return -EINVAL; | |
156 | } | |
157 | ||
158 | mask = ~(1 << (31 - src)); | |
159 | ||
160 | spin_lock_irqsave(&uic->lock, flags); | |
161 | tr = mfdcr(uic->dcrbase + UIC_TR); | |
162 | pr = mfdcr(uic->dcrbase + UIC_PR); | |
163 | tr = (tr & mask) | (trigger << (31-src)); | |
164 | pr = (pr & mask) | (polarity << (31-src)); | |
165 | ||
166 | mtdcr(uic->dcrbase + UIC_PR, pr); | |
167 | mtdcr(uic->dcrbase + UIC_TR, tr); | |
168 | ||
169 | desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL); | |
170 | desc->status |= flow_type & IRQ_TYPE_SENSE_MASK; | |
4dc7b4b0 | 171 | if (!trigger) |
e58923ed DG |
172 | desc->status |= IRQ_LEVEL; |
173 | ||
174 | spin_unlock_irqrestore(&uic->lock, flags); | |
175 | ||
176 | return 0; | |
177 | } | |
178 | ||
179 | static struct irq_chip uic_irq_chip = { | |
b27df672 | 180 | .name = " UIC ", |
e58923ed DG |
181 | .unmask = uic_unmask_irq, |
182 | .mask = uic_mask_irq, | |
b8b799a4 | 183 | .mask_ack = uic_mask_ack_irq, |
e58923ed DG |
184 | .ack = uic_ack_irq, |
185 | .set_type = uic_set_irq_type, | |
186 | }; | |
187 | ||
e58923ed DG |
188 | static int uic_host_map(struct irq_host *h, unsigned int virq, |
189 | irq_hw_number_t hw) | |
190 | { | |
191 | struct uic *uic = h->host_data; | |
192 | ||
193 | set_irq_chip_data(virq, uic); | |
194 | /* Despite the name, handle_level_irq() works for both level | |
195 | * and edge irqs on UIC. FIXME: check this is correct */ | |
c8090563 | 196 | set_irq_chip_and_handler(virq, &uic_irq_chip, handle_level_irq); |
e58923ed DG |
197 | |
198 | /* Set default irq type */ | |
199 | set_irq_type(virq, IRQ_TYPE_NONE); | |
200 | ||
201 | return 0; | |
202 | } | |
203 | ||
204 | static int uic_host_xlate(struct irq_host *h, struct device_node *ct, | |
40d50cf7 | 205 | const u32 *intspec, unsigned int intsize, |
e58923ed DG |
206 | irq_hw_number_t *out_hwirq, unsigned int *out_type) |
207 | ||
208 | { | |
209 | /* UIC intspecs must have 2 cells */ | |
210 | BUG_ON(intsize != 2); | |
211 | *out_hwirq = intspec[0]; | |
212 | *out_type = intspec[1]; | |
213 | return 0; | |
214 | } | |
215 | ||
216 | static struct irq_host_ops uic_host_ops = { | |
e58923ed DG |
217 | .map = uic_host_map, |
218 | .xlate = uic_host_xlate, | |
219 | }; | |
220 | ||
5aac48dc | 221 | void uic_irq_cascade(unsigned int virq, struct irq_desc *desc) |
e58923ed | 222 | { |
5aac48dc | 223 | struct uic *uic = get_irq_data(virq); |
e58923ed DG |
224 | u32 msr; |
225 | int src; | |
226 | int subvirq; | |
227 | ||
239007b8 | 228 | raw_spin_lock(&desc->lock); |
5aac48dc VB |
229 | if (desc->status & IRQ_LEVEL) |
230 | desc->chip->mask(virq); | |
231 | else | |
232 | desc->chip->mask_ack(virq); | |
239007b8 | 233 | raw_spin_unlock(&desc->lock); |
5aac48dc | 234 | |
e58923ed | 235 | msr = mfdcr(uic->dcrbase + UIC_MSR); |
553fdff6 | 236 | if (!msr) /* spurious interrupt */ |
5aac48dc | 237 | goto uic_irq_ret; |
553fdff6 | 238 | |
e58923ed DG |
239 | src = 32 - ffs(msr); |
240 | ||
241 | subvirq = irq_linear_revmap(uic->irqhost, src); | |
242 | generic_handle_irq(subvirq); | |
243 | ||
5aac48dc | 244 | uic_irq_ret: |
239007b8 | 245 | raw_spin_lock(&desc->lock); |
5aac48dc VB |
246 | if (desc->status & IRQ_LEVEL) |
247 | desc->chip->ack(virq); | |
248 | if (!(desc->status & IRQ_DISABLED) && desc->chip->unmask) | |
249 | desc->chip->unmask(virq); | |
239007b8 | 250 | raw_spin_unlock(&desc->lock); |
e58923ed DG |
251 | } |
252 | ||
253 | static struct uic * __init uic_init_one(struct device_node *node) | |
254 | { | |
255 | struct uic *uic; | |
256 | const u32 *indexp, *dcrreg; | |
257 | int len; | |
258 | ||
55b61fec | 259 | BUG_ON(! of_device_is_compatible(node, "ibm,uic")); |
e58923ed | 260 | |
ea96025a | 261 | uic = kzalloc(sizeof(*uic), GFP_KERNEL); |
e58923ed DG |
262 | if (! uic) |
263 | return NULL; /* FIXME: panic? */ | |
264 | ||
e58923ed | 265 | spin_lock_init(&uic->lock); |
12d371a6 | 266 | indexp = of_get_property(node, "cell-index", &len); |
e58923ed DG |
267 | if (!indexp || (len != sizeof(u32))) { |
268 | printk(KERN_ERR "uic: Device node %s has missing or invalid " | |
269 | "cell-index property\n", node->full_name); | |
270 | return NULL; | |
271 | } | |
272 | uic->index = *indexp; | |
273 | ||
12d371a6 | 274 | dcrreg = of_get_property(node, "dcr-reg", &len); |
e58923ed DG |
275 | if (!dcrreg || (len != 2*sizeof(u32))) { |
276 | printk(KERN_ERR "uic: Device node %s has missing or invalid " | |
277 | "dcr-reg property\n", node->full_name); | |
278 | return NULL; | |
279 | } | |
280 | uic->dcrbase = *dcrreg; | |
281 | ||
19fc65b5 | 282 | uic->irqhost = irq_alloc_host(node, IRQ_HOST_MAP_LINEAR, |
52964f87 | 283 | NR_UIC_INTS, &uic_host_ops, -1); |
19fc65b5 | 284 | if (! uic->irqhost) |
e58923ed | 285 | return NULL; /* FIXME: panic? */ |
e58923ed DG |
286 | |
287 | uic->irqhost->host_data = uic; | |
288 | ||
289 | /* Start with all interrupts disabled, level and non-critical */ | |
290 | mtdcr(uic->dcrbase + UIC_ER, 0); | |
291 | mtdcr(uic->dcrbase + UIC_CR, 0); | |
292 | mtdcr(uic->dcrbase + UIC_TR, 0); | |
293 | /* Clear any pending interrupts, in case the firmware left some */ | |
294 | mtdcr(uic->dcrbase + UIC_SR, 0xffffffff); | |
295 | ||
296 | printk ("UIC%d (%d IRQ sources) at DCR 0x%x\n", uic->index, | |
297 | NR_UIC_INTS, uic->dcrbase); | |
298 | ||
299 | return uic; | |
300 | } | |
301 | ||
302 | void __init uic_init_tree(void) | |
303 | { | |
304 | struct device_node *np; | |
305 | struct uic *uic; | |
306 | const u32 *interrupts; | |
307 | ||
308 | /* First locate and initialize the top-level UIC */ | |
26cb7d8b | 309 | for_each_compatible_node(np, NULL, "ibm,uic") { |
12d371a6 | 310 | interrupts = of_get_property(np, "interrupts", NULL); |
26cb7d8b | 311 | if (!interrupts) |
e58923ed | 312 | break; |
e58923ed DG |
313 | } |
314 | ||
315 | BUG_ON(!np); /* uic_init_tree() assumes there's a UIC as the | |
316 | * top-level interrupt controller */ | |
317 | primary_uic = uic_init_one(np); | |
26cb7d8b | 318 | if (!primary_uic) |
e58923ed DG |
319 | panic("Unable to initialize primary UIC %s\n", np->full_name); |
320 | ||
321 | irq_set_default_host(primary_uic->irqhost); | |
322 | of_node_put(np); | |
323 | ||
324 | /* The scan again for cascaded UICs */ | |
26cb7d8b | 325 | for_each_compatible_node(np, NULL, "ibm,uic") { |
12d371a6 | 326 | interrupts = of_get_property(np, "interrupts", NULL); |
e58923ed DG |
327 | if (interrupts) { |
328 | /* Secondary UIC */ | |
329 | int cascade_virq; | |
e58923ed DG |
330 | |
331 | uic = uic_init_one(np); | |
332 | if (! uic) | |
333 | panic("Unable to initialize a secondary UIC %s\n", | |
334 | np->full_name); | |
335 | ||
336 | cascade_virq = irq_of_parse_and_map(np, 0); | |
337 | ||
5aac48dc VB |
338 | set_irq_data(cascade_virq, uic); |
339 | set_irq_chained_handler(cascade_virq, uic_irq_cascade); | |
e58923ed DG |
340 | |
341 | /* FIXME: setup critical cascade?? */ | |
342 | } | |
e58923ed DG |
343 | } |
344 | } | |
345 | ||
346 | /* Return an interrupt vector or NO_IRQ if no interrupt is pending. */ | |
347 | unsigned int uic_get_irq(void) | |
348 | { | |
349 | u32 msr; | |
350 | int src; | |
351 | ||
352 | BUG_ON(! primary_uic); | |
353 | ||
354 | msr = mfdcr(primary_uic->dcrbase + UIC_MSR); | |
355 | src = 32 - ffs(msr); | |
356 | ||
357 | return irq_linear_revmap(primary_uic->irqhost, src); | |
358 | } |