diff options
author | Linus Walleij <linus.walleij@linaro.org> | 2012-04-28 09:33:47 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2012-05-04 11:53:37 -0400 |
commit | 3108e6ab21a9b9dbd88f0b2ff99f73e95b8b1580 (patch) | |
tree | 9e87e1e4196849090103317d109dc95ba9d159f8 /arch/arm/plat-versatile | |
parent | 69964ea4c7b68c9399f7977aa5b9aa6539a6a98a (diff) |
ARM: 7389/2: plat-versatile: modernize FPGA IRQ controller
This does two things to the FPGA IRQ controller in the versatile
family:
- Convert to MULTI_IRQ_HANDLER so we can drop the entry macro
from the Integrator. The C IRQ handler was inspired from
arch/arm/common/vic.c, recent bug discovered in this handler was
accounted for.
- Convert to using IRQ domains so we can get rid of the NO_IRQ
mess and proceed with device tree and such stuff.
As part of the exercise, bump all the low IRQ numbers on the
Integrator PIC to start from 1 rather than 0, since IRQ 0 is
now NO_IRQ. The Linux IRQ numbers are thus entirely decoupled
from the hardware IRQ numbers in this controller.
I was unable to split this patch. The main reason is the half-done
conversion to device tree in Versatile.
Tested on Integrator/AP and Integrator/CP.
Cc: Grant Likely <grant.likely@secretlab.ca>
Acked-by: Rob Herring <rob.herring@calxeda.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/plat-versatile')
-rw-r--r-- | arch/arm/plat-versatile/Kconfig | 6 | ||||
-rw-r--r-- | arch/arm/plat-versatile/fpga-irq.c | 116 | ||||
-rw-r--r-- | arch/arm/plat-versatile/include/plat/fpga-irq.h | 11 |
3 files changed, 112 insertions, 21 deletions
diff --git a/arch/arm/plat-versatile/Kconfig b/arch/arm/plat-versatile/Kconfig index 043f7b02a9e7..81ee7cc34457 100644 --- a/arch/arm/plat-versatile/Kconfig +++ b/arch/arm/plat-versatile/Kconfig | |||
@@ -5,6 +5,12 @@ config PLAT_VERSATILE_CLCD | |||
5 | 5 | ||
6 | config PLAT_VERSATILE_FPGA_IRQ | 6 | config PLAT_VERSATILE_FPGA_IRQ |
7 | bool | 7 | bool |
8 | select IRQ_DOMAIN | ||
9 | |||
10 | config PLAT_VERSATILE_FPGA_IRQ_NR | ||
11 | int | ||
12 | default 4 | ||
13 | depends on PLAT_VERSATILE_FPGA_IRQ | ||
8 | 14 | ||
9 | config PLAT_VERSATILE_LEDS | 15 | config PLAT_VERSATILE_LEDS |
10 | def_bool y if LEDS_CLASS | 16 | def_bool y if LEDS_CLASS |
diff --git a/arch/arm/plat-versatile/fpga-irq.c b/arch/arm/plat-versatile/fpga-irq.c index f0cc8e19b094..6e70d03824a1 100644 --- a/arch/arm/plat-versatile/fpga-irq.c +++ b/arch/arm/plat-versatile/fpga-irq.c | |||
@@ -3,7 +3,10 @@ | |||
3 | */ | 3 | */ |
4 | #include <linux/irq.h> | 4 | #include <linux/irq.h> |
5 | #include <linux/io.h> | 5 | #include <linux/io.h> |
6 | #include <linux/irqdomain.h> | ||
7 | #include <linux/module.h> | ||
6 | 8 | ||
9 | #include <asm/exception.h> | ||
7 | #include <asm/mach/irq.h> | 10 | #include <asm/mach/irq.h> |
8 | #include <plat/fpga-irq.h> | 11 | #include <plat/fpga-irq.h> |
9 | 12 | ||
@@ -12,10 +15,32 @@ | |||
12 | #define IRQ_ENABLE_SET 0x08 | 15 | #define IRQ_ENABLE_SET 0x08 |
13 | #define IRQ_ENABLE_CLEAR 0x0c | 16 | #define IRQ_ENABLE_CLEAR 0x0c |
14 | 17 | ||
18 | /** | ||
19 | * struct fpga_irq_data - irq data container for the FPGA IRQ controller | ||
20 | * @base: memory offset in virtual memory | ||
21 | * @irq_start: first IRQ number handled by this instance | ||
22 | * @chip: chip container for this instance | ||
23 | * @domain: IRQ domain for this instance | ||
24 | * @valid: mask for valid IRQs on this controller | ||
25 | * @used_irqs: number of active IRQs on this controller | ||
26 | */ | ||
27 | struct fpga_irq_data { | ||
28 | void __iomem *base; | ||
29 | unsigned int irq_start; | ||
30 | struct irq_chip chip; | ||
31 | u32 valid; | ||
32 | struct irq_domain *domain; | ||
33 | u8 used_irqs; | ||
34 | }; | ||
35 | |||
36 | /* we cannot allocate memory when the controllers are initially registered */ | ||
37 | static struct fpga_irq_data fpga_irq_devices[CONFIG_PLAT_VERSATILE_FPGA_IRQ_NR]; | ||
38 | static int fpga_irq_id; | ||
39 | |||
15 | static void fpga_irq_mask(struct irq_data *d) | 40 | static void fpga_irq_mask(struct irq_data *d) |
16 | { | 41 | { |
17 | struct fpga_irq_data *f = irq_data_get_irq_chip_data(d); | 42 | struct fpga_irq_data *f = irq_data_get_irq_chip_data(d); |
18 | u32 mask = 1 << (d->irq - f->irq_start); | 43 | u32 mask = 1 << d->hwirq; |
19 | 44 | ||
20 | writel(mask, f->base + IRQ_ENABLE_CLEAR); | 45 | writel(mask, f->base + IRQ_ENABLE_CLEAR); |
21 | } | 46 | } |
@@ -23,7 +48,7 @@ static void fpga_irq_mask(struct irq_data *d) | |||
23 | static void fpga_irq_unmask(struct irq_data *d) | 48 | static void fpga_irq_unmask(struct irq_data *d) |
24 | { | 49 | { |
25 | struct fpga_irq_data *f = irq_data_get_irq_chip_data(d); | 50 | struct fpga_irq_data *f = irq_data_get_irq_chip_data(d); |
26 | u32 mask = 1 << (d->irq - f->irq_start); | 51 | u32 mask = 1 << d->hwirq; |
27 | 52 | ||
28 | writel(mask, f->base + IRQ_ENABLE_SET); | 53 | writel(mask, f->base + IRQ_ENABLE_SET); |
29 | } | 54 | } |
@@ -41,32 +66,93 @@ static void fpga_irq_handle(unsigned int irq, struct irq_desc *desc) | |||
41 | do { | 66 | do { |
42 | irq = ffs(status) - 1; | 67 | irq = ffs(status) - 1; |
43 | status &= ~(1 << irq); | 68 | status &= ~(1 << irq); |
44 | 69 | generic_handle_irq(irq_find_mapping(f->domain, irq)); | |
45 | generic_handle_irq(irq + f->irq_start); | ||
46 | } while (status); | 70 | } while (status); |
47 | } | 71 | } |
48 | 72 | ||
49 | void __init fpga_irq_init(int parent_irq, u32 valid, struct fpga_irq_data *f) | 73 | /* |
74 | * Handle each interrupt in a single FPGA IRQ controller. Returns non-zero | ||
75 | * if we've handled at least one interrupt. This does a single read of the | ||
76 | * status register and handles all interrupts in order from LSB first. | ||
77 | */ | ||
78 | static int handle_one_fpga(struct fpga_irq_data *f, struct pt_regs *regs) | ||
79 | { | ||
80 | int handled = 0; | ||
81 | int irq; | ||
82 | u32 status; | ||
83 | |||
84 | while ((status = readl(f->base + IRQ_STATUS))) { | ||
85 | irq = ffs(status) - 1; | ||
86 | handle_IRQ(irq_find_mapping(f->domain, irq), regs); | ||
87 | handled = 1; | ||
88 | } | ||
89 | |||
90 | return handled; | ||
91 | } | ||
92 | |||
93 | /* | ||
94 | * Keep iterating over all registered FPGA IRQ controllers until there are | ||
95 | * no pending interrupts. | ||
96 | */ | ||
97 | asmlinkage void __exception_irq_entry fpga_handle_irq(struct pt_regs *regs) | ||
50 | { | 98 | { |
51 | unsigned int i; | 99 | int i, handled; |
52 | 100 | ||
101 | do { | ||
102 | for (i = 0, handled = 0; i < fpga_irq_id; ++i) | ||
103 | handled |= handle_one_fpga(&fpga_irq_devices[i], regs); | ||
104 | } while (handled); | ||
105 | } | ||
106 | |||
107 | static int fpga_irqdomain_map(struct irq_domain *d, unsigned int irq, | ||
108 | irq_hw_number_t hwirq) | ||
109 | { | ||
110 | struct fpga_irq_data *f = d->host_data; | ||
111 | |||
112 | /* Skip invalid IRQs, only register handlers for the real ones */ | ||
113 | if (!(f->valid & (1 << hwirq))) | ||
114 | return -ENOTSUPP; | ||
115 | irq_set_chip_data(irq, f); | ||
116 | irq_set_chip_and_handler(irq, &f->chip, | ||
117 | handle_level_irq); | ||
118 | set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); | ||
119 | f->used_irqs++; | ||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | static struct irq_domain_ops fpga_irqdomain_ops = { | ||
124 | .map = fpga_irqdomain_map, | ||
125 | .xlate = irq_domain_xlate_onetwocell, | ||
126 | }; | ||
127 | |||
128 | void __init fpga_irq_init(void __iomem *base, const char *name, int irq_start, | ||
129 | int parent_irq, u32 valid, struct device_node *node) | ||
130 | { | ||
131 | struct fpga_irq_data *f; | ||
132 | |||
133 | if (fpga_irq_id >= ARRAY_SIZE(fpga_irq_devices)) { | ||
134 | printk(KERN_ERR "%s: too few FPGA IRQ controllers, increase CONFIG_PLAT_VERSATILE_FPGA_IRQ_NR\n", __func__); | ||
135 | return; | ||
136 | } | ||
137 | |||
138 | f = &fpga_irq_devices[fpga_irq_id]; | ||
139 | f->base = base; | ||
140 | f->irq_start = irq_start; | ||
141 | f->chip.name = name; | ||
53 | f->chip.irq_ack = fpga_irq_mask; | 142 | f->chip.irq_ack = fpga_irq_mask; |
54 | f->chip.irq_mask = fpga_irq_mask; | 143 | f->chip.irq_mask = fpga_irq_mask; |
55 | f->chip.irq_unmask = fpga_irq_unmask; | 144 | f->chip.irq_unmask = fpga_irq_unmask; |
145 | f->valid = valid; | ||
56 | 146 | ||
57 | if (parent_irq != -1) { | 147 | if (parent_irq != -1) { |
58 | irq_set_handler_data(parent_irq, f); | 148 | irq_set_handler_data(parent_irq, f); |
59 | irq_set_chained_handler(parent_irq, fpga_irq_handle); | 149 | irq_set_chained_handler(parent_irq, fpga_irq_handle); |
60 | } | 150 | } |
61 | 151 | ||
62 | for (i = 0; i < 32; i++) { | 152 | f->domain = irq_domain_add_legacy(node, fls(valid), f->irq_start, 0, |
63 | if (valid & (1 << i)) { | 153 | &fpga_irqdomain_ops, f); |
64 | unsigned int irq = f->irq_start + i; | 154 | pr_info("FPGA IRQ chip %d \"%s\" @ %p, %u irqs\n", |
155 | fpga_irq_id, name, base, f->used_irqs); | ||
65 | 156 | ||
66 | irq_set_chip_data(irq, f); | 157 | fpga_irq_id++; |
67 | irq_set_chip_and_handler(irq, &f->chip, | ||
68 | handle_level_irq); | ||
69 | set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); | ||
70 | } | ||
71 | } | ||
72 | } | 158 | } |
diff --git a/arch/arm/plat-versatile/include/plat/fpga-irq.h b/arch/arm/plat-versatile/include/plat/fpga-irq.h index 627fafd1e595..91bcfb67551d 100644 --- a/arch/arm/plat-versatile/include/plat/fpga-irq.h +++ b/arch/arm/plat-versatile/include/plat/fpga-irq.h | |||
@@ -1,12 +1,11 @@ | |||
1 | #ifndef PLAT_FPGA_IRQ_H | 1 | #ifndef PLAT_FPGA_IRQ_H |
2 | #define PLAT_FPGA_IRQ_H | 2 | #define PLAT_FPGA_IRQ_H |
3 | 3 | ||
4 | struct fpga_irq_data { | 4 | struct device_node; |
5 | void __iomem *base; | 5 | struct pt_regs; |
6 | unsigned int irq_start; | ||
7 | struct irq_chip chip; | ||
8 | }; | ||
9 | 6 | ||
10 | void fpga_irq_init(int, u32, struct fpga_irq_data *); | 7 | void fpga_handle_irq(struct pt_regs *regs); |
8 | void fpga_irq_init(void __iomem *, const char *, int, int, u32, | ||
9 | struct device_node *node); | ||
11 | 10 | ||
12 | #endif | 11 | #endif |