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