1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Driver for Socionext External Interrupt Unit (EXIU)
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (c) 2017-2019 Linaro, Ltd. <ard.biesheuvel@linaro.org>
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * Based on irq-tegra.c:
8*4882a593Smuzhiyun * Copyright (C) 2011 Google, Inc.
9*4882a593Smuzhiyun * Copyright (C) 2010,2013, NVIDIA Corporation
10*4882a593Smuzhiyun */
11*4882a593Smuzhiyun
12*4882a593Smuzhiyun #include <linux/interrupt.h>
13*4882a593Smuzhiyun #include <linux/io.h>
14*4882a593Smuzhiyun #include <linux/irq.h>
15*4882a593Smuzhiyun #include <linux/irqchip.h>
16*4882a593Smuzhiyun #include <linux/irqdomain.h>
17*4882a593Smuzhiyun #include <linux/of.h>
18*4882a593Smuzhiyun #include <linux/of_address.h>
19*4882a593Smuzhiyun #include <linux/of_irq.h>
20*4882a593Smuzhiyun #include <linux/platform_device.h>
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun #include <dt-bindings/interrupt-controller/arm-gic.h>
23*4882a593Smuzhiyun
24*4882a593Smuzhiyun #define NUM_IRQS 32
25*4882a593Smuzhiyun
26*4882a593Smuzhiyun #define EIMASK 0x00
27*4882a593Smuzhiyun #define EISRCSEL 0x04
28*4882a593Smuzhiyun #define EIREQSTA 0x08
29*4882a593Smuzhiyun #define EIRAWREQSTA 0x0C
30*4882a593Smuzhiyun #define EIREQCLR 0x10
31*4882a593Smuzhiyun #define EILVL 0x14
32*4882a593Smuzhiyun #define EIEDG 0x18
33*4882a593Smuzhiyun #define EISIR 0x1C
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun struct exiu_irq_data {
36*4882a593Smuzhiyun void __iomem *base;
37*4882a593Smuzhiyun u32 spi_base;
38*4882a593Smuzhiyun };
39*4882a593Smuzhiyun
exiu_irq_ack(struct irq_data * d)40*4882a593Smuzhiyun static void exiu_irq_ack(struct irq_data *d)
41*4882a593Smuzhiyun {
42*4882a593Smuzhiyun struct exiu_irq_data *data = irq_data_get_irq_chip_data(d);
43*4882a593Smuzhiyun
44*4882a593Smuzhiyun writel(BIT(d->hwirq), data->base + EIREQCLR);
45*4882a593Smuzhiyun }
46*4882a593Smuzhiyun
exiu_irq_eoi(struct irq_data * d)47*4882a593Smuzhiyun static void exiu_irq_eoi(struct irq_data *d)
48*4882a593Smuzhiyun {
49*4882a593Smuzhiyun struct exiu_irq_data *data = irq_data_get_irq_chip_data(d);
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun /*
52*4882a593Smuzhiyun * Level triggered interrupts are latched and must be cleared during
53*4882a593Smuzhiyun * EOI or the interrupt will be jammed on. Of course if a level
54*4882a593Smuzhiyun * triggered interrupt is still asserted then the write will not clear
55*4882a593Smuzhiyun * the interrupt.
56*4882a593Smuzhiyun */
57*4882a593Smuzhiyun if (irqd_is_level_type(d))
58*4882a593Smuzhiyun writel(BIT(d->hwirq), data->base + EIREQCLR);
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun irq_chip_eoi_parent(d);
61*4882a593Smuzhiyun }
62*4882a593Smuzhiyun
exiu_irq_mask(struct irq_data * d)63*4882a593Smuzhiyun static void exiu_irq_mask(struct irq_data *d)
64*4882a593Smuzhiyun {
65*4882a593Smuzhiyun struct exiu_irq_data *data = irq_data_get_irq_chip_data(d);
66*4882a593Smuzhiyun u32 val;
67*4882a593Smuzhiyun
68*4882a593Smuzhiyun val = readl_relaxed(data->base + EIMASK) | BIT(d->hwirq);
69*4882a593Smuzhiyun writel_relaxed(val, data->base + EIMASK);
70*4882a593Smuzhiyun irq_chip_mask_parent(d);
71*4882a593Smuzhiyun }
72*4882a593Smuzhiyun
exiu_irq_unmask(struct irq_data * d)73*4882a593Smuzhiyun static void exiu_irq_unmask(struct irq_data *d)
74*4882a593Smuzhiyun {
75*4882a593Smuzhiyun struct exiu_irq_data *data = irq_data_get_irq_chip_data(d);
76*4882a593Smuzhiyun u32 val;
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun val = readl_relaxed(data->base + EIMASK) & ~BIT(d->hwirq);
79*4882a593Smuzhiyun writel_relaxed(val, data->base + EIMASK);
80*4882a593Smuzhiyun irq_chip_unmask_parent(d);
81*4882a593Smuzhiyun }
82*4882a593Smuzhiyun
exiu_irq_enable(struct irq_data * d)83*4882a593Smuzhiyun static void exiu_irq_enable(struct irq_data *d)
84*4882a593Smuzhiyun {
85*4882a593Smuzhiyun struct exiu_irq_data *data = irq_data_get_irq_chip_data(d);
86*4882a593Smuzhiyun u32 val;
87*4882a593Smuzhiyun
88*4882a593Smuzhiyun /* clear interrupts that were latched while disabled */
89*4882a593Smuzhiyun writel_relaxed(BIT(d->hwirq), data->base + EIREQCLR);
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun val = readl_relaxed(data->base + EIMASK) & ~BIT(d->hwirq);
92*4882a593Smuzhiyun writel_relaxed(val, data->base + EIMASK);
93*4882a593Smuzhiyun irq_chip_enable_parent(d);
94*4882a593Smuzhiyun }
95*4882a593Smuzhiyun
exiu_irq_set_type(struct irq_data * d,unsigned int type)96*4882a593Smuzhiyun static int exiu_irq_set_type(struct irq_data *d, unsigned int type)
97*4882a593Smuzhiyun {
98*4882a593Smuzhiyun struct exiu_irq_data *data = irq_data_get_irq_chip_data(d);
99*4882a593Smuzhiyun u32 val;
100*4882a593Smuzhiyun
101*4882a593Smuzhiyun val = readl_relaxed(data->base + EILVL);
102*4882a593Smuzhiyun if (type == IRQ_TYPE_EDGE_RISING || type == IRQ_TYPE_LEVEL_HIGH)
103*4882a593Smuzhiyun val |= BIT(d->hwirq);
104*4882a593Smuzhiyun else
105*4882a593Smuzhiyun val &= ~BIT(d->hwirq);
106*4882a593Smuzhiyun writel_relaxed(val, data->base + EILVL);
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun val = readl_relaxed(data->base + EIEDG);
109*4882a593Smuzhiyun if (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_LEVEL_HIGH) {
110*4882a593Smuzhiyun val &= ~BIT(d->hwirq);
111*4882a593Smuzhiyun irq_set_handler_locked(d, handle_fasteoi_irq);
112*4882a593Smuzhiyun } else {
113*4882a593Smuzhiyun val |= BIT(d->hwirq);
114*4882a593Smuzhiyun irq_set_handler_locked(d, handle_fasteoi_ack_irq);
115*4882a593Smuzhiyun }
116*4882a593Smuzhiyun writel_relaxed(val, data->base + EIEDG);
117*4882a593Smuzhiyun
118*4882a593Smuzhiyun writel_relaxed(BIT(d->hwirq), data->base + EIREQCLR);
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH);
121*4882a593Smuzhiyun }
122*4882a593Smuzhiyun
123*4882a593Smuzhiyun static struct irq_chip exiu_irq_chip = {
124*4882a593Smuzhiyun .name = "EXIU",
125*4882a593Smuzhiyun .irq_ack = exiu_irq_ack,
126*4882a593Smuzhiyun .irq_eoi = exiu_irq_eoi,
127*4882a593Smuzhiyun .irq_enable = exiu_irq_enable,
128*4882a593Smuzhiyun .irq_mask = exiu_irq_mask,
129*4882a593Smuzhiyun .irq_unmask = exiu_irq_unmask,
130*4882a593Smuzhiyun .irq_set_type = exiu_irq_set_type,
131*4882a593Smuzhiyun .irq_set_affinity = irq_chip_set_affinity_parent,
132*4882a593Smuzhiyun .flags = IRQCHIP_SET_TYPE_MASKED |
133*4882a593Smuzhiyun IRQCHIP_SKIP_SET_WAKE |
134*4882a593Smuzhiyun IRQCHIP_EOI_THREADED |
135*4882a593Smuzhiyun IRQCHIP_MASK_ON_SUSPEND,
136*4882a593Smuzhiyun };
137*4882a593Smuzhiyun
exiu_domain_translate(struct irq_domain * domain,struct irq_fwspec * fwspec,unsigned long * hwirq,unsigned int * type)138*4882a593Smuzhiyun static int exiu_domain_translate(struct irq_domain *domain,
139*4882a593Smuzhiyun struct irq_fwspec *fwspec,
140*4882a593Smuzhiyun unsigned long *hwirq,
141*4882a593Smuzhiyun unsigned int *type)
142*4882a593Smuzhiyun {
143*4882a593Smuzhiyun struct exiu_irq_data *info = domain->host_data;
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun if (is_of_node(fwspec->fwnode)) {
146*4882a593Smuzhiyun if (fwspec->param_count != 3)
147*4882a593Smuzhiyun return -EINVAL;
148*4882a593Smuzhiyun
149*4882a593Smuzhiyun if (fwspec->param[0] != GIC_SPI)
150*4882a593Smuzhiyun return -EINVAL; /* No PPI should point to this domain */
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun *hwirq = fwspec->param[1] - info->spi_base;
153*4882a593Smuzhiyun *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
154*4882a593Smuzhiyun } else {
155*4882a593Smuzhiyun if (fwspec->param_count != 2)
156*4882a593Smuzhiyun return -EINVAL;
157*4882a593Smuzhiyun *hwirq = fwspec->param[0];
158*4882a593Smuzhiyun *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
159*4882a593Smuzhiyun }
160*4882a593Smuzhiyun return 0;
161*4882a593Smuzhiyun }
162*4882a593Smuzhiyun
exiu_domain_alloc(struct irq_domain * dom,unsigned int virq,unsigned int nr_irqs,void * data)163*4882a593Smuzhiyun static int exiu_domain_alloc(struct irq_domain *dom, unsigned int virq,
164*4882a593Smuzhiyun unsigned int nr_irqs, void *data)
165*4882a593Smuzhiyun {
166*4882a593Smuzhiyun struct irq_fwspec *fwspec = data;
167*4882a593Smuzhiyun struct irq_fwspec parent_fwspec;
168*4882a593Smuzhiyun struct exiu_irq_data *info = dom->host_data;
169*4882a593Smuzhiyun irq_hw_number_t hwirq;
170*4882a593Smuzhiyun
171*4882a593Smuzhiyun parent_fwspec = *fwspec;
172*4882a593Smuzhiyun if (is_of_node(dom->parent->fwnode)) {
173*4882a593Smuzhiyun if (fwspec->param_count != 3)
174*4882a593Smuzhiyun return -EINVAL; /* Not GIC compliant */
175*4882a593Smuzhiyun if (fwspec->param[0] != GIC_SPI)
176*4882a593Smuzhiyun return -EINVAL; /* No PPI should point to this domain */
177*4882a593Smuzhiyun
178*4882a593Smuzhiyun hwirq = fwspec->param[1] - info->spi_base;
179*4882a593Smuzhiyun } else {
180*4882a593Smuzhiyun hwirq = fwspec->param[0];
181*4882a593Smuzhiyun parent_fwspec.param[0] = hwirq + info->spi_base + 32;
182*4882a593Smuzhiyun }
183*4882a593Smuzhiyun WARN_ON(nr_irqs != 1);
184*4882a593Smuzhiyun irq_domain_set_hwirq_and_chip(dom, virq, hwirq, &exiu_irq_chip, info);
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun parent_fwspec.fwnode = dom->parent->fwnode;
187*4882a593Smuzhiyun return irq_domain_alloc_irqs_parent(dom, virq, nr_irqs, &parent_fwspec);
188*4882a593Smuzhiyun }
189*4882a593Smuzhiyun
190*4882a593Smuzhiyun static const struct irq_domain_ops exiu_domain_ops = {
191*4882a593Smuzhiyun .translate = exiu_domain_translate,
192*4882a593Smuzhiyun .alloc = exiu_domain_alloc,
193*4882a593Smuzhiyun .free = irq_domain_free_irqs_common,
194*4882a593Smuzhiyun };
195*4882a593Smuzhiyun
exiu_init(const struct fwnode_handle * fwnode,struct resource * res)196*4882a593Smuzhiyun static struct exiu_irq_data *exiu_init(const struct fwnode_handle *fwnode,
197*4882a593Smuzhiyun struct resource *res)
198*4882a593Smuzhiyun {
199*4882a593Smuzhiyun struct exiu_irq_data *data;
200*4882a593Smuzhiyun int err;
201*4882a593Smuzhiyun
202*4882a593Smuzhiyun data = kzalloc(sizeof(*data), GFP_KERNEL);
203*4882a593Smuzhiyun if (!data)
204*4882a593Smuzhiyun return ERR_PTR(-ENOMEM);
205*4882a593Smuzhiyun
206*4882a593Smuzhiyun if (fwnode_property_read_u32_array(fwnode, "socionext,spi-base",
207*4882a593Smuzhiyun &data->spi_base, 1)) {
208*4882a593Smuzhiyun err = -ENODEV;
209*4882a593Smuzhiyun goto out_free;
210*4882a593Smuzhiyun }
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun data->base = ioremap(res->start, resource_size(res));
213*4882a593Smuzhiyun if (!data->base) {
214*4882a593Smuzhiyun err = -ENODEV;
215*4882a593Smuzhiyun goto out_free;
216*4882a593Smuzhiyun }
217*4882a593Smuzhiyun
218*4882a593Smuzhiyun /* clear and mask all interrupts */
219*4882a593Smuzhiyun writel_relaxed(0xFFFFFFFF, data->base + EIREQCLR);
220*4882a593Smuzhiyun writel_relaxed(0xFFFFFFFF, data->base + EIMASK);
221*4882a593Smuzhiyun
222*4882a593Smuzhiyun return data;
223*4882a593Smuzhiyun
224*4882a593Smuzhiyun out_free:
225*4882a593Smuzhiyun kfree(data);
226*4882a593Smuzhiyun return ERR_PTR(err);
227*4882a593Smuzhiyun }
228*4882a593Smuzhiyun
exiu_dt_init(struct device_node * node,struct device_node * parent)229*4882a593Smuzhiyun static int __init exiu_dt_init(struct device_node *node,
230*4882a593Smuzhiyun struct device_node *parent)
231*4882a593Smuzhiyun {
232*4882a593Smuzhiyun struct irq_domain *parent_domain, *domain;
233*4882a593Smuzhiyun struct exiu_irq_data *data;
234*4882a593Smuzhiyun struct resource res;
235*4882a593Smuzhiyun
236*4882a593Smuzhiyun if (!parent) {
237*4882a593Smuzhiyun pr_err("%pOF: no parent, giving up\n", node);
238*4882a593Smuzhiyun return -ENODEV;
239*4882a593Smuzhiyun }
240*4882a593Smuzhiyun
241*4882a593Smuzhiyun parent_domain = irq_find_host(parent);
242*4882a593Smuzhiyun if (!parent_domain) {
243*4882a593Smuzhiyun pr_err("%pOF: unable to obtain parent domain\n", node);
244*4882a593Smuzhiyun return -ENXIO;
245*4882a593Smuzhiyun }
246*4882a593Smuzhiyun
247*4882a593Smuzhiyun if (of_address_to_resource(node, 0, &res)) {
248*4882a593Smuzhiyun pr_err("%pOF: failed to parse memory resource\n", node);
249*4882a593Smuzhiyun return -ENXIO;
250*4882a593Smuzhiyun }
251*4882a593Smuzhiyun
252*4882a593Smuzhiyun data = exiu_init(of_node_to_fwnode(node), &res);
253*4882a593Smuzhiyun if (IS_ERR(data))
254*4882a593Smuzhiyun return PTR_ERR(data);
255*4882a593Smuzhiyun
256*4882a593Smuzhiyun domain = irq_domain_add_hierarchy(parent_domain, 0, NUM_IRQS, node,
257*4882a593Smuzhiyun &exiu_domain_ops, data);
258*4882a593Smuzhiyun if (!domain) {
259*4882a593Smuzhiyun pr_err("%pOF: failed to allocate domain\n", node);
260*4882a593Smuzhiyun goto out_unmap;
261*4882a593Smuzhiyun }
262*4882a593Smuzhiyun
263*4882a593Smuzhiyun pr_info("%pOF: %d interrupts forwarded to %pOF\n", node, NUM_IRQS,
264*4882a593Smuzhiyun parent);
265*4882a593Smuzhiyun
266*4882a593Smuzhiyun return 0;
267*4882a593Smuzhiyun
268*4882a593Smuzhiyun out_unmap:
269*4882a593Smuzhiyun iounmap(data->base);
270*4882a593Smuzhiyun kfree(data);
271*4882a593Smuzhiyun return -ENOMEM;
272*4882a593Smuzhiyun }
273*4882a593Smuzhiyun IRQCHIP_DECLARE(exiu, "socionext,synquacer-exiu", exiu_dt_init);
274*4882a593Smuzhiyun
275*4882a593Smuzhiyun #ifdef CONFIG_ACPI
exiu_acpi_probe(struct platform_device * pdev)276*4882a593Smuzhiyun static int exiu_acpi_probe(struct platform_device *pdev)
277*4882a593Smuzhiyun {
278*4882a593Smuzhiyun struct irq_domain *domain;
279*4882a593Smuzhiyun struct exiu_irq_data *data;
280*4882a593Smuzhiyun struct resource *res;
281*4882a593Smuzhiyun
282*4882a593Smuzhiyun res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
283*4882a593Smuzhiyun if (!res) {
284*4882a593Smuzhiyun dev_err(&pdev->dev, "failed to parse memory resource\n");
285*4882a593Smuzhiyun return -ENXIO;
286*4882a593Smuzhiyun }
287*4882a593Smuzhiyun
288*4882a593Smuzhiyun data = exiu_init(dev_fwnode(&pdev->dev), res);
289*4882a593Smuzhiyun if (IS_ERR(data))
290*4882a593Smuzhiyun return PTR_ERR(data);
291*4882a593Smuzhiyun
292*4882a593Smuzhiyun domain = acpi_irq_create_hierarchy(0, NUM_IRQS, dev_fwnode(&pdev->dev),
293*4882a593Smuzhiyun &exiu_domain_ops, data);
294*4882a593Smuzhiyun if (!domain) {
295*4882a593Smuzhiyun dev_err(&pdev->dev, "failed to create IRQ domain\n");
296*4882a593Smuzhiyun goto out_unmap;
297*4882a593Smuzhiyun }
298*4882a593Smuzhiyun
299*4882a593Smuzhiyun dev_info(&pdev->dev, "%d interrupts forwarded\n", NUM_IRQS);
300*4882a593Smuzhiyun
301*4882a593Smuzhiyun return 0;
302*4882a593Smuzhiyun
303*4882a593Smuzhiyun out_unmap:
304*4882a593Smuzhiyun iounmap(data->base);
305*4882a593Smuzhiyun kfree(data);
306*4882a593Smuzhiyun return -ENOMEM;
307*4882a593Smuzhiyun }
308*4882a593Smuzhiyun
309*4882a593Smuzhiyun static const struct acpi_device_id exiu_acpi_ids[] = {
310*4882a593Smuzhiyun { "SCX0008" },
311*4882a593Smuzhiyun { /* sentinel */ }
312*4882a593Smuzhiyun };
313*4882a593Smuzhiyun MODULE_DEVICE_TABLE(acpi, exiu_acpi_ids);
314*4882a593Smuzhiyun
315*4882a593Smuzhiyun static struct platform_driver exiu_driver = {
316*4882a593Smuzhiyun .driver = {
317*4882a593Smuzhiyun .name = "exiu",
318*4882a593Smuzhiyun .acpi_match_table = exiu_acpi_ids,
319*4882a593Smuzhiyun },
320*4882a593Smuzhiyun .probe = exiu_acpi_probe,
321*4882a593Smuzhiyun };
322*4882a593Smuzhiyun builtin_platform_driver(exiu_driver);
323*4882a593Smuzhiyun #endif
324