diff options
Diffstat (limited to 'arch/arm/common')
-rw-r--r-- | arch/arm/common/Kconfig | 6 | ||||
-rw-r--r-- | arch/arm/common/gic.c | 164 | ||||
-rw-r--r-- | arch/arm/common/vic.c | 143 |
3 files changed, 262 insertions, 51 deletions
diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig index 74df9ca2be31..81a933eb0903 100644 --- a/arch/arm/common/Kconfig +++ b/arch/arm/common/Kconfig | |||
@@ -1,8 +1,14 @@ | |||
1 | config ARM_GIC | 1 | config ARM_GIC |
2 | select IRQ_DOMAIN | 2 | select IRQ_DOMAIN |
3 | select MULTI_IRQ_HANDLER | ||
4 | bool | ||
5 | |||
6 | config GIC_NON_BANKED | ||
3 | bool | 7 | bool |
4 | 8 | ||
5 | config ARM_VIC | 9 | config ARM_VIC |
10 | select IRQ_DOMAIN | ||
11 | select MULTI_IRQ_HANDLER | ||
6 | bool | 12 | bool |
7 | 13 | ||
8 | config ARM_VIC_NR | 14 | config ARM_VIC_NR |
diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c index 0e6ae470c94f..a1feb6b4f9f5 100644 --- a/arch/arm/common/gic.c +++ b/arch/arm/common/gic.c | |||
@@ -40,13 +40,36 @@ | |||
40 | #include <linux/slab.h> | 40 | #include <linux/slab.h> |
41 | 41 | ||
42 | #include <asm/irq.h> | 42 | #include <asm/irq.h> |
43 | #include <asm/exception.h> | ||
43 | #include <asm/mach/irq.h> | 44 | #include <asm/mach/irq.h> |
44 | #include <asm/hardware/gic.h> | 45 | #include <asm/hardware/gic.h> |
45 | 46 | ||
46 | static DEFINE_RAW_SPINLOCK(irq_controller_lock); | 47 | union gic_base { |
48 | void __iomem *common_base; | ||
49 | void __percpu __iomem **percpu_base; | ||
50 | }; | ||
47 | 51 | ||
48 | /* Address of GIC 0 CPU interface */ | 52 | struct gic_chip_data { |
49 | void __iomem *gic_cpu_base_addr __read_mostly; | 53 | unsigned int irq_offset; |
54 | union gic_base dist_base; | ||
55 | union gic_base cpu_base; | ||
56 | #ifdef CONFIG_CPU_PM | ||
57 | u32 saved_spi_enable[DIV_ROUND_UP(1020, 32)]; | ||
58 | u32 saved_spi_conf[DIV_ROUND_UP(1020, 16)]; | ||
59 | u32 saved_spi_target[DIV_ROUND_UP(1020, 4)]; | ||
60 | u32 __percpu *saved_ppi_enable; | ||
61 | u32 __percpu *saved_ppi_conf; | ||
62 | #endif | ||
63 | #ifdef CONFIG_IRQ_DOMAIN | ||
64 | struct irq_domain domain; | ||
65 | #endif | ||
66 | unsigned int gic_irqs; | ||
67 | #ifdef CONFIG_GIC_NON_BANKED | ||
68 | void __iomem *(*get_base)(union gic_base *); | ||
69 | #endif | ||
70 | }; | ||
71 | |||
72 | static DEFINE_RAW_SPINLOCK(irq_controller_lock); | ||
50 | 73 | ||
51 | /* | 74 | /* |
52 | * Supported arch specific GIC irq extension. | 75 | * Supported arch specific GIC irq extension. |
@@ -67,16 +90,48 @@ struct irq_chip gic_arch_extn = { | |||
67 | 90 | ||
68 | static struct gic_chip_data gic_data[MAX_GIC_NR] __read_mostly; | 91 | static struct gic_chip_data gic_data[MAX_GIC_NR] __read_mostly; |
69 | 92 | ||
93 | #ifdef CONFIG_GIC_NON_BANKED | ||
94 | static void __iomem *gic_get_percpu_base(union gic_base *base) | ||
95 | { | ||
96 | return *__this_cpu_ptr(base->percpu_base); | ||
97 | } | ||
98 | |||
99 | static void __iomem *gic_get_common_base(union gic_base *base) | ||
100 | { | ||
101 | return base->common_base; | ||
102 | } | ||
103 | |||
104 | static inline void __iomem *gic_data_dist_base(struct gic_chip_data *data) | ||
105 | { | ||
106 | return data->get_base(&data->dist_base); | ||
107 | } | ||
108 | |||
109 | static inline void __iomem *gic_data_cpu_base(struct gic_chip_data *data) | ||
110 | { | ||
111 | return data->get_base(&data->cpu_base); | ||
112 | } | ||
113 | |||
114 | static inline void gic_set_base_accessor(struct gic_chip_data *data, | ||
115 | void __iomem *(*f)(union gic_base *)) | ||
116 | { | ||
117 | data->get_base = f; | ||
118 | } | ||
119 | #else | ||
120 | #define gic_data_dist_base(d) ((d)->dist_base.common_base) | ||
121 | #define gic_data_cpu_base(d) ((d)->cpu_base.common_base) | ||
122 | #define gic_set_base_accessor(d,f) | ||
123 | #endif | ||
124 | |||
70 | static inline void __iomem *gic_dist_base(struct irq_data *d) | 125 | static inline void __iomem *gic_dist_base(struct irq_data *d) |
71 | { | 126 | { |
72 | struct gic_chip_data *gic_data = irq_data_get_irq_chip_data(d); | 127 | struct gic_chip_data *gic_data = irq_data_get_irq_chip_data(d); |
73 | return gic_data->dist_base; | 128 | return gic_data_dist_base(gic_data); |
74 | } | 129 | } |
75 | 130 | ||
76 | static inline void __iomem *gic_cpu_base(struct irq_data *d) | 131 | static inline void __iomem *gic_cpu_base(struct irq_data *d) |
77 | { | 132 | { |
78 | struct gic_chip_data *gic_data = irq_data_get_irq_chip_data(d); | 133 | struct gic_chip_data *gic_data = irq_data_get_irq_chip_data(d); |
79 | return gic_data->cpu_base; | 134 | return gic_data_cpu_base(gic_data); |
80 | } | 135 | } |
81 | 136 | ||
82 | static inline unsigned int gic_irq(struct irq_data *d) | 137 | static inline unsigned int gic_irq(struct irq_data *d) |
@@ -215,6 +270,32 @@ static int gic_set_wake(struct irq_data *d, unsigned int on) | |||
215 | #define gic_set_wake NULL | 270 | #define gic_set_wake NULL |
216 | #endif | 271 | #endif |
217 | 272 | ||
273 | asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs) | ||
274 | { | ||
275 | u32 irqstat, irqnr; | ||
276 | struct gic_chip_data *gic = &gic_data[0]; | ||
277 | void __iomem *cpu_base = gic_data_cpu_base(gic); | ||
278 | |||
279 | do { | ||
280 | irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK); | ||
281 | irqnr = irqstat & ~0x1c00; | ||
282 | |||
283 | if (likely(irqnr > 15 && irqnr < 1021)) { | ||
284 | irqnr = irq_domain_to_irq(&gic->domain, irqnr); | ||
285 | handle_IRQ(irqnr, regs); | ||
286 | continue; | ||
287 | } | ||
288 | if (irqnr < 16) { | ||
289 | writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI); | ||
290 | #ifdef CONFIG_SMP | ||
291 | handle_IPI(irqnr, regs); | ||
292 | #endif | ||
293 | continue; | ||
294 | } | ||
295 | break; | ||
296 | } while (1); | ||
297 | } | ||
298 | |||
218 | static void gic_handle_cascade_irq(unsigned int irq, struct irq_desc *desc) | 299 | static void gic_handle_cascade_irq(unsigned int irq, struct irq_desc *desc) |
219 | { | 300 | { |
220 | struct gic_chip_data *chip_data = irq_get_handler_data(irq); | 301 | struct gic_chip_data *chip_data = irq_get_handler_data(irq); |
@@ -225,7 +306,7 @@ static void gic_handle_cascade_irq(unsigned int irq, struct irq_desc *desc) | |||
225 | chained_irq_enter(chip, desc); | 306 | chained_irq_enter(chip, desc); |
226 | 307 | ||
227 | raw_spin_lock(&irq_controller_lock); | 308 | raw_spin_lock(&irq_controller_lock); |
228 | status = readl_relaxed(chip_data->cpu_base + GIC_CPU_INTACK); | 309 | status = readl_relaxed(gic_data_cpu_base(chip_data) + GIC_CPU_INTACK); |
229 | raw_spin_unlock(&irq_controller_lock); | 310 | raw_spin_unlock(&irq_controller_lock); |
230 | 311 | ||
231 | gic_irq = (status & 0x3ff); | 312 | gic_irq = (status & 0x3ff); |
@@ -270,7 +351,7 @@ static void __init gic_dist_init(struct gic_chip_data *gic) | |||
270 | u32 cpumask; | 351 | u32 cpumask; |
271 | unsigned int gic_irqs = gic->gic_irqs; | 352 | unsigned int gic_irqs = gic->gic_irqs; |
272 | struct irq_domain *domain = &gic->domain; | 353 | struct irq_domain *domain = &gic->domain; |
273 | void __iomem *base = gic->dist_base; | 354 | void __iomem *base = gic_data_dist_base(gic); |
274 | u32 cpu = 0; | 355 | u32 cpu = 0; |
275 | 356 | ||
276 | #ifdef CONFIG_SMP | 357 | #ifdef CONFIG_SMP |
@@ -330,8 +411,8 @@ static void __init gic_dist_init(struct gic_chip_data *gic) | |||
330 | 411 | ||
331 | static void __cpuinit gic_cpu_init(struct gic_chip_data *gic) | 412 | static void __cpuinit gic_cpu_init(struct gic_chip_data *gic) |
332 | { | 413 | { |
333 | void __iomem *dist_base = gic->dist_base; | 414 | void __iomem *dist_base = gic_data_dist_base(gic); |
334 | void __iomem *base = gic->cpu_base; | 415 | void __iomem *base = gic_data_cpu_base(gic); |
335 | int i; | 416 | int i; |
336 | 417 | ||
337 | /* | 418 | /* |
@@ -368,7 +449,7 @@ static void gic_dist_save(unsigned int gic_nr) | |||
368 | BUG(); | 449 | BUG(); |
369 | 450 | ||
370 | gic_irqs = gic_data[gic_nr].gic_irqs; | 451 | gic_irqs = gic_data[gic_nr].gic_irqs; |
371 | dist_base = gic_data[gic_nr].dist_base; | 452 | dist_base = gic_data_dist_base(&gic_data[gic_nr]); |
372 | 453 | ||
373 | if (!dist_base) | 454 | if (!dist_base) |
374 | return; | 455 | return; |
@@ -403,7 +484,7 @@ static void gic_dist_restore(unsigned int gic_nr) | |||
403 | BUG(); | 484 | BUG(); |
404 | 485 | ||
405 | gic_irqs = gic_data[gic_nr].gic_irqs; | 486 | gic_irqs = gic_data[gic_nr].gic_irqs; |
406 | dist_base = gic_data[gic_nr].dist_base; | 487 | dist_base = gic_data_dist_base(&gic_data[gic_nr]); |
407 | 488 | ||
408 | if (!dist_base) | 489 | if (!dist_base) |
409 | return; | 490 | return; |
@@ -439,8 +520,8 @@ static void gic_cpu_save(unsigned int gic_nr) | |||
439 | if (gic_nr >= MAX_GIC_NR) | 520 | if (gic_nr >= MAX_GIC_NR) |
440 | BUG(); | 521 | BUG(); |
441 | 522 | ||
442 | dist_base = gic_data[gic_nr].dist_base; | 523 | dist_base = gic_data_dist_base(&gic_data[gic_nr]); |
443 | cpu_base = gic_data[gic_nr].cpu_base; | 524 | cpu_base = gic_data_cpu_base(&gic_data[gic_nr]); |
444 | 525 | ||
445 | if (!dist_base || !cpu_base) | 526 | if (!dist_base || !cpu_base) |
446 | return; | 527 | return; |
@@ -465,8 +546,8 @@ static void gic_cpu_restore(unsigned int gic_nr) | |||
465 | if (gic_nr >= MAX_GIC_NR) | 546 | if (gic_nr >= MAX_GIC_NR) |
466 | BUG(); | 547 | BUG(); |
467 | 548 | ||
468 | dist_base = gic_data[gic_nr].dist_base; | 549 | dist_base = gic_data_dist_base(&gic_data[gic_nr]); |
469 | cpu_base = gic_data[gic_nr].cpu_base; | 550 | cpu_base = gic_data_cpu_base(&gic_data[gic_nr]); |
470 | 551 | ||
471 | if (!dist_base || !cpu_base) | 552 | if (!dist_base || !cpu_base) |
472 | return; | 553 | return; |
@@ -491,6 +572,11 @@ static int gic_notifier(struct notifier_block *self, unsigned long cmd, void *v) | |||
491 | int i; | 572 | int i; |
492 | 573 | ||
493 | for (i = 0; i < MAX_GIC_NR; i++) { | 574 | for (i = 0; i < MAX_GIC_NR; i++) { |
575 | #ifdef CONFIG_GIC_NON_BANKED | ||
576 | /* Skip over unused GICs */ | ||
577 | if (!gic_data[i].get_base) | ||
578 | continue; | ||
579 | #endif | ||
494 | switch (cmd) { | 580 | switch (cmd) { |
495 | case CPU_PM_ENTER: | 581 | case CPU_PM_ENTER: |
496 | gic_cpu_save(i); | 582 | gic_cpu_save(i); |
@@ -563,8 +649,9 @@ const struct irq_domain_ops gic_irq_domain_ops = { | |||
563 | #endif | 649 | #endif |
564 | }; | 650 | }; |
565 | 651 | ||
566 | void __init gic_init(unsigned int gic_nr, int irq_start, | 652 | void __init gic_init_bases(unsigned int gic_nr, int irq_start, |
567 | void __iomem *dist_base, void __iomem *cpu_base) | 653 | void __iomem *dist_base, void __iomem *cpu_base, |
654 | u32 percpu_offset) | ||
568 | { | 655 | { |
569 | struct gic_chip_data *gic; | 656 | struct gic_chip_data *gic; |
570 | struct irq_domain *domain; | 657 | struct irq_domain *domain; |
@@ -574,15 +661,42 @@ void __init gic_init(unsigned int gic_nr, int irq_start, | |||
574 | 661 | ||
575 | gic = &gic_data[gic_nr]; | 662 | gic = &gic_data[gic_nr]; |
576 | domain = &gic->domain; | 663 | domain = &gic->domain; |
577 | gic->dist_base = dist_base; | 664 | #ifdef CONFIG_GIC_NON_BANKED |
578 | gic->cpu_base = cpu_base; | 665 | if (percpu_offset) { /* Frankein-GIC without banked registers... */ |
666 | unsigned int cpu; | ||
667 | |||
668 | gic->dist_base.percpu_base = alloc_percpu(void __iomem *); | ||
669 | gic->cpu_base.percpu_base = alloc_percpu(void __iomem *); | ||
670 | if (WARN_ON(!gic->dist_base.percpu_base || | ||
671 | !gic->cpu_base.percpu_base)) { | ||
672 | free_percpu(gic->dist_base.percpu_base); | ||
673 | free_percpu(gic->cpu_base.percpu_base); | ||
674 | return; | ||
675 | } | ||
676 | |||
677 | for_each_possible_cpu(cpu) { | ||
678 | unsigned long offset = percpu_offset * cpu_logical_map(cpu); | ||
679 | *per_cpu_ptr(gic->dist_base.percpu_base, cpu) = dist_base + offset; | ||
680 | *per_cpu_ptr(gic->cpu_base.percpu_base, cpu) = cpu_base + offset; | ||
681 | } | ||
682 | |||
683 | gic_set_base_accessor(gic, gic_get_percpu_base); | ||
684 | } else | ||
685 | #endif | ||
686 | { /* Normal, sane GIC... */ | ||
687 | WARN(percpu_offset, | ||
688 | "GIC_NON_BANKED not enabled, ignoring %08x offset!", | ||
689 | percpu_offset); | ||
690 | gic->dist_base.common_base = dist_base; | ||
691 | gic->cpu_base.common_base = cpu_base; | ||
692 | gic_set_base_accessor(gic, gic_get_common_base); | ||
693 | } | ||
579 | 694 | ||
580 | /* | 695 | /* |
581 | * For primary GICs, skip over SGIs. | 696 | * For primary GICs, skip over SGIs. |
582 | * For secondary GICs, skip over PPIs, too. | 697 | * For secondary GICs, skip over PPIs, too. |
583 | */ | 698 | */ |
584 | if (gic_nr == 0) { | 699 | if (gic_nr == 0) { |
585 | gic_cpu_base_addr = cpu_base; | ||
586 | domain->hwirq_base = 16; | 700 | domain->hwirq_base = 16; |
587 | if (irq_start > 0) | 701 | if (irq_start > 0) |
588 | irq_start = (irq_start & ~31) + 16; | 702 | irq_start = (irq_start & ~31) + 16; |
@@ -593,7 +707,7 @@ void __init gic_init(unsigned int gic_nr, int irq_start, | |||
593 | * Find out how many interrupts are supported. | 707 | * Find out how many interrupts are supported. |
594 | * The GIC only supports up to 1020 interrupt sources. | 708 | * The GIC only supports up to 1020 interrupt sources. |
595 | */ | 709 | */ |
596 | gic_irqs = readl_relaxed(dist_base + GIC_DIST_CTR) & 0x1f; | 710 | gic_irqs = readl_relaxed(gic_data_dist_base(gic) + GIC_DIST_CTR) & 0x1f; |
597 | gic_irqs = (gic_irqs + 1) * 32; | 711 | gic_irqs = (gic_irqs + 1) * 32; |
598 | if (gic_irqs > 1020) | 712 | if (gic_irqs > 1020) |
599 | gic_irqs = 1020; | 713 | gic_irqs = 1020; |
@@ -641,7 +755,7 @@ void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) | |||
641 | dsb(); | 755 | dsb(); |
642 | 756 | ||
643 | /* this always happens on GIC0 */ | 757 | /* this always happens on GIC0 */ |
644 | writel_relaxed(map << 16 | irq, gic_data[0].dist_base + GIC_DIST_SOFTINT); | 758 | writel_relaxed(map << 16 | irq, gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT); |
645 | } | 759 | } |
646 | #endif | 760 | #endif |
647 | 761 | ||
@@ -652,6 +766,7 @@ int __init gic_of_init(struct device_node *node, struct device_node *parent) | |||
652 | { | 766 | { |
653 | void __iomem *cpu_base; | 767 | void __iomem *cpu_base; |
654 | void __iomem *dist_base; | 768 | void __iomem *dist_base; |
769 | u32 percpu_offset; | ||
655 | int irq; | 770 | int irq; |
656 | struct irq_domain *domain = &gic_data[gic_cnt].domain; | 771 | struct irq_domain *domain = &gic_data[gic_cnt].domain; |
657 | 772 | ||
@@ -664,9 +779,12 @@ int __init gic_of_init(struct device_node *node, struct device_node *parent) | |||
664 | cpu_base = of_iomap(node, 1); | 779 | cpu_base = of_iomap(node, 1); |
665 | WARN(!cpu_base, "unable to map gic cpu registers\n"); | 780 | WARN(!cpu_base, "unable to map gic cpu registers\n"); |
666 | 781 | ||
782 | if (of_property_read_u32(node, "cpu-offset", &percpu_offset)) | ||
783 | percpu_offset = 0; | ||
784 | |||
667 | domain->of_node = of_node_get(node); | 785 | domain->of_node = of_node_get(node); |
668 | 786 | ||
669 | gic_init(gic_cnt, -1, dist_base, cpu_base); | 787 | gic_init_bases(gic_cnt, -1, dist_base, cpu_base, percpu_offset); |
670 | 788 | ||
671 | if (parent) { | 789 | if (parent) { |
672 | irq = irq_of_parse_and_map(node, 0); | 790 | irq = irq_of_parse_and_map(node, 0); |
diff --git a/arch/arm/common/vic.c b/arch/arm/common/vic.c index 01f18a421b17..6ed41ec2bbf5 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 | v->domain.ops = &irq_domain_simple_ops; | ||
201 | #endif /* CONFIG_OF */ | ||
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) |
@@ -330,15 +347,9 @@ static void __init vic_init_st(void __iomem *base, unsigned int irq_start, | |||
330 | vic_set_irq_sources(base, irq_start, vic_sources); | 347 | vic_set_irq_sources(base, irq_start, vic_sources); |
331 | } | 348 | } |
332 | 349 | ||
333 | /** | 350 | static void __init __vic_init(void __iomem *base, unsigned int irq_start, |
334 | * vic_init - initialise a vectored interrupt controller | 351 | u32 vic_sources, u32 resume_sources, |
335 | * @base: iomem base address | 352 | 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 | { | 353 | { |
343 | unsigned int i; | 354 | unsigned int i; |
344 | u32 cellid = 0; | 355 | u32 cellid = 0; |
@@ -375,5 +386,81 @@ void __init vic_init(void __iomem *base, unsigned int irq_start, | |||
375 | 386 | ||
376 | vic_set_irq_sources(base, irq_start, vic_sources); | 387 | vic_set_irq_sources(base, irq_start, vic_sources); |
377 | 388 | ||
378 | vic_pm_register(base, irq_start, resume_sources); | 389 | vic_register(base, irq_start, resume_sources, node); |
390 | } | ||
391 | |||
392 | /** | ||
393 | * vic_init() - initialise a vectored interrupt controller | ||
394 | * @base: iomem base address | ||
395 | * @irq_start: starting interrupt number, must be muliple of 32 | ||
396 | * @vic_sources: bitmask of interrupt sources to allow | ||
397 | * @resume_sources: bitmask of interrupt sources to allow for resume | ||
398 | */ | ||
399 | void __init vic_init(void __iomem *base, unsigned int irq_start, | ||
400 | u32 vic_sources, u32 resume_sources) | ||
401 | { | ||
402 | __vic_init(base, irq_start, vic_sources, resume_sources, NULL); | ||
403 | } | ||
404 | |||
405 | #ifdef CONFIG_OF | ||
406 | int __init vic_of_init(struct device_node *node, struct device_node *parent) | ||
407 | { | ||
408 | void __iomem *regs; | ||
409 | int irq_base; | ||
410 | |||
411 | if (WARN(parent, "non-root VICs are not supported")) | ||
412 | return -EINVAL; | ||
413 | |||
414 | regs = of_iomap(node, 0); | ||
415 | if (WARN_ON(!regs)) | ||
416 | return -EIO; | ||
417 | |||
418 | irq_base = irq_alloc_descs(-1, 0, 32, numa_node_id()); | ||
419 | if (WARN_ON(irq_base < 0)) | ||
420 | goto out_unmap; | ||
421 | |||
422 | __vic_init(regs, irq_base, ~0, ~0, node); | ||
423 | |||
424 | return 0; | ||
425 | |||
426 | out_unmap: | ||
427 | iounmap(regs); | ||
428 | |||
429 | return -EIO; | ||
430 | } | ||
431 | #endif /* CONFIG OF */ | ||
432 | |||
433 | /* | ||
434 | * Handle each interrupt in a single VIC. Returns non-zero if we've | ||
435 | * handled at least one interrupt. This does a single read of the | ||
436 | * status register and handles all interrupts in order from LSB first. | ||
437 | */ | ||
438 | static int handle_one_vic(struct vic_device *vic, struct pt_regs *regs) | ||
439 | { | ||
440 | u32 stat, irq; | ||
441 | int handled = 0; | ||
442 | |||
443 | stat = readl_relaxed(vic->base + VIC_IRQ_STATUS); | ||
444 | while (stat) { | ||
445 | irq = ffs(stat) - 1; | ||
446 | handle_IRQ(irq_domain_to_irq(&vic->domain, irq), regs); | ||
447 | stat &= ~(1 << irq); | ||
448 | handled = 1; | ||
449 | } | ||
450 | |||
451 | return handled; | ||
452 | } | ||
453 | |||
454 | /* | ||
455 | * Keep iterating over all registered VIC's until there are no pending | ||
456 | * interrupts. | ||
457 | */ | ||
458 | asmlinkage void __exception_irq_entry vic_handle_irq(struct pt_regs *regs) | ||
459 | { | ||
460 | int i, handled; | ||
461 | |||
462 | do { | ||
463 | for (i = 0, handled = 0; i < vic_id; ++i) | ||
464 | handled |= handle_one_vic(&vic_devices[i], regs); | ||
465 | } while (handled); | ||
379 | } | 466 | } |