diff options
Diffstat (limited to 'arch/arm/common/vic.c')
-rw-r--r-- | arch/arm/common/vic.c | 148 |
1 files changed, 118 insertions, 30 deletions
diff --git a/arch/arm/common/vic.c b/arch/arm/common/vic.c index 01f18a421b17..dcb004a804c7 100644 --- a/arch/arm/common/vic.c +++ b/arch/arm/common/vic.c | |||
@@ -19,17 +19,22 @@ | |||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
20 | */ | 20 | */ |
21 | 21 | ||
22 | #include <linux/export.h> | ||
22 | #include <linux/init.h> | 23 | #include <linux/init.h> |
23 | #include <linux/list.h> | 24 | #include <linux/list.h> |
24 | #include <linux/io.h> | 25 | #include <linux/io.h> |
26 | #include <linux/irqdomain.h> | ||
27 | #include <linux/of.h> | ||
28 | #include <linux/of_address.h> | ||
29 | #include <linux/of_irq.h> | ||
25 | #include <linux/syscore_ops.h> | 30 | #include <linux/syscore_ops.h> |
26 | #include <linux/device.h> | 31 | #include <linux/device.h> |
27 | #include <linux/amba/bus.h> | 32 | #include <linux/amba/bus.h> |
28 | 33 | ||
34 | #include <asm/exception.h> | ||
29 | #include <asm/mach/irq.h> | 35 | #include <asm/mach/irq.h> |
30 | #include <asm/hardware/vic.h> | 36 | #include <asm/hardware/vic.h> |
31 | 37 | ||
32 | #ifdef CONFIG_PM | ||
33 | /** | 38 | /** |
34 | * struct vic_device - VIC PM device | 39 | * struct vic_device - VIC PM device |
35 | * @irq: The IRQ number for the base of the VIC. | 40 | * @irq: The IRQ number for the base of the VIC. |
@@ -40,6 +45,7 @@ | |||
40 | * @int_enable: Save for VIC_INT_ENABLE. | 45 | * @int_enable: Save for VIC_INT_ENABLE. |
41 | * @soft_int: Save for VIC_INT_SOFT. | 46 | * @soft_int: Save for VIC_INT_SOFT. |
42 | * @protect: Save for VIC_PROTECT. | 47 | * @protect: Save for VIC_PROTECT. |
48 | * @domain: The IRQ domain for the VIC. | ||
43 | */ | 49 | */ |
44 | struct vic_device { | 50 | struct vic_device { |
45 | void __iomem *base; | 51 | void __iomem *base; |
@@ -50,13 +56,13 @@ struct vic_device { | |||
50 | u32 int_enable; | 56 | u32 int_enable; |
51 | u32 soft_int; | 57 | u32 soft_int; |
52 | u32 protect; | 58 | u32 protect; |
59 | struct irq_domain domain; | ||
53 | }; | 60 | }; |
54 | 61 | ||
55 | /* we cannot allocate memory when VICs are initially registered */ | 62 | /* we cannot allocate memory when VICs are initially registered */ |
56 | static struct vic_device vic_devices[CONFIG_ARM_VIC_NR]; | 63 | static struct vic_device vic_devices[CONFIG_ARM_VIC_NR]; |
57 | 64 | ||
58 | static int vic_id; | 65 | static int vic_id; |
59 | #endif /* CONFIG_PM */ | ||
60 | 66 | ||
61 | /** | 67 | /** |
62 | * vic_init2 - common initialisation code | 68 | * vic_init2 - common initialisation code |
@@ -156,39 +162,50 @@ static int __init vic_pm_init(void) | |||
156 | return 0; | 162 | return 0; |
157 | } | 163 | } |
158 | late_initcall(vic_pm_init); | 164 | late_initcall(vic_pm_init); |
165 | #endif /* CONFIG_PM */ | ||
159 | 166 | ||
160 | /** | 167 | /** |
161 | * vic_pm_register - Register a VIC for later power management control | 168 | * vic_register() - Register a VIC. |
162 | * @base: The base address of the VIC. | 169 | * @base: The base address of the VIC. |
163 | * @irq: The base IRQ for the VIC. | 170 | * @irq: The base IRQ for the VIC. |
164 | * @resume_sources: bitmask of interrupts allowed for resume sources. | 171 | * @resume_sources: bitmask of interrupts allowed for resume sources. |
172 | * @node: The device tree node associated with the VIC. | ||
165 | * | 173 | * |
166 | * Register the VIC with the system device tree so that it can be notified | 174 | * Register the VIC with the system device tree so that it can be notified |
167 | * of suspend and resume requests and ensure that the correct actions are | 175 | * of suspend and resume requests and ensure that the correct actions are |
168 | * taken to re-instate the settings on resume. | 176 | * taken to re-instate the settings on resume. |
177 | * | ||
178 | * This also configures the IRQ domain for the VIC. | ||
169 | */ | 179 | */ |
170 | static void __init vic_pm_register(void __iomem *base, unsigned int irq, u32 resume_sources) | 180 | static void __init vic_register(void __iomem *base, unsigned int irq, |
181 | u32 resume_sources, struct device_node *node) | ||
171 | { | 182 | { |
172 | struct vic_device *v; | 183 | struct vic_device *v; |
173 | 184 | ||
174 | if (vic_id >= ARRAY_SIZE(vic_devices)) | 185 | if (vic_id >= ARRAY_SIZE(vic_devices)) { |
175 | printk(KERN_ERR "%s: too few VICs, increase CONFIG_ARM_VIC_NR\n", __func__); | 186 | printk(KERN_ERR "%s: too few VICs, increase CONFIG_ARM_VIC_NR\n", __func__); |
176 | else { | 187 | return; |
177 | v = &vic_devices[vic_id]; | ||
178 | v->base = base; | ||
179 | v->resume_sources = resume_sources; | ||
180 | v->irq = irq; | ||
181 | vic_id++; | ||
182 | } | 188 | } |
189 | |||
190 | v = &vic_devices[vic_id]; | ||
191 | v->base = base; | ||
192 | v->resume_sources = resume_sources; | ||
193 | v->irq = irq; | ||
194 | vic_id++; | ||
195 | |||
196 | v->domain.irq_base = irq; | ||
197 | v->domain.nr_irq = 32; | ||
198 | #ifdef CONFIG_OF_IRQ | ||
199 | v->domain.of_node = of_node_get(node); | ||
200 | #endif /* CONFIG_OF */ | ||
201 | v->domain.ops = &irq_domain_simple_ops; | ||
202 | irq_domain_add(&v->domain); | ||
183 | } | 203 | } |
184 | #else | ||
185 | static inline void vic_pm_register(void __iomem *base, unsigned int irq, u32 arg1) { } | ||
186 | #endif /* CONFIG_PM */ | ||
187 | 204 | ||
188 | static void vic_ack_irq(struct irq_data *d) | 205 | static void vic_ack_irq(struct irq_data *d) |
189 | { | 206 | { |
190 | void __iomem *base = irq_data_get_irq_chip_data(d); | 207 | void __iomem *base = irq_data_get_irq_chip_data(d); |
191 | unsigned int irq = d->irq & 31; | 208 | unsigned int irq = d->hwirq; |
192 | writel(1 << irq, base + VIC_INT_ENABLE_CLEAR); | 209 | writel(1 << irq, base + VIC_INT_ENABLE_CLEAR); |
193 | /* moreover, clear the soft-triggered, in case it was the reason */ | 210 | /* moreover, clear the soft-triggered, in case it was the reason */ |
194 | writel(1 << irq, base + VIC_INT_SOFT_CLEAR); | 211 | writel(1 << irq, base + VIC_INT_SOFT_CLEAR); |
@@ -197,14 +214,14 @@ static void vic_ack_irq(struct irq_data *d) | |||
197 | static void vic_mask_irq(struct irq_data *d) | 214 | static void vic_mask_irq(struct irq_data *d) |
198 | { | 215 | { |
199 | void __iomem *base = irq_data_get_irq_chip_data(d); | 216 | void __iomem *base = irq_data_get_irq_chip_data(d); |
200 | unsigned int irq = d->irq & 31; | 217 | unsigned int irq = d->hwirq; |
201 | writel(1 << irq, base + VIC_INT_ENABLE_CLEAR); | 218 | writel(1 << irq, base + VIC_INT_ENABLE_CLEAR); |
202 | } | 219 | } |
203 | 220 | ||
204 | static void vic_unmask_irq(struct irq_data *d) | 221 | static void vic_unmask_irq(struct irq_data *d) |
205 | { | 222 | { |
206 | void __iomem *base = irq_data_get_irq_chip_data(d); | 223 | void __iomem *base = irq_data_get_irq_chip_data(d); |
207 | unsigned int irq = d->irq & 31; | 224 | unsigned int irq = d->hwirq; |
208 | writel(1 << irq, base + VIC_INT_ENABLE); | 225 | writel(1 << irq, base + VIC_INT_ENABLE); |
209 | } | 226 | } |
210 | 227 | ||
@@ -226,7 +243,7 @@ static struct vic_device *vic_from_irq(unsigned int irq) | |||
226 | static int vic_set_wake(struct irq_data *d, unsigned int on) | 243 | static int vic_set_wake(struct irq_data *d, unsigned int on) |
227 | { | 244 | { |
228 | struct vic_device *v = vic_from_irq(d->irq); | 245 | struct vic_device *v = vic_from_irq(d->irq); |
229 | unsigned int off = d->irq & 31; | 246 | unsigned int off = d->hwirq; |
230 | u32 bit = 1 << off; | 247 | u32 bit = 1 << off; |
231 | 248 | ||
232 | if (!v) | 249 | if (!v) |
@@ -301,7 +318,7 @@ static void __init vic_set_irq_sources(void __iomem *base, | |||
301 | * and 020 within the page. We call this "second block". | 318 | * and 020 within the page. We call this "second block". |
302 | */ | 319 | */ |
303 | static void __init vic_init_st(void __iomem *base, unsigned int irq_start, | 320 | static void __init vic_init_st(void __iomem *base, unsigned int irq_start, |
304 | u32 vic_sources) | 321 | u32 vic_sources, struct device_node *node) |
305 | { | 322 | { |
306 | unsigned int i; | 323 | unsigned int i; |
307 | int vic_2nd_block = ((unsigned long)base & ~PAGE_MASK) != 0; | 324 | int vic_2nd_block = ((unsigned long)base & ~PAGE_MASK) != 0; |
@@ -328,17 +345,12 @@ static void __init vic_init_st(void __iomem *base, unsigned int irq_start, | |||
328 | } | 345 | } |
329 | 346 | ||
330 | vic_set_irq_sources(base, irq_start, vic_sources); | 347 | vic_set_irq_sources(base, irq_start, vic_sources); |
348 | vic_register(base, irq_start, 0, node); | ||
331 | } | 349 | } |
332 | 350 | ||
333 | /** | 351 | static void __init __vic_init(void __iomem *base, unsigned int irq_start, |
334 | * vic_init - initialise a vectored interrupt controller | 352 | u32 vic_sources, u32 resume_sources, |
335 | * @base: iomem base address | 353 | struct device_node *node) |
336 | * @irq_start: starting interrupt number, must be muliple of 32 | ||
337 | * @vic_sources: bitmask of interrupt sources to allow | ||
338 | * @resume_sources: bitmask of interrupt sources to allow for resume | ||
339 | */ | ||
340 | void __init vic_init(void __iomem *base, unsigned int irq_start, | ||
341 | u32 vic_sources, u32 resume_sources) | ||
342 | { | 354 | { |
343 | unsigned int i; | 355 | unsigned int i; |
344 | u32 cellid = 0; | 356 | u32 cellid = 0; |
@@ -356,7 +368,7 @@ void __init vic_init(void __iomem *base, unsigned int irq_start, | |||
356 | 368 | ||
357 | switch(vendor) { | 369 | switch(vendor) { |
358 | case AMBA_VENDOR_ST: | 370 | case AMBA_VENDOR_ST: |
359 | vic_init_st(base, irq_start, vic_sources); | 371 | vic_init_st(base, irq_start, vic_sources, node); |
360 | return; | 372 | return; |
361 | default: | 373 | default: |
362 | printk(KERN_WARNING "VIC: unknown vendor, continuing anyways\n"); | 374 | printk(KERN_WARNING "VIC: unknown vendor, continuing anyways\n"); |
@@ -375,5 +387,81 @@ void __init vic_init(void __iomem *base, unsigned int irq_start, | |||
375 | 387 | ||
376 | vic_set_irq_sources(base, irq_start, vic_sources); | 388 | vic_set_irq_sources(base, irq_start, vic_sources); |
377 | 389 | ||
378 | vic_pm_register(base, irq_start, resume_sources); | 390 | vic_register(base, irq_start, resume_sources, node); |
391 | } | ||
392 | |||
393 | /** | ||
394 | * vic_init() - initialise a vectored interrupt controller | ||
395 | * @base: iomem base address | ||
396 | * @irq_start: starting interrupt number, must be muliple of 32 | ||
397 | * @vic_sources: bitmask of interrupt sources to allow | ||
398 | * @resume_sources: bitmask of interrupt sources to allow for resume | ||
399 | */ | ||
400 | void __init vic_init(void __iomem *base, unsigned int irq_start, | ||
401 | u32 vic_sources, u32 resume_sources) | ||
402 | { | ||
403 | __vic_init(base, irq_start, vic_sources, resume_sources, NULL); | ||
404 | } | ||
405 | |||
406 | #ifdef CONFIG_OF | ||
407 | int __init vic_of_init(struct device_node *node, struct device_node *parent) | ||
408 | { | ||
409 | void __iomem *regs; | ||
410 | int irq_base; | ||
411 | |||
412 | if (WARN(parent, "non-root VICs are not supported")) | ||
413 | return -EINVAL; | ||
414 | |||
415 | regs = of_iomap(node, 0); | ||
416 | if (WARN_ON(!regs)) | ||
417 | return -EIO; | ||
418 | |||
419 | irq_base = irq_alloc_descs(-1, 0, 32, numa_node_id()); | ||
420 | if (WARN_ON(irq_base < 0)) | ||
421 | goto out_unmap; | ||
422 | |||
423 | __vic_init(regs, irq_base, ~0, ~0, node); | ||
424 | |||
425 | return 0; | ||
426 | |||
427 | out_unmap: | ||
428 | iounmap(regs); | ||
429 | |||
430 | return -EIO; | ||
431 | } | ||
432 | #endif /* CONFIG OF */ | ||
433 | |||
434 | /* | ||
435 | * Handle each interrupt in a single VIC. Returns non-zero if we've | ||
436 | * handled at least one interrupt. This does a single read of the | ||
437 | * status register and handles all interrupts in order from LSB first. | ||
438 | */ | ||
439 | static int handle_one_vic(struct vic_device *vic, struct pt_regs *regs) | ||
440 | { | ||
441 | u32 stat, irq; | ||
442 | int handled = 0; | ||
443 | |||
444 | stat = readl_relaxed(vic->base + VIC_IRQ_STATUS); | ||
445 | while (stat) { | ||
446 | irq = ffs(stat) - 1; | ||
447 | handle_IRQ(irq_domain_to_irq(&vic->domain, irq), regs); | ||
448 | stat &= ~(1 << irq); | ||
449 | handled = 1; | ||
450 | } | ||
451 | |||
452 | return handled; | ||
453 | } | ||
454 | |||
455 | /* | ||
456 | * Keep iterating over all registered VIC's until there are no pending | ||
457 | * interrupts. | ||
458 | */ | ||
459 | asmlinkage void __exception_irq_entry vic_handle_irq(struct pt_regs *regs) | ||
460 | { | ||
461 | int i, handled; | ||
462 | |||
463 | do { | ||
464 | for (i = 0, handled = 0; i < vic_id; ++i) | ||
465 | handled |= handle_one_vic(&vic_devices[i], regs); | ||
466 | } while (handled); | ||
379 | } | 467 | } |