12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
25c7c1e94SBenjamin Herrenschmidt /*
35c7c1e94SBenjamin Herrenschmidt  * ICS backend for OPAL managed interrupts.
45c7c1e94SBenjamin Herrenschmidt  *
55c7c1e94SBenjamin Herrenschmidt  * Copyright 2011 IBM Corp.
65c7c1e94SBenjamin Herrenschmidt  */
75c7c1e94SBenjamin Herrenschmidt 
85c7c1e94SBenjamin Herrenschmidt #undef DEBUG
95c7c1e94SBenjamin Herrenschmidt 
105c7c1e94SBenjamin Herrenschmidt #include <linux/types.h>
115c7c1e94SBenjamin Herrenschmidt #include <linux/kernel.h>
125c7c1e94SBenjamin Herrenschmidt #include <linux/irq.h>
135c7c1e94SBenjamin Herrenschmidt #include <linux/smp.h>
145c7c1e94SBenjamin Herrenschmidt #include <linux/interrupt.h>
155c7c1e94SBenjamin Herrenschmidt #include <linux/init.h>
165c7c1e94SBenjamin Herrenschmidt #include <linux/cpu.h>
175c7c1e94SBenjamin Herrenschmidt #include <linux/of.h>
185c7c1e94SBenjamin Herrenschmidt #include <linux/spinlock.h>
195c7c1e94SBenjamin Herrenschmidt #include <linux/msi.h>
205c7c1e94SBenjamin Herrenschmidt 
215c7c1e94SBenjamin Herrenschmidt #include <asm/smp.h>
225c7c1e94SBenjamin Herrenschmidt #include <asm/machdep.h>
235c7c1e94SBenjamin Herrenschmidt #include <asm/irq.h>
245c7c1e94SBenjamin Herrenschmidt #include <asm/errno.h>
255c7c1e94SBenjamin Herrenschmidt #include <asm/xics.h>
265c7c1e94SBenjamin Herrenschmidt #include <asm/opal.h>
275c7c1e94SBenjamin Herrenschmidt #include <asm/firmware.h>
285c7c1e94SBenjamin Herrenschmidt 
ics_opal_mangle_server(int server)295c7c1e94SBenjamin Herrenschmidt static int ics_opal_mangle_server(int server)
305c7c1e94SBenjamin Herrenschmidt {
315c7c1e94SBenjamin Herrenschmidt 	/* No link for now */
325c7c1e94SBenjamin Herrenschmidt 	return server << 2;
335c7c1e94SBenjamin Herrenschmidt }
345c7c1e94SBenjamin Herrenschmidt 
ics_opal_unmangle_server(int server)355c7c1e94SBenjamin Herrenschmidt static int ics_opal_unmangle_server(int server)
365c7c1e94SBenjamin Herrenschmidt {
375c7c1e94SBenjamin Herrenschmidt 	/* No link for now */
385c7c1e94SBenjamin Herrenschmidt 	return server >> 2;
395c7c1e94SBenjamin Herrenschmidt }
405c7c1e94SBenjamin Herrenschmidt 
ics_opal_unmask_irq(struct irq_data * d)415c7c1e94SBenjamin Herrenschmidt static void ics_opal_unmask_irq(struct irq_data *d)
425c7c1e94SBenjamin Herrenschmidt {
435c7c1e94SBenjamin Herrenschmidt 	unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d);
445c7c1e94SBenjamin Herrenschmidt 	int64_t rc;
455c7c1e94SBenjamin Herrenschmidt 	int server;
465c7c1e94SBenjamin Herrenschmidt 
475c7c1e94SBenjamin Herrenschmidt 	pr_devel("ics-hal: unmask virq %d [hw 0x%x]\n", d->irq, hw_irq);
485c7c1e94SBenjamin Herrenschmidt 
495c7c1e94SBenjamin Herrenschmidt 	if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS)
505c7c1e94SBenjamin Herrenschmidt 		return;
515c7c1e94SBenjamin Herrenschmidt 
52da92b4ebSJiang Liu 	server = xics_get_irq_server(d->irq, irq_data_get_affinity_mask(d), 0);
535c7c1e94SBenjamin Herrenschmidt 	server = ics_opal_mangle_server(server);
545c7c1e94SBenjamin Herrenschmidt 
555c7c1e94SBenjamin Herrenschmidt 	rc = opal_set_xive(hw_irq, server, DEFAULT_PRIORITY);
565c7c1e94SBenjamin Herrenschmidt 	if (rc != OPAL_SUCCESS)
575c7c1e94SBenjamin Herrenschmidt 		pr_err("%s: opal_set_xive(irq=%d [hw 0x%x] server=%x)"
585c7c1e94SBenjamin Herrenschmidt 		       " error %lld\n",
595c7c1e94SBenjamin Herrenschmidt 		       __func__, d->irq, hw_irq, server, rc);
605c7c1e94SBenjamin Herrenschmidt }
615c7c1e94SBenjamin Herrenschmidt 
ics_opal_startup(struct irq_data * d)625c7c1e94SBenjamin Herrenschmidt static unsigned int ics_opal_startup(struct irq_data *d)
635c7c1e94SBenjamin Herrenschmidt {
645c7c1e94SBenjamin Herrenschmidt 	ics_opal_unmask_irq(d);
655c7c1e94SBenjamin Herrenschmidt 	return 0;
665c7c1e94SBenjamin Herrenschmidt }
675c7c1e94SBenjamin Herrenschmidt 
ics_opal_mask_real_irq(unsigned int hw_irq)685c7c1e94SBenjamin Herrenschmidt static void ics_opal_mask_real_irq(unsigned int hw_irq)
695c7c1e94SBenjamin Herrenschmidt {
705c7c1e94SBenjamin Herrenschmidt 	int server = ics_opal_mangle_server(xics_default_server);
715c7c1e94SBenjamin Herrenschmidt 	int64_t rc;
725c7c1e94SBenjamin Herrenschmidt 
735c7c1e94SBenjamin Herrenschmidt 	if (hw_irq == XICS_IPI)
745c7c1e94SBenjamin Herrenschmidt 		return;
755c7c1e94SBenjamin Herrenschmidt 
765c7c1e94SBenjamin Herrenschmidt 	/* Have to set XIVE to 0xff to be able to remove a slot */
775c7c1e94SBenjamin Herrenschmidt 	rc = opal_set_xive(hw_irq, server, 0xff);
785c7c1e94SBenjamin Herrenschmidt 	if (rc != OPAL_SUCCESS)
795c7c1e94SBenjamin Herrenschmidt 		pr_err("%s: opal_set_xive(0xff) irq=%u returned %lld\n",
805c7c1e94SBenjamin Herrenschmidt 		       __func__, hw_irq, rc);
815c7c1e94SBenjamin Herrenschmidt }
825c7c1e94SBenjamin Herrenschmidt 
ics_opal_mask_irq(struct irq_data * d)835c7c1e94SBenjamin Herrenschmidt static void ics_opal_mask_irq(struct irq_data *d)
845c7c1e94SBenjamin Herrenschmidt {
855c7c1e94SBenjamin Herrenschmidt 	unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d);
865c7c1e94SBenjamin Herrenschmidt 
875c7c1e94SBenjamin Herrenschmidt 	pr_devel("ics-hal: mask virq %d [hw 0x%x]\n", d->irq, hw_irq);
885c7c1e94SBenjamin Herrenschmidt 
895c7c1e94SBenjamin Herrenschmidt 	if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS)
905c7c1e94SBenjamin Herrenschmidt 		return;
915c7c1e94SBenjamin Herrenschmidt 	ics_opal_mask_real_irq(hw_irq);
925c7c1e94SBenjamin Herrenschmidt }
935c7c1e94SBenjamin Herrenschmidt 
ics_opal_set_affinity(struct irq_data * d,const struct cpumask * cpumask,bool force)945c7c1e94SBenjamin Herrenschmidt static int ics_opal_set_affinity(struct irq_data *d,
955c7c1e94SBenjamin Herrenschmidt 				 const struct cpumask *cpumask,
965c7c1e94SBenjamin Herrenschmidt 				 bool force)
975c7c1e94SBenjamin Herrenschmidt {
985c7c1e94SBenjamin Herrenschmidt 	unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d);
99bf8e0f89SBenjamin Herrenschmidt 	__be16 oserver;
1005c7c1e94SBenjamin Herrenschmidt 	int16_t server;
1015c7c1e94SBenjamin Herrenschmidt 	int8_t priority;
1025c7c1e94SBenjamin Herrenschmidt 	int64_t rc;
1035c7c1e94SBenjamin Herrenschmidt 	int wanted_server;
1045c7c1e94SBenjamin Herrenschmidt 
1055c7c1e94SBenjamin Herrenschmidt 	if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS)
1065c7c1e94SBenjamin Herrenschmidt 		return -1;
1075c7c1e94SBenjamin Herrenschmidt 
108bf8e0f89SBenjamin Herrenschmidt 	rc = opal_get_xive(hw_irq, &oserver, &priority);
1095c7c1e94SBenjamin Herrenschmidt 	if (rc != OPAL_SUCCESS) {
110bf8e0f89SBenjamin Herrenschmidt 		pr_err("%s: opal_get_xive(irq=%d [hw 0x%x]) error %lld\n",
111bf8e0f89SBenjamin Herrenschmidt 		       __func__, d->irq, hw_irq, rc);
1125c7c1e94SBenjamin Herrenschmidt 		return -1;
1135c7c1e94SBenjamin Herrenschmidt 	}
1145c7c1e94SBenjamin Herrenschmidt 
1155c7c1e94SBenjamin Herrenschmidt 	wanted_server = xics_get_irq_server(d->irq, cpumask, 1);
1165c7c1e94SBenjamin Herrenschmidt 	if (wanted_server < 0) {
117f2c2cbccSJoe Perches 		pr_warn("%s: No online cpus in the mask %*pb for irq %d\n",
1180c118b7bSTejun Heo 			__func__, cpumask_pr_args(cpumask), d->irq);
1195c7c1e94SBenjamin Herrenschmidt 		return -1;
1205c7c1e94SBenjamin Herrenschmidt 	}
1215c7c1e94SBenjamin Herrenschmidt 	server = ics_opal_mangle_server(wanted_server);
1225c7c1e94SBenjamin Herrenschmidt 
123*53b34e8dSCédric Le Goater 	pr_debug("ics-hal: set-affinity irq %d [hw 0x%x] server: 0x%x/0x%x\n",
1245c7c1e94SBenjamin Herrenschmidt 		 d->irq, hw_irq, wanted_server, server);
1255c7c1e94SBenjamin Herrenschmidt 
1265c7c1e94SBenjamin Herrenschmidt 	rc = opal_set_xive(hw_irq, server, priority);
1275c7c1e94SBenjamin Herrenschmidt 	if (rc != OPAL_SUCCESS) {
1285c7c1e94SBenjamin Herrenschmidt 		pr_err("%s: opal_set_xive(irq=%d [hw 0x%x] server=%x)"
1295c7c1e94SBenjamin Herrenschmidt 		       " error %lld\n",
1305c7c1e94SBenjamin Herrenschmidt 		       __func__, d->irq, hw_irq, server, rc);
1315c7c1e94SBenjamin Herrenschmidt 		return -1;
1325c7c1e94SBenjamin Herrenschmidt 	}
133dcb615aeSAlexander Gordeev 	return IRQ_SET_MASK_OK;
1345c7c1e94SBenjamin Herrenschmidt }
1355c7c1e94SBenjamin Herrenschmidt 
1365c7c1e94SBenjamin Herrenschmidt static struct irq_chip ics_opal_irq_chip = {
1375c7c1e94SBenjamin Herrenschmidt 	.name = "OPAL ICS",
1385c7c1e94SBenjamin Herrenschmidt 	.irq_startup = ics_opal_startup,
1395c7c1e94SBenjamin Herrenschmidt 	.irq_mask = ics_opal_mask_irq,
1405c7c1e94SBenjamin Herrenschmidt 	.irq_unmask = ics_opal_unmask_irq,
1415c7c1e94SBenjamin Herrenschmidt 	.irq_eoi = NULL, /* Patched at init time */
142880a3d6aSBenjamin Herrenschmidt 	.irq_set_affinity = ics_opal_set_affinity,
143880a3d6aSBenjamin Herrenschmidt 	.irq_set_type = xics_set_irq_type,
144880a3d6aSBenjamin Herrenschmidt 	.irq_retrigger = xics_retrigger,
1455c7c1e94SBenjamin Herrenschmidt };
1465c7c1e94SBenjamin Herrenschmidt 
ics_opal_host_match(struct ics * ics,struct device_node * node)1475c7c1e94SBenjamin Herrenschmidt static int ics_opal_host_match(struct ics *ics, struct device_node *node)
1485c7c1e94SBenjamin Herrenschmidt {
1495c7c1e94SBenjamin Herrenschmidt 	return 1;
1505c7c1e94SBenjamin Herrenschmidt }
1515c7c1e94SBenjamin Herrenschmidt 
ics_opal_check(struct ics * ics,unsigned int hw_irq)152248af248SCédric Le Goater static int ics_opal_check(struct ics *ics, unsigned int hw_irq)
1535c7c1e94SBenjamin Herrenschmidt {
1545c7c1e94SBenjamin Herrenschmidt 	int64_t rc;
155bf8e0f89SBenjamin Herrenschmidt 	__be16 server;
1565c7c1e94SBenjamin Herrenschmidt 	int8_t priority;
1575c7c1e94SBenjamin Herrenschmidt 
1585c7c1e94SBenjamin Herrenschmidt 	if (WARN_ON(hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS))
1595c7c1e94SBenjamin Herrenschmidt 		return -EINVAL;
1605c7c1e94SBenjamin Herrenschmidt 
1615c7c1e94SBenjamin Herrenschmidt 	/* Check if HAL knows about this interrupt */
1625c7c1e94SBenjamin Herrenschmidt 	rc = opal_get_xive(hw_irq, &server, &priority);
1635c7c1e94SBenjamin Herrenschmidt 	if (rc != OPAL_SUCCESS)
1645c7c1e94SBenjamin Herrenschmidt 		return -ENXIO;
1655c7c1e94SBenjamin Herrenschmidt 
1665c7c1e94SBenjamin Herrenschmidt 	return 0;
1675c7c1e94SBenjamin Herrenschmidt }
1685c7c1e94SBenjamin Herrenschmidt 
ics_opal_mask_unknown(struct ics * ics,unsigned long vec)1695c7c1e94SBenjamin Herrenschmidt static void ics_opal_mask_unknown(struct ics *ics, unsigned long vec)
1705c7c1e94SBenjamin Herrenschmidt {
1715c7c1e94SBenjamin Herrenschmidt 	int64_t rc;
172bf8e0f89SBenjamin Herrenschmidt 	__be16 server;
1735c7c1e94SBenjamin Herrenschmidt 	int8_t priority;
1745c7c1e94SBenjamin Herrenschmidt 
1755c7c1e94SBenjamin Herrenschmidt 	/* Check if HAL knows about this interrupt */
1765c7c1e94SBenjamin Herrenschmidt 	rc = opal_get_xive(vec, &server, &priority);
1775c7c1e94SBenjamin Herrenschmidt 	if (rc != OPAL_SUCCESS)
1785c7c1e94SBenjamin Herrenschmidt 		return;
1795c7c1e94SBenjamin Herrenschmidt 
1805c7c1e94SBenjamin Herrenschmidt 	ics_opal_mask_real_irq(vec);
1815c7c1e94SBenjamin Herrenschmidt }
1825c7c1e94SBenjamin Herrenschmidt 
ics_opal_get_server(struct ics * ics,unsigned long vec)1835c7c1e94SBenjamin Herrenschmidt static long ics_opal_get_server(struct ics *ics, unsigned long vec)
1845c7c1e94SBenjamin Herrenschmidt {
1855c7c1e94SBenjamin Herrenschmidt 	int64_t rc;
186bf8e0f89SBenjamin Herrenschmidt 	__be16 server;
1875c7c1e94SBenjamin Herrenschmidt 	int8_t priority;
1885c7c1e94SBenjamin Herrenschmidt 
1895c7c1e94SBenjamin Herrenschmidt 	/* Check if HAL knows about this interrupt */
1905c7c1e94SBenjamin Herrenschmidt 	rc = opal_get_xive(vec, &server, &priority);
1915c7c1e94SBenjamin Herrenschmidt 	if (rc != OPAL_SUCCESS)
1925c7c1e94SBenjamin Herrenschmidt 		return -1;
193bf8e0f89SBenjamin Herrenschmidt 	return ics_opal_unmangle_server(be16_to_cpu(server));
1945c7c1e94SBenjamin Herrenschmidt }
1955c7c1e94SBenjamin Herrenschmidt 
196248af248SCédric Le Goater /* Only one global & state struct ics */
197248af248SCédric Le Goater static struct ics ics_hal = {
198248af248SCédric Le Goater 	.check		= ics_opal_check,
199248af248SCédric Le Goater 	.mask_unknown	= ics_opal_mask_unknown,
200248af248SCédric Le Goater 	.get_server	= ics_opal_get_server,
201248af248SCédric Le Goater 	.host_match	= ics_opal_host_match,
202248af248SCédric Le Goater 	.chip		= &ics_opal_irq_chip,
203248af248SCédric Le Goater };
204248af248SCédric Le Goater 
ics_opal_init(void)2055c7c1e94SBenjamin Herrenschmidt int __init ics_opal_init(void)
2065c7c1e94SBenjamin Herrenschmidt {
2075c7c1e94SBenjamin Herrenschmidt 	if (!firmware_has_feature(FW_FEATURE_OPAL))
2085c7c1e94SBenjamin Herrenschmidt 		return -ENODEV;
2095c7c1e94SBenjamin Herrenschmidt 
2105c7c1e94SBenjamin Herrenschmidt 	/* We need to patch our irq chip's EOI to point to the
2115c7c1e94SBenjamin Herrenschmidt 	 * right ICP
2125c7c1e94SBenjamin Herrenschmidt 	 */
2135c7c1e94SBenjamin Herrenschmidt 	ics_opal_irq_chip.irq_eoi = icp_ops->eoi;
2145c7c1e94SBenjamin Herrenschmidt 
2155c7c1e94SBenjamin Herrenschmidt 	/* Register ourselves */
2165c7c1e94SBenjamin Herrenschmidt 	xics_register_ics(&ics_hal);
2175c7c1e94SBenjamin Herrenschmidt 
2185c7c1e94SBenjamin Herrenschmidt 	pr_info("ICS OPAL backend registered\n");
2195c7c1e94SBenjamin Herrenschmidt 
2205c7c1e94SBenjamin Herrenschmidt 	return 0;
2215c7c1e94SBenjamin Herrenschmidt }
222