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