diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-11-02 00:02:35 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-11-02 00:02:35 -0400 |
commit | 367069f16e32e188d4687fe2c3e30f2ca583836f (patch) | |
tree | 977f87038b75e53af9d132cba2f7a2aecb2fa005 /arch/arm/common/gic.c | |
parent | 81a3c10ce8a7fd5bf9a06bfc38bd417512911831 (diff) | |
parent | c72dbae971400e466ad9ff16c920cd6d9d8c55a1 (diff) |
Merge branch 'next/dt' of git://git.linaro.org/people/arnd/arm-soc
* 'next/dt' of git://git.linaro.org/people/arnd/arm-soc:
ARM: gic: use module.h instead of export.h
ARM: gic: fix irq_alloc_descs handling for sparse irq
ARM: gic: add OF based initialization
ARM: gic: add irq_domain support
irq: support domains with non-zero hwirq base
of/irq: introduce of_irq_init
ARM: at91: add at91sam9g20 and Calao USB A9G20 DT support
ARM: at91: dt: at91sam9g45 family and board device tree files
arm/mx5: add device tree support for imx51 babbage
arm/mx5: add device tree support for imx53 boards
ARM: msm: Add devicetree support for msm8660-surf
msm_serial: Add devicetree support
msm_serial: Use relative resources for iomem
Fix up conflicts in arch/arm/mach-at91/{at91sam9260.c,at91sam9g45.c}
Diffstat (limited to 'arch/arm/common/gic.c')
-rw-r--r-- | arch/arm/common/gic.c | 188 |
1 files changed, 125 insertions, 63 deletions
diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c index a8fc6b237592..0e6ae470c94f 100644 --- a/arch/arm/common/gic.c +++ b/arch/arm/common/gic.c | |||
@@ -24,11 +24,17 @@ | |||
24 | */ | 24 | */ |
25 | #include <linux/init.h> | 25 | #include <linux/init.h> |
26 | #include <linux/kernel.h> | 26 | #include <linux/kernel.h> |
27 | #include <linux/err.h> | ||
28 | #include <linux/module.h> | ||
27 | #include <linux/list.h> | 29 | #include <linux/list.h> |
28 | #include <linux/smp.h> | 30 | #include <linux/smp.h> |
29 | #include <linux/cpu_pm.h> | 31 | #include <linux/cpu_pm.h> |
30 | #include <linux/cpumask.h> | 32 | #include <linux/cpumask.h> |
31 | #include <linux/io.h> | 33 | #include <linux/io.h> |
34 | #include <linux/of.h> | ||
35 | #include <linux/of_address.h> | ||
36 | #include <linux/of_irq.h> | ||
37 | #include <linux/irqdomain.h> | ||
32 | #include <linux/interrupt.h> | 38 | #include <linux/interrupt.h> |
33 | #include <linux/percpu.h> | 39 | #include <linux/percpu.h> |
34 | #include <linux/slab.h> | 40 | #include <linux/slab.h> |
@@ -75,8 +81,7 @@ static inline void __iomem *gic_cpu_base(struct irq_data *d) | |||
75 | 81 | ||
76 | static inline unsigned int gic_irq(struct irq_data *d) | 82 | static inline unsigned int gic_irq(struct irq_data *d) |
77 | { | 83 | { |
78 | struct gic_chip_data *gic_data = irq_data_get_irq_chip_data(d); | 84 | return d->hwirq; |
79 | return d->irq - gic_data->irq_offset; | ||
80 | } | 85 | } |
81 | 86 | ||
82 | /* | 87 | /* |
@@ -84,7 +89,7 @@ static inline unsigned int gic_irq(struct irq_data *d) | |||
84 | */ | 89 | */ |
85 | static void gic_mask_irq(struct irq_data *d) | 90 | static void gic_mask_irq(struct irq_data *d) |
86 | { | 91 | { |
87 | u32 mask = 1 << (d->irq % 32); | 92 | u32 mask = 1 << (gic_irq(d) % 32); |
88 | 93 | ||
89 | raw_spin_lock(&irq_controller_lock); | 94 | raw_spin_lock(&irq_controller_lock); |
90 | writel_relaxed(mask, gic_dist_base(d) + GIC_DIST_ENABLE_CLEAR + (gic_irq(d) / 32) * 4); | 95 | writel_relaxed(mask, gic_dist_base(d) + GIC_DIST_ENABLE_CLEAR + (gic_irq(d) / 32) * 4); |
@@ -95,7 +100,7 @@ static void gic_mask_irq(struct irq_data *d) | |||
95 | 100 | ||
96 | static void gic_unmask_irq(struct irq_data *d) | 101 | static void gic_unmask_irq(struct irq_data *d) |
97 | { | 102 | { |
98 | u32 mask = 1 << (d->irq % 32); | 103 | u32 mask = 1 << (gic_irq(d) % 32); |
99 | 104 | ||
100 | raw_spin_lock(&irq_controller_lock); | 105 | raw_spin_lock(&irq_controller_lock); |
101 | if (gic_arch_extn.irq_unmask) | 106 | if (gic_arch_extn.irq_unmask) |
@@ -176,7 +181,7 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val, | |||
176 | bool force) | 181 | bool force) |
177 | { | 182 | { |
178 | void __iomem *reg = gic_dist_base(d) + GIC_DIST_TARGET + (gic_irq(d) & ~3); | 183 | void __iomem *reg = gic_dist_base(d) + GIC_DIST_TARGET + (gic_irq(d) & ~3); |
179 | unsigned int shift = (d->irq % 4) * 8; | 184 | unsigned int shift = (gic_irq(d) % 4) * 8; |
180 | unsigned int cpu = cpumask_any_and(mask_val, cpu_online_mask); | 185 | unsigned int cpu = cpumask_any_and(mask_val, cpu_online_mask); |
181 | u32 val, mask, bit; | 186 | u32 val, mask, bit; |
182 | 187 | ||
@@ -227,7 +232,7 @@ static void gic_handle_cascade_irq(unsigned int irq, struct irq_desc *desc) | |||
227 | if (gic_irq == 1023) | 232 | if (gic_irq == 1023) |
228 | goto out; | 233 | goto out; |
229 | 234 | ||
230 | cascade_irq = gic_irq + chip_data->irq_offset; | 235 | cascade_irq = irq_domain_to_irq(&chip_data->domain, gic_irq); |
231 | if (unlikely(gic_irq < 32 || gic_irq > 1020 || cascade_irq >= NR_IRQS)) | 236 | if (unlikely(gic_irq < 32 || gic_irq > 1020 || cascade_irq >= NR_IRQS)) |
232 | do_bad_IRQ(cascade_irq, desc); | 237 | do_bad_IRQ(cascade_irq, desc); |
233 | else | 238 | else |
@@ -259,14 +264,14 @@ void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq) | |||
259 | irq_set_chained_handler(irq, gic_handle_cascade_irq); | 264 | irq_set_chained_handler(irq, gic_handle_cascade_irq); |
260 | } | 265 | } |
261 | 266 | ||
262 | static void __init gic_dist_init(struct gic_chip_data *gic, | 267 | static void __init gic_dist_init(struct gic_chip_data *gic) |
263 | unsigned int irq_start) | ||
264 | { | 268 | { |
265 | unsigned int gic_irqs, irq_limit, i; | 269 | unsigned int i, irq; |
266 | u32 cpumask; | 270 | u32 cpumask; |
271 | unsigned int gic_irqs = gic->gic_irqs; | ||
272 | struct irq_domain *domain = &gic->domain; | ||
267 | void __iomem *base = gic->dist_base; | 273 | void __iomem *base = gic->dist_base; |
268 | u32 cpu = 0; | 274 | u32 cpu = 0; |
269 | u32 nrppis = 0, ppi_base = 0; | ||
270 | 275 | ||
271 | #ifdef CONFIG_SMP | 276 | #ifdef CONFIG_SMP |
272 | cpu = cpu_logical_map(smp_processor_id()); | 277 | cpu = cpu_logical_map(smp_processor_id()); |
@@ -279,34 +284,6 @@ static void __init gic_dist_init(struct gic_chip_data *gic, | |||
279 | writel_relaxed(0, base + GIC_DIST_CTRL); | 284 | writel_relaxed(0, base + GIC_DIST_CTRL); |
280 | 285 | ||
281 | /* | 286 | /* |
282 | * Find out how many interrupts are supported. | ||
283 | * The GIC only supports up to 1020 interrupt sources. | ||
284 | */ | ||
285 | gic_irqs = readl_relaxed(base + GIC_DIST_CTR) & 0x1f; | ||
286 | gic_irqs = (gic_irqs + 1) * 32; | ||
287 | if (gic_irqs > 1020) | ||
288 | gic_irqs = 1020; | ||
289 | |||
290 | gic->gic_irqs = gic_irqs; | ||
291 | |||
292 | /* | ||
293 | * Nobody would be insane enough to use PPIs on a secondary | ||
294 | * GIC, right? | ||
295 | */ | ||
296 | if (gic == &gic_data[0]) { | ||
297 | nrppis = (32 - irq_start) & 31; | ||
298 | |||
299 | /* The GIC only supports up to 16 PPIs. */ | ||
300 | if (nrppis > 16) | ||
301 | BUG(); | ||
302 | |||
303 | ppi_base = gic->irq_offset + 32 - nrppis; | ||
304 | } | ||
305 | |||
306 | pr_info("Configuring GIC with %d sources (%d PPIs)\n", | ||
307 | gic_irqs, (gic == &gic_data[0]) ? nrppis : 0); | ||
308 | |||
309 | /* | ||
310 | * Set all global interrupts to be level triggered, active low. | 287 | * Set all global interrupts to be level triggered, active low. |
311 | */ | 288 | */ |
312 | for (i = 32; i < gic_irqs; i += 16) | 289 | for (i = 32; i < gic_irqs; i += 16) |
@@ -332,29 +309,20 @@ static void __init gic_dist_init(struct gic_chip_data *gic, | |||
332 | writel_relaxed(0xffffffff, base + GIC_DIST_ENABLE_CLEAR + i * 4 / 32); | 309 | writel_relaxed(0xffffffff, base + GIC_DIST_ENABLE_CLEAR + i * 4 / 32); |
333 | 310 | ||
334 | /* | 311 | /* |
335 | * Limit number of interrupts registered to the platform maximum | ||
336 | */ | ||
337 | irq_limit = gic->irq_offset + gic_irqs; | ||
338 | if (WARN_ON(irq_limit > NR_IRQS)) | ||
339 | irq_limit = NR_IRQS; | ||
340 | |||
341 | /* | ||
342 | * Setup the Linux IRQ subsystem. | 312 | * Setup the Linux IRQ subsystem. |
343 | */ | 313 | */ |
344 | for (i = 0; i < nrppis; i++) { | 314 | irq_domain_for_each_irq(domain, i, irq) { |
345 | int ppi = i + ppi_base; | 315 | if (i < 32) { |
346 | 316 | irq_set_percpu_devid(irq); | |
347 | irq_set_percpu_devid(ppi); | 317 | irq_set_chip_and_handler(irq, &gic_chip, |
348 | irq_set_chip_and_handler(ppi, &gic_chip, | 318 | handle_percpu_devid_irq); |
349 | handle_percpu_devid_irq); | 319 | set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN); |
350 | irq_set_chip_data(ppi, gic); | 320 | } else { |
351 | set_irq_flags(ppi, IRQF_VALID | IRQF_NOAUTOEN); | 321 | irq_set_chip_and_handler(irq, &gic_chip, |
352 | } | 322 | handle_fasteoi_irq); |
353 | 323 | set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); | |
354 | for (i = irq_start + nrppis; i < irq_limit; i++) { | 324 | } |
355 | irq_set_chip_and_handler(i, &gic_chip, handle_fasteoi_irq); | 325 | irq_set_chip_data(irq, gic); |
356 | irq_set_chip_data(i, gic); | ||
357 | set_irq_flags(i, IRQF_VALID | IRQF_PROBE); | ||
358 | } | 326 | } |
359 | 327 | ||
360 | writel_relaxed(1, base + GIC_DIST_CTRL); | 328 | writel_relaxed(1, base + GIC_DIST_CTRL); |
@@ -566,23 +534,85 @@ static void __init gic_pm_init(struct gic_chip_data *gic) | |||
566 | } | 534 | } |
567 | #endif | 535 | #endif |
568 | 536 | ||
569 | void __init gic_init(unsigned int gic_nr, unsigned int irq_start, | 537 | #ifdef CONFIG_OF |
538 | static int gic_irq_domain_dt_translate(struct irq_domain *d, | ||
539 | struct device_node *controller, | ||
540 | const u32 *intspec, unsigned int intsize, | ||
541 | unsigned long *out_hwirq, unsigned int *out_type) | ||
542 | { | ||
543 | if (d->of_node != controller) | ||
544 | return -EINVAL; | ||
545 | if (intsize < 3) | ||
546 | return -EINVAL; | ||
547 | |||
548 | /* Get the interrupt number and add 16 to skip over SGIs */ | ||
549 | *out_hwirq = intspec[1] + 16; | ||
550 | |||
551 | /* For SPIs, we need to add 16 more to get the GIC irq ID number */ | ||
552 | if (!intspec[0]) | ||
553 | *out_hwirq += 16; | ||
554 | |||
555 | *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK; | ||
556 | return 0; | ||
557 | } | ||
558 | #endif | ||
559 | |||
560 | const struct irq_domain_ops gic_irq_domain_ops = { | ||
561 | #ifdef CONFIG_OF | ||
562 | .dt_translate = gic_irq_domain_dt_translate, | ||
563 | #endif | ||
564 | }; | ||
565 | |||
566 | void __init gic_init(unsigned int gic_nr, int irq_start, | ||
570 | void __iomem *dist_base, void __iomem *cpu_base) | 567 | void __iomem *dist_base, void __iomem *cpu_base) |
571 | { | 568 | { |
572 | struct gic_chip_data *gic; | 569 | struct gic_chip_data *gic; |
570 | struct irq_domain *domain; | ||
571 | int gic_irqs; | ||
573 | 572 | ||
574 | BUG_ON(gic_nr >= MAX_GIC_NR); | 573 | BUG_ON(gic_nr >= MAX_GIC_NR); |
575 | 574 | ||
576 | gic = &gic_data[gic_nr]; | 575 | gic = &gic_data[gic_nr]; |
576 | domain = &gic->domain; | ||
577 | gic->dist_base = dist_base; | 577 | gic->dist_base = dist_base; |
578 | gic->cpu_base = cpu_base; | 578 | gic->cpu_base = cpu_base; |
579 | gic->irq_offset = (irq_start - 1) & ~31; | ||
580 | 579 | ||
581 | if (gic_nr == 0) | 580 | /* |
581 | * For primary GICs, skip over SGIs. | ||
582 | * For secondary GICs, skip over PPIs, too. | ||
583 | */ | ||
584 | if (gic_nr == 0) { | ||
582 | gic_cpu_base_addr = cpu_base; | 585 | gic_cpu_base_addr = cpu_base; |
586 | domain->hwirq_base = 16; | ||
587 | if (irq_start > 0) | ||
588 | irq_start = (irq_start & ~31) + 16; | ||
589 | } else | ||
590 | domain->hwirq_base = 32; | ||
591 | |||
592 | /* | ||
593 | * Find out how many interrupts are supported. | ||
594 | * The GIC only supports up to 1020 interrupt sources. | ||
595 | */ | ||
596 | gic_irqs = readl_relaxed(dist_base + GIC_DIST_CTR) & 0x1f; | ||
597 | gic_irqs = (gic_irqs + 1) * 32; | ||
598 | if (gic_irqs > 1020) | ||
599 | gic_irqs = 1020; | ||
600 | gic->gic_irqs = gic_irqs; | ||
601 | |||
602 | domain->nr_irq = gic_irqs - domain->hwirq_base; | ||
603 | domain->irq_base = irq_alloc_descs(irq_start, 16, domain->nr_irq, | ||
604 | numa_node_id()); | ||
605 | if (IS_ERR_VALUE(domain->irq_base)) { | ||
606 | WARN(1, "Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n", | ||
607 | irq_start); | ||
608 | domain->irq_base = irq_start; | ||
609 | } | ||
610 | domain->priv = gic; | ||
611 | domain->ops = &gic_irq_domain_ops; | ||
612 | irq_domain_add(domain); | ||
583 | 613 | ||
584 | gic_chip.flags |= gic_arch_extn.flags; | 614 | gic_chip.flags |= gic_arch_extn.flags; |
585 | gic_dist_init(gic, irq_start); | 615 | gic_dist_init(gic); |
586 | gic_cpu_init(gic); | 616 | gic_cpu_init(gic); |
587 | gic_pm_init(gic); | 617 | gic_pm_init(gic); |
588 | } | 618 | } |
@@ -614,3 +644,35 @@ void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) | |||
614 | writel_relaxed(map << 16 | irq, gic_data[0].dist_base + GIC_DIST_SOFTINT); | 644 | writel_relaxed(map << 16 | irq, gic_data[0].dist_base + GIC_DIST_SOFTINT); |
615 | } | 645 | } |
616 | #endif | 646 | #endif |
647 | |||
648 | #ifdef CONFIG_OF | ||
649 | static int gic_cnt __initdata = 0; | ||
650 | |||
651 | int __init gic_of_init(struct device_node *node, struct device_node *parent) | ||
652 | { | ||
653 | void __iomem *cpu_base; | ||
654 | void __iomem *dist_base; | ||
655 | int irq; | ||
656 | struct irq_domain *domain = &gic_data[gic_cnt].domain; | ||
657 | |||
658 | if (WARN_ON(!node)) | ||
659 | return -ENODEV; | ||
660 | |||
661 | dist_base = of_iomap(node, 0); | ||
662 | WARN(!dist_base, "unable to map gic dist registers\n"); | ||
663 | |||
664 | cpu_base = of_iomap(node, 1); | ||
665 | WARN(!cpu_base, "unable to map gic cpu registers\n"); | ||
666 | |||
667 | domain->of_node = of_node_get(node); | ||
668 | |||
669 | gic_init(gic_cnt, -1, dist_base, cpu_base); | ||
670 | |||
671 | if (parent) { | ||
672 | irq = irq_of_parse_and_map(node, 0); | ||
673 | gic_cascade_irq(gic_cnt, irq); | ||
674 | } | ||
675 | gic_cnt++; | ||
676 | return 0; | ||
677 | } | ||
678 | #endif | ||