2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2019 Michal Meloun <mmel@FreeBSD.org>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
32 #include <sys/param.h>
33 #include <sys/systm.h>
36 #include <sys/bitset.h>
37 #include <sys/kernel.h>
41 #include <sys/module.h>
42 #include <sys/mutex.h>
44 #include <machine/bus.h>
45 #include <machine/intr.h>
46 #include <machine/resource.h>
48 #include <dev/fdt/simplebus.h>
50 #include <dev/ofw/ofw_bus.h>
51 #include <dev/ofw/ofw_bus_subr.h>
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);
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)
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)
79 #define MV_AP806_SEI_SETSPI_OFFSET 0x30
81 BITSET_DEFINE(sei_msi_bitmap, MV_AP806_SEI_CP_SIZE);
83 struct mv_ap806_sei_irqsrc {
84 struct intr_irqsrc isrc;
88 struct mv_ap806_sei_softc {
90 struct resource *mem_res;
91 struct resource *irq_res;
95 struct mv_ap806_sei_irqsrc *isrcs;
97 struct sei_msi_bitmap msi_bitmap;
100 static struct ofw_compat_data compat_data[] = {
101 {"marvell,ap806-sei", 1},
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))
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;
113 mv_ap806_sei_isrc_mask(struct mv_ap806_sei_softc *sc,
114 struct mv_ap806_sei_irqsrc *sisrc, uint32_t val)
119 bit = GICP_SEMR_BIT(sisrc->irq);
120 MV_AP806_SEI_LOCK(sc);
121 tmp = RD4(sc, GICP_SEMR(sisrc->irq));
126 WR4(sc, GICP_SEMR(sisrc->irq), tmp);
127 MV_AP806_SEI_UNLOCK(sc);
131 mv_ap806_sei_isrc_eoi(struct mv_ap806_sei_softc *sc,
132 struct mv_ap806_sei_irqsrc *sisrc)
135 WR4(sc, GICP_SECR(sisrc->irq), GICP_SECR_BIT(sisrc->irq));
139 mv_ap806_sei_enable_intr(device_t dev, struct intr_irqsrc *isrc)
141 struct mv_ap806_sei_softc *sc;
142 struct mv_ap806_sei_irqsrc *sisrc;
144 sc = device_get_softc(dev);
145 sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
146 mv_ap806_sei_isrc_mask(sc, sisrc, 0);
150 mv_ap806_sei_disable_intr(device_t dev, struct intr_irqsrc *isrc)
152 struct mv_ap806_sei_softc *sc;
153 struct mv_ap806_sei_irqsrc *sisrc;
155 sc = device_get_softc(dev);
156 sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
157 mv_ap806_sei_isrc_mask(sc, sisrc, 1);
161 mv_ap806_sei_map(device_t dev, struct intr_map_data *data, u_int *irqp)
163 struct mv_ap806_sei_softc *sc;
164 struct intr_map_data_fdt *daf;
167 sc = device_get_softc(dev);
169 if (data->type != INTR_MAP_DATA_FDT)
172 daf = (struct intr_map_data_fdt *)data;
173 if (daf->ncells != 1)
176 if (daf->cells[0] < MV_AP806_SEI_AP_FIRST ||
177 daf->cells[0] >= MV_AP806_SEI_AP_FIRST + MV_AP806_SEI_AP_SIZE)
188 mv_ap806_sei_map_intr(device_t dev, struct intr_map_data *data,
189 struct intr_irqsrc **isrcp)
191 struct mv_ap806_sei_softc *sc;
195 sc = device_get_softc(dev);
196 rv = mv_ap806_sei_map(dev, data, &irq);
198 *isrcp = &sc->isrcs[irq].isrc;
204 mv_ap806_sei_setup_intr(device_t dev, struct intr_irqsrc *isrc,
205 struct resource *res, struct intr_map_data *data)
207 struct mv_ap806_sei_softc *sc;
208 struct mv_ap806_sei_irqsrc *sisrc;
212 sc = device_get_softc(dev);
213 sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
216 rv = mv_ap806_sei_map(dev, data, &irq);
219 if (irq != sisrc->irq)
221 mv_ap806_sei_isrc_mask(sc, sisrc, 0);
226 mv_ap806_sei_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
227 struct resource *res, struct intr_map_data *data)
229 struct mv_ap806_sei_softc *sc;
230 struct mv_ap806_sei_irqsrc *sisrc;
232 sc = device_get_softc(dev);
233 sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
235 mv_ap806_sei_isrc_mask(sc, sisrc, 1);
240 mv_ap806_sei_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
242 struct mv_ap806_sei_softc *sc;
243 struct mv_ap806_sei_irqsrc *sisrc;
245 sc = device_get_softc(dev);
246 sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
248 mv_ap806_sei_isrc_mask(sc, sisrc, 1);
249 mv_ap806_sei_isrc_eoi(sc, sisrc);
253 mv_ap806_sei_post_ithread(device_t dev, struct intr_irqsrc *isrc)
255 struct mv_ap806_sei_softc *sc;
256 struct mv_ap806_sei_irqsrc *sisrc;
258 sc = device_get_softc(dev);
259 sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
261 mv_ap806_sei_isrc_mask(sc, sisrc, 1);
265 mv_ap806_sei_post_filter(device_t dev, struct intr_irqsrc *isrc)
267 struct mv_ap806_sei_softc *sc;
268 struct mv_ap806_sei_irqsrc *sisrc;
270 sc = device_get_softc(dev);
271 sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
273 mv_ap806_sei_isrc_mask(sc, sisrc, 1);
274 mv_ap806_sei_isrc_eoi(sc, sisrc);
277 /* ----------------------------------------------------------------------------
279 * B u s i n t e r f a c e
282 mv_ap806_sei_intr(void *arg)
284 struct mv_ap806_sei_softc *sc;
285 struct mv_ap806_sei_irqsrc *sirq;
286 struct trapframe *tf;
290 sc = (struct mv_ap806_sei_softc *)arg;
291 tf = curthread->td_intr_frame;
293 cause = RD4(sc, GICP_SECR1);
295 cause |= RD4(sc, GICP_SECR0);
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);
309 return (FILTER_HANDLED);
313 mv_ap806_sei_probe(device_t dev)
316 if (!ofw_bus_status_okay(dev))
319 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
322 device_set_desc(dev, "Marvell SEI");
323 return (BUS_PROBE_DEFAULT);
327 mv_ap806_sei_attach(device_t dev)
329 struct mv_ap806_sei_softc *sc;
330 phandle_t xref, node;
335 sc = device_get_softc(dev);
337 node = ofw_bus_get_node(dev);
338 MV_AP806_SEI_LOCK_INIT(sc);
340 /* Allocate resources. */
342 sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
344 if (sc->mem_res == NULL) {
345 device_printf(dev, "Cannot allocate memory resources\n");
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");
358 /* Mask all interrupts) */
359 WR4(sc, GICP_SEMR0, 0xFFFFFFFF);
360 WR4(sc, GICP_SEMR1, 0xFFFFFFFF);
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);
371 goto fail; /* XXX deregister ISRCs */
373 xref = OF_xref_from_node(node);;
374 if (intr_pic_register(dev, xref) == NULL) {
375 device_printf(dev, "Cannot register SEI\n");
379 if (bus_setup_intr(dev, sc->irq_res,INTR_TYPE_MISC | INTR_MPSAFE,
380 mv_ap806_sei_intr, NULL, sc, &sc->irq_ih)) {
382 "Unable to register interrupt handler\n");
388 * Bitmap of all IRQs.
389 * 1 - available, 0 - used.
391 BIT_FILL(MV_AP806_SEI_CP_SIZE, &sc->msi_bitmap);
393 OF_device_register_xref(xref, dev);
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);
408 mv_ap806_sei_detach(device_t dev)
415 mv_ap806_sei_alloc_msi(device_t dev, device_t child, int count, int maxcount,
416 device_t *pic, struct intr_irqsrc **srcs)
418 struct mv_ap806_sei_softc *sc;
419 int i, ret = 0, vector;
421 sc = device_get_softc(dev);
423 for (i = 0; i < count; i++) {
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.
429 vector = BIT_FFS_AT(MV_AP806_SEI_CP_SIZE, &sc->msi_bitmap, 0);
437 BIT_CLR(MV_AP806_SEI_CP_SIZE, vector, &sc->msi_bitmap);
438 vector += MV_AP806_SEI_CP_FIRST;
440 srcs[i] = &sc->isrcs[vector].isrc;
445 mv_ap806_sei_release_msi(dev, child, i + 1, srcs);
450 mv_ap806_sei_release_msi(device_t dev, device_t child, int count, struct intr_irqsrc **srcs)
452 struct mv_ap806_sei_softc *sc;
455 sc = device_get_softc(dev);
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,
467 mv_ap806_sei_map_msi(device_t dev, device_t child, struct intr_irqsrc *isrc,
468 uint64_t *addr, uint32_t *data)
470 struct mv_ap806_sei_softc *sc;
472 sc = device_get_softc(dev);
474 *addr = rman_get_start(sc->mem_res) + MV_AP806_SEI_SETSPI_OFFSET;
475 *data = isrc->isrc_irq;
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),
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),
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),
504 static devclass_t mv_ap806_sei_devclass;
506 static driver_t mv_ap806_sei_driver = {
508 mv_ap806_sei_methods,
509 sizeof(struct mv_ap806_sei_softc),
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);