aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/sh/intc/core.c
diff options
context:
space:
mode:
authorPaul Mundt <lethal@linux-sh.org>2010-10-05 09:10:30 -0400
committerPaul Mundt <lethal@linux-sh.org>2010-10-05 09:10:30 -0400
commit2be6bb0c79c7fbda3425b65ee51c558bbaf4cf91 (patch)
treedb0dafd7e7f83945edc2c50c358a3d81fca960c3 /drivers/sh/intc/core.c
parentd74310d3b18aabbb7d0549ea9e3fd3259c1dce00 (diff)
sh: intc: Split up the INTC code.
This splits up the sh intc core in to something more vaguely resembling a subsystem. Most of the functionality was alread fairly well compartmentalized, and there were only a handful of interdependencies that needed to be resolved in the process. This also serves as future-proofing for the genirq and sparseirq rework, which will make some of the split out functionality wholly generic, allowing things to be killed off in place with minimal migration pain. Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'drivers/sh/intc/core.c')
-rw-r--r--drivers/sh/intc/core.c469
1 files changed, 469 insertions, 0 deletions
diff --git a/drivers/sh/intc/core.c b/drivers/sh/intc/core.c
new file mode 100644
index 000000000000..306ed287077a
--- /dev/null
+++ b/drivers/sh/intc/core.c
@@ -0,0 +1,469 @@
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, 2010 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/interrupt.h>
26#include <linux/sh_intc.h>
27#include <linux/sysdev.h>
28#include <linux/list.h>
29#include <linux/spinlock.h>
30#include <linux/radix-tree.h>
31#include "internals.h"
32
33LIST_HEAD(intc_list);
34DEFINE_RAW_SPINLOCK(intc_big_lock);
35unsigned int nr_intc_controllers;
36
37/*
38 * Default priority level
39 * - this needs to be at least 2 for 5-bit priorities on 7780
40 */
41static unsigned int default_prio_level = 2; /* 2 - 16 */
42static unsigned int intc_prio_level[NR_IRQS]; /* for now */
43
44unsigned int intc_get_dfl_prio_level(void)
45{
46 return default_prio_level;
47}
48
49unsigned int intc_get_prio_level(unsigned int irq)
50{
51 return intc_prio_level[irq];
52}
53
54void intc_set_prio_level(unsigned int irq, unsigned int level)
55{
56 unsigned long flags;
57
58 raw_spin_lock_irqsave(&intc_big_lock, flags);
59 intc_prio_level[irq] = level;
60 raw_spin_unlock_irqrestore(&intc_big_lock, flags);
61}
62
63static void intc_redirect_irq(unsigned int irq, struct irq_desc *desc)
64{
65 generic_handle_irq((unsigned int)get_irq_data(irq));
66}
67
68static void __init intc_register_irq(struct intc_desc *desc,
69 struct intc_desc_int *d,
70 intc_enum enum_id,
71 unsigned int irq)
72{
73 struct intc_handle_int *hp;
74 unsigned int data[2], primary;
75 unsigned long flags;
76
77 /*
78 * Register the IRQ position with the global IRQ map, then insert
79 * it in to the radix tree.
80 */
81 reserve_irq_vector(irq);
82
83 raw_spin_lock_irqsave(&intc_big_lock, flags);
84 radix_tree_insert(&d->tree, 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, 0);
96 data[1] = intc_get_prio_handle(desc, d, enum_id, 0);
97
98 primary = 0;
99 if (!data[0] && data[1])
100 primary = 1;
101
102 if (!data[0] && !data[1])
103 pr_warning("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, 1);
107 data[1] = data[1] ? data[1] : intc_get_prio_handle(desc, d, enum_id, 1);
108
109 if (!data[primary])
110 primary ^= 1;
111
112 BUG_ON(!data[primary]); /* must have primary masking method */
113
114 disable_irq_nosync(irq);
115 set_irq_chip_and_handler_name(irq, &d->chip,
116 handle_level_irq, "level");
117 set_irq_chip_data(irq, (void *)data[primary]);
118
119 /*
120 * set priority level
121 */
122 intc_set_prio_level(irq, intc_get_dfl_prio_level());
123
124 /* enable secondary masking method if present */
125 if (data[!primary])
126 _intc_enable(irq, data[!primary]);
127
128 /* add irq to d->prio list if priority is available */
129 if (data[1]) {
130 hp = d->prio + d->nr_prio;
131 hp->irq = irq;
132 hp->handle = data[1];
133
134 if (primary) {
135 /*
136 * only secondary priority should access registers, so
137 * set _INTC_FN(h) = REG_FN_ERR for intc_set_priority()
138 */
139 hp->handle &= ~_INTC_MK(0x0f, 0, 0, 0, 0, 0);
140 hp->handle |= _INTC_MK(REG_FN_ERR, 0, 0, 0, 0, 0);
141 }
142 d->nr_prio++;
143 }
144
145 /* add irq to d->sense list if sense is available */
146 data[0] = intc_get_sense_handle(desc, d, enum_id);
147 if (data[0]) {
148 (d->sense + d->nr_sense)->irq = irq;
149 (d->sense + d->nr_sense)->handle = data[0];
150 d->nr_sense++;
151 }
152
153 /* irq should be disabled by default */
154 d->chip.mask(irq);
155
156 intc_set_ack_handle(irq, desc, d, enum_id);
157 intc_set_dist_handle(irq, desc, d, enum_id);
158
159 activate_irq(irq);
160}
161
162static unsigned int __init save_reg(struct intc_desc_int *d,
163 unsigned int cnt,
164 unsigned long value,
165 unsigned int smp)
166{
167 if (value) {
168 value = intc_phys_to_virt(d, value);
169
170 d->reg[cnt] = value;
171#ifdef CONFIG_SMP
172 d->smp[cnt] = smp;
173#endif
174 return 1;
175 }
176
177 return 0;
178}
179
180int __init register_intc_controller(struct intc_desc *desc)
181{
182 unsigned int i, k, smp;
183 struct intc_hw_desc *hw = &desc->hw;
184 struct intc_desc_int *d;
185 struct resource *res;
186
187 pr_info("Registered controller '%s' with %u IRQs\n",
188 desc->name, hw->nr_vectors);
189
190 d = kzalloc(sizeof(*d), GFP_NOWAIT);
191 if (!d)
192 goto err0;
193
194 INIT_LIST_HEAD(&d->list);
195 list_add_tail(&d->list, &intc_list);
196
197 raw_spin_lock_init(&d->lock);
198
199 d->index = nr_intc_controllers;
200
201 if (desc->num_resources) {
202 d->nr_windows = desc->num_resources;
203 d->window = kzalloc(d->nr_windows * sizeof(*d->window),
204 GFP_NOWAIT);
205 if (!d->window)
206 goto err1;
207
208 for (k = 0; k < d->nr_windows; k++) {
209 res = desc->resource + k;
210 WARN_ON(resource_type(res) != IORESOURCE_MEM);
211 d->window[k].phys = res->start;
212 d->window[k].size = resource_size(res);
213 d->window[k].virt = ioremap_nocache(res->start,
214 resource_size(res));
215 if (!d->window[k].virt)
216 goto err2;
217 }
218 }
219
220 d->nr_reg = hw->mask_regs ? hw->nr_mask_regs * 2 : 0;
221#ifdef CONFIG_INTC_BALANCING
222 if (d->nr_reg)
223 d->nr_reg += hw->nr_mask_regs;
224#endif
225 d->nr_reg += hw->prio_regs ? hw->nr_prio_regs * 2 : 0;
226 d->nr_reg += hw->sense_regs ? hw->nr_sense_regs : 0;
227 d->nr_reg += hw->ack_regs ? hw->nr_ack_regs : 0;
228 d->nr_reg += hw->subgroups ? hw->nr_subgroups : 0;
229
230 d->reg = kzalloc(d->nr_reg * sizeof(*d->reg), GFP_NOWAIT);
231 if (!d->reg)
232 goto err2;
233
234#ifdef CONFIG_SMP
235 d->smp = kzalloc(d->nr_reg * sizeof(*d->smp), GFP_NOWAIT);
236 if (!d->smp)
237 goto err3;
238#endif
239 k = 0;
240
241 if (hw->mask_regs) {
242 for (i = 0; i < hw->nr_mask_regs; i++) {
243 smp = IS_SMP(hw->mask_regs[i]);
244 k += save_reg(d, k, hw->mask_regs[i].set_reg, smp);
245 k += save_reg(d, k, hw->mask_regs[i].clr_reg, smp);
246#ifdef CONFIG_INTC_BALANCING
247 k += save_reg(d, k, hw->mask_regs[i].dist_reg, 0);
248#endif
249 }
250 }
251
252 if (hw->prio_regs) {
253 d->prio = kzalloc(hw->nr_vectors * sizeof(*d->prio),
254 GFP_NOWAIT);
255 if (!d->prio)
256 goto err4;
257
258 for (i = 0; i < hw->nr_prio_regs; i++) {
259 smp = IS_SMP(hw->prio_regs[i]);
260 k += save_reg(d, k, hw->prio_regs[i].set_reg, smp);
261 k += save_reg(d, k, hw->prio_regs[i].clr_reg, smp);
262 }
263 }
264
265 if (hw->sense_regs) {
266 d->sense = kzalloc(hw->nr_vectors * sizeof(*d->sense),
267 GFP_NOWAIT);
268 if (!d->sense)
269 goto err5;
270
271 for (i = 0; i < hw->nr_sense_regs; i++)
272 k += save_reg(d, k, hw->sense_regs[i].reg, 0);
273 }
274
275 if (hw->subgroups)
276 for (i = 0; i < hw->nr_subgroups; i++)
277 if (hw->subgroups[i].reg)
278 k+= save_reg(d, k, hw->subgroups[i].reg, 0);
279
280 memcpy(&d->chip, &intc_irq_chip, sizeof(struct irq_chip));
281 d->chip.name = desc->name;
282
283 if (hw->ack_regs)
284 for (i = 0; i < hw->nr_ack_regs; i++)
285 k += save_reg(d, k, hw->ack_regs[i].set_reg, 0);
286 else
287 d->chip.mask_ack = d->chip.disable;
288
289 /* disable bits matching force_disable before registering irqs */
290 if (desc->force_disable)
291 intc_enable_disable_enum(desc, d, desc->force_disable, 0);
292
293 /* disable bits matching force_enable before registering irqs */
294 if (desc->force_enable)
295 intc_enable_disable_enum(desc, d, desc->force_enable, 0);
296
297 BUG_ON(k > 256); /* _INTC_ADDR_E() and _INTC_ADDR_D() are 8 bits */
298
299 /* register the vectors one by one */
300 for (i = 0; i < hw->nr_vectors; i++) {
301 struct intc_vect *vect = hw->vectors + i;
302 unsigned int irq = evt2irq(vect->vect);
303 struct irq_desc *irq_desc;
304
305 if (!vect->enum_id)
306 continue;
307
308 irq_desc = irq_to_desc_alloc_node(irq, numa_node_id());
309 if (unlikely(!irq_desc)) {
310 pr_err("can't get irq_desc for %d\n", irq);
311 continue;
312 }
313
314 intc_irq_xlate_set(irq, vect->enum_id, d);
315 intc_register_irq(desc, d, vect->enum_id, irq);
316
317 for (k = i + 1; k < hw->nr_vectors; k++) {
318 struct intc_vect *vect2 = hw->vectors + k;
319 unsigned int irq2 = evt2irq(vect2->vect);
320
321 if (vect->enum_id != vect2->enum_id)
322 continue;
323
324 /*
325 * In the case of multi-evt handling and sparse
326 * IRQ support, each vector still needs to have
327 * its own backing irq_desc.
328 */
329 irq_desc = irq_to_desc_alloc_node(irq2, numa_node_id());
330 if (unlikely(!irq_desc)) {
331 pr_err("can't get irq_desc for %d\n", irq2);
332 continue;
333 }
334
335 vect2->enum_id = 0;
336
337 /* redirect this interrupts to the first one */
338 set_irq_chip(irq2, &dummy_irq_chip);
339 set_irq_chained_handler(irq2, intc_redirect_irq);
340 set_irq_data(irq2, (void *)irq);
341 }
342 }
343
344 intc_subgroup_init(desc, d);
345
346 /* enable bits matching force_enable after registering irqs */
347 if (desc->force_enable)
348 intc_enable_disable_enum(desc, d, desc->force_enable, 1);
349
350 nr_intc_controllers++;
351
352 return 0;
353err5:
354 kfree(d->prio);
355err4:
356#ifdef CONFIG_SMP
357 kfree(d->smp);
358err3:
359#endif
360 kfree(d->reg);
361err2:
362 for (k = 0; k < d->nr_windows; k++)
363 if (d->window[k].virt)
364 iounmap(d->window[k].virt);
365
366 kfree(d->window);
367err1:
368 kfree(d);
369err0:
370 pr_err("unable to allocate INTC memory\n");
371
372 return -ENOMEM;
373}
374
375static ssize_t
376show_intc_name(struct sys_device *dev, struct sysdev_attribute *attr, char *buf)
377{
378 struct intc_desc_int *d;
379
380 d = container_of(dev, struct intc_desc_int, sysdev);
381
382 return sprintf(buf, "%s\n", d->chip.name);
383}
384
385static SYSDEV_ATTR(name, S_IRUGO, show_intc_name, NULL);
386
387static int intc_suspend(struct sys_device *dev, pm_message_t state)
388{
389 struct intc_desc_int *d;
390 struct irq_desc *desc;
391 int irq;
392
393 /* get intc controller associated with this sysdev */
394 d = container_of(dev, struct intc_desc_int, sysdev);
395
396 switch (state.event) {
397 case PM_EVENT_ON:
398 if (d->state.event != PM_EVENT_FREEZE)
399 break;
400
401 for_each_irq_desc(irq, desc) {
402 /*
403 * This will catch the redirect and VIRQ cases
404 * due to the dummy_irq_chip being inserted.
405 */
406 if (desc->chip != &d->chip)
407 continue;
408 if (desc->status & IRQ_DISABLED)
409 desc->chip->disable(irq);
410 else
411 desc->chip->enable(irq);
412 }
413 break;
414 case PM_EVENT_FREEZE:
415 /* nothing has to be done */
416 break;
417 case PM_EVENT_SUSPEND:
418 /* enable wakeup irqs belonging to this intc controller */
419 for_each_irq_desc(irq, desc) {
420 if (desc->chip != &d->chip)
421 continue;
422 if ((desc->status & IRQ_WAKEUP))
423 desc->chip->enable(irq);
424 }
425 break;
426 }
427
428 d->state = state;
429
430 return 0;
431}
432
433static int intc_resume(struct sys_device *dev)
434{
435 return intc_suspend(dev, PMSG_ON);
436}
437
438struct sysdev_class intc_sysdev_class = {
439 .name = "intc",
440 .suspend = intc_suspend,
441 .resume = intc_resume,
442};
443
444/* register this intc as sysdev to allow suspend/resume */
445static int __init register_intc_sysdevs(void)
446{
447 struct intc_desc_int *d;
448 int error;
449
450 error = sysdev_class_register(&intc_sysdev_class);
451 if (!error) {
452 list_for_each_entry(d, &intc_list, list) {
453 d->sysdev.id = d->index;
454 d->sysdev.cls = &intc_sysdev_class;
455 error = sysdev_register(&d->sysdev);
456 if (error == 0)
457 error = sysdev_create_file(&d->sysdev,
458 &attr_name);
459 if (error)
460 break;
461 }
462 }
463
464 if (error)
465 pr_err("sysdev registration error\n");
466
467 return error;
468}
469device_initcall(register_intc_sysdevs);