xref: /netbsd-src/sys/arch/arm/fdt/gicv3_fdt.c (revision 8ecbf5f02b752fcb7debe1a8fab1dc82602bc760)
1 /* $NetBSD: gicv3_fdt.c,v 1.8 2019/07/19 12:14:15 hkenken Exp $ */
2 
3 /*-
4  * Copyright (c) 2015-2018 Jared McNeill <jmcneill@invisible.ca>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include "pci.h"
30 
31 #define	_INTR_PRIVATE
32 
33 #include <sys/cdefs.h>
34 __KERNEL_RCSID(0, "$NetBSD: gicv3_fdt.c,v 1.8 2019/07/19 12:14:15 hkenken Exp $");
35 
36 #include <sys/param.h>
37 #include <sys/bus.h>
38 #include <sys/device.h>
39 #include <sys/intr.h>
40 #include <sys/systm.h>
41 #include <sys/kernel.h>
42 #include <sys/lwp.h>
43 #include <sys/kmem.h>
44 #include <sys/queue.h>
45 
46 #include <dev/fdt/fdtvar.h>
47 
48 #include <arm/cortex/gicv3.h>
49 #include <arm/cortex/gicv3_its.h>
50 #include <arm/cortex/gic_reg.h>
51 
52 #define	GICV3_MAXIRQ	1020
53 
54 #define	IRQ_PPI(n)	((n) + 16)
55 #define	IRQ_SPI(n)	((n) + 32)
56 
57 struct gicv3_fdt_softc;
58 struct gicv3_fdt_irq;
59 
60 static int	gicv3_fdt_match(device_t, cfdata_t, void *);
61 static void	gicv3_fdt_attach(device_t, device_t, void *);
62 
63 static int	gicv3_fdt_map_registers(struct gicv3_fdt_softc *);
64 #if NPCI > 0 && defined(__HAVE_PCI_MSI_MSIX)
65 static void	gicv3_fdt_attach_its(struct gicv3_fdt_softc *, bus_space_tag_t, int);
66 #endif
67 
68 static int	gicv3_fdt_intr(void *);
69 
70 static void *	gicv3_fdt_establish(device_t, u_int *, int, int,
71 		    int (*)(void *), void *);
72 static void	gicv3_fdt_disestablish(device_t, void *);
73 static bool	gicv3_fdt_intrstr(device_t, u_int *, char *, size_t);
74 
75 struct fdtbus_interrupt_controller_func gicv3_fdt_funcs = {
76 	.establish = gicv3_fdt_establish,
77 	.disestablish = gicv3_fdt_disestablish,
78 	.intrstr = gicv3_fdt_intrstr
79 };
80 
81 struct gicv3_fdt_irqhandler {
82 	struct gicv3_fdt_irq	*ih_irq;
83 	int			(*ih_fn)(void *);
84 	void			*ih_arg;
85 	bool			ih_mpsafe;
86 	TAILQ_ENTRY(gicv3_fdt_irqhandler) ih_next;
87 };
88 
89 struct gicv3_fdt_irq {
90 	struct gicv3_fdt_softc	*intr_sc;
91 	void			*intr_ih;
92 	void			*intr_arg;
93 	int			intr_refcnt;
94 	int			intr_ipl;
95 	int			intr_level;
96 	int			intr_mpsafe;
97 	TAILQ_HEAD(, gicv3_fdt_irqhandler) intr_handlers;
98 	int			intr_irq;
99 };
100 
101 struct gicv3_fdt_softc {
102 	struct gicv3_softc	sc_gic;
103 	int			sc_phandle;
104 
105 	struct gicv3_fdt_irq	*sc_irq[GICV3_MAXIRQ];
106 };
107 
108 CFATTACH_DECL_NEW(gicv3_fdt, sizeof(struct gicv3_fdt_softc),
109 	gicv3_fdt_match, gicv3_fdt_attach, NULL, NULL);
110 
111 static int
112 gicv3_fdt_match(device_t parent, cfdata_t cf, void *aux)
113 {
114 	const char * const compatible[] = {
115 		"arm,gic-v3",
116 		NULL
117 	};
118 	struct fdt_attach_args * const faa = aux;
119 	const int phandle = faa->faa_phandle;
120 
121 	return of_match_compatible(phandle, compatible);
122 }
123 
124 static void
125 gicv3_fdt_attach(device_t parent, device_t self, void *aux)
126 {
127 	struct gicv3_fdt_softc * const sc = device_private(self);
128 	struct fdt_attach_args * const faa = aux;
129 	const int phandle = faa->faa_phandle;
130 	int error;
131 
132 	error = fdtbus_register_interrupt_controller(self, phandle,
133 	    &gicv3_fdt_funcs);
134 	if (error) {
135 		aprint_error(": couldn't register with fdtbus: %d\n", error);
136 		return;
137 	}
138 
139 	aprint_naive("\n");
140 	aprint_normal(": GICv3\n");
141 
142 	sc->sc_phandle = phandle;
143 	sc->sc_gic.sc_dev = self;
144 	sc->sc_gic.sc_bst = faa->faa_bst;
145 	sc->sc_gic.sc_dmat = faa->faa_dmat;
146 
147 	error = gicv3_fdt_map_registers(sc);
148 	if (error) {
149 		aprint_error_dev(self, "couldn't map registers\n");
150 		return;
151 	}
152 
153 	aprint_debug_dev(self, "%d redistributors\n", sc->sc_gic.sc_bsh_r_count);
154 
155 	error = gicv3_init(&sc->sc_gic);
156 	if (error) {
157 		aprint_error_dev(self, "failed to initialize GIC: %d\n", error);
158 		return;
159 	}
160 
161 #if NPCI > 0 && defined(__HAVE_PCI_MSI_MSIX)
162 	for (int child = OF_child(phandle); child; child = OF_peer(child)) {
163 		if (!fdtbus_status_okay(child))
164 			continue;
165 		const char * const its_compat[] = { "arm,gic-v3-its", NULL };
166 		if (of_match_compatible(child, its_compat))
167 			gicv3_fdt_attach_its(sc, faa->faa_bst, child);
168 	}
169 #endif
170 
171 	arm_fdt_irq_set_handler(gicv3_irq_handler);
172 }
173 
174 static int
175 gicv3_fdt_map_registers(struct gicv3_fdt_softc *sc)
176 {
177 	struct gicv3_softc *gic = &sc->sc_gic;
178 	const int phandle = sc->sc_phandle;
179 	u_int redistributor_regions, redistributor_stride;
180 	bus_space_handle_t bsh;
181 	bus_size_t size, region_off;
182 	bus_addr_t addr;
183 	size_t reg_off;
184 	int n, r, max_redist, redist;
185 
186 	if (of_getprop_uint32(phandle, "#redistributor-regions", &redistributor_regions))
187 		redistributor_regions = 1;
188 	if (of_getprop_uint32(phandle, "redistributor-stride", &redistributor_stride))
189 		redistributor_stride = 0x20000;
190 
191 	/*
192 	 * Map GIC Distributor interface (GICD)
193 	 */
194 	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
195 		aprint_error_dev(gic->sc_dev, "couldn't get distributor registers\n");
196 		return ENXIO;
197 	}
198 	if (bus_space_map(sc->sc_gic.sc_bst, addr, size, 0, &sc->sc_gic.sc_bsh_d) != 0) {
199 		aprint_error_dev(gic->sc_dev, "couldn't map distributor registers\n");
200 		return ENXIO;
201 	}
202 
203 	/*
204 	 * GIC Redistributors (GICR)
205 	 */
206 	for (reg_off = 1, max_redist = 0, n = 0; n < redistributor_regions; n++, reg_off++) {
207 		if (fdtbus_get_reg(phandle, reg_off, NULL, &size) != 0) {
208 			aprint_error_dev(gic->sc_dev, "couldn't get redistributor registers\n");
209 			return ENXIO;
210 		}
211 		max_redist += howmany(size, redistributor_stride);
212 	}
213 	gic->sc_bsh_r = kmem_alloc(sizeof(bus_space_handle_t) * max_redist, KM_SLEEP);
214 	for (reg_off = 1, redist = 0, n = 0; n < redistributor_regions; n++, reg_off++) {
215 		if (fdtbus_get_reg(phandle, reg_off, &addr, &size) != 0) {
216 			aprint_error_dev(gic->sc_dev, "couldn't get redistributor registers\n");
217 			return ENXIO;
218 		}
219 		if (bus_space_map(sc->sc_gic.sc_bst, addr, size, 0, &bsh) != 0) {
220 			aprint_error_dev(gic->sc_dev, "couldn't map redistributor registers\n");
221 			return ENXIO;
222 		}
223 		const int count = howmany(size, redistributor_stride);
224 		for (r = 0, region_off = 0; r < count; r++, region_off += redistributor_stride) {
225 			if (bus_space_subregion(sc->sc_gic.sc_bst, bsh, region_off, redistributor_stride, &gic->sc_bsh_r[redist++]) != 0) {
226 				aprint_error_dev(gic->sc_dev, "couldn't subregion redistributor registers\n");
227 				return ENXIO;
228 			}
229 
230 			/* If this is the last redist in this region, skip to the next one */
231 			const uint32_t typer = bus_space_read_4(sc->sc_gic.sc_bst, gic->sc_bsh_r[redist - 1], GICR_TYPER);
232 			if (typer & GICR_TYPER_Last)
233 				break;
234 		}
235 	}
236 	gic->sc_bsh_r_count = redist;
237 
238 	return 0;
239 }
240 
241 #if NPCI > 0 && defined(__HAVE_PCI_MSI_MSIX)
242 static void
243 gicv3_fdt_attach_its(struct gicv3_fdt_softc *sc, bus_space_tag_t bst, int phandle)
244 {
245 	bus_space_handle_t bsh;
246 	bus_addr_t addr;
247 	bus_size_t size;
248 
249 	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
250 		aprint_error_dev(sc->sc_gic.sc_dev, "couldn't get ITS address\n");
251 		return;
252 	}
253 
254 	if (bus_space_map(bst, addr, size, 0, &bsh) != 0) {
255 		aprint_error_dev(sc->sc_gic.sc_dev, "couldn't map ITS\n");
256 		return;
257 	}
258 
259 	gicv3_its_init(&sc->sc_gic, bsh, addr, 0);
260 
261 	aprint_verbose_dev(sc->sc_gic.sc_dev, "ITS @ %#" PRIxBUSADDR "\n",
262 	    addr);
263 }
264 #endif
265 
266 static void *
267 gicv3_fdt_establish(device_t dev, u_int *specifier, int ipl, int flags,
268     int (*func)(void *), void *arg)
269 {
270 	struct gicv3_fdt_softc * const sc = device_private(dev);
271 	struct gicv3_fdt_irq *firq;
272 	struct gicv3_fdt_irqhandler *firqh;
273 
274 	/* 1st cell is the interrupt type; 0 is SPI, 1 is PPI */
275 	/* 2nd cell is the interrupt number */
276 	/* 3rd cell is flags */
277 	/* 4th cell is affinity */
278 
279 	const u_int type = be32toh(specifier[0]);
280 	const u_int intr = be32toh(specifier[1]);
281 	const u_int irq = type == 0 ? IRQ_SPI(intr) : IRQ_PPI(intr);
282 	const u_int trig = be32toh(specifier[2]) & 0xf;
283 	const u_int level = (trig & FDT_INTR_TYPE_DOUBLE_EDGE)
284 	    ? IST_EDGE : IST_LEVEL;
285 
286 	const u_int mpsafe = (flags & FDT_INTR_MPSAFE) ? IST_MPSAFE : 0;
287 
288 	firq = sc->sc_irq[irq];
289 	if (firq == NULL) {
290 		firq = kmem_alloc(sizeof(*firq), KM_SLEEP);
291 		firq->intr_sc = sc;
292 		firq->intr_refcnt = 0;
293 		firq->intr_arg = arg;
294 		firq->intr_ipl = ipl;
295 		firq->intr_level = level;
296 		firq->intr_mpsafe = mpsafe;
297 		TAILQ_INIT(&firq->intr_handlers);
298 		firq->intr_irq = irq;
299 		if (arg == NULL) {
300 			firq->intr_ih = intr_establish(irq, ipl, level | mpsafe,
301 			    func, NULL);
302 		} else {
303 			firq->intr_ih = intr_establish(irq, ipl, level | mpsafe,
304 			    gicv3_fdt_intr, firq);
305 		}
306 		if (firq->intr_ih == NULL) {
307 			kmem_free(firq, sizeof(*firq));
308 			return NULL;
309 		}
310 		sc->sc_irq[irq] = firq;
311 	} else {
312 		if (firq->intr_arg == NULL && arg != NULL) {
313 			device_printf(dev, "cannot share irq with NULL arg\n");
314 			return NULL;
315 		}
316 		if (firq->intr_ipl != ipl) {
317 			device_printf(dev, "cannot share irq with different "
318 			    "ipl\n");
319 			return NULL;
320 		}
321 		if (firq->intr_level != level) {
322 			device_printf(dev, "cannot share edge and level "
323 			    "interrupts\n");
324 			return NULL;
325 		}
326 		if (firq->intr_mpsafe != mpsafe) {
327 			device_printf(dev, "cannot share between "
328 			    "mpsafe/non-mpsafe\n");
329 			return NULL;
330 		}
331 	}
332 
333 	firq->intr_refcnt++;
334 
335 	firqh = kmem_alloc(sizeof(*firqh), KM_SLEEP);
336 	firqh->ih_mpsafe = (flags & FDT_INTR_MPSAFE) != 0;
337 	firqh->ih_irq = firq;
338 	firqh->ih_fn = func;
339 	firqh->ih_arg = arg;
340 	TAILQ_INSERT_TAIL(&firq->intr_handlers, firqh, ih_next);
341 
342 	return firq->intr_ih;
343 }
344 
345 static void
346 gicv3_fdt_disestablish(device_t dev, void *ih)
347 {
348 	struct gicv3_fdt_softc * const sc = device_private(dev);
349 	struct gicv3_fdt_irqhandler *firqh;
350 	struct gicv3_fdt_irq *firq;
351 	u_int n;
352 
353 	for (n = 0; n < GICV3_MAXIRQ; n++) {
354 		firq = sc->sc_irq[n];
355 		if (firq == NULL || firq->intr_ih != ih)
356 			continue;
357 
358 		KASSERT(firq->intr_refcnt > 0);
359 
360 		if (firq->intr_refcnt > 1)
361 			panic("%s: cannot disestablish shared irq", __func__);
362 
363 		firqh = TAILQ_FIRST(&firq->intr_handlers);
364 		kmem_free(firqh, sizeof(*firqh));
365 		intr_disestablish(firq->intr_ih);
366 		kmem_free(firq, sizeof(*firq));
367 		sc->sc_irq[n] = NULL;
368 		return;
369 	}
370 
371 	panic("%s: interrupt not established", __func__);
372 }
373 
374 static int
375 gicv3_fdt_intr(void *priv)
376 {
377 	struct gicv3_fdt_irq *firq = priv;
378 	struct gicv3_fdt_irqhandler *firqh;
379 	int handled = 0;
380 
381 	TAILQ_FOREACH(firqh, &firq->intr_handlers, ih_next)
382 		handled += firqh->ih_fn(firqh->ih_arg);
383 
384 	return handled;
385 }
386 
387 static bool
388 gicv3_fdt_intrstr(device_t dev, u_int *specifier, char *buf, size_t buflen)
389 {
390 	/* 1st cell is the interrupt type; 0 is SPI, 1 is PPI */
391 	/* 2nd cell is the interrupt number */
392 	/* 3rd cell is flags */
393 	/* 4th cell is affinity */
394 
395 	if (!specifier)
396 		return false;
397 	const u_int type = be32toh(specifier[0]);
398 	const u_int intr = be32toh(specifier[1]);
399 	const u_int irq = type == 0 ? IRQ_SPI(intr) : IRQ_PPI(intr);
400 
401 	snprintf(buf, buflen, "GICv3 irq %d", irq);
402 
403 	return true;
404 }
405