1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * irqchip for the IXP4xx interrupt controller |
4 | * Copyright (C) 2019 Linus Walleij <linus.walleij@linaro.org> |
5 | * |
6 | * Based on arch/arm/mach-ixp4xx/common.c |
7 | * Copyright 2002 (C) Intel Corporation |
8 | * Copyright 2003-2004 (C) MontaVista, Software, Inc. |
9 | * Copyright (C) Deepak Saxena <dsaxena@plexity.net> |
10 | */ |
11 | #include <linux/bitops.h> |
12 | #include <linux/gpio/driver.h> |
13 | #include <linux/irq.h> |
14 | #include <linux/io.h> |
15 | #include <linux/irqchip.h> |
16 | #include <linux/irqdomain.h> |
17 | #include <linux/of.h> |
18 | #include <linux/of_address.h> |
19 | #include <linux/of_irq.h> |
20 | #include <linux/platform_device.h> |
21 | #include <linux/cpu.h> |
22 | |
23 | #include <asm/exception.h> |
24 | #include <asm/mach/irq.h> |
25 | |
26 | #define IXP4XX_ICPR 0x00 /* Interrupt Status */ |
27 | #define IXP4XX_ICMR 0x04 /* Interrupt Enable */ |
28 | #define IXP4XX_ICLR 0x08 /* Interrupt IRQ/FIQ Select */ |
29 | #define IXP4XX_ICIP 0x0C /* IRQ Status */ |
30 | #define IXP4XX_ICFP 0x10 /* FIQ Status */ |
31 | #define IXP4XX_ICHR 0x14 /* Interrupt Priority */ |
32 | #define IXP4XX_ICIH 0x18 /* IRQ Highest Pri Int */ |
33 | #define IXP4XX_ICFH 0x1C /* FIQ Highest Pri Int */ |
34 | |
35 | /* IXP43x and IXP46x-only */ |
36 | #define IXP4XX_ICPR2 0x20 /* Interrupt Status 2 */ |
37 | #define IXP4XX_ICMR2 0x24 /* Interrupt Enable 2 */ |
38 | #define IXP4XX_ICLR2 0x28 /* Interrupt IRQ/FIQ Select 2 */ |
39 | #define IXP4XX_ICIP2 0x2C /* IRQ Status */ |
40 | #define IXP4XX_ICFP2 0x30 /* FIQ Status */ |
41 | #define IXP4XX_ICEEN 0x34 /* Error High Pri Enable */ |
42 | |
43 | /** |
44 | * struct ixp4xx_irq - state container for the Faraday IRQ controller |
45 | * @irqbase: IRQ controller memory base in virtual memory |
46 | * @is_356: if this is an IXP43x, IXP45x or IX46x SoC (with 64 IRQs) |
47 | * @irqchip: irqchip for this instance |
48 | * @domain: IRQ domain for this instance |
49 | */ |
50 | struct ixp4xx_irq { |
51 | void __iomem *irqbase; |
52 | bool is_356; |
53 | struct irq_chip irqchip; |
54 | struct irq_domain *domain; |
55 | }; |
56 | |
57 | /* Local static state container */ |
58 | static struct ixp4xx_irq ixirq; |
59 | |
60 | /* GPIO Clocks */ |
61 | #define IXP4XX_GPIO_CLK_0 14 |
62 | #define IXP4XX_GPIO_CLK_1 15 |
63 | |
64 | static int ixp4xx_set_irq_type(struct irq_data *d, unsigned int type) |
65 | { |
66 | /* All are level active high (asserted) here */ |
67 | if (type != IRQ_TYPE_LEVEL_HIGH) |
68 | return -EINVAL; |
69 | return 0; |
70 | } |
71 | |
72 | static void ixp4xx_irq_mask(struct irq_data *d) |
73 | { |
74 | struct ixp4xx_irq *ixi = irq_data_get_irq_chip_data(d); |
75 | u32 val; |
76 | |
77 | if (ixi->is_356 && d->hwirq >= 32) { |
78 | val = __raw_readl(addr: ixi->irqbase + IXP4XX_ICMR2); |
79 | val &= ~BIT(d->hwirq - 32); |
80 | __raw_writel(val, addr: ixi->irqbase + IXP4XX_ICMR2); |
81 | } else { |
82 | val = __raw_readl(addr: ixi->irqbase + IXP4XX_ICMR); |
83 | val &= ~BIT(d->hwirq); |
84 | __raw_writel(val, addr: ixi->irqbase + IXP4XX_ICMR); |
85 | } |
86 | } |
87 | |
88 | /* |
89 | * Level triggered interrupts on GPIO lines can only be cleared when the |
90 | * interrupt condition disappears. |
91 | */ |
92 | static void ixp4xx_irq_unmask(struct irq_data *d) |
93 | { |
94 | struct ixp4xx_irq *ixi = irq_data_get_irq_chip_data(d); |
95 | u32 val; |
96 | |
97 | if (ixi->is_356 && d->hwirq >= 32) { |
98 | val = __raw_readl(addr: ixi->irqbase + IXP4XX_ICMR2); |
99 | val |= BIT(d->hwirq - 32); |
100 | __raw_writel(val, addr: ixi->irqbase + IXP4XX_ICMR2); |
101 | } else { |
102 | val = __raw_readl(addr: ixi->irqbase + IXP4XX_ICMR); |
103 | val |= BIT(d->hwirq); |
104 | __raw_writel(val, addr: ixi->irqbase + IXP4XX_ICMR); |
105 | } |
106 | } |
107 | |
108 | static asmlinkage void __exception_irq_entry |
109 | ixp4xx_handle_irq(struct pt_regs *regs) |
110 | { |
111 | struct ixp4xx_irq *ixi = &ixirq; |
112 | unsigned long status; |
113 | int i; |
114 | |
115 | status = __raw_readl(ixi->irqbase + IXP4XX_ICIP); |
116 | for_each_set_bit(i, &status, 32) |
117 | generic_handle_domain_irq(ixi->domain, i); |
118 | |
119 | /* |
120 | * IXP465/IXP435 has an upper IRQ status register |
121 | */ |
122 | if (ixi->is_356) { |
123 | status = __raw_readl(ixi->irqbase + IXP4XX_ICIP2); |
124 | for_each_set_bit(i, &status, 32) |
125 | generic_handle_domain_irq(ixi->domain, i + 32); |
126 | } |
127 | } |
128 | |
129 | static int ixp4xx_irq_domain_translate(struct irq_domain *domain, |
130 | struct irq_fwspec *fwspec, |
131 | unsigned long *hwirq, |
132 | unsigned int *type) |
133 | { |
134 | /* We support standard DT translation */ |
135 | if (is_of_node(fwnode: fwspec->fwnode) && fwspec->param_count == 2) { |
136 | *hwirq = fwspec->param[0]; |
137 | *type = fwspec->param[1]; |
138 | return 0; |
139 | } |
140 | |
141 | if (is_fwnode_irqchip(fwnode: fwspec->fwnode)) { |
142 | if (fwspec->param_count != 2) |
143 | return -EINVAL; |
144 | *hwirq = fwspec->param[0]; |
145 | *type = fwspec->param[1]; |
146 | WARN_ON(*type == IRQ_TYPE_NONE); |
147 | return 0; |
148 | } |
149 | |
150 | return -EINVAL; |
151 | } |
152 | |
153 | static int ixp4xx_irq_domain_alloc(struct irq_domain *d, |
154 | unsigned int irq, unsigned int nr_irqs, |
155 | void *data) |
156 | { |
157 | struct ixp4xx_irq *ixi = d->host_data; |
158 | irq_hw_number_t hwirq; |
159 | unsigned int type = IRQ_TYPE_NONE; |
160 | struct irq_fwspec *fwspec = data; |
161 | int ret; |
162 | int i; |
163 | |
164 | ret = ixp4xx_irq_domain_translate(domain: d, fwspec, hwirq: &hwirq, type: &type); |
165 | if (ret) |
166 | return ret; |
167 | |
168 | for (i = 0; i < nr_irqs; i++) { |
169 | /* |
170 | * TODO: after converting IXP4xx to only device tree, set |
171 | * handle_bad_irq as default handler and assume all consumers |
172 | * call .set_type() as this is provided in the second cell in |
173 | * the device tree phandle. |
174 | */ |
175 | irq_domain_set_info(domain: d, |
176 | virq: irq + i, |
177 | hwirq: hwirq + i, |
178 | chip: &ixi->irqchip, |
179 | chip_data: ixi, |
180 | handler: handle_level_irq, |
181 | NULL, NULL); |
182 | irq_set_probe(irq: irq + i); |
183 | } |
184 | |
185 | return 0; |
186 | } |
187 | |
188 | /* |
189 | * This needs to be a hierarchical irqdomain to work well with the |
190 | * GPIO irqchip (which is lower in the hierarchy) |
191 | */ |
192 | static const struct irq_domain_ops ixp4xx_irqdomain_ops = { |
193 | .translate = ixp4xx_irq_domain_translate, |
194 | .alloc = ixp4xx_irq_domain_alloc, |
195 | .free = irq_domain_free_irqs_common, |
196 | }; |
197 | |
198 | /** |
199 | * ixp4x_irq_setup() - Common setup code for the IXP4xx interrupt controller |
200 | * @ixi: State container |
201 | * @irqbase: Virtual memory base for the interrupt controller |
202 | * @fwnode: Corresponding fwnode abstraction for this controller |
203 | * @is_356: if this is an IXP43x, IXP45x or IXP46x SoC variant |
204 | */ |
205 | static int __init ixp4xx_irq_setup(struct ixp4xx_irq *ixi, |
206 | void __iomem *irqbase, |
207 | struct fwnode_handle *fwnode, |
208 | bool is_356) |
209 | { |
210 | int nr_irqs; |
211 | |
212 | ixi->irqbase = irqbase; |
213 | ixi->is_356 = is_356; |
214 | |
215 | /* Route all sources to IRQ instead of FIQ */ |
216 | __raw_writel(val: 0x0, addr: ixi->irqbase + IXP4XX_ICLR); |
217 | |
218 | /* Disable all interrupts */ |
219 | __raw_writel(val: 0x0, addr: ixi->irqbase + IXP4XX_ICMR); |
220 | |
221 | if (is_356) { |
222 | /* Route upper 32 sources to IRQ instead of FIQ */ |
223 | __raw_writel(val: 0x0, addr: ixi->irqbase + IXP4XX_ICLR2); |
224 | |
225 | /* Disable upper 32 interrupts */ |
226 | __raw_writel(val: 0x0, addr: ixi->irqbase + IXP4XX_ICMR2); |
227 | |
228 | nr_irqs = 64; |
229 | } else { |
230 | nr_irqs = 32; |
231 | } |
232 | |
233 | ixi->irqchip.name = "IXP4xx" ; |
234 | ixi->irqchip.irq_mask = ixp4xx_irq_mask; |
235 | ixi->irqchip.irq_unmask = ixp4xx_irq_unmask; |
236 | ixi->irqchip.irq_set_type = ixp4xx_set_irq_type; |
237 | |
238 | ixi->domain = irq_domain_create_linear(fwnode, size: nr_irqs, |
239 | ops: &ixp4xx_irqdomain_ops, |
240 | host_data: ixi); |
241 | if (!ixi->domain) { |
242 | pr_crit("IXP4XX: can not add primary irqdomain\n" ); |
243 | return -ENODEV; |
244 | } |
245 | |
246 | set_handle_irq(ixp4xx_handle_irq); |
247 | |
248 | return 0; |
249 | } |
250 | |
251 | static int __init ixp4xx_of_init_irq(struct device_node *np, |
252 | struct device_node *parent) |
253 | { |
254 | struct ixp4xx_irq *ixi = &ixirq; |
255 | void __iomem *base; |
256 | struct fwnode_handle *fwnode; |
257 | bool is_356; |
258 | int ret; |
259 | |
260 | base = of_iomap(node: np, index: 0); |
261 | if (!base) { |
262 | pr_crit("IXP4XX: could not ioremap interrupt controller\n" ); |
263 | return -ENODEV; |
264 | } |
265 | fwnode = of_node_to_fwnode(node: np); |
266 | |
267 | /* These chip variants have 64 interrupts */ |
268 | is_356 = of_device_is_compatible(device: np, "intel,ixp43x-interrupt" ) || |
269 | of_device_is_compatible(device: np, "intel,ixp45x-interrupt" ) || |
270 | of_device_is_compatible(device: np, "intel,ixp46x-interrupt" ); |
271 | |
272 | ret = ixp4xx_irq_setup(ixi, irqbase: base, fwnode, is_356); |
273 | if (ret) |
274 | pr_crit("IXP4XX: failed to set up irqchip\n" ); |
275 | |
276 | return ret; |
277 | } |
278 | IRQCHIP_DECLARE(ixp42x, "intel,ixp42x-interrupt" , |
279 | ixp4xx_of_init_irq); |
280 | IRQCHIP_DECLARE(ixp43x, "intel,ixp43x-interrupt" , |
281 | ixp4xx_of_init_irq); |
282 | IRQCHIP_DECLARE(ixp45x, "intel,ixp45x-interrupt" , |
283 | ixp4xx_of_init_irq); |
284 | IRQCHIP_DECLARE(ixp46x, "intel,ixp46x-interrupt" , |
285 | ixp4xx_of_init_irq); |
286 | |