1 | /* |
2 | * Shared interrupt handling code for IPR and INTC2 types of IRQs. |
3 | * |
4 | * Copyright (C) 2007, 2008 Magnus Damm |
5 | * Copyright (C) 2009 - 2012 Paul Mundt |
6 | * |
7 | * Based on intc2.c and ipr.c |
8 | * |
9 | * Copyright (C) 1999 Niibe Yutaka & Takeshi Yaegashi |
10 | * Copyright (C) 2000 Kazumoto Kojima |
11 | * Copyright (C) 2001 David J. Mckay (david.mckay@st.com) |
12 | * Copyright (C) 2003 Takashi Kusuda <kusuda-takashi@hitachi-ul.co.jp> |
13 | * Copyright (C) 2005, 2006 Paul Mundt |
14 | * |
15 | * This file is subject to the terms and conditions of the GNU General Public |
16 | * License. See the file "COPYING" in the main directory of this archive |
17 | * for more details. |
18 | */ |
19 | #define pr_fmt(fmt) "intc: " fmt |
20 | |
21 | #include <linux/init.h> |
22 | #include <linux/irq.h> |
23 | #include <linux/io.h> |
24 | #include <linux/slab.h> |
25 | #include <linux/stat.h> |
26 | #include <linux/interrupt.h> |
27 | #include <linux/sh_intc.h> |
28 | #include <linux/irqdomain.h> |
29 | #include <linux/device.h> |
30 | #include <linux/syscore_ops.h> |
31 | #include <linux/list.h> |
32 | #include <linux/spinlock.h> |
33 | #include <linux/radix-tree.h> |
34 | #include <linux/export.h> |
35 | #include <linux/sort.h> |
36 | #include "internals.h" |
37 | |
38 | LIST_HEAD(intc_list); |
39 | DEFINE_RAW_SPINLOCK(intc_big_lock); |
40 | static unsigned int nr_intc_controllers; |
41 | |
42 | /* |
43 | * Default priority level |
44 | * - this needs to be at least 2 for 5-bit priorities on 7780 |
45 | */ |
46 | static unsigned int default_prio_level = 2; /* 2 - 16 */ |
47 | static unsigned int intc_prio_level[INTC_NR_IRQS]; /* for now */ |
48 | |
49 | unsigned int intc_get_dfl_prio_level(void) |
50 | { |
51 | return default_prio_level; |
52 | } |
53 | |
54 | unsigned int intc_get_prio_level(unsigned int irq) |
55 | { |
56 | return intc_prio_level[irq]; |
57 | } |
58 | |
59 | void intc_set_prio_level(unsigned int irq, unsigned int level) |
60 | { |
61 | unsigned long flags; |
62 | |
63 | raw_spin_lock_irqsave(&intc_big_lock, flags); |
64 | intc_prio_level[irq] = level; |
65 | raw_spin_unlock_irqrestore(&intc_big_lock, flags); |
66 | } |
67 | |
68 | static void intc_redirect_irq(struct irq_desc *desc) |
69 | { |
70 | generic_handle_irq(irq: (unsigned int)irq_desc_get_handler_data(desc)); |
71 | } |
72 | |
73 | static void __init intc_register_irq(struct intc_desc *desc, |
74 | struct intc_desc_int *d, |
75 | intc_enum enum_id, |
76 | unsigned int irq) |
77 | { |
78 | struct intc_handle_int *hp; |
79 | struct irq_data *irq_data; |
80 | unsigned int data[2], primary; |
81 | unsigned long flags; |
82 | |
83 | raw_spin_lock_irqsave(&intc_big_lock, flags); |
84 | radix_tree_insert(&d->tree, index: enum_id, intc_irq_xlate_get(irq)); |
85 | raw_spin_unlock_irqrestore(&intc_big_lock, flags); |
86 | |
87 | /* |
88 | * Prefer single interrupt source bitmap over other combinations: |
89 | * |
90 | * 1. bitmap, single interrupt source |
91 | * 2. priority, single interrupt source |
92 | * 3. bitmap, multiple interrupt sources (groups) |
93 | * 4. priority, multiple interrupt sources (groups) |
94 | */ |
95 | data[0] = intc_get_mask_handle(desc, d, enum_id, do_grps: 0); |
96 | data[1] = intc_get_prio_handle(desc, d, enum_id, do_grps: 0); |
97 | |
98 | primary = 0; |
99 | if (!data[0] && data[1]) |
100 | primary = 1; |
101 | |
102 | if (!data[0] && !data[1]) |
103 | pr_warn("missing unique irq mask for irq %d (vect 0x%04x)\n" , |
104 | irq, irq2evt(irq)); |
105 | |
106 | data[0] = data[0] ? data[0] : intc_get_mask_handle(desc, d, enum_id, do_grps: 1); |
107 | data[1] = data[1] ? data[1] : intc_get_prio_handle(desc, d, enum_id, do_grps: 1); |
108 | |
109 | if (!data[primary]) |
110 | primary ^= 1; |
111 | |
112 | BUG_ON(!data[primary]); /* must have primary masking method */ |
113 | |
114 | irq_data = irq_get_irq_data(irq); |
115 | |
116 | disable_irq_nosync(irq); |
117 | irq_set_chip_and_handler_name(irq, chip: &d->chip, handle: handle_level_irq, |
118 | name: "level" ); |
119 | irq_set_chip_data(irq, data: (void *)data[primary]); |
120 | |
121 | /* |
122 | * set priority level |
123 | */ |
124 | intc_set_prio_level(irq, level: intc_get_dfl_prio_level()); |
125 | |
126 | /* enable secondary masking method if present */ |
127 | if (data[!primary]) |
128 | _intc_enable(data: irq_data, handle: data[!primary]); |
129 | |
130 | /* add irq to d->prio list if priority is available */ |
131 | if (data[1]) { |
132 | hp = d->prio + d->nr_prio; |
133 | hp->irq = irq; |
134 | hp->handle = data[1]; |
135 | |
136 | if (primary) { |
137 | /* |
138 | * only secondary priority should access registers, so |
139 | * set _INTC_FN(h) = REG_FN_ERR for intc_set_priority() |
140 | */ |
141 | hp->handle &= ~_INTC_MK(0x0f, 0, 0, 0, 0, 0); |
142 | hp->handle |= _INTC_MK(REG_FN_ERR, 0, 0, 0, 0, 0); |
143 | } |
144 | d->nr_prio++; |
145 | } |
146 | |
147 | /* add irq to d->sense list if sense is available */ |
148 | data[0] = intc_get_sense_handle(desc, d, enum_id); |
149 | if (data[0]) { |
150 | (d->sense + d->nr_sense)->irq = irq; |
151 | (d->sense + d->nr_sense)->handle = data[0]; |
152 | d->nr_sense++; |
153 | } |
154 | |
155 | /* irq should be disabled by default */ |
156 | d->chip.irq_mask(irq_data); |
157 | |
158 | intc_set_ack_handle(irq, desc, d, id: enum_id); |
159 | intc_set_dist_handle(irq, desc, d, id: enum_id); |
160 | |
161 | activate_irq(irq); |
162 | } |
163 | |
164 | static unsigned int __init save_reg(struct intc_desc_int *d, |
165 | unsigned int cnt, |
166 | unsigned long value, |
167 | unsigned int smp) |
168 | { |
169 | if (value) { |
170 | value = intc_phys_to_virt(d, address: value); |
171 | |
172 | d->reg[cnt] = value; |
173 | #ifdef CONFIG_SMP |
174 | d->smp[cnt] = smp; |
175 | #endif |
176 | return 1; |
177 | } |
178 | |
179 | return 0; |
180 | } |
181 | |
182 | static bool __init intc_map(struct irq_domain *domain, int irq) |
183 | { |
184 | if (!irq_to_desc(irq) && irq_alloc_desc_at(irq, NUMA_NO_NODE) != irq) { |
185 | pr_err("uname to allocate IRQ %d\n" , irq); |
186 | return false; |
187 | } |
188 | |
189 | if (irq_domain_associate(domain, irq, hwirq: irq)) { |
190 | pr_err("domain association failure\n" ); |
191 | return false; |
192 | } |
193 | |
194 | return true; |
195 | } |
196 | |
197 | int __init register_intc_controller(struct intc_desc *desc) |
198 | { |
199 | unsigned int i, k, smp; |
200 | struct intc_hw_desc *hw = &desc->hw; |
201 | struct intc_desc_int *d; |
202 | struct resource *res; |
203 | |
204 | pr_info("Registered controller '%s' with %u IRQs\n" , |
205 | desc->name, hw->nr_vectors); |
206 | |
207 | d = kzalloc(size: sizeof(*d), GFP_NOWAIT); |
208 | if (!d) |
209 | goto err0; |
210 | |
211 | INIT_LIST_HEAD(list: &d->list); |
212 | list_add_tail(new: &d->list, head: &intc_list); |
213 | |
214 | raw_spin_lock_init(&d->lock); |
215 | INIT_RADIX_TREE(&d->tree, GFP_ATOMIC); |
216 | |
217 | d->index = nr_intc_controllers; |
218 | |
219 | if (desc->num_resources) { |
220 | d->nr_windows = desc->num_resources; |
221 | d->window = kcalloc(n: d->nr_windows, size: sizeof(*d->window), |
222 | GFP_NOWAIT); |
223 | if (!d->window) |
224 | goto err1; |
225 | |
226 | for (k = 0; k < d->nr_windows; k++) { |
227 | res = desc->resource + k; |
228 | WARN_ON(resource_type(res) != IORESOURCE_MEM); |
229 | d->window[k].phys = res->start; |
230 | d->window[k].size = resource_size(res); |
231 | d->window[k].virt = ioremap(offset: res->start, |
232 | size: resource_size(res)); |
233 | if (!d->window[k].virt) |
234 | goto err2; |
235 | } |
236 | } |
237 | |
238 | d->nr_reg = hw->mask_regs ? hw->nr_mask_regs * 2 : 0; |
239 | #ifdef CONFIG_INTC_BALANCING |
240 | if (d->nr_reg) |
241 | d->nr_reg += hw->nr_mask_regs; |
242 | #endif |
243 | d->nr_reg += hw->prio_regs ? hw->nr_prio_regs * 2 : 0; |
244 | d->nr_reg += hw->sense_regs ? hw->nr_sense_regs : 0; |
245 | d->nr_reg += hw->ack_regs ? hw->nr_ack_regs : 0; |
246 | d->nr_reg += hw->subgroups ? hw->nr_subgroups : 0; |
247 | |
248 | d->reg = kcalloc(n: d->nr_reg, size: sizeof(*d->reg), GFP_NOWAIT); |
249 | if (!d->reg) |
250 | goto err2; |
251 | |
252 | #ifdef CONFIG_SMP |
253 | d->smp = kcalloc(n: d->nr_reg, size: sizeof(*d->smp), GFP_NOWAIT); |
254 | if (!d->smp) |
255 | goto err3; |
256 | #endif |
257 | k = 0; |
258 | |
259 | if (hw->mask_regs) { |
260 | for (i = 0; i < hw->nr_mask_regs; i++) { |
261 | smp = IS_SMP(hw->mask_regs[i]); |
262 | k += save_reg(d, cnt: k, value: hw->mask_regs[i].set_reg, smp); |
263 | k += save_reg(d, cnt: k, value: hw->mask_regs[i].clr_reg, smp); |
264 | #ifdef CONFIG_INTC_BALANCING |
265 | k += save_reg(d, k, hw->mask_regs[i].dist_reg, 0); |
266 | #endif |
267 | } |
268 | } |
269 | |
270 | if (hw->prio_regs) { |
271 | d->prio = kcalloc(n: hw->nr_vectors, size: sizeof(*d->prio), |
272 | GFP_NOWAIT); |
273 | if (!d->prio) |
274 | goto err4; |
275 | |
276 | for (i = 0; i < hw->nr_prio_regs; i++) { |
277 | smp = IS_SMP(hw->prio_regs[i]); |
278 | k += save_reg(d, cnt: k, value: hw->prio_regs[i].set_reg, smp); |
279 | k += save_reg(d, cnt: k, value: hw->prio_regs[i].clr_reg, smp); |
280 | } |
281 | |
282 | sort(base: d->prio, num: hw->nr_prio_regs, size: sizeof(*d->prio), |
283 | cmp_func: intc_handle_int_cmp, NULL); |
284 | } |
285 | |
286 | if (hw->sense_regs) { |
287 | d->sense = kcalloc(n: hw->nr_vectors, size: sizeof(*d->sense), |
288 | GFP_NOWAIT); |
289 | if (!d->sense) |
290 | goto err5; |
291 | |
292 | for (i = 0; i < hw->nr_sense_regs; i++) |
293 | k += save_reg(d, cnt: k, value: hw->sense_regs[i].reg, smp: 0); |
294 | |
295 | sort(base: d->sense, num: hw->nr_sense_regs, size: sizeof(*d->sense), |
296 | cmp_func: intc_handle_int_cmp, NULL); |
297 | } |
298 | |
299 | if (hw->subgroups) |
300 | for (i = 0; i < hw->nr_subgroups; i++) |
301 | if (hw->subgroups[i].reg) |
302 | k+= save_reg(d, cnt: k, value: hw->subgroups[i].reg, smp: 0); |
303 | |
304 | memcpy(&d->chip, &intc_irq_chip, sizeof(struct irq_chip)); |
305 | d->chip.name = desc->name; |
306 | |
307 | if (hw->ack_regs) |
308 | for (i = 0; i < hw->nr_ack_regs; i++) |
309 | k += save_reg(d, cnt: k, value: hw->ack_regs[i].set_reg, smp: 0); |
310 | else |
311 | d->chip.irq_mask_ack = d->chip.irq_disable; |
312 | |
313 | /* disable bits matching force_disable before registering irqs */ |
314 | if (desc->force_disable) |
315 | intc_enable_disable_enum(desc, d, enum_id: desc->force_disable, enable: 0); |
316 | |
317 | /* disable bits matching force_enable before registering irqs */ |
318 | if (desc->force_enable) |
319 | intc_enable_disable_enum(desc, d, enum_id: desc->force_enable, enable: 0); |
320 | |
321 | BUG_ON(k > 256); /* _INTC_ADDR_E() and _INTC_ADDR_D() are 8 bits */ |
322 | |
323 | intc_irq_domain_init(d, hw); |
324 | |
325 | /* register the vectors one by one */ |
326 | for (i = 0; i < hw->nr_vectors; i++) { |
327 | struct intc_vect *vect = hw->vectors + i; |
328 | unsigned int irq = evt2irq(vect->vect); |
329 | |
330 | if (!vect->enum_id) |
331 | continue; |
332 | |
333 | if (!intc_map(domain: d->domain, irq)) |
334 | continue; |
335 | |
336 | intc_irq_xlate_set(irq, id: vect->enum_id, d); |
337 | intc_register_irq(desc, d, enum_id: vect->enum_id, irq); |
338 | |
339 | for (k = i + 1; k < hw->nr_vectors; k++) { |
340 | struct intc_vect *vect2 = hw->vectors + k; |
341 | unsigned int irq2 = evt2irq(vect2->vect); |
342 | |
343 | if (vect->enum_id != vect2->enum_id) |
344 | continue; |
345 | |
346 | /* |
347 | * In the case of multi-evt handling and sparse |
348 | * IRQ support, each vector still needs to have |
349 | * its own backing irq_desc. |
350 | */ |
351 | if (!intc_map(domain: d->domain, irq: irq2)) |
352 | continue; |
353 | |
354 | vect2->enum_id = 0; |
355 | |
356 | /* redirect this interrupts to the first one */ |
357 | irq_set_chip(irq: irq2, chip: &dummy_irq_chip); |
358 | irq_set_chained_handler_and_data(irq: irq2, |
359 | handle: intc_redirect_irq, |
360 | data: (void *)irq); |
361 | } |
362 | } |
363 | |
364 | intc_subgroup_init(desc, d); |
365 | |
366 | /* enable bits matching force_enable after registering irqs */ |
367 | if (desc->force_enable) |
368 | intc_enable_disable_enum(desc, d, enum_id: desc->force_enable, enable: 1); |
369 | |
370 | d->skip_suspend = desc->skip_syscore_suspend; |
371 | |
372 | nr_intc_controllers++; |
373 | |
374 | return 0; |
375 | err5: |
376 | kfree(objp: d->prio); |
377 | err4: |
378 | #ifdef CONFIG_SMP |
379 | kfree(objp: d->smp); |
380 | err3: |
381 | #endif |
382 | kfree(objp: d->reg); |
383 | err2: |
384 | for (k = 0; k < d->nr_windows; k++) |
385 | if (d->window[k].virt) |
386 | iounmap(addr: d->window[k].virt); |
387 | |
388 | kfree(objp: d->window); |
389 | err1: |
390 | kfree(objp: d); |
391 | err0: |
392 | pr_err("unable to allocate INTC memory\n" ); |
393 | |
394 | return -ENOMEM; |
395 | } |
396 | |
397 | static int intc_suspend(void) |
398 | { |
399 | struct intc_desc_int *d; |
400 | |
401 | list_for_each_entry(d, &intc_list, list) { |
402 | int irq; |
403 | |
404 | if (d->skip_suspend) |
405 | continue; |
406 | |
407 | /* enable wakeup irqs belonging to this intc controller */ |
408 | for_each_active_irq(irq) { |
409 | struct irq_data *data; |
410 | struct irq_chip *chip; |
411 | |
412 | data = irq_get_irq_data(irq); |
413 | chip = irq_data_get_irq_chip(d: data); |
414 | if (chip != &d->chip) |
415 | continue; |
416 | if (irqd_is_wakeup_set(d: data)) |
417 | chip->irq_enable(data); |
418 | } |
419 | } |
420 | return 0; |
421 | } |
422 | |
423 | static void intc_resume(void) |
424 | { |
425 | struct intc_desc_int *d; |
426 | |
427 | list_for_each_entry(d, &intc_list, list) { |
428 | int irq; |
429 | |
430 | if (d->skip_suspend) |
431 | continue; |
432 | |
433 | for_each_active_irq(irq) { |
434 | struct irq_data *data; |
435 | struct irq_chip *chip; |
436 | |
437 | data = irq_get_irq_data(irq); |
438 | chip = irq_data_get_irq_chip(d: data); |
439 | /* |
440 | * This will catch the redirect and VIRQ cases |
441 | * due to the dummy_irq_chip being inserted. |
442 | */ |
443 | if (chip != &d->chip) |
444 | continue; |
445 | if (irqd_irq_disabled(d: data)) |
446 | chip->irq_disable(data); |
447 | else |
448 | chip->irq_enable(data); |
449 | } |
450 | } |
451 | } |
452 | |
453 | struct syscore_ops intc_syscore_ops = { |
454 | .suspend = intc_suspend, |
455 | .resume = intc_resume, |
456 | }; |
457 | |
458 | struct bus_type intc_subsys = { |
459 | .name = "intc" , |
460 | .dev_name = "intc" , |
461 | }; |
462 | |
463 | static ssize_t |
464 | show_intc_name(struct device *dev, struct device_attribute *attr, char *buf) |
465 | { |
466 | struct intc_desc_int *d; |
467 | |
468 | d = container_of(dev, struct intc_desc_int, dev); |
469 | |
470 | return sprintf(buf, fmt: "%s\n" , d->chip.name); |
471 | } |
472 | |
473 | static DEVICE_ATTR(name, S_IRUGO, show_intc_name, NULL); |
474 | |
475 | static int __init register_intc_devs(void) |
476 | { |
477 | struct intc_desc_int *d; |
478 | int error; |
479 | |
480 | register_syscore_ops(ops: &intc_syscore_ops); |
481 | |
482 | error = subsys_system_register(subsys: &intc_subsys, NULL); |
483 | if (!error) { |
484 | list_for_each_entry(d, &intc_list, list) { |
485 | d->dev.id = d->index; |
486 | d->dev.bus = &intc_subsys; |
487 | error = device_register(dev: &d->dev); |
488 | if (error == 0) |
489 | error = device_create_file(device: &d->dev, |
490 | entry: &dev_attr_name); |
491 | if (error) |
492 | break; |
493 | } |
494 | } |
495 | |
496 | if (error) |
497 | pr_err("device registration error\n" ); |
498 | |
499 | return error; |
500 | } |
501 | device_initcall(register_intc_devs); |
502 | |