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