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