1aa9c5adfSBenjamin Herrenschmidt // SPDX-License-Identifier: GPL-2.0-or-later
2aa9c5adfSBenjamin Herrenschmidt /*
3aa9c5adfSBenjamin Herrenschmidt * ICS backend for OPAL managed interrupts.
4aa9c5adfSBenjamin Herrenschmidt *
5aa9c5adfSBenjamin Herrenschmidt * Copyright 2011 IBM Corp.
6aa9c5adfSBenjamin Herrenschmidt */
7aa9c5adfSBenjamin Herrenschmidt
8aa9c5adfSBenjamin Herrenschmidt //#define DEBUG
9aa9c5adfSBenjamin Herrenschmidt
10aa9c5adfSBenjamin Herrenschmidt #include <linux/types.h>
11aa9c5adfSBenjamin Herrenschmidt #include <linux/kernel.h>
12aa9c5adfSBenjamin Herrenschmidt #include <linux/irq.h>
13aa9c5adfSBenjamin Herrenschmidt #include <linux/smp.h>
14aa9c5adfSBenjamin Herrenschmidt #include <linux/interrupt.h>
15aa9c5adfSBenjamin Herrenschmidt #include <linux/init.h>
16aa9c5adfSBenjamin Herrenschmidt #include <linux/cpu.h>
17aa9c5adfSBenjamin Herrenschmidt #include <linux/of.h>
18*14554d92SChristophe Leroy #include <linux/of_address.h>
19aa9c5adfSBenjamin Herrenschmidt #include <linux/spinlock.h>
20aa9c5adfSBenjamin Herrenschmidt #include <linux/msi.h>
21aa9c5adfSBenjamin Herrenschmidt #include <linux/list.h>
22aa9c5adfSBenjamin Herrenschmidt
23aa9c5adfSBenjamin Herrenschmidt #include <asm/smp.h>
24aa9c5adfSBenjamin Herrenschmidt #include <asm/machdep.h>
25aa9c5adfSBenjamin Herrenschmidt #include <asm/irq.h>
26aa9c5adfSBenjamin Herrenschmidt #include <asm/errno.h>
27aa9c5adfSBenjamin Herrenschmidt #include <asm/xics.h>
28aa9c5adfSBenjamin Herrenschmidt #include <asm/opal.h>
29aa9c5adfSBenjamin Herrenschmidt #include <asm/firmware.h>
30aa9c5adfSBenjamin Herrenschmidt
31aa9c5adfSBenjamin Herrenschmidt struct ics_native {
32aa9c5adfSBenjamin Herrenschmidt struct ics ics;
33aa9c5adfSBenjamin Herrenschmidt struct device_node *node;
34aa9c5adfSBenjamin Herrenschmidt void __iomem *base;
35aa9c5adfSBenjamin Herrenschmidt u32 ibase;
36aa9c5adfSBenjamin Herrenschmidt u32 icount;
37aa9c5adfSBenjamin Herrenschmidt };
38aa9c5adfSBenjamin Herrenschmidt #define to_ics_native(_ics) container_of(_ics, struct ics_native, ics)
39aa9c5adfSBenjamin Herrenschmidt
ics_native_xive(struct ics_native * in,unsigned int vec)40aa9c5adfSBenjamin Herrenschmidt static void __iomem *ics_native_xive(struct ics_native *in, unsigned int vec)
41aa9c5adfSBenjamin Herrenschmidt {
42aa9c5adfSBenjamin Herrenschmidt return in->base + 0x800 + ((vec - in->ibase) << 2);
43aa9c5adfSBenjamin Herrenschmidt }
44aa9c5adfSBenjamin Herrenschmidt
ics_native_unmask_irq(struct irq_data * d)45aa9c5adfSBenjamin Herrenschmidt static void ics_native_unmask_irq(struct irq_data *d)
46aa9c5adfSBenjamin Herrenschmidt {
47aa9c5adfSBenjamin Herrenschmidt unsigned int vec = (unsigned int)irqd_to_hwirq(d);
48aa9c5adfSBenjamin Herrenschmidt struct ics *ics = irq_data_get_irq_chip_data(d);
49aa9c5adfSBenjamin Herrenschmidt struct ics_native *in = to_ics_native(ics);
50aa9c5adfSBenjamin Herrenschmidt unsigned int server;
51aa9c5adfSBenjamin Herrenschmidt
52aa9c5adfSBenjamin Herrenschmidt pr_devel("ics-native: unmask virq %d [hw 0x%x]\n", d->irq, vec);
53aa9c5adfSBenjamin Herrenschmidt
54aa9c5adfSBenjamin Herrenschmidt if (vec < in->ibase || vec >= (in->ibase + in->icount))
55aa9c5adfSBenjamin Herrenschmidt return;
56aa9c5adfSBenjamin Herrenschmidt
57aa9c5adfSBenjamin Herrenschmidt server = xics_get_irq_server(d->irq, irq_data_get_affinity_mask(d), 0);
58aa9c5adfSBenjamin Herrenschmidt out_be32(ics_native_xive(in, vec), (server << 8) | DEFAULT_PRIORITY);
59aa9c5adfSBenjamin Herrenschmidt }
60aa9c5adfSBenjamin Herrenschmidt
ics_native_startup(struct irq_data * d)61aa9c5adfSBenjamin Herrenschmidt static unsigned int ics_native_startup(struct irq_data *d)
62aa9c5adfSBenjamin Herrenschmidt {
63aa9c5adfSBenjamin Herrenschmidt #ifdef CONFIG_PCI_MSI
64aa9c5adfSBenjamin Herrenschmidt /*
65aa9c5adfSBenjamin Herrenschmidt * The generic MSI code returns with the interrupt disabled on the
66aa9c5adfSBenjamin Herrenschmidt * card, using the MSI mask bits. Firmware doesn't appear to unmask
67aa9c5adfSBenjamin Herrenschmidt * at that level, so we do it here by hand.
68aa9c5adfSBenjamin Herrenschmidt */
69aa9c5adfSBenjamin Herrenschmidt if (irq_data_get_msi_desc(d))
70aa9c5adfSBenjamin Herrenschmidt pci_msi_unmask_irq(d);
71aa9c5adfSBenjamin Herrenschmidt #endif
72aa9c5adfSBenjamin Herrenschmidt
73aa9c5adfSBenjamin Herrenschmidt /* unmask it */
74aa9c5adfSBenjamin Herrenschmidt ics_native_unmask_irq(d);
75aa9c5adfSBenjamin Herrenschmidt return 0;
76aa9c5adfSBenjamin Herrenschmidt }
77aa9c5adfSBenjamin Herrenschmidt
ics_native_do_mask(struct ics_native * in,unsigned int vec)78aa9c5adfSBenjamin Herrenschmidt static void ics_native_do_mask(struct ics_native *in, unsigned int vec)
79aa9c5adfSBenjamin Herrenschmidt {
80aa9c5adfSBenjamin Herrenschmidt out_be32(ics_native_xive(in, vec), 0xff);
81aa9c5adfSBenjamin Herrenschmidt }
82aa9c5adfSBenjamin Herrenschmidt
ics_native_mask_irq(struct irq_data * d)83aa9c5adfSBenjamin Herrenschmidt static void ics_native_mask_irq(struct irq_data *d)
84aa9c5adfSBenjamin Herrenschmidt {
85aa9c5adfSBenjamin Herrenschmidt unsigned int vec = (unsigned int)irqd_to_hwirq(d);
86aa9c5adfSBenjamin Herrenschmidt struct ics *ics = irq_data_get_irq_chip_data(d);
87aa9c5adfSBenjamin Herrenschmidt struct ics_native *in = to_ics_native(ics);
88aa9c5adfSBenjamin Herrenschmidt
89aa9c5adfSBenjamin Herrenschmidt pr_devel("ics-native: mask virq %d [hw 0x%x]\n", d->irq, vec);
90aa9c5adfSBenjamin Herrenschmidt
91aa9c5adfSBenjamin Herrenschmidt if (vec < in->ibase || vec >= (in->ibase + in->icount))
92aa9c5adfSBenjamin Herrenschmidt return;
93aa9c5adfSBenjamin Herrenschmidt ics_native_do_mask(in, vec);
94aa9c5adfSBenjamin Herrenschmidt }
95aa9c5adfSBenjamin Herrenschmidt
ics_native_set_affinity(struct irq_data * d,const struct cpumask * cpumask,bool force)96aa9c5adfSBenjamin Herrenschmidt static int ics_native_set_affinity(struct irq_data *d,
97aa9c5adfSBenjamin Herrenschmidt const struct cpumask *cpumask,
98aa9c5adfSBenjamin Herrenschmidt bool force)
99aa9c5adfSBenjamin Herrenschmidt {
100aa9c5adfSBenjamin Herrenschmidt unsigned int vec = (unsigned int)irqd_to_hwirq(d);
101aa9c5adfSBenjamin Herrenschmidt struct ics *ics = irq_data_get_irq_chip_data(d);
102aa9c5adfSBenjamin Herrenschmidt struct ics_native *in = to_ics_native(ics);
103aa9c5adfSBenjamin Herrenschmidt int server;
104aa9c5adfSBenjamin Herrenschmidt u32 xive;
105aa9c5adfSBenjamin Herrenschmidt
106aa9c5adfSBenjamin Herrenschmidt if (vec < in->ibase || vec >= (in->ibase + in->icount))
107aa9c5adfSBenjamin Herrenschmidt return -EINVAL;
108aa9c5adfSBenjamin Herrenschmidt
109aa9c5adfSBenjamin Herrenschmidt server = xics_get_irq_server(d->irq, cpumask, 1);
110aa9c5adfSBenjamin Herrenschmidt if (server == -1) {
111aa9c5adfSBenjamin Herrenschmidt pr_warn("%s: No online cpus in the mask %*pb for irq %d\n",
112aa9c5adfSBenjamin Herrenschmidt __func__, cpumask_pr_args(cpumask), d->irq);
113aa9c5adfSBenjamin Herrenschmidt return -1;
114aa9c5adfSBenjamin Herrenschmidt }
115aa9c5adfSBenjamin Herrenschmidt
116aa9c5adfSBenjamin Herrenschmidt xive = in_be32(ics_native_xive(in, vec));
117aa9c5adfSBenjamin Herrenschmidt xive = (xive & 0xff) | (server << 8);
118aa9c5adfSBenjamin Herrenschmidt out_be32(ics_native_xive(in, vec), xive);
119aa9c5adfSBenjamin Herrenschmidt
120aa9c5adfSBenjamin Herrenschmidt return IRQ_SET_MASK_OK;
121aa9c5adfSBenjamin Herrenschmidt }
122aa9c5adfSBenjamin Herrenschmidt
123aa9c5adfSBenjamin Herrenschmidt static struct irq_chip ics_native_irq_chip = {
124aa9c5adfSBenjamin Herrenschmidt .name = "ICS",
125aa9c5adfSBenjamin Herrenschmidt .irq_startup = ics_native_startup,
126aa9c5adfSBenjamin Herrenschmidt .irq_mask = ics_native_mask_irq,
127aa9c5adfSBenjamin Herrenschmidt .irq_unmask = ics_native_unmask_irq,
128aa9c5adfSBenjamin Herrenschmidt .irq_eoi = NULL, /* Patched at init time */
129aa9c5adfSBenjamin Herrenschmidt .irq_set_affinity = ics_native_set_affinity,
130aa9c5adfSBenjamin Herrenschmidt .irq_set_type = xics_set_irq_type,
131aa9c5adfSBenjamin Herrenschmidt .irq_retrigger = xics_retrigger,
132aa9c5adfSBenjamin Herrenschmidt };
133aa9c5adfSBenjamin Herrenschmidt
ics_native_check(struct ics * ics,unsigned int hw_irq)134248af248SCédric Le Goater static int ics_native_check(struct ics *ics, unsigned int hw_irq)
135aa9c5adfSBenjamin Herrenschmidt {
136aa9c5adfSBenjamin Herrenschmidt struct ics_native *in = to_ics_native(ics);
137aa9c5adfSBenjamin Herrenschmidt
138248af248SCédric Le Goater pr_devel("%s: hw_irq=0x%x\n", __func__, hw_irq);
139aa9c5adfSBenjamin Herrenschmidt
140248af248SCédric Le Goater if (hw_irq < in->ibase || hw_irq >= (in->ibase + in->icount))
141aa9c5adfSBenjamin Herrenschmidt return -EINVAL;
142aa9c5adfSBenjamin Herrenschmidt
143aa9c5adfSBenjamin Herrenschmidt return 0;
144aa9c5adfSBenjamin Herrenschmidt }
145aa9c5adfSBenjamin Herrenschmidt
ics_native_mask_unknown(struct ics * ics,unsigned long vec)146aa9c5adfSBenjamin Herrenschmidt static void ics_native_mask_unknown(struct ics *ics, unsigned long vec)
147aa9c5adfSBenjamin Herrenschmidt {
148aa9c5adfSBenjamin Herrenschmidt struct ics_native *in = to_ics_native(ics);
149aa9c5adfSBenjamin Herrenschmidt
150aa9c5adfSBenjamin Herrenschmidt if (vec < in->ibase || vec >= (in->ibase + in->icount))
151aa9c5adfSBenjamin Herrenschmidt return;
152aa9c5adfSBenjamin Herrenschmidt
153aa9c5adfSBenjamin Herrenschmidt ics_native_do_mask(in, vec);
154aa9c5adfSBenjamin Herrenschmidt }
155aa9c5adfSBenjamin Herrenschmidt
ics_native_get_server(struct ics * ics,unsigned long vec)156aa9c5adfSBenjamin Herrenschmidt static long ics_native_get_server(struct ics *ics, unsigned long vec)
157aa9c5adfSBenjamin Herrenschmidt {
158aa9c5adfSBenjamin Herrenschmidt struct ics_native *in = to_ics_native(ics);
159aa9c5adfSBenjamin Herrenschmidt u32 xive;
160aa9c5adfSBenjamin Herrenschmidt
161aa9c5adfSBenjamin Herrenschmidt if (vec < in->ibase || vec >= (in->ibase + in->icount))
162aa9c5adfSBenjamin Herrenschmidt return -EINVAL;
163aa9c5adfSBenjamin Herrenschmidt
164aa9c5adfSBenjamin Herrenschmidt xive = in_be32(ics_native_xive(in, vec));
165aa9c5adfSBenjamin Herrenschmidt return (xive >> 8) & 0xfff;
166aa9c5adfSBenjamin Herrenschmidt }
167aa9c5adfSBenjamin Herrenschmidt
ics_native_host_match(struct ics * ics,struct device_node * node)168aa9c5adfSBenjamin Herrenschmidt static int ics_native_host_match(struct ics *ics, struct device_node *node)
169aa9c5adfSBenjamin Herrenschmidt {
170aa9c5adfSBenjamin Herrenschmidt struct ics_native *in = to_ics_native(ics);
171aa9c5adfSBenjamin Herrenschmidt
172aa9c5adfSBenjamin Herrenschmidt return in->node == node;
173aa9c5adfSBenjamin Herrenschmidt }
174aa9c5adfSBenjamin Herrenschmidt
175aa9c5adfSBenjamin Herrenschmidt static struct ics ics_native_template = {
176248af248SCédric Le Goater .check = ics_native_check,
177aa9c5adfSBenjamin Herrenschmidt .mask_unknown = ics_native_mask_unknown,
178aa9c5adfSBenjamin Herrenschmidt .get_server = ics_native_get_server,
179aa9c5adfSBenjamin Herrenschmidt .host_match = ics_native_host_match,
180248af248SCédric Le Goater .chip = &ics_native_irq_chip,
181aa9c5adfSBenjamin Herrenschmidt };
182aa9c5adfSBenjamin Herrenschmidt
ics_native_add_one(struct device_node * np)183aa9c5adfSBenjamin Herrenschmidt static int __init ics_native_add_one(struct device_node *np)
184aa9c5adfSBenjamin Herrenschmidt {
185aa9c5adfSBenjamin Herrenschmidt struct ics_native *ics;
186aa9c5adfSBenjamin Herrenschmidt u32 ranges[2];
187aa9c5adfSBenjamin Herrenschmidt int rc, count;
188aa9c5adfSBenjamin Herrenschmidt
189aa9c5adfSBenjamin Herrenschmidt ics = kzalloc(sizeof(struct ics_native), GFP_KERNEL);
190aa9c5adfSBenjamin Herrenschmidt if (!ics)
191aa9c5adfSBenjamin Herrenschmidt return -ENOMEM;
192aa9c5adfSBenjamin Herrenschmidt ics->node = of_node_get(np);
193aa9c5adfSBenjamin Herrenschmidt memcpy(&ics->ics, &ics_native_template, sizeof(struct ics));
194aa9c5adfSBenjamin Herrenschmidt
195aa9c5adfSBenjamin Herrenschmidt ics->base = of_iomap(np, 0);
196aa9c5adfSBenjamin Herrenschmidt if (!ics->base) {
197aa9c5adfSBenjamin Herrenschmidt pr_err("Failed to map %pOFP\n", np);
198aa9c5adfSBenjamin Herrenschmidt rc = -ENOMEM;
199aa9c5adfSBenjamin Herrenschmidt goto fail;
200aa9c5adfSBenjamin Herrenschmidt }
201aa9c5adfSBenjamin Herrenschmidt
202aa9c5adfSBenjamin Herrenschmidt count = of_property_count_u32_elems(np, "interrupt-ranges");
203aa9c5adfSBenjamin Herrenschmidt if (count < 2 || count & 1) {
204aa9c5adfSBenjamin Herrenschmidt pr_err("Failed to read interrupt-ranges of %pOFP\n", np);
205aa9c5adfSBenjamin Herrenschmidt rc = -EINVAL;
206aa9c5adfSBenjamin Herrenschmidt goto fail;
207aa9c5adfSBenjamin Herrenschmidt }
208aa9c5adfSBenjamin Herrenschmidt if (count > 2) {
209aa9c5adfSBenjamin Herrenschmidt pr_warn("ICS %pOFP has %d ranges, only one supported\n",
210aa9c5adfSBenjamin Herrenschmidt np, count >> 1);
211aa9c5adfSBenjamin Herrenschmidt }
212aa9c5adfSBenjamin Herrenschmidt rc = of_property_read_u32_array(np, "interrupt-ranges",
213aa9c5adfSBenjamin Herrenschmidt ranges, 2);
214aa9c5adfSBenjamin Herrenschmidt if (rc) {
215aa9c5adfSBenjamin Herrenschmidt pr_err("Failed to read interrupt-ranges of %pOFP\n", np);
216aa9c5adfSBenjamin Herrenschmidt goto fail;
217aa9c5adfSBenjamin Herrenschmidt }
218aa9c5adfSBenjamin Herrenschmidt ics->ibase = ranges[0];
219aa9c5adfSBenjamin Herrenschmidt ics->icount = ranges[1];
220aa9c5adfSBenjamin Herrenschmidt
221aa9c5adfSBenjamin Herrenschmidt pr_info("ICS native initialized for sources %d..%d\n",
222aa9c5adfSBenjamin Herrenschmidt ics->ibase, ics->ibase + ics->icount - 1);
223aa9c5adfSBenjamin Herrenschmidt
224aa9c5adfSBenjamin Herrenschmidt /* Register ourselves */
225aa9c5adfSBenjamin Herrenschmidt xics_register_ics(&ics->ics);
226aa9c5adfSBenjamin Herrenschmidt
227aa9c5adfSBenjamin Herrenschmidt return 0;
228aa9c5adfSBenjamin Herrenschmidt fail:
229aa9c5adfSBenjamin Herrenschmidt of_node_put(ics->node);
230aa9c5adfSBenjamin Herrenschmidt kfree(ics);
231aa9c5adfSBenjamin Herrenschmidt return rc;
232aa9c5adfSBenjamin Herrenschmidt }
233aa9c5adfSBenjamin Herrenschmidt
ics_native_init(void)234aa9c5adfSBenjamin Herrenschmidt int __init ics_native_init(void)
235aa9c5adfSBenjamin Herrenschmidt {
236aa9c5adfSBenjamin Herrenschmidt struct device_node *ics;
237aa9c5adfSBenjamin Herrenschmidt bool found_one = false;
238aa9c5adfSBenjamin Herrenschmidt
239aa9c5adfSBenjamin Herrenschmidt /* We need to patch our irq chip's EOI to point to the
240aa9c5adfSBenjamin Herrenschmidt * right ICP
241aa9c5adfSBenjamin Herrenschmidt */
242aa9c5adfSBenjamin Herrenschmidt ics_native_irq_chip.irq_eoi = icp_ops->eoi;
243aa9c5adfSBenjamin Herrenschmidt
244aa9c5adfSBenjamin Herrenschmidt /* Find native ICS in the device-tree */
245aa9c5adfSBenjamin Herrenschmidt for_each_compatible_node(ics, NULL, "openpower,xics-sources") {
246aa9c5adfSBenjamin Herrenschmidt if (ics_native_add_one(ics) == 0)
247aa9c5adfSBenjamin Herrenschmidt found_one = true;
248aa9c5adfSBenjamin Herrenschmidt }
249aa9c5adfSBenjamin Herrenschmidt
250aa9c5adfSBenjamin Herrenschmidt if (found_one)
251aa9c5adfSBenjamin Herrenschmidt pr_info("ICS native backend registered\n");
252aa9c5adfSBenjamin Herrenschmidt
253aa9c5adfSBenjamin Herrenschmidt return found_one ? 0 : -ENODEV;
254aa9c5adfSBenjamin Herrenschmidt }
255