xref: /freebsd/sys/riscv/riscv/intc.c (revision fd45b686)
1 /*-
2  * Copyright (c) 2015-2017 Ruslan Bukin <br@bsdpad.com>
3  * All rights reserved.
4  * Copyright (c) 2021 Jessica Clarke <jrtc27@FreeBSD.org>
5  *
6  * Portions of this software were developed by SRI International and the
7  * University of Cambridge Computer Laboratory under DARPA/AFRL contract
8  * FA8750-10-C-0237 ("CTSRD"), as part of the DARPA CRASH research programme.
9  *
10  * Portions of this software were developed by the University of Cambridge
11  * Computer Laboratory as part of the CTSRD Project, with support from the
12  * UK Higher Education Innovation Fund (HEIF).
13  *
14  * Redistribution and use in source and binary forms, with or without
15  * modification, are permitted provided that the following conditions
16  * are met:
17  * 1. Redistributions of source code must retain the above copyright
18  *    notice, this list of conditions and the following disclaimer.
19  * 2. Redistributions in binary form must reproduce the above copyright
20  *    notice, this list of conditions and the following disclaimer in the
21  *    documentation and/or other materials provided with the distribution.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include <sys/cdefs.h>
37 
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/bus.h>
41 #include <sys/kernel.h>
42 #include <sys/module.h>
43 #include <sys/proc.h>
44 #include <sys/cpuset.h>
45 #include <sys/interrupt.h>
46 #include <sys/smp.h>
47 
48 #include <machine/bus.h>
49 #include <machine/cpu.h>
50 #include <machine/cpufunc.h>
51 #include <machine/frame.h>
52 #include <machine/intr.h>
53 
54 #include <dev/fdt/simplebus.h>
55 
56 #include <dev/ofw/openfirm.h>
57 #include <dev/ofw/ofw_bus.h>
58 #include <dev/ofw/ofw_bus_subr.h>
59 
60 #include "pic_if.h"
61 
62 #define	INTC_NIRQS	16
63 
64 struct intc_irqsrc {
65 	struct intr_irqsrc	isrc;
66 	u_int			irq;
67 };
68 
69 struct intc_softc {
70 	device_t		dev;
71 	struct intc_irqsrc	isrcs[INTC_NIRQS];
72 };
73 
74 static int	intc_intr(void *arg);
75 
76 static phandle_t
77 intc_ofw_find(device_t dev, uint32_t hartid)
78 {
79 	phandle_t node;
80 	pcell_t reg;
81 
82 	node = OF_finddevice("/cpus");
83 	if (node == -1) {
84 		device_printf(dev, "Can't find cpus node\n");
85 		return ((phandle_t)-1);
86 	}
87 
88 	for (node = OF_child(node); node != 0; node = OF_peer(node)) {
89 		if (!ofw_bus_node_status_okay(node))
90 			continue;
91 
92 		if (!ofw_bus_node_is_compatible(node, "riscv"))
93 			continue;
94 
95 		if (OF_searchencprop(node, "reg", &reg, sizeof(reg)) == -1)
96 			continue;
97 
98 		if (reg == hartid)
99 			break;
100 	}
101 
102 	if (node == 0) {
103 		device_printf(dev, "Can't find boot cpu node\n");
104 		return ((phandle_t)-1);
105 	}
106 
107 	for (node = OF_child(node); node != 0; node = OF_peer(node)) {
108 		if (!ofw_bus_node_status_okay(node))
109 			continue;
110 
111 		if (ofw_bus_node_is_compatible(node, "riscv,cpu-intc"))
112 			break;
113 	}
114 
115 	if (node == 0) {
116 		device_printf(dev,
117 		    "Can't find boot cpu local interrupt controller\n");
118 		return ((phandle_t)-1);
119 	}
120 
121 	return (node);
122 }
123 
124 static void
125 intc_identify(driver_t *driver, device_t parent)
126 {
127 	device_t dev;
128 	phandle_t node;
129 
130 	if (device_find_child(parent, "intc", -1) != NULL)
131 		return;
132 
133 	node = intc_ofw_find(parent, PCPU_GET(hart));
134 	if (node == -1)
135 		return;
136 
137 	dev = simplebus_add_device(parent, node, 0, "intc", -1, NULL);
138 	if (dev == NULL)
139 		device_printf(parent, "Can't add intc child\n");
140 }
141 
142 static int
143 intc_probe(device_t dev)
144 {
145 	device_set_desc(dev, "RISC-V Local Interrupt Controller");
146 
147 	return (BUS_PROBE_NOWILDCARD);
148 }
149 
150 static int
151 intc_attach(device_t dev)
152 {
153 	struct intc_irqsrc *isrcs;
154 	struct intc_softc *sc;
155 	struct intr_pic *pic;
156 	const char *name;
157 	phandle_t xref;
158 	u_int flags;
159 	int i, error;
160 
161 	sc = device_get_softc(dev);
162 	sc->dev = dev;
163 
164 	name = device_get_nameunit(dev);
165 	xref = OF_xref_from_node(ofw_bus_get_node(dev));
166 
167 	isrcs = sc->isrcs;
168 	for (i = 0; i < INTC_NIRQS; i++) {
169 		isrcs[i].irq = i;
170 		flags = i == IRQ_SOFTWARE_SUPERVISOR
171 		    ? INTR_ISRCF_IPI : INTR_ISRCF_PPI;
172 		error = intr_isrc_register(&isrcs[i].isrc, sc->dev, flags,
173 		    "%s,%u", name, i);
174 		if (error != 0) {
175 			device_printf(dev, "Can't register interrupt %d\n", i);
176 			return (error);
177 		}
178 	}
179 
180 	pic = intr_pic_register(sc->dev, xref);
181 	if (pic == NULL)
182 		return (ENXIO);
183 
184 	return (intr_pic_claim_root(sc->dev, xref, intc_intr, sc));
185 }
186 
187 static void
188 intc_disable_intr(device_t dev, struct intr_irqsrc *isrc)
189 {
190 	u_int irq;
191 
192 	irq = ((struct intc_irqsrc *)isrc)->irq;
193 	if (irq >= INTC_NIRQS)
194 		panic("%s: Unsupported IRQ %u", __func__, irq);
195 
196 	csr_clear(sie, 1ul << irq);
197 }
198 
199 static void
200 intc_enable_intr(device_t dev, struct intr_irqsrc *isrc)
201 {
202 	u_int irq;
203 
204 	irq = ((struct intc_irqsrc *)isrc)->irq;
205 	if (irq >= INTC_NIRQS)
206 		panic("%s: Unsupported IRQ %u", __func__, irq);
207 
208 	csr_set(sie, 1ul << irq);
209 }
210 
211 static int
212 intc_map_intr(device_t dev, struct intr_map_data *data,
213     struct intr_irqsrc **isrcp)
214 {
215 	struct intr_map_data_fdt *daf;
216 	struct intc_softc *sc;
217 
218 	sc = device_get_softc(dev);
219 
220 	if (data->type != INTR_MAP_DATA_FDT)
221 		return (ENOTSUP);
222 
223 	daf = (struct intr_map_data_fdt *)data;
224 	if (daf->ncells != 1 || daf->cells[0] >= INTC_NIRQS)
225 		return (EINVAL);
226 
227 	*isrcp = &sc->isrcs[daf->cells[0]].isrc;
228 
229 	return (0);
230 }
231 
232 static int
233 intc_setup_intr(device_t dev, struct intr_irqsrc *isrc,
234     struct resource *res, struct intr_map_data *data)
235 {
236 	if (isrc->isrc_flags & INTR_ISRCF_PPI)
237 		CPU_SET(PCPU_GET(cpuid), &isrc->isrc_cpu);
238 
239 	return (0);
240 }
241 
242 #ifdef SMP
243 static void
244 intc_init_secondary(device_t dev)
245 {
246 	struct intc_softc *sc;
247 	struct intr_irqsrc *isrc;
248 	u_int cpu, irq;
249 
250 	sc = device_get_softc(dev);
251 	cpu = PCPU_GET(cpuid);
252 
253 	/* Unmask attached interrupts */
254 	for (irq = 0; irq < INTC_NIRQS; irq++) {
255 		isrc = &sc->isrcs[irq].isrc;
256 		if (intr_isrc_init_on_cpu(isrc, cpu))
257 			intc_enable_intr(dev, isrc);
258 	}
259 }
260 #endif
261 
262 static int
263 intc_intr(void *arg)
264 {
265 	struct trapframe *frame;
266 	struct intc_softc *sc;
267 	uint64_t active_irq;
268 	struct intc_irqsrc *src;
269 
270 	sc = arg;
271 	frame = curthread->td_intr_frame;
272 
273 	KASSERT((frame->tf_scause & SCAUSE_INTR) != 0,
274 	    ("%s: not an interrupt frame", __func__));
275 
276 	active_irq = frame->tf_scause & SCAUSE_CODE;
277 
278 	if (active_irq >= INTC_NIRQS)
279 		return (FILTER_HANDLED);
280 
281 	src = &sc->isrcs[active_irq];
282 	if (intr_isrc_dispatch(&src->isrc, frame) != 0) {
283 		intc_disable_intr(sc->dev, &src->isrc);
284 		device_printf(sc->dev, "Stray irq %lu disabled\n",
285 		    active_irq);
286 	}
287 
288 	return (FILTER_HANDLED);
289 }
290 
291 static device_method_t intc_methods[] = {
292 	/* Device interface */
293 	DEVMETHOD(device_identify,	intc_identify),
294 	DEVMETHOD(device_probe,		intc_probe),
295 	DEVMETHOD(device_attach,	intc_attach),
296 
297 	/* Interrupt controller interface */
298 	DEVMETHOD(pic_disable_intr,	intc_disable_intr),
299 	DEVMETHOD(pic_enable_intr,	intc_enable_intr),
300 	DEVMETHOD(pic_map_intr,		intc_map_intr),
301 	DEVMETHOD(pic_setup_intr,	intc_setup_intr),
302 #ifdef SMP
303 	DEVMETHOD(pic_init_secondary,	intc_init_secondary),
304 #endif
305 
306 	DEVMETHOD_END
307 };
308 
309 DEFINE_CLASS_0(intc, intc_driver, intc_methods, sizeof(struct intc_softc));
310 EARLY_DRIVER_MODULE(intc, ofwbus, intc_driver, 0, 0,
311     BUS_PASS_INTERRUPT + BUS_PASS_ORDER_FIRST);
312