xref: /freebsd/sys/arm/mv/mv_ap806_sei.c (revision 6ba2210ee039f2f12878c217bcf058e9c8b26b29)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2019 Michal Meloun <mmel@FreeBSD.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/bus.h>
35 
36 #include <sys/bitset.h>
37 #include <sys/kernel.h>
38 #include <sys/proc.h>
39 #include <sys/rman.h>
40 #include <sys/lock.h>
41 #include <sys/module.h>
42 #include <sys/mutex.h>
43 
44 #include <machine/bus.h>
45 #include <machine/intr.h>
46 #include <machine/resource.h>
47 
48 #include <dev/fdt/simplebus.h>
49 
50 #include <dev/ofw/ofw_bus.h>
51 #include <dev/ofw/ofw_bus_subr.h>
52 
53 #include "msi_if.h"
54 #include "pic_if.h"
55 
56 #define	MV_AP806_SEI_LOCK(_sc)		mtx_lock(&(_sc)->mtx)
57 #define	MV_AP806_SEI_UNLOCK(_sc)	mtx_unlock(&(_sc)->mtx)
58 #define	MV_AP806_SEI_LOCK_INIT(_sc)	mtx_init(&_sc->mtx, 			\
59 	    device_get_nameunit(_sc->dev), "mv_ap806_sei", MTX_DEF)
60 #define	MV_AP806_SEI_LOCK_DESTROY(_sc)	mtx_destroy(&_sc->mtx);
61 #define	MV_AP806_SEI_ASSERT_LOCKED(_sc)	mtx_assert(&_sc->mtx, MA_OWNED);
62 #define	MV_AP806_SEI_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->mtx, MA_NOTOWNED);
63 
64 #define GICP_SECR0		0x00
65 #define GICP_SECR1		0x04
66 #define GICP_SECR(i)		(0x00  + (((i)/32) * 0x4))
67 #define GICP_SECR_BIT(i)	((i) % 32)
68 #define GICP_SEMR0		0x20
69 #define GICP_SEMR1		0x24
70 #define GICP_SEMR(i)		(0x20  + (((i)/32) * 0x4))
71 #define GICP_SEMR_BIT(i)	((i) % 32)
72 
73 #define	MV_AP806_SEI_AP_FIRST	0
74 #define	MV_AP806_SEI_AP_SIZE	21
75 #define	MV_AP806_SEI_CP_FIRST	21
76 #define	MV_AP806_SEI_CP_SIZE	43
77 #define	MV_AP806_SEI_MAX_NIRQS	(MV_AP806_SEI_AP_SIZE + MV_AP806_SEI_CP_SIZE)
78 
79 #define	MV_AP806_SEI_SETSPI_OFFSET	0x30
80 
81 BITSET_DEFINE(sei_msi_bitmap, MV_AP806_SEI_CP_SIZE);
82 
83 struct mv_ap806_sei_irqsrc {
84 	struct intr_irqsrc	isrc;
85 	u_int			irq;
86 };
87 
88 struct mv_ap806_sei_softc {
89 	device_t		dev;
90 	struct resource		*mem_res;
91 	struct resource		*irq_res;
92 	void			*irq_ih;
93 	struct mtx		mtx;
94 
95 	struct mv_ap806_sei_irqsrc *isrcs;
96 
97 	struct sei_msi_bitmap	msi_bitmap;
98 };
99 
100 static struct ofw_compat_data compat_data[] = {
101 	{"marvell,ap806-sei", 1},
102 	{NULL,             0}
103 };
104 
105 #define	RD4(sc, reg)		bus_read_4((sc)->mem_res, (reg))
106 #define	WR4(sc, reg, val)	bus_write_4((sc)->mem_res, (reg), (val))
107 
108 static msi_alloc_msi_t mv_ap806_sei_alloc_msi;
109 static msi_release_msi_t mv_ap806_sei_release_msi;
110 static msi_map_msi_t mv_ap806_sei_map_msi;
111 
112 static inline void
113 mv_ap806_sei_isrc_mask(struct mv_ap806_sei_softc *sc,
114      struct mv_ap806_sei_irqsrc *sisrc, uint32_t val)
115 {
116 	uint32_t tmp;
117 	int bit;
118 
119 	bit = GICP_SEMR_BIT(sisrc->irq);
120 	MV_AP806_SEI_LOCK(sc);
121 	tmp = RD4(sc, GICP_SEMR(sisrc->irq));
122 	if (val != 0)
123 		tmp |= 1 << bit;
124 	else
125 		tmp &= ~(1 << bit);
126 	WR4(sc, GICP_SEMR(sisrc->irq), tmp);
127 	MV_AP806_SEI_UNLOCK(sc);
128 }
129 
130 static inline void
131 mv_ap806_sei_isrc_eoi(struct mv_ap806_sei_softc *sc,
132      struct mv_ap806_sei_irqsrc *sisrc)
133 {
134 
135 	WR4(sc, GICP_SECR(sisrc->irq), GICP_SECR_BIT(sisrc->irq));
136 }
137 
138 static void
139 mv_ap806_sei_enable_intr(device_t dev, struct intr_irqsrc *isrc)
140 {
141 	struct mv_ap806_sei_softc *sc;
142 	struct mv_ap806_sei_irqsrc *sisrc;
143 
144 	sc = device_get_softc(dev);
145 	sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
146 	mv_ap806_sei_isrc_mask(sc, sisrc, 0);
147 }
148 
149 static void
150 mv_ap806_sei_disable_intr(device_t dev, struct intr_irqsrc *isrc)
151 {
152 	struct mv_ap806_sei_softc *sc;
153 	struct mv_ap806_sei_irqsrc *sisrc;
154 
155 	sc = device_get_softc(dev);
156 	sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
157 	mv_ap806_sei_isrc_mask(sc, sisrc, 1);
158 }
159 
160 static int
161 mv_ap806_sei_map(device_t dev, struct intr_map_data *data, u_int *irqp)
162 {
163 	struct mv_ap806_sei_softc *sc;
164 	struct intr_map_data_fdt *daf;
165 	u_int irq;
166 
167 	sc = device_get_softc(dev);
168 
169 	if (data->type != INTR_MAP_DATA_FDT)
170 		return (ENOTSUP);
171 
172 	daf = (struct intr_map_data_fdt *)data;
173 	if (daf->ncells != 1)
174 		return (EINVAL);
175 
176 	if (daf->cells[0] < MV_AP806_SEI_AP_FIRST ||
177 	    daf->cells[0] >= MV_AP806_SEI_AP_FIRST + MV_AP806_SEI_AP_SIZE)
178 		return (EINVAL);
179 
180 	irq = daf->cells[0];
181 	if (irqp != NULL)
182 		*irqp = irq;
183 
184 	return(0);
185 }
186 
187 static int
188 mv_ap806_sei_map_intr(device_t dev, struct intr_map_data *data,
189     struct intr_irqsrc **isrcp)
190 {
191 	struct mv_ap806_sei_softc *sc;
192 	u_int irq;
193 	int rv;
194 
195 	sc = device_get_softc(dev);
196 	rv = mv_ap806_sei_map(dev, data, &irq);
197 	if (rv == 0)
198 		*isrcp = &sc->isrcs[irq].isrc;
199 
200 	return (rv);
201 }
202 
203 static int
204 mv_ap806_sei_setup_intr(device_t dev, struct intr_irqsrc *isrc,
205     struct resource *res, struct intr_map_data *data)
206 {
207 	struct mv_ap806_sei_softc *sc;
208 	struct mv_ap806_sei_irqsrc *sisrc;
209 	u_int irq;
210 	int rv;
211 
212 	sc = device_get_softc(dev);
213 	sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
214 	if (data == NULL)
215 		return (ENOTSUP);
216 	rv = mv_ap806_sei_map(dev, data, &irq);
217 	if (rv != 0)
218 		return (rv);
219 	if (irq != sisrc->irq)
220 		return (EINVAL);
221 	mv_ap806_sei_isrc_mask(sc, sisrc, 0);
222 	return (0);
223 }
224 
225 static int
226 mv_ap806_sei_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
227     struct resource *res, struct intr_map_data *data)
228 {
229 	struct mv_ap806_sei_softc *sc;
230 	struct mv_ap806_sei_irqsrc *sisrc;
231 
232 	sc = device_get_softc(dev);
233 	sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
234 
235 	mv_ap806_sei_isrc_mask(sc, sisrc, 1);
236 	return (0);
237 }
238 
239 static void
240 mv_ap806_sei_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
241 {
242 	struct mv_ap806_sei_softc *sc;
243 	struct mv_ap806_sei_irqsrc *sisrc;
244 
245 	sc = device_get_softc(dev);
246 	sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
247 
248 	mv_ap806_sei_isrc_mask(sc, sisrc, 1);
249 	mv_ap806_sei_isrc_eoi(sc, sisrc);
250 }
251 
252 static void
253 mv_ap806_sei_post_ithread(device_t dev, struct intr_irqsrc *isrc)
254 {
255 	struct mv_ap806_sei_softc *sc;
256 	struct mv_ap806_sei_irqsrc *sisrc;
257 
258 	sc = device_get_softc(dev);
259 	sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
260 
261 	mv_ap806_sei_isrc_mask(sc, sisrc, 1);
262 }
263 
264 static void
265 mv_ap806_sei_post_filter(device_t dev, struct intr_irqsrc *isrc)
266 {
267 	struct mv_ap806_sei_softc *sc;
268 	struct mv_ap806_sei_irqsrc *sisrc;
269 
270 	sc = device_get_softc(dev);
271 	sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
272 
273 	mv_ap806_sei_isrc_mask(sc, sisrc, 1);
274 	mv_ap806_sei_isrc_eoi(sc, sisrc);
275 }
276 
277 /* ----------------------------------------------------------------------------
278  *
279  *		B u s    i n t e r f a c e
280  */
281 static int
282 mv_ap806_sei_intr(void *arg)
283 {
284 	struct mv_ap806_sei_softc *sc;
285 	struct mv_ap806_sei_irqsrc *sirq;
286 	struct trapframe *tf;
287 	uint64_t cause;
288 	u_int irq;
289 
290 	sc = (struct mv_ap806_sei_softc *)arg;
291 	tf = curthread->td_intr_frame;
292 	while (1) {
293 		cause = RD4(sc, GICP_SECR1);
294 		cause <<= 32;
295 		cause |= RD4(sc, GICP_SECR0);
296 
297 		irq = ffsll(cause);
298 		if (irq == 0) break;
299 		irq--;
300 		sirq = &sc->isrcs[irq];
301 		if (intr_isrc_dispatch(&sirq->isrc, tf) != 0) {
302 			mv_ap806_sei_isrc_mask(sc, sirq, 0);
303 			mv_ap806_sei_isrc_eoi(sc, sirq);
304 			device_printf(sc->dev,
305 			    "Stray irq %u disabled\n", irq);
306 		}
307 	}
308 
309 	return (FILTER_HANDLED);
310 }
311 
312 static int
313 mv_ap806_sei_probe(device_t dev)
314 {
315 
316 	if (!ofw_bus_status_okay(dev))
317 		return (ENXIO);
318 
319 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
320 		return (ENXIO);
321 
322 	device_set_desc(dev, "Marvell SEI");
323 	return (BUS_PROBE_DEFAULT);
324 }
325 
326 static int
327 mv_ap806_sei_attach(device_t dev)
328 {
329 	struct mv_ap806_sei_softc *sc;
330 	phandle_t xref, node;
331 	uint32_t irq;
332 	const char *name;
333 	int rv, rid;
334 
335 	sc = device_get_softc(dev);
336 	sc->dev = dev;
337 	node = ofw_bus_get_node(dev);
338 	MV_AP806_SEI_LOCK_INIT(sc);
339 
340 	/* Allocate resources. */
341 	rid = 0;
342 	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
343 	    RF_ACTIVE);
344 	if (sc->mem_res == NULL) {
345 		device_printf(dev, "Cannot allocate memory resources\n");
346 		rv = ENXIO;
347 		goto fail;
348 	}
349 
350 	rid = 0;
351 	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
352 	if (sc->irq_res == NULL) {
353 		device_printf(dev, "Cannot allocate IRQ resources\n");
354 		rv = ENXIO;
355 		goto fail;
356 	}
357 
358 	/* Mask all interrupts) */
359 	WR4(sc, GICP_SEMR0, 0xFFFFFFFF);
360 	WR4(sc, GICP_SEMR1, 0xFFFFFFFF);
361 
362 	/* Create all interrupt sources */
363 	sc->isrcs = malloc(sizeof(*sc->isrcs) * MV_AP806_SEI_MAX_NIRQS,
364 	    M_DEVBUF, M_WAITOK | M_ZERO);
365 	name = device_get_nameunit(sc->dev);
366 	for (irq = 0; irq < MV_AP806_SEI_MAX_NIRQS; irq++) {
367 		sc->isrcs[irq].irq = irq;
368 		rv = intr_isrc_register(&sc->isrcs[irq].isrc,
369 		    sc->dev, 0, "%s,%u", name, irq);
370 		if (rv != 0)
371 			goto fail; /* XXX deregister ISRCs */
372 	}
373 	xref = OF_xref_from_node(node);;
374 	if (intr_pic_register(dev, xref) == NULL) {
375 		device_printf(dev, "Cannot register SEI\n");
376 		rv = ENXIO;
377 		goto fail;
378 	}
379 	if (bus_setup_intr(dev, sc->irq_res,INTR_TYPE_MISC | INTR_MPSAFE,
380 	    mv_ap806_sei_intr, NULL, sc, &sc->irq_ih)) {
381 		device_printf(dev,
382 		    "Unable to register interrupt handler\n");
383 		rv = ENXIO;
384 		goto fail;
385 	}
386 
387 	/*
388 	 * Bitmap of all IRQs.
389 	 * 1 - available, 0 - used.
390 	 */
391 	BIT_FILL(MV_AP806_SEI_CP_SIZE, &sc->msi_bitmap);
392 
393 	OF_device_register_xref(xref, dev);
394 	return (0);
395 
396 fail:
397 	if (sc->irq_ih != NULL)
398 		bus_teardown_intr(dev, sc->irq_res, sc->irq_ih);
399 	if (sc->irq_res != NULL)
400 		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
401 	if (sc->mem_res != NULL)
402 		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
403 	MV_AP806_SEI_LOCK_DESTROY(sc);
404 	return (ENXIO);
405 }
406 
407 static int
408 mv_ap806_sei_detach(device_t dev)
409 {
410 
411 	return (EBUSY);
412 }
413 
414 static int
415 mv_ap806_sei_alloc_msi(device_t dev, device_t child, int count, int maxcount,
416     device_t *pic, struct intr_irqsrc **srcs)
417 {
418 	struct mv_ap806_sei_softc *sc;
419 	int i, ret = 0, vector;
420 
421 	sc = device_get_softc(dev);
422 
423 	for (i = 0; i < count; i++) {
424 		/*
425 		 * Find first available MSI vector represented by first set bit
426 		 * in the bitmap. BIT_FFS starts the count from 1,
427 		 * 0 means that nothing was found.
428 		 */
429 		vector = BIT_FFS_AT(MV_AP806_SEI_CP_SIZE, &sc->msi_bitmap, 0);
430 		if (vector == 0) {
431 			ret = ENOMEM;
432 			i--;
433 			goto fail;
434 		}
435 
436 		vector--;
437 		BIT_CLR(MV_AP806_SEI_CP_SIZE, vector, &sc->msi_bitmap);
438 		vector += MV_AP806_SEI_CP_FIRST;
439 
440 		srcs[i] = &sc->isrcs[vector].isrc;
441 	}
442 
443 	return (ret);
444 fail:
445 	mv_ap806_sei_release_msi(dev, child, i + 1, srcs);
446 	return (ret);
447 }
448 
449 static int
450 mv_ap806_sei_release_msi(device_t dev, device_t child, int count, struct intr_irqsrc **srcs)
451 {
452 	struct mv_ap806_sei_softc *sc;
453 	int i;
454 
455 	sc = device_get_softc(dev);
456 
457 	for (i = 0; i < count; i++) {
458 		BIT_SET(MV_AP806_SEI_CP_SIZE,
459 		    srcs[i]->isrc_irq - MV_AP806_SEI_CP_FIRST,
460 		    &sc->msi_bitmap);
461 	}
462 
463 	return (0);
464 }
465 
466 static int
467 mv_ap806_sei_map_msi(device_t dev, device_t child, struct intr_irqsrc *isrc,
468     uint64_t *addr, uint32_t *data)
469 {
470 	struct mv_ap806_sei_softc *sc;
471 
472 	sc = device_get_softc(dev);
473 
474 	*addr = rman_get_start(sc->mem_res) + MV_AP806_SEI_SETSPI_OFFSET;
475 	*data = isrc->isrc_irq;
476 
477 	return (0);
478 }
479 
480 static device_method_t mv_ap806_sei_methods[] = {
481 	/* Device interface */
482 	DEVMETHOD(device_probe,		mv_ap806_sei_probe),
483 	DEVMETHOD(device_attach,	mv_ap806_sei_attach),
484 	DEVMETHOD(device_detach,	mv_ap806_sei_detach),
485 
486 	/* Interrupt controller interface */
487 	DEVMETHOD(pic_disable_intr,	mv_ap806_sei_disable_intr),
488 	DEVMETHOD(pic_enable_intr,	mv_ap806_sei_enable_intr),
489 	DEVMETHOD(pic_map_intr,		mv_ap806_sei_map_intr),
490 	DEVMETHOD(pic_setup_intr,	mv_ap806_sei_setup_intr),
491 	DEVMETHOD(pic_teardown_intr,	mv_ap806_sei_teardown_intr),
492 	DEVMETHOD(pic_post_filter,	mv_ap806_sei_post_filter),
493 	DEVMETHOD(pic_post_ithread,	mv_ap806_sei_post_ithread),
494 	DEVMETHOD(pic_pre_ithread,	mv_ap806_sei_pre_ithread),
495 
496 	/* MSI interface */
497 	DEVMETHOD(msi_alloc_msi,	mv_ap806_sei_alloc_msi),
498 	DEVMETHOD(msi_release_msi,	mv_ap806_sei_release_msi),
499 	DEVMETHOD(msi_map_msi,		mv_ap806_sei_map_msi),
500 
501 	DEVMETHOD_END
502 };
503 
504 static devclass_t mv_ap806_sei_devclass;
505 
506 static driver_t mv_ap806_sei_driver = {
507 	"mv_ap806_sei",
508 	mv_ap806_sei_methods,
509 	sizeof(struct mv_ap806_sei_softc),
510 };
511 
512 EARLY_DRIVER_MODULE(mv_ap806_sei, simplebus, mv_ap806_sei_driver,
513     mv_ap806_sei_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
514