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