intc_fdt.c revision 1.3 1 1.3 skrll /* $NetBSD: intc_fdt.c,v 1.3 2023/12/25 13:21:30 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.3 skrll __KERNEL_RCSID(0, "$NetBSD: intc_fdt.c,v 1.3 2023/12/25 13:21:30 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 struct intc_softc;
61 1.1 skrll
62 1.1 skrll typedef int (*intcih_t)(void *);
63 1.1 skrll
64 1.1 skrll struct intc_irqhandler {
65 1.1 skrll struct intc_irq *ih_irq;
66 1.1 skrll intcih_t ih_fn;
67 1.1 skrll void *ih_arg;
68 1.1 skrll TAILQ_ENTRY(intc_irqhandler)
69 1.1 skrll ih_next;
70 1.1 skrll };
71 1.1 skrll
72 1.1 skrll struct intc_irq {
73 1.1 skrll struct intc_fdt_softc *intr_sc;
74 1.1 skrll void *intr_ih;
75 1.1 skrll void *intr_arg;
76 1.1 skrll int intr_refcnt;
77 1.1 skrll int intr_ipl;
78 1.1 skrll int intr_source;
79 1.1 skrll int intr_istflags;
80 1.1 skrll struct evcnt *pcpu_evs;
81 1.1 skrll TAILQ_HEAD(, intc_irqhandler)
82 1.1 skrll intr_handlers;
83 1.1 skrll };
84 1.1 skrll
85 1.1 skrll
86 1.1 skrll struct intc_fdt_softc {
87 1.1 skrll device_t sc_dev;
88 1.1 skrll bus_space_tag_t sc_bst;
89 1.1 skrll bus_space_handle_t sc_bsh;
90 1.1 skrll
91 1.1 skrll struct intc_irq *sc_irq[IRQ_NSOURCES];
92 1.1 skrll
93 1.3 skrll struct evcnt sc_evs[IRQ_NSOURCES];
94 1.1 skrll
95 1.1 skrll struct cpu_info *sc_ci;
96 1.1 skrll cpuid_t sc_hartid;
97 1.1 skrll };
98 1.1 skrll
99 1.3 skrll static const char * const intc_sources[IRQ_NSOURCES] = {
100 1.3 skrll /* Software interrupts */
101 1.3 skrll "(reserved)",
102 1.3 skrll "Supervisor software",
103 1.3 skrll "Virtual Supervisor software",
104 1.3 skrll "Machine software",
105 1.3 skrll
106 1.3 skrll /* Timer interrupts */
107 1.3 skrll "(reserved)",
108 1.3 skrll "Supervisor timer",
109 1.3 skrll "Virtual Supervisor timer",
110 1.3 skrll "Machine timer",
111 1.3 skrll
112 1.3 skrll /* External interrupts */
113 1.3 skrll "(reserved)",
114 1.3 skrll "Supervisor external",
115 1.3 skrll "Virtual Supervisor external",
116 1.3 skrll "Machine external",
117 1.3 skrll
118 1.3 skrll "(reserved)",
119 1.3 skrll "Supervisor guest external.",
120 1.3 skrll "(reserved)",
121 1.3 skrll "(reserved)"
122 1.3 skrll };
123 1.1 skrll
124 1.1 skrll
125 1.1 skrll static void *
126 1.1 skrll intc_intr_establish(struct intc_fdt_softc *sc, u_int source, u_int ipl,
127 1.1 skrll u_int istflags, int (*func)(void *), void *arg, const char *xname)
128 1.1 skrll {
129 1.1 skrll if (source > IRQ_NSOURCES)
130 1.1 skrll return NULL;
131 1.1 skrll
132 1.1 skrll const device_t dev = sc->sc_dev;
133 1.1 skrll struct intc_irq *irq = sc->sc_irq[source];
134 1.1 skrll if (irq == NULL) {
135 1.1 skrll irq = kmem_alloc(sizeof(*irq), KM_SLEEP);
136 1.1 skrll irq->intr_sc = sc;
137 1.1 skrll irq->intr_refcnt = 0;
138 1.1 skrll irq->intr_arg = arg;
139 1.1 skrll irq->intr_ipl = ipl;
140 1.1 skrll irq->intr_istflags = istflags;
141 1.1 skrll irq->intr_source = source;
142 1.1 skrll TAILQ_INIT(&irq->intr_handlers);
143 1.1 skrll sc->sc_irq[source] = irq;
144 1.3 skrll
145 1.3 skrll evcnt_attach_dynamic(&sc->sc_evs[source], EVCNT_TYPE_INTR, NULL,
146 1.3 skrll device_xname(sc->sc_dev), intc_sources[source]);
147 1.1 skrll } else {
148 1.1 skrll if (irq->intr_arg == NULL || arg == NULL) {
149 1.1 skrll device_printf(dev,
150 1.1 skrll "cannot share irq with NULL-arg handler\n");
151 1.1 skrll return NULL;
152 1.1 skrll }
153 1.1 skrll if (irq->intr_ipl != ipl) {
154 1.1 skrll device_printf(dev,
155 1.1 skrll "cannot share irq with different ipl\n");
156 1.1 skrll return NULL;
157 1.1 skrll }
158 1.1 skrll if (irq->intr_istflags != istflags) {
159 1.1 skrll device_printf(dev,
160 1.1 skrll "cannot share irq between mpsafe/non-mpsafe\n");
161 1.1 skrll return NULL;
162 1.1 skrll }
163 1.1 skrll }
164 1.1 skrll
165 1.1 skrll struct intc_irqhandler *irqh = kmem_alloc(sizeof(*irqh), KM_SLEEP);
166 1.1 skrll irqh->ih_irq = irq;
167 1.1 skrll irqh->ih_fn = func;
168 1.1 skrll irqh->ih_arg = arg;
169 1.1 skrll
170 1.1 skrll irq->intr_refcnt++;
171 1.1 skrll TAILQ_INSERT_TAIL(&irq->intr_handlers, irqh, ih_next);
172 1.1 skrll
173 1.1 skrll /*
174 1.1 skrll * XXX interrupt_distribute(9) assumes that any interrupt
175 1.1 skrll * handle can be used as an input to the MD interrupt_distribute
176 1.1 skrll * implementationm, so we are forced to return the handle
177 1.1 skrll * we got back from intr_establish(). Upshot is that the
178 1.1 skrll * input to bcm2835_icu_fdt_disestablish() is ambiguous for
179 1.1 skrll * shared IRQs, rendering them un-disestablishable.
180 1.1 skrll */
181 1.1 skrll
182 1.1 skrll return irqh;
183 1.1 skrll }
184 1.1 skrll
185 1.1 skrll
186 1.1 skrll static void *
187 1.1 skrll intc_fdt_establish(device_t dev, u_int *specifier, int ipl, int flags,
188 1.1 skrll int (*func)(void *), void *arg, const char *xname)
189 1.1 skrll {
190 1.1 skrll struct intc_fdt_softc * const sc = device_private(dev);
191 1.1 skrll
192 1.1 skrll /*
193 1.1 skrll * 1st (and only) cell is the interrupt source, e.g.
194 1.1 skrll * 1 IRQ_SUPERVISOR_SOFTWARE
195 1.1 skrll * 5 IRQ_SUPERVISOR_TIMER
196 1.1 skrll * 9 IRQ_SUPERVISOR_EXTERNAL
197 1.1 skrll */
198 1.1 skrll
199 1.1 skrll const u_int source = be32toh(specifier[0]);
200 1.1 skrll const u_int mpsafe = (flags & FDT_INTR_MPSAFE) ? IST_MPSAFE : 0;
201 1.1 skrll
202 1.1 skrll return intc_intr_establish(sc, source, ipl, mpsafe, func, arg, xname);
203 1.1 skrll }
204 1.1 skrll
205 1.1 skrll static void
206 1.1 skrll intc_fdt_disestablish(device_t dev, void *ih)
207 1.1 skrll {
208 1.1 skrll #if 0
209 1.1 skrll struct intc_fdt_softc * const sc = device_private(dev);
210 1.1 skrll #endif
211 1.1 skrll
212 1.1 skrll }
213 1.1 skrll
214 1.1 skrll static bool
215 1.1 skrll intc_fdt_intrstr(device_t dev, u_int *specifier, char *buf, size_t buflen)
216 1.1 skrll {
217 1.1 skrll if (!specifier)
218 1.1 skrll return false;
219 1.1 skrll
220 1.1 skrll struct intc_fdt_softc * const sc = device_private(dev);
221 1.1 skrll if (sc->sc_ci == NULL)
222 1.1 skrll return false;
223 1.1 skrll
224 1.1 skrll const u_int source = be32toh(specifier[0]);
225 1.1 skrll snprintf(buf, buflen, "cpu%lu/%s #%u", sc->sc_hartid,
226 1.1 skrll intc_sources[source], source);
227 1.1 skrll
228 1.1 skrll return true;
229 1.1 skrll }
230 1.1 skrll
231 1.1 skrll
232 1.1 skrll struct fdtbus_interrupt_controller_func intc_fdt_funcs = {
233 1.1 skrll .establish = intc_fdt_establish,
234 1.1 skrll .disestablish = intc_fdt_disestablish,
235 1.1 skrll .intrstr = intc_fdt_intrstr
236 1.1 skrll };
237 1.1 skrll
238 1.1 skrll
239 1.1 skrll static void
240 1.1 skrll intc_intr_handler(struct trapframe *tf, register_t epc, register_t status,
241 1.1 skrll register_t cause)
242 1.1 skrll {
243 1.2 skrll const int ppl = splhigh();
244 1.1 skrll struct cpu_info * const ci = curcpu();
245 1.2 skrll unsigned long pending;
246 1.2 skrll int ipl;
247 1.1 skrll
248 1.1 skrll KASSERT(CAUSE_INTERRUPT_P(cause));
249 1.1 skrll
250 1.3 skrll struct intc_fdt_softc * const sc = ci->ci_intcsoftc;
251 1.1 skrll
252 1.1 skrll ci->ci_intr_depth++;
253 1.2 skrll ci->ci_data.cpu_nintr++;
254 1.1 skrll
255 1.2 skrll while (ppl < (ipl = splintr(&pending))) {
256 1.2 skrll if (pending == 0)
257 1.2 skrll continue;
258 1.2 skrll
259 1.2 skrll splx(ipl);
260 1.2 skrll
261 1.2 skrll int source = ffs(pending) - 1;
262 1.2 skrll struct intc_irq *irq = sc->sc_irq[source];
263 1.3 skrll sc->sc_evs[source].ev_count++;
264 1.3 skrll
265 1.2 skrll KASSERTMSG(irq != NULL, "source %d\n", source);
266 1.2 skrll
267 1.2 skrll if (irq) {
268 1.2 skrll struct intc_irqhandler *iih;
269 1.2 skrll
270 1.2 skrll bool mpsafe =
271 1.2 skrll source != IRQ_SUPERVISOR_EXTERNAL ||
272 1.2 skrll (irq->intr_istflags & IST_MPSAFE) != 0;
273 1.2 skrll struct clockframe cf = {
274 1.2 skrll .cf_epc = epc,
275 1.2 skrll .cf_status = status,
276 1.2 skrll .cf_intr_depth = ci->ci_intr_depth
277 1.2 skrll };
278 1.2 skrll
279 1.2 skrll if (!mpsafe) {
280 1.2 skrll KERNEL_LOCK(1, NULL);
281 1.2 skrll }
282 1.2 skrll
283 1.2 skrll TAILQ_FOREACH(iih, &irq->intr_handlers, ih_next) {
284 1.2 skrll int handled =
285 1.2 skrll iih->ih_fn(iih->ih_arg ? iih->ih_arg : &cf);
286 1.2 skrll if (handled)
287 1.2 skrll break;
288 1.2 skrll }
289 1.2 skrll
290 1.2 skrll if (!mpsafe) {
291 1.2 skrll KERNEL_UNLOCK_ONE(NULL);
292 1.2 skrll }
293 1.1 skrll }
294 1.2 skrll splhigh();
295 1.1 skrll }
296 1.1 skrll ci->ci_intr_depth--;
297 1.2 skrll splx(ppl);
298 1.1 skrll }
299 1.1 skrll
300 1.1 skrll
301 1.1 skrll
302 1.1 skrll static int
303 1.1 skrll intc_match(device_t parent, cfdata_t cf, void *aux)
304 1.1 skrll {
305 1.1 skrll struct fdt_attach_args * const faa = aux;
306 1.1 skrll return of_compatible_match(faa->faa_phandle, compat_data);
307 1.1 skrll }
308 1.1 skrll
309 1.1 skrll static void
310 1.1 skrll intc_attach(device_t parent, device_t self, void *aux)
311 1.1 skrll {
312 1.1 skrll const struct fdt_attach_args * const faa = aux;
313 1.1 skrll const int phandle = faa->faa_phandle;
314 1.1 skrll
315 1.1 skrll int error = fdtbus_register_interrupt_controller(self, phandle,
316 1.1 skrll &intc_fdt_funcs);
317 1.1 skrll if (error) {
318 1.1 skrll aprint_error(": couldn't register with fdtbus: %d\n", error);
319 1.1 skrll return;
320 1.1 skrll }
321 1.1 skrll
322 1.1 skrll struct cpu_info * const ci = device_private(parent);
323 1.1 skrll if (ci == NULL) {
324 1.1 skrll aprint_naive(": disabled\n");
325 1.1 skrll aprint_normal(": disabled\n");
326 1.1 skrll return;
327 1.1 skrll }
328 1.1 skrll aprint_naive("\n");
329 1.1 skrll aprint_normal(": local interrupt controller\n");
330 1.1 skrll
331 1.1 skrll struct intc_fdt_softc * const sc = device_private(self);
332 1.1 skrll
333 1.1 skrll riscv_intr_set_handler(intc_intr_handler);
334 1.1 skrll
335 1.1 skrll sc->sc_dev = self;
336 1.1 skrll sc->sc_ci = ci;
337 1.1 skrll sc->sc_hartid = ci->ci_cpuid;
338 1.3 skrll ci->ci_intcsoftc = sc;
339 1.1 skrll
340 1.1 skrll intc_intr_establish(sc, IRQ_SUPERVISOR_TIMER, IPL_SCHED, IST_MPSAFE,
341 1.1 skrll riscv_timer_intr, NULL, "clock");
342 1.1 skrll #ifdef MULTIPROCESSOR
343 1.1 skrll intc_intr_establish(sc, IRQ_SUPERVISOR_SOFTWARE, IPL_HIGH, IST_MPSAFE,
344 1.1 skrll riscv_ipi_intr, NULL, "ipi");
345 1.1 skrll #endif
346 1.1 skrll }
347 1.1 skrll
348 1.1 skrll CFATTACH_DECL_NEW(intc_fdt, sizeof(struct intc_fdt_softc),
349 1.1 skrll intc_match, intc_attach, NULL, NULL);
350