intc_fdt.c revision 1.8 1 1.8 skrll /* $NetBSD: intc_fdt.c,v 1.8 2025/02/09 10:08:37 skrll Exp $ */
2 1.1 skrll
3 1.1 skrll /*-
4 1.1 skrll * Copyright (c) 2023 The NetBSD Foundation, Inc.
5 1.1 skrll * All rights reserved.
6 1.1 skrll *
7 1.1 skrll * This code is derived from software contributed to The NetBSD Foundation
8 1.1 skrll * by Nick Hudson
9 1.1 skrll *
10 1.1 skrll * Redistribution and use in source and binary forms, with or without
11 1.1 skrll * modification, are permitted provided that the following conditions
12 1.1 skrll * are met:
13 1.1 skrll * 1. Redistributions of source code must retain the above copyright
14 1.1 skrll * notice, this list of conditions and the following disclaimer.
15 1.1 skrll * 2. Redistributions in binary form must reproduce the above copyright
16 1.1 skrll * notice, this list of conditions and the following disclaimer in the
17 1.1 skrll * documentation and/or other materials provided with the distribution.
18 1.1 skrll *
19 1.1 skrll * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 1.1 skrll * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 1.1 skrll * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 1.1 skrll * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 1.1 skrll * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 1.1 skrll * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 1.1 skrll * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 1.1 skrll * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 1.1 skrll * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 1.1 skrll * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 1.1 skrll * POSSIBILITY OF SUCH DAMAGE.
30 1.1 skrll */
31 1.1 skrll
32 1.2 skrll #include "opt_multiprocessor.h"
33 1.2 skrll
34 1.1 skrll #include <sys/cdefs.h>
35 1.8 skrll __KERNEL_RCSID(0, "$NetBSD: intc_fdt.c,v 1.8 2025/02/09 10:08:37 skrll Exp $");
36 1.1 skrll
37 1.1 skrll #include <sys/param.h>
38 1.1 skrll
39 1.1 skrll #include <sys/bus.h>
40 1.1 skrll #include <sys/cpu.h>
41 1.1 skrll #include <sys/device.h>
42 1.1 skrll #include <sys/evcnt.h>
43 1.1 skrll #include <sys/kmem.h>
44 1.1 skrll #include <sys/intr.h>
45 1.1 skrll
46 1.1 skrll #include <dev/fdt/fdtvar.h>
47 1.1 skrll
48 1.1 skrll #include <machine/frame.h>
49 1.1 skrll #include <machine/machdep.h>
50 1.1 skrll #include <machine/sysreg.h>
51 1.1 skrll
52 1.1 skrll static const struct device_compatible_entry compat_data[] = {
53 1.1 skrll { .compat = "riscv,cpu-intc" },
54 1.1 skrll DEVICE_COMPAT_EOL
55 1.1 skrll };
56 1.1 skrll
57 1.1 skrll
58 1.1 skrll struct intc_irqhandler;
59 1.1 skrll struct intc_irq;
60 1.1 skrll
61 1.1 skrll typedef int (*intcih_t)(void *);
62 1.1 skrll
63 1.1 skrll struct intc_irqhandler {
64 1.1 skrll struct intc_irq *ih_irq;
65 1.1 skrll intcih_t ih_fn;
66 1.1 skrll void *ih_arg;
67 1.1 skrll TAILQ_ENTRY(intc_irqhandler)
68 1.1 skrll ih_next;
69 1.1 skrll };
70 1.1 skrll
71 1.1 skrll struct intc_irq {
72 1.1 skrll struct intc_fdt_softc *intr_sc;
73 1.1 skrll void *intr_ih;
74 1.1 skrll void *intr_arg;
75 1.1 skrll int intr_refcnt;
76 1.1 skrll int intr_ipl;
77 1.1 skrll int intr_source;
78 1.1 skrll int intr_istflags;
79 1.1 skrll struct evcnt *pcpu_evs;
80 1.1 skrll TAILQ_HEAD(, intc_irqhandler)
81 1.1 skrll intr_handlers;
82 1.1 skrll };
83 1.1 skrll
84 1.1 skrll
85 1.1 skrll struct intc_fdt_softc {
86 1.1 skrll device_t sc_dev;
87 1.1 skrll bus_space_tag_t sc_bst;
88 1.1 skrll bus_space_handle_t sc_bsh;
89 1.1 skrll
90 1.1 skrll struct intc_irq *sc_irq[IRQ_NSOURCES];
91 1.1 skrll
92 1.3 skrll struct evcnt sc_evs[IRQ_NSOURCES];
93 1.1 skrll
94 1.1 skrll struct cpu_info *sc_ci;
95 1.1 skrll cpuid_t sc_hartid;
96 1.1 skrll };
97 1.1 skrll
98 1.3 skrll static const char * const intc_sources[IRQ_NSOURCES] = {
99 1.3 skrll /* Software interrupts */
100 1.3 skrll "(reserved)",
101 1.3 skrll "Supervisor software",
102 1.3 skrll "Virtual Supervisor software",
103 1.3 skrll "Machine software",
104 1.3 skrll
105 1.3 skrll /* Timer interrupts */
106 1.3 skrll "(reserved)",
107 1.3 skrll "Supervisor timer",
108 1.3 skrll "Virtual Supervisor timer",
109 1.3 skrll "Machine timer",
110 1.3 skrll
111 1.3 skrll /* External interrupts */
112 1.3 skrll "(reserved)",
113 1.3 skrll "Supervisor external",
114 1.3 skrll "Virtual Supervisor external",
115 1.3 skrll "Machine external",
116 1.3 skrll
117 1.3 skrll "(reserved)",
118 1.3 skrll "Supervisor guest external.",
119 1.3 skrll "(reserved)",
120 1.3 skrll "(reserved)"
121 1.3 skrll };
122 1.1 skrll
123 1.6 skrll #ifndef MULTIPROCESSOR
124 1.6 skrll struct intc_fdt_softc *intc_sc;
125 1.6 skrll #endif
126 1.6 skrll
127 1.6 skrll
128 1.6 skrll static inline struct intc_fdt_softc *
129 1.6 skrll intc_getsc(struct cpu_info *ci)
130 1.6 skrll {
131 1.6 skrll #ifdef MULTIPROCESSOR
132 1.6 skrll return ci->ci_intcsoftc;
133 1.6 skrll #else
134 1.6 skrll return intc_sc;
135 1.6 skrll #endif
136 1.6 skrll }
137 1.1 skrll
138 1.1 skrll static void *
139 1.1 skrll intc_intr_establish(struct intc_fdt_softc *sc, u_int source, u_int ipl,
140 1.1 skrll u_int istflags, int (*func)(void *), void *arg, const char *xname)
141 1.1 skrll {
142 1.1 skrll if (source > IRQ_NSOURCES)
143 1.1 skrll return NULL;
144 1.1 skrll
145 1.1 skrll const device_t dev = sc->sc_dev;
146 1.1 skrll struct intc_irq *irq = sc->sc_irq[source];
147 1.1 skrll if (irq == NULL) {
148 1.1 skrll irq = kmem_alloc(sizeof(*irq), KM_SLEEP);
149 1.1 skrll irq->intr_sc = sc;
150 1.1 skrll irq->intr_refcnt = 0;
151 1.1 skrll irq->intr_arg = arg;
152 1.1 skrll irq->intr_ipl = ipl;
153 1.1 skrll irq->intr_istflags = istflags;
154 1.1 skrll irq->intr_source = source;
155 1.1 skrll TAILQ_INIT(&irq->intr_handlers);
156 1.1 skrll sc->sc_irq[source] = irq;
157 1.3 skrll
158 1.3 skrll evcnt_attach_dynamic(&sc->sc_evs[source], EVCNT_TYPE_INTR, NULL,
159 1.3 skrll device_xname(sc->sc_dev), intc_sources[source]);
160 1.1 skrll } else {
161 1.1 skrll if (irq->intr_arg == NULL || arg == NULL) {
162 1.1 skrll device_printf(dev,
163 1.1 skrll "cannot share irq with NULL-arg handler\n");
164 1.1 skrll return NULL;
165 1.1 skrll }
166 1.1 skrll if (irq->intr_ipl != ipl) {
167 1.1 skrll device_printf(dev,
168 1.1 skrll "cannot share irq with different ipl\n");
169 1.1 skrll return NULL;
170 1.1 skrll }
171 1.1 skrll if (irq->intr_istflags != istflags) {
172 1.1 skrll device_printf(dev,
173 1.1 skrll "cannot share irq between mpsafe/non-mpsafe\n");
174 1.1 skrll return NULL;
175 1.1 skrll }
176 1.1 skrll }
177 1.1 skrll
178 1.1 skrll struct intc_irqhandler *irqh = kmem_alloc(sizeof(*irqh), KM_SLEEP);
179 1.1 skrll irqh->ih_irq = irq;
180 1.1 skrll irqh->ih_fn = func;
181 1.1 skrll irqh->ih_arg = arg;
182 1.1 skrll
183 1.1 skrll irq->intr_refcnt++;
184 1.1 skrll TAILQ_INSERT_TAIL(&irq->intr_handlers, irqh, ih_next);
185 1.1 skrll
186 1.1 skrll /*
187 1.1 skrll * XXX interrupt_distribute(9) assumes that any interrupt
188 1.1 skrll * handle can be used as an input to the MD interrupt_distribute
189 1.7 skrll * implementation, so we are forced to return the handle
190 1.1 skrll * we got back from intr_establish(). Upshot is that the
191 1.1 skrll * input to bcm2835_icu_fdt_disestablish() is ambiguous for
192 1.1 skrll * shared IRQs, rendering them un-disestablishable.
193 1.1 skrll */
194 1.1 skrll
195 1.1 skrll return irqh;
196 1.1 skrll }
197 1.1 skrll
198 1.1 skrll
199 1.1 skrll static void *
200 1.1 skrll intc_fdt_establish(device_t dev, u_int *specifier, int ipl, int flags,
201 1.1 skrll int (*func)(void *), void *arg, const char *xname)
202 1.1 skrll {
203 1.1 skrll struct intc_fdt_softc * const sc = device_private(dev);
204 1.1 skrll
205 1.1 skrll /*
206 1.1 skrll * 1st (and only) cell is the interrupt source, e.g.
207 1.1 skrll * 1 IRQ_SUPERVISOR_SOFTWARE
208 1.1 skrll * 5 IRQ_SUPERVISOR_TIMER
209 1.1 skrll * 9 IRQ_SUPERVISOR_EXTERNAL
210 1.1 skrll */
211 1.1 skrll
212 1.1 skrll const u_int source = be32toh(specifier[0]);
213 1.1 skrll const u_int mpsafe = (flags & FDT_INTR_MPSAFE) ? IST_MPSAFE : 0;
214 1.1 skrll
215 1.1 skrll return intc_intr_establish(sc, source, ipl, mpsafe, func, arg, xname);
216 1.1 skrll }
217 1.1 skrll
218 1.1 skrll static void
219 1.1 skrll intc_fdt_disestablish(device_t dev, void *ih)
220 1.1 skrll {
221 1.1 skrll #if 0
222 1.1 skrll struct intc_fdt_softc * const sc = device_private(dev);
223 1.1 skrll #endif
224 1.1 skrll }
225 1.1 skrll
226 1.1 skrll static bool
227 1.1 skrll intc_fdt_intrstr(device_t dev, u_int *specifier, char *buf, size_t buflen)
228 1.1 skrll {
229 1.1 skrll if (!specifier)
230 1.1 skrll return false;
231 1.1 skrll
232 1.1 skrll struct intc_fdt_softc * const sc = device_private(dev);
233 1.1 skrll if (sc->sc_ci == NULL)
234 1.1 skrll return false;
235 1.1 skrll
236 1.1 skrll const u_int source = be32toh(specifier[0]);
237 1.8 skrll snprintf(buf, buflen, "cpu%u/%s #%u", cpu_index(sc->sc_ci),
238 1.1 skrll intc_sources[source], source);
239 1.1 skrll
240 1.1 skrll return true;
241 1.1 skrll }
242 1.1 skrll
243 1.1 skrll
244 1.1 skrll struct fdtbus_interrupt_controller_func intc_fdt_funcs = {
245 1.1 skrll .establish = intc_fdt_establish,
246 1.1 skrll .disestablish = intc_fdt_disestablish,
247 1.1 skrll .intrstr = intc_fdt_intrstr
248 1.1 skrll };
249 1.1 skrll
250 1.1 skrll
251 1.1 skrll static void
252 1.1 skrll intc_intr_handler(struct trapframe *tf, register_t epc, register_t status,
253 1.1 skrll register_t cause)
254 1.1 skrll {
255 1.2 skrll const int ppl = splhigh();
256 1.1 skrll struct cpu_info * const ci = curcpu();
257 1.2 skrll unsigned long pending;
258 1.2 skrll int ipl;
259 1.1 skrll
260 1.1 skrll KASSERT(CAUSE_INTERRUPT_P(cause));
261 1.1 skrll
262 1.6 skrll struct intc_fdt_softc * const sc = intc_getsc(ci);
263 1.1 skrll
264 1.1 skrll ci->ci_intr_depth++;
265 1.2 skrll ci->ci_data.cpu_nintr++;
266 1.1 skrll
267 1.2 skrll while (ppl < (ipl = splintr(&pending))) {
268 1.2 skrll if (pending == 0)
269 1.2 skrll continue;
270 1.2 skrll
271 1.2 skrll splx(ipl);
272 1.2 skrll
273 1.2 skrll int source = ffs(pending) - 1;
274 1.2 skrll struct intc_irq *irq = sc->sc_irq[source];
275 1.3 skrll sc->sc_evs[source].ev_count++;
276 1.3 skrll
277 1.2 skrll KASSERTMSG(irq != NULL, "source %d\n", source);
278 1.2 skrll
279 1.2 skrll if (irq) {
280 1.2 skrll struct intc_irqhandler *iih;
281 1.2 skrll
282 1.2 skrll bool mpsafe =
283 1.2 skrll source != IRQ_SUPERVISOR_EXTERNAL ||
284 1.2 skrll (irq->intr_istflags & IST_MPSAFE) != 0;
285 1.2 skrll struct clockframe cf = {
286 1.2 skrll .cf_epc = epc,
287 1.2 skrll .cf_status = status,
288 1.2 skrll .cf_intr_depth = ci->ci_intr_depth
289 1.2 skrll };
290 1.2 skrll
291 1.2 skrll if (!mpsafe) {
292 1.2 skrll KERNEL_LOCK(1, NULL);
293 1.2 skrll }
294 1.2 skrll
295 1.2 skrll TAILQ_FOREACH(iih, &irq->intr_handlers, ih_next) {
296 1.2 skrll int handled =
297 1.2 skrll iih->ih_fn(iih->ih_arg ? iih->ih_arg : &cf);
298 1.2 skrll if (handled)
299 1.2 skrll break;
300 1.2 skrll }
301 1.2 skrll
302 1.2 skrll if (!mpsafe) {
303 1.2 skrll KERNEL_UNLOCK_ONE(NULL);
304 1.2 skrll }
305 1.1 skrll }
306 1.2 skrll splhigh();
307 1.1 skrll }
308 1.1 skrll ci->ci_intr_depth--;
309 1.2 skrll splx(ppl);
310 1.1 skrll }
311 1.1 skrll
312 1.1 skrll
313 1.1 skrll
314 1.1 skrll static int
315 1.1 skrll intc_match(device_t parent, cfdata_t cf, void *aux)
316 1.1 skrll {
317 1.1 skrll struct fdt_attach_args * const faa = aux;
318 1.1 skrll return of_compatible_match(faa->faa_phandle, compat_data);
319 1.1 skrll }
320 1.1 skrll
321 1.1 skrll static void
322 1.1 skrll intc_attach(device_t parent, device_t self, void *aux)
323 1.1 skrll {
324 1.1 skrll const struct fdt_attach_args * const faa = aux;
325 1.1 skrll const int phandle = faa->faa_phandle;
326 1.1 skrll
327 1.1 skrll int error = fdtbus_register_interrupt_controller(self, phandle,
328 1.4 skrll &intc_fdt_funcs);
329 1.1 skrll if (error) {
330 1.1 skrll aprint_error(": couldn't register with fdtbus: %d\n", error);
331 1.1 skrll return;
332 1.1 skrll }
333 1.1 skrll
334 1.1 skrll struct cpu_info * const ci = device_private(parent);
335 1.1 skrll if (ci == NULL) {
336 1.1 skrll aprint_naive(": disabled\n");
337 1.1 skrll aprint_normal(": disabled\n");
338 1.1 skrll return;
339 1.1 skrll }
340 1.1 skrll aprint_naive("\n");
341 1.1 skrll aprint_normal(": local interrupt controller\n");
342 1.1 skrll
343 1.1 skrll struct intc_fdt_softc * const sc = device_private(self);
344 1.1 skrll
345 1.1 skrll riscv_intr_set_handler(intc_intr_handler);
346 1.1 skrll
347 1.1 skrll sc->sc_dev = self;
348 1.1 skrll sc->sc_ci = ci;
349 1.1 skrll sc->sc_hartid = ci->ci_cpuid;
350 1.1 skrll
351 1.1 skrll intc_intr_establish(sc, IRQ_SUPERVISOR_TIMER, IPL_SCHED, IST_MPSAFE,
352 1.1 skrll riscv_timer_intr, NULL, "clock");
353 1.1 skrll #ifdef MULTIPROCESSOR
354 1.6 skrll ci->ci_intcsoftc = sc;
355 1.1 skrll intc_intr_establish(sc, IRQ_SUPERVISOR_SOFTWARE, IPL_HIGH, IST_MPSAFE,
356 1.1 skrll riscv_ipi_intr, NULL, "ipi");
357 1.6 skrll #else
358 1.6 skrll intc_sc = sc;
359 1.1 skrll #endif
360 1.1 skrll }
361 1.1 skrll
362 1.1 skrll CFATTACH_DECL_NEW(intc_fdt, sizeof(struct intc_fdt_softc),
363 1.1 skrll intc_match, intc_attach, NULL, NULL);
364