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
intc_ofw_find(device_t dev,uint32_t hartid)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", ®, 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
intc_identify(driver_t * driver,device_t parent)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", DEVICE_UNIT_ANY) != 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
intc_probe(device_t dev)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
intc_attach(device_t dev)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, INTR_ROOT_IRQ));
185 }
186
187 static void
intc_disable_intr(device_t dev,struct intr_irqsrc * isrc)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
intc_enable_intr(device_t dev,struct intr_irqsrc * isrc)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
intc_map_intr(device_t dev,struct intr_map_data * data,struct intr_irqsrc ** isrcp)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
intc_setup_intr(device_t dev,struct intr_irqsrc * isrc,struct resource * res,struct intr_map_data * data)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
intc_init_secondary(device_t dev,uint32_t rootnum)244 intc_init_secondary(device_t dev, uint32_t rootnum)
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
intc_intr(void * arg)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