xref: /linux/drivers/irqchip/irq-aclint-sswi.c (revision df0f030ee7e444c55341f4210124115878284125)
1*df0f030eSVladimir Kondratiev // SPDX-License-Identifier: GPL-2.0
2*df0f030eSVladimir Kondratiev /*
3*df0f030eSVladimir Kondratiev  * Copyright (C) 2024 Inochi Amaoto <inochiama@gmail.com>
4*df0f030eSVladimir Kondratiev  */
5*df0f030eSVladimir Kondratiev 
6*df0f030eSVladimir Kondratiev #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
7*df0f030eSVladimir Kondratiev 
8*df0f030eSVladimir Kondratiev #include <linux/cpu.h>
9*df0f030eSVladimir Kondratiev #include <linux/interrupt.h>
10*df0f030eSVladimir Kondratiev #include <linux/io.h>
11*df0f030eSVladimir Kondratiev #include <linux/irq.h>
12*df0f030eSVladimir Kondratiev #include <linux/irqchip.h>
13*df0f030eSVladimir Kondratiev #include <linux/irqchip/chained_irq.h>
14*df0f030eSVladimir Kondratiev #include <linux/module.h>
15*df0f030eSVladimir Kondratiev #include <linux/of.h>
16*df0f030eSVladimir Kondratiev #include <linux/of_address.h>
17*df0f030eSVladimir Kondratiev #include <linux/of_irq.h>
18*df0f030eSVladimir Kondratiev #include <linux/pci.h>
19*df0f030eSVladimir Kondratiev #include <linux/spinlock.h>
20*df0f030eSVladimir Kondratiev #include <linux/smp.h>
21*df0f030eSVladimir Kondratiev #include <linux/string_choices.h>
22*df0f030eSVladimir Kondratiev #include <asm/sbi.h>
23*df0f030eSVladimir Kondratiev #include <asm/vendorid_list.h>
24*df0f030eSVladimir Kondratiev 
25*df0f030eSVladimir Kondratiev static int sswi_ipi_virq __ro_after_init;
26*df0f030eSVladimir Kondratiev static DEFINE_PER_CPU(void __iomem *, sswi_cpu_regs);
27*df0f030eSVladimir Kondratiev 
28*df0f030eSVladimir Kondratiev static void aclint_sswi_ipi_send(unsigned int cpu)
29*df0f030eSVladimir Kondratiev {
30*df0f030eSVladimir Kondratiev 	writel(0x1, per_cpu(sswi_cpu_regs, cpu));
31*df0f030eSVladimir Kondratiev }
32*df0f030eSVladimir Kondratiev 
33*df0f030eSVladimir Kondratiev static void aclint_sswi_ipi_clear(void)
34*df0f030eSVladimir Kondratiev {
35*df0f030eSVladimir Kondratiev 	writel_relaxed(0x0, this_cpu_read(sswi_cpu_regs));
36*df0f030eSVladimir Kondratiev }
37*df0f030eSVladimir Kondratiev 
38*df0f030eSVladimir Kondratiev static void aclint_sswi_ipi_handle(struct irq_desc *desc)
39*df0f030eSVladimir Kondratiev {
40*df0f030eSVladimir Kondratiev 	struct irq_chip *chip = irq_desc_get_chip(desc);
41*df0f030eSVladimir Kondratiev 
42*df0f030eSVladimir Kondratiev 	chained_irq_enter(chip, desc);
43*df0f030eSVladimir Kondratiev 
44*df0f030eSVladimir Kondratiev 	csr_clear(CSR_IP, IE_SIE);
45*df0f030eSVladimir Kondratiev 	aclint_sswi_ipi_clear();
46*df0f030eSVladimir Kondratiev 
47*df0f030eSVladimir Kondratiev 	ipi_mux_process();
48*df0f030eSVladimir Kondratiev 
49*df0f030eSVladimir Kondratiev 	chained_irq_exit(chip, desc);
50*df0f030eSVladimir Kondratiev }
51*df0f030eSVladimir Kondratiev 
52*df0f030eSVladimir Kondratiev static int aclint_sswi_starting_cpu(unsigned int cpu)
53*df0f030eSVladimir Kondratiev {
54*df0f030eSVladimir Kondratiev 	enable_percpu_irq(sswi_ipi_virq, irq_get_trigger_type(sswi_ipi_virq));
55*df0f030eSVladimir Kondratiev 
56*df0f030eSVladimir Kondratiev 	return 0;
57*df0f030eSVladimir Kondratiev }
58*df0f030eSVladimir Kondratiev 
59*df0f030eSVladimir Kondratiev static int aclint_sswi_dying_cpu(unsigned int cpu)
60*df0f030eSVladimir Kondratiev {
61*df0f030eSVladimir Kondratiev 	aclint_sswi_ipi_clear();
62*df0f030eSVladimir Kondratiev 
63*df0f030eSVladimir Kondratiev 	disable_percpu_irq(sswi_ipi_virq);
64*df0f030eSVladimir Kondratiev 
65*df0f030eSVladimir Kondratiev 	return 0;
66*df0f030eSVladimir Kondratiev }
67*df0f030eSVladimir Kondratiev 
68*df0f030eSVladimir Kondratiev static int __init aclint_sswi_parse_irq(struct fwnode_handle *fwnode, void __iomem *reg)
69*df0f030eSVladimir Kondratiev {
70*df0f030eSVladimir Kondratiev 	struct of_phandle_args parent;
71*df0f030eSVladimir Kondratiev 	unsigned long hartid;
72*df0f030eSVladimir Kondratiev 	u32 contexts, i;
73*df0f030eSVladimir Kondratiev 	int rc, cpu;
74*df0f030eSVladimir Kondratiev 
75*df0f030eSVladimir Kondratiev 	contexts = of_irq_count(to_of_node(fwnode));
76*df0f030eSVladimir Kondratiev 	if (!(contexts)) {
77*df0f030eSVladimir Kondratiev 		pr_err("%pfwP: no ACLINT SSWI context available\n", fwnode);
78*df0f030eSVladimir Kondratiev 		return -EINVAL;
79*df0f030eSVladimir Kondratiev 	}
80*df0f030eSVladimir Kondratiev 
81*df0f030eSVladimir Kondratiev 	for (i = 0; i < contexts; i++) {
82*df0f030eSVladimir Kondratiev 		rc = of_irq_parse_one(to_of_node(fwnode), i, &parent);
83*df0f030eSVladimir Kondratiev 		if (rc)
84*df0f030eSVladimir Kondratiev 			return rc;
85*df0f030eSVladimir Kondratiev 
86*df0f030eSVladimir Kondratiev 		rc = riscv_of_parent_hartid(parent.np, &hartid);
87*df0f030eSVladimir Kondratiev 		if (rc)
88*df0f030eSVladimir Kondratiev 			return rc;
89*df0f030eSVladimir Kondratiev 
90*df0f030eSVladimir Kondratiev 		if (parent.args[0] != RV_IRQ_SOFT)
91*df0f030eSVladimir Kondratiev 			return -ENOTSUPP;
92*df0f030eSVladimir Kondratiev 
93*df0f030eSVladimir Kondratiev 		cpu = riscv_hartid_to_cpuid(hartid);
94*df0f030eSVladimir Kondratiev 
95*df0f030eSVladimir Kondratiev 		per_cpu(sswi_cpu_regs, cpu) = reg + hart_index * 4;
96*df0f030eSVladimir Kondratiev 	}
97*df0f030eSVladimir Kondratiev 
98*df0f030eSVladimir Kondratiev 	pr_info("%pfwP: register %u CPU%s\n", fwnode, contexts, str_plural(contexts));
99*df0f030eSVladimir Kondratiev 
100*df0f030eSVladimir Kondratiev 	return 0;
101*df0f030eSVladimir Kondratiev }
102*df0f030eSVladimir Kondratiev 
103*df0f030eSVladimir Kondratiev static int __init aclint_sswi_probe(struct fwnode_handle *fwnode)
104*df0f030eSVladimir Kondratiev {
105*df0f030eSVladimir Kondratiev 	struct irq_domain *domain;
106*df0f030eSVladimir Kondratiev 	void __iomem *reg;
107*df0f030eSVladimir Kondratiev 	int virq, rc;
108*df0f030eSVladimir Kondratiev 
109*df0f030eSVladimir Kondratiev 	if (!is_of_node(fwnode))
110*df0f030eSVladimir Kondratiev 		return -EINVAL;
111*df0f030eSVladimir Kondratiev 
112*df0f030eSVladimir Kondratiev 	reg = of_iomap(to_of_node(fwnode), 0);
113*df0f030eSVladimir Kondratiev 	if (!reg)
114*df0f030eSVladimir Kondratiev 		return -ENOMEM;
115*df0f030eSVladimir Kondratiev 
116*df0f030eSVladimir Kondratiev 	/* Parse SSWI setting */
117*df0f030eSVladimir Kondratiev 	rc = aclint_sswi_parse_irq(fwnode, reg);
118*df0f030eSVladimir Kondratiev 	if (rc < 0)
119*df0f030eSVladimir Kondratiev 		return rc;
120*df0f030eSVladimir Kondratiev 
121*df0f030eSVladimir Kondratiev 	/* If mulitple SSWI devices are present, do not register irq again */
122*df0f030eSVladimir Kondratiev 	if (sswi_ipi_virq)
123*df0f030eSVladimir Kondratiev 		return 0;
124*df0f030eSVladimir Kondratiev 
125*df0f030eSVladimir Kondratiev 	/* Find riscv intc domain and create IPI irq mapping */
126*df0f030eSVladimir Kondratiev 	domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(), DOMAIN_BUS_ANY);
127*df0f030eSVladimir Kondratiev 	if (!domain) {
128*df0f030eSVladimir Kondratiev 		pr_err("%pfwP: Failed to find INTC domain\n", fwnode);
129*df0f030eSVladimir Kondratiev 		return -ENOENT;
130*df0f030eSVladimir Kondratiev 	}
131*df0f030eSVladimir Kondratiev 
132*df0f030eSVladimir Kondratiev 	sswi_ipi_virq = irq_create_mapping(domain, RV_IRQ_SOFT);
133*df0f030eSVladimir Kondratiev 	if (!sswi_ipi_virq) {
134*df0f030eSVladimir Kondratiev 		pr_err("unable to create ACLINT SSWI IRQ mapping\n");
135*df0f030eSVladimir Kondratiev 		return -ENOMEM;
136*df0f030eSVladimir Kondratiev 	}
137*df0f030eSVladimir Kondratiev 
138*df0f030eSVladimir Kondratiev 	/* Register SSWI irq and handler */
139*df0f030eSVladimir Kondratiev 	virq = ipi_mux_create(BITS_PER_BYTE, aclint_sswi_ipi_send);
140*df0f030eSVladimir Kondratiev 	if (virq <= 0) {
141*df0f030eSVladimir Kondratiev 		pr_err("unable to create muxed IPIs\n");
142*df0f030eSVladimir Kondratiev 		irq_dispose_mapping(sswi_ipi_virq);
143*df0f030eSVladimir Kondratiev 		return virq < 0 ? virq : -ENOMEM;
144*df0f030eSVladimir Kondratiev 	}
145*df0f030eSVladimir Kondratiev 
146*df0f030eSVladimir Kondratiev 	irq_set_chained_handler(sswi_ipi_virq, aclint_sswi_ipi_handle);
147*df0f030eSVladimir Kondratiev 
148*df0f030eSVladimir Kondratiev 	cpuhp_setup_state(CPUHP_AP_IRQ_ACLINT_SSWI_STARTING,
149*df0f030eSVladimir Kondratiev 			  "irqchip/aclint-sswi:starting",
150*df0f030eSVladimir Kondratiev 			  aclint_sswi_starting_cpu,
151*df0f030eSVladimir Kondratiev 			  aclint_sswi_dying_cpu);
152*df0f030eSVladimir Kondratiev 
153*df0f030eSVladimir Kondratiev 	riscv_ipi_set_virq_range(virq, BITS_PER_BYTE);
154*df0f030eSVladimir Kondratiev 
155*df0f030eSVladimir Kondratiev 	return 0;
156*df0f030eSVladimir Kondratiev }
157*df0f030eSVladimir Kondratiev 
158*df0f030eSVladimir Kondratiev /* generic/MIPS variant */
159*df0f030eSVladimir Kondratiev static int __init generic_aclint_sswi_probe(struct fwnode_handle *fwnode)
160*df0f030eSVladimir Kondratiev {
161*df0f030eSVladimir Kondratiev 	int rc;
162*df0f030eSVladimir Kondratiev 
163*df0f030eSVladimir Kondratiev 	rc = aclint_sswi_probe(fwnode);
164*df0f030eSVladimir Kondratiev 	if (rc)
165*df0f030eSVladimir Kondratiev 		return rc;
166*df0f030eSVladimir Kondratiev 
167*df0f030eSVladimir Kondratiev 	/* Announce that SSWI is providing IPIs */
168*df0f030eSVladimir Kondratiev 	pr_info("providing IPIs using ACLINT SSWI\n");
169*df0f030eSVladimir Kondratiev 
170*df0f030eSVladimir Kondratiev 	return 0;
171*df0f030eSVladimir Kondratiev }
172*df0f030eSVladimir Kondratiev 
173*df0f030eSVladimir Kondratiev static int __init generic_aclint_sswi_early_probe(struct device_node *node,
174*df0f030eSVladimir Kondratiev 						  struct device_node *parent)
175*df0f030eSVladimir Kondratiev {
176*df0f030eSVladimir Kondratiev 	return generic_aclint_sswi_probe(&node->fwnode);
177*df0f030eSVladimir Kondratiev }
178*df0f030eSVladimir Kondratiev IRQCHIP_DECLARE(generic_aclint_sswi, "mips,p8700-aclint-sswi", generic_aclint_sswi_early_probe);
179*df0f030eSVladimir Kondratiev 
180*df0f030eSVladimir Kondratiev /* THEAD variant */
181*df0f030eSVladimir Kondratiev #define THEAD_C9XX_CSR_SXSTATUS			0x5c0
182*df0f030eSVladimir Kondratiev #define THEAD_C9XX_SXSTATUS_CLINTEE		BIT(17)
183*df0f030eSVladimir Kondratiev 
184*df0f030eSVladimir Kondratiev static int __init thead_aclint_sswi_probe(struct fwnode_handle *fwnode)
185*df0f030eSVladimir Kondratiev {
186*df0f030eSVladimir Kondratiev 	int rc;
187*df0f030eSVladimir Kondratiev 
188*df0f030eSVladimir Kondratiev 	/* If it is T-HEAD CPU, check whether SSWI is enabled */
189*df0f030eSVladimir Kondratiev 	if (riscv_cached_mvendorid(0) == THEAD_VENDOR_ID &&
190*df0f030eSVladimir Kondratiev 	    !(csr_read(THEAD_C9XX_CSR_SXSTATUS) & THEAD_C9XX_SXSTATUS_CLINTEE))
191*df0f030eSVladimir Kondratiev 		return -ENOTSUPP;
192*df0f030eSVladimir Kondratiev 
193*df0f030eSVladimir Kondratiev 	rc = aclint_sswi_probe(fwnode);
194*df0f030eSVladimir Kondratiev 	if (rc)
195*df0f030eSVladimir Kondratiev 		return rc;
196*df0f030eSVladimir Kondratiev 
197*df0f030eSVladimir Kondratiev 	/* Announce that SSWI is providing IPIs */
198*df0f030eSVladimir Kondratiev 	pr_info("providing IPIs using THEAD ACLINT SSWI\n");
199*df0f030eSVladimir Kondratiev 
200*df0f030eSVladimir Kondratiev 	return 0;
201*df0f030eSVladimir Kondratiev }
202*df0f030eSVladimir Kondratiev 
203*df0f030eSVladimir Kondratiev static int __init thead_aclint_sswi_early_probe(struct device_node *node,
204*df0f030eSVladimir Kondratiev 						struct device_node *parent)
205*df0f030eSVladimir Kondratiev {
206*df0f030eSVladimir Kondratiev 	return thead_aclint_sswi_probe(&node->fwnode);
207*df0f030eSVladimir Kondratiev }
208*df0f030eSVladimir Kondratiev IRQCHIP_DECLARE(thead_aclint_sswi, "thead,c900-aclint-sswi", thead_aclint_sswi_early_probe);
209