14b84206bSMichal Meloun /*-
2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
34b84206bSMichal Meloun *
44b84206bSMichal Meloun * Copyright (c) 2019 Michal Meloun <mmel@FreeBSD.org>
54b84206bSMichal Meloun *
64b84206bSMichal Meloun * Redistribution and use in source and binary forms, with or without
74b84206bSMichal Meloun * modification, are permitted provided that the following conditions
84b84206bSMichal Meloun * are met:
94b84206bSMichal Meloun * 1. Redistributions of source code must retain the above copyright
104b84206bSMichal Meloun * notice, this list of conditions and the following disclaimer.
114b84206bSMichal Meloun * 2. Redistributions in binary form must reproduce the above copyright
124b84206bSMichal Meloun * notice, this list of conditions and the following disclaimer in the
134b84206bSMichal Meloun * documentation and/or other materials provided with the distribution.
144b84206bSMichal Meloun *
154b84206bSMichal Meloun * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
164b84206bSMichal Meloun * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
174b84206bSMichal Meloun * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
184b84206bSMichal Meloun * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
194b84206bSMichal Meloun * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
204b84206bSMichal Meloun * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
214b84206bSMichal Meloun * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
224b84206bSMichal Meloun * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
234b84206bSMichal Meloun * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
244b84206bSMichal Meloun * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
254b84206bSMichal Meloun * SUCH DAMAGE.
264b84206bSMichal Meloun *
274b84206bSMichal Meloun */
284b84206bSMichal Meloun
294b84206bSMichal Meloun #include <sys/param.h>
304b84206bSMichal Meloun #include <sys/systm.h>
314b84206bSMichal Meloun #include <sys/bus.h>
32986bbba9SKornel Duleba
33986bbba9SKornel Duleba #include <sys/bitset.h>
344b84206bSMichal Meloun #include <sys/kernel.h>
354b84206bSMichal Meloun #include <sys/proc.h>
364b84206bSMichal Meloun #include <sys/rman.h>
374b84206bSMichal Meloun #include <sys/lock.h>
384b84206bSMichal Meloun #include <sys/module.h>
394b84206bSMichal Meloun #include <sys/mutex.h>
404b84206bSMichal Meloun
414b84206bSMichal Meloun #include <machine/bus.h>
424b84206bSMichal Meloun #include <machine/intr.h>
434b84206bSMichal Meloun #include <machine/resource.h>
444b84206bSMichal Meloun
454b84206bSMichal Meloun #include <dev/fdt/simplebus.h>
464b84206bSMichal Meloun
474b84206bSMichal Meloun #include <dev/ofw/ofw_bus.h>
484b84206bSMichal Meloun #include <dev/ofw/ofw_bus_subr.h>
494b84206bSMichal Meloun
50986bbba9SKornel Duleba #include "msi_if.h"
514b84206bSMichal Meloun #include "pic_if.h"
524b84206bSMichal Meloun
534b84206bSMichal Meloun #define MV_AP806_SEI_LOCK(_sc) mtx_lock(&(_sc)->mtx)
544b84206bSMichal Meloun #define MV_AP806_SEI_UNLOCK(_sc) mtx_unlock(&(_sc)->mtx)
554b84206bSMichal Meloun #define MV_AP806_SEI_LOCK_INIT(_sc) mtx_init(&_sc->mtx, \
564b84206bSMichal Meloun device_get_nameunit(_sc->dev), "mv_ap806_sei", MTX_DEF)
574b84206bSMichal Meloun #define MV_AP806_SEI_LOCK_DESTROY(_sc) mtx_destroy(&_sc->mtx);
584b84206bSMichal Meloun #define MV_AP806_SEI_ASSERT_LOCKED(_sc) mtx_assert(&_sc->mtx, MA_OWNED);
594b84206bSMichal Meloun #define MV_AP806_SEI_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->mtx, MA_NOTOWNED);
604b84206bSMichal Meloun
614b84206bSMichal Meloun #define GICP_SECR0 0x00
624b84206bSMichal Meloun #define GICP_SECR1 0x04
634b84206bSMichal Meloun #define GICP_SECR(i) (0x00 + (((i)/32) * 0x4))
644b84206bSMichal Meloun #define GICP_SECR_BIT(i) ((i) % 32)
654b84206bSMichal Meloun #define GICP_SEMR0 0x20
664b84206bSMichal Meloun #define GICP_SEMR1 0x24
674b84206bSMichal Meloun #define GICP_SEMR(i) (0x20 + (((i)/32) * 0x4))
684b84206bSMichal Meloun #define GICP_SEMR_BIT(i) ((i) % 32)
694b84206bSMichal Meloun
70986bbba9SKornel Duleba #define MV_AP806_SEI_AP_FIRST 0
71986bbba9SKornel Duleba #define MV_AP806_SEI_AP_SIZE 21
72986bbba9SKornel Duleba #define MV_AP806_SEI_CP_FIRST 21
73986bbba9SKornel Duleba #define MV_AP806_SEI_CP_SIZE 43
74986bbba9SKornel Duleba #define MV_AP806_SEI_MAX_NIRQS (MV_AP806_SEI_AP_SIZE + MV_AP806_SEI_CP_SIZE)
75986bbba9SKornel Duleba
76986bbba9SKornel Duleba #define MV_AP806_SEI_SETSPI_OFFSET 0x30
77986bbba9SKornel Duleba
78986bbba9SKornel Duleba BITSET_DEFINE(sei_msi_bitmap, MV_AP806_SEI_CP_SIZE);
79986bbba9SKornel Duleba
804b84206bSMichal Meloun struct mv_ap806_sei_irqsrc {
814b84206bSMichal Meloun struct intr_irqsrc isrc;
824b84206bSMichal Meloun u_int irq;
834b84206bSMichal Meloun };
844b84206bSMichal Meloun
854b84206bSMichal Meloun struct mv_ap806_sei_softc {
864b84206bSMichal Meloun device_t dev;
874b84206bSMichal Meloun struct resource *mem_res;
884b84206bSMichal Meloun struct resource *irq_res;
894b84206bSMichal Meloun void *irq_ih;
904b84206bSMichal Meloun struct mtx mtx;
914b84206bSMichal Meloun
924b84206bSMichal Meloun struct mv_ap806_sei_irqsrc *isrcs;
93986bbba9SKornel Duleba
94986bbba9SKornel Duleba struct sei_msi_bitmap msi_bitmap;
954b84206bSMichal Meloun };
964b84206bSMichal Meloun
974b84206bSMichal Meloun static struct ofw_compat_data compat_data[] = {
984b84206bSMichal Meloun {"marvell,ap806-sei", 1},
994b84206bSMichal Meloun {NULL, 0}
1004b84206bSMichal Meloun };
1014b84206bSMichal Meloun
1024b84206bSMichal Meloun #define RD4(sc, reg) bus_read_4((sc)->mem_res, (reg))
1034b84206bSMichal Meloun #define WR4(sc, reg, val) bus_write_4((sc)->mem_res, (reg), (val))
1044b84206bSMichal Meloun
105986bbba9SKornel Duleba static msi_alloc_msi_t mv_ap806_sei_alloc_msi;
106986bbba9SKornel Duleba static msi_release_msi_t mv_ap806_sei_release_msi;
107986bbba9SKornel Duleba static msi_map_msi_t mv_ap806_sei_map_msi;
108986bbba9SKornel Duleba
1094b84206bSMichal Meloun static inline void
mv_ap806_sei_isrc_mask(struct mv_ap806_sei_softc * sc,struct mv_ap806_sei_irqsrc * sisrc,uint32_t val)1104b84206bSMichal Meloun mv_ap806_sei_isrc_mask(struct mv_ap806_sei_softc *sc,
1114b84206bSMichal Meloun struct mv_ap806_sei_irqsrc *sisrc, uint32_t val)
1124b84206bSMichal Meloun {
1134b84206bSMichal Meloun uint32_t tmp;
1144b84206bSMichal Meloun int bit;
1154b84206bSMichal Meloun
1164b84206bSMichal Meloun bit = GICP_SEMR_BIT(sisrc->irq);
1174b84206bSMichal Meloun MV_AP806_SEI_LOCK(sc);
1184b84206bSMichal Meloun tmp = RD4(sc, GICP_SEMR(sisrc->irq));
1194b84206bSMichal Meloun if (val != 0)
1204b84206bSMichal Meloun tmp |= 1 << bit;
1214b84206bSMichal Meloun else
1224b84206bSMichal Meloun tmp &= ~(1 << bit);
1234b84206bSMichal Meloun WR4(sc, GICP_SEMR(sisrc->irq), tmp);
1244b84206bSMichal Meloun MV_AP806_SEI_UNLOCK(sc);
1254b84206bSMichal Meloun }
1264b84206bSMichal Meloun
1274b84206bSMichal Meloun static inline void
mv_ap806_sei_isrc_eoi(struct mv_ap806_sei_softc * sc,struct mv_ap806_sei_irqsrc * sisrc)1284b84206bSMichal Meloun mv_ap806_sei_isrc_eoi(struct mv_ap806_sei_softc *sc,
1294b84206bSMichal Meloun struct mv_ap806_sei_irqsrc *sisrc)
1304b84206bSMichal Meloun {
1314b84206bSMichal Meloun
1324b84206bSMichal Meloun WR4(sc, GICP_SECR(sisrc->irq), GICP_SECR_BIT(sisrc->irq));
1334b84206bSMichal Meloun }
1344b84206bSMichal Meloun
1354b84206bSMichal Meloun static void
mv_ap806_sei_enable_intr(device_t dev,struct intr_irqsrc * isrc)1364b84206bSMichal Meloun mv_ap806_sei_enable_intr(device_t dev, struct intr_irqsrc *isrc)
1374b84206bSMichal Meloun {
1384b84206bSMichal Meloun struct mv_ap806_sei_softc *sc;
1394b84206bSMichal Meloun struct mv_ap806_sei_irqsrc *sisrc;
1404b84206bSMichal Meloun
1414b84206bSMichal Meloun sc = device_get_softc(dev);
1424b84206bSMichal Meloun sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
1434b84206bSMichal Meloun mv_ap806_sei_isrc_mask(sc, sisrc, 0);
1444b84206bSMichal Meloun }
1454b84206bSMichal Meloun
1464b84206bSMichal Meloun static void
mv_ap806_sei_disable_intr(device_t dev,struct intr_irqsrc * isrc)1474b84206bSMichal Meloun mv_ap806_sei_disable_intr(device_t dev, struct intr_irqsrc *isrc)
1484b84206bSMichal Meloun {
1494b84206bSMichal Meloun struct mv_ap806_sei_softc *sc;
1504b84206bSMichal Meloun struct mv_ap806_sei_irqsrc *sisrc;
1514b84206bSMichal Meloun
1524b84206bSMichal Meloun sc = device_get_softc(dev);
1534b84206bSMichal Meloun sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
1544b84206bSMichal Meloun mv_ap806_sei_isrc_mask(sc, sisrc, 1);
1554b84206bSMichal Meloun }
1564b84206bSMichal Meloun
1574b84206bSMichal Meloun static int
mv_ap806_sei_map(device_t dev,struct intr_map_data * data,u_int * irqp)1584b84206bSMichal Meloun mv_ap806_sei_map(device_t dev, struct intr_map_data *data, u_int *irqp)
1594b84206bSMichal Meloun {
1604b84206bSMichal Meloun struct intr_map_data_fdt *daf;
1614b84206bSMichal Meloun u_int irq;
1624b84206bSMichal Meloun
1634b84206bSMichal Meloun if (data->type != INTR_MAP_DATA_FDT)
1644b84206bSMichal Meloun return (ENOTSUP);
1654b84206bSMichal Meloun
1664b84206bSMichal Meloun daf = (struct intr_map_data_fdt *)data;
167986bbba9SKornel Duleba if (daf->ncells != 1)
1684b84206bSMichal Meloun return (EINVAL);
169986bbba9SKornel Duleba
170986bbba9SKornel Duleba if (daf->cells[0] < MV_AP806_SEI_AP_FIRST ||
171986bbba9SKornel Duleba daf->cells[0] >= MV_AP806_SEI_AP_FIRST + MV_AP806_SEI_AP_SIZE)
172986bbba9SKornel Duleba return (EINVAL);
173986bbba9SKornel Duleba
1744b84206bSMichal Meloun irq = daf->cells[0];
1754b84206bSMichal Meloun if (irqp != NULL)
1764b84206bSMichal Meloun *irqp = irq;
1774b84206bSMichal Meloun
1784b84206bSMichal Meloun return(0);
1794b84206bSMichal Meloun }
1804b84206bSMichal Meloun
1814b84206bSMichal Meloun static int
mv_ap806_sei_map_intr(device_t dev,struct intr_map_data * data,struct intr_irqsrc ** isrcp)1824b84206bSMichal Meloun mv_ap806_sei_map_intr(device_t dev, struct intr_map_data *data,
1834b84206bSMichal Meloun struct intr_irqsrc **isrcp)
1844b84206bSMichal Meloun {
1854b84206bSMichal Meloun struct mv_ap806_sei_softc *sc;
1864b84206bSMichal Meloun u_int irq;
1874b84206bSMichal Meloun int rv;
1884b84206bSMichal Meloun
1894b84206bSMichal Meloun sc = device_get_softc(dev);
1904b84206bSMichal Meloun rv = mv_ap806_sei_map(dev, data, &irq);
1914b84206bSMichal Meloun if (rv == 0)
1924b84206bSMichal Meloun *isrcp = &sc->isrcs[irq].isrc;
1934b84206bSMichal Meloun
1944b84206bSMichal Meloun return (rv);
1954b84206bSMichal Meloun }
1964b84206bSMichal Meloun
1974b84206bSMichal Meloun static int
mv_ap806_sei_setup_intr(device_t dev,struct intr_irqsrc * isrc,struct resource * res,struct intr_map_data * data)1984b84206bSMichal Meloun mv_ap806_sei_setup_intr(device_t dev, struct intr_irqsrc *isrc,
1994b84206bSMichal Meloun struct resource *res, struct intr_map_data *data)
2004b84206bSMichal Meloun {
2014b84206bSMichal Meloun struct mv_ap806_sei_softc *sc;
2024b84206bSMichal Meloun struct mv_ap806_sei_irqsrc *sisrc;
2034b84206bSMichal Meloun u_int irq;
2044b84206bSMichal Meloun int rv;
2054b84206bSMichal Meloun
2064b84206bSMichal Meloun sc = device_get_softc(dev);
2074b84206bSMichal Meloun sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
2084b84206bSMichal Meloun if (data == NULL)
2094b84206bSMichal Meloun return (ENOTSUP);
2104b84206bSMichal Meloun rv = mv_ap806_sei_map(dev, data, &irq);
2114b84206bSMichal Meloun if (rv != 0)
2124b84206bSMichal Meloun return (rv);
2134b84206bSMichal Meloun if (irq != sisrc->irq)
2144b84206bSMichal Meloun return (EINVAL);
2154b84206bSMichal Meloun mv_ap806_sei_isrc_mask(sc, sisrc, 0);
2164b84206bSMichal Meloun return (0);
2174b84206bSMichal Meloun }
2184b84206bSMichal Meloun
2194b84206bSMichal Meloun static int
mv_ap806_sei_teardown_intr(device_t dev,struct intr_irqsrc * isrc,struct resource * res,struct intr_map_data * data)2204b84206bSMichal Meloun mv_ap806_sei_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
2214b84206bSMichal Meloun struct resource *res, struct intr_map_data *data)
2224b84206bSMichal Meloun {
2234b84206bSMichal Meloun struct mv_ap806_sei_softc *sc;
2244b84206bSMichal Meloun struct mv_ap806_sei_irqsrc *sisrc;
2254b84206bSMichal Meloun
2264b84206bSMichal Meloun sc = device_get_softc(dev);
2274b84206bSMichal Meloun sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
2284b84206bSMichal Meloun
2294b84206bSMichal Meloun mv_ap806_sei_isrc_mask(sc, sisrc, 1);
2304b84206bSMichal Meloun return (0);
2314b84206bSMichal Meloun }
2324b84206bSMichal Meloun
2334b84206bSMichal Meloun static void
mv_ap806_sei_pre_ithread(device_t dev,struct intr_irqsrc * isrc)2344b84206bSMichal Meloun mv_ap806_sei_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
2354b84206bSMichal Meloun {
2364b84206bSMichal Meloun struct mv_ap806_sei_softc *sc;
2374b84206bSMichal Meloun struct mv_ap806_sei_irqsrc *sisrc;
2384b84206bSMichal Meloun
2394b84206bSMichal Meloun sc = device_get_softc(dev);
2404b84206bSMichal Meloun sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
2414b84206bSMichal Meloun
2424b84206bSMichal Meloun mv_ap806_sei_isrc_mask(sc, sisrc, 1);
2434b84206bSMichal Meloun mv_ap806_sei_isrc_eoi(sc, sisrc);
2444b84206bSMichal Meloun }
2454b84206bSMichal Meloun
2464b84206bSMichal Meloun static void
mv_ap806_sei_post_ithread(device_t dev,struct intr_irqsrc * isrc)2474b84206bSMichal Meloun mv_ap806_sei_post_ithread(device_t dev, struct intr_irqsrc *isrc)
2484b84206bSMichal Meloun {
2494b84206bSMichal Meloun struct mv_ap806_sei_softc *sc;
2504b84206bSMichal Meloun struct mv_ap806_sei_irqsrc *sisrc;
2514b84206bSMichal Meloun
2524b84206bSMichal Meloun sc = device_get_softc(dev);
2534b84206bSMichal Meloun sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
2544b84206bSMichal Meloun
2554b84206bSMichal Meloun mv_ap806_sei_isrc_mask(sc, sisrc, 1);
2564b84206bSMichal Meloun }
2574b84206bSMichal Meloun
2584b84206bSMichal Meloun static void
mv_ap806_sei_post_filter(device_t dev,struct intr_irqsrc * isrc)2594b84206bSMichal Meloun mv_ap806_sei_post_filter(device_t dev, struct intr_irqsrc *isrc)
2604b84206bSMichal Meloun {
2614b84206bSMichal Meloun struct mv_ap806_sei_softc *sc;
2624b84206bSMichal Meloun struct mv_ap806_sei_irqsrc *sisrc;
2634b84206bSMichal Meloun
2644b84206bSMichal Meloun sc = device_get_softc(dev);
2654b84206bSMichal Meloun sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
2664b84206bSMichal Meloun
2674b84206bSMichal Meloun mv_ap806_sei_isrc_mask(sc, sisrc, 1);
2684b84206bSMichal Meloun mv_ap806_sei_isrc_eoi(sc, sisrc);
2694b84206bSMichal Meloun }
2704b84206bSMichal Meloun
2714b84206bSMichal Meloun /* ----------------------------------------------------------------------------
2724b84206bSMichal Meloun *
2734b84206bSMichal Meloun * B u s i n t e r f a c e
2744b84206bSMichal Meloun */
2754b84206bSMichal Meloun static int
mv_ap806_sei_intr(void * arg)2764b84206bSMichal Meloun mv_ap806_sei_intr(void *arg)
2774b84206bSMichal Meloun {
2784b84206bSMichal Meloun struct mv_ap806_sei_softc *sc;
2794b84206bSMichal Meloun struct mv_ap806_sei_irqsrc *sirq;
2804b84206bSMichal Meloun struct trapframe *tf;
2814b84206bSMichal Meloun uint64_t cause;
2824b84206bSMichal Meloun u_int irq;
2834b84206bSMichal Meloun
2844b84206bSMichal Meloun sc = (struct mv_ap806_sei_softc *)arg;
2854b84206bSMichal Meloun tf = curthread->td_intr_frame;
2864b84206bSMichal Meloun while (1) {
2874b84206bSMichal Meloun cause = RD4(sc, GICP_SECR1);
2884b84206bSMichal Meloun cause <<= 32;
2894b84206bSMichal Meloun cause |= RD4(sc, GICP_SECR0);
2904b84206bSMichal Meloun
2914b84206bSMichal Meloun irq = ffsll(cause);
2924b84206bSMichal Meloun if (irq == 0) break;
2934b84206bSMichal Meloun irq--;
2944b84206bSMichal Meloun sirq = &sc->isrcs[irq];
2954b84206bSMichal Meloun if (intr_isrc_dispatch(&sirq->isrc, tf) != 0) {
2964b84206bSMichal Meloun mv_ap806_sei_isrc_mask(sc, sirq, 0);
2974b84206bSMichal Meloun mv_ap806_sei_isrc_eoi(sc, sirq);
2984b84206bSMichal Meloun device_printf(sc->dev,
2994b84206bSMichal Meloun "Stray irq %u disabled\n", irq);
3004b84206bSMichal Meloun }
3014b84206bSMichal Meloun }
3024b84206bSMichal Meloun
3034b84206bSMichal Meloun return (FILTER_HANDLED);
3044b84206bSMichal Meloun }
3054b84206bSMichal Meloun
3064b84206bSMichal Meloun static int
mv_ap806_sei_probe(device_t dev)3074b84206bSMichal Meloun mv_ap806_sei_probe(device_t dev)
3084b84206bSMichal Meloun {
3094b84206bSMichal Meloun
3104b84206bSMichal Meloun if (!ofw_bus_status_okay(dev))
3114b84206bSMichal Meloun return (ENXIO);
3124b84206bSMichal Meloun
3134b84206bSMichal Meloun if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
3144b84206bSMichal Meloun return (ENXIO);
3154b84206bSMichal Meloun
3164b84206bSMichal Meloun device_set_desc(dev, "Marvell SEI");
3174b84206bSMichal Meloun return (BUS_PROBE_DEFAULT);
3184b84206bSMichal Meloun }
3194b84206bSMichal Meloun
3204b84206bSMichal Meloun static int
mv_ap806_sei_attach(device_t dev)3214b84206bSMichal Meloun mv_ap806_sei_attach(device_t dev)
3224b84206bSMichal Meloun {
3234b84206bSMichal Meloun struct mv_ap806_sei_softc *sc;
3244b84206bSMichal Meloun phandle_t xref, node;
3254b84206bSMichal Meloun uint32_t irq;
3264b84206bSMichal Meloun const char *name;
3274b84206bSMichal Meloun int rv, rid;
3284b84206bSMichal Meloun
3294b84206bSMichal Meloun sc = device_get_softc(dev);
3304b84206bSMichal Meloun sc->dev = dev;
3314b84206bSMichal Meloun node = ofw_bus_get_node(dev);
3324b84206bSMichal Meloun MV_AP806_SEI_LOCK_INIT(sc);
3334b84206bSMichal Meloun
3344b84206bSMichal Meloun /* Allocate resources. */
3354b84206bSMichal Meloun rid = 0;
3364b84206bSMichal Meloun sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
3374b84206bSMichal Meloun RF_ACTIVE);
3384b84206bSMichal Meloun if (sc->mem_res == NULL) {
3394b84206bSMichal Meloun device_printf(dev, "Cannot allocate memory resources\n");
3404b84206bSMichal Meloun rv = ENXIO;
3414b84206bSMichal Meloun goto fail;
3424b84206bSMichal Meloun }
3434b84206bSMichal Meloun
3444b84206bSMichal Meloun rid = 0;
3454b84206bSMichal Meloun sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
3464b84206bSMichal Meloun if (sc->irq_res == NULL) {
3474b84206bSMichal Meloun device_printf(dev, "Cannot allocate IRQ resources\n");
3484b84206bSMichal Meloun rv = ENXIO;
3494b84206bSMichal Meloun goto fail;
3504b84206bSMichal Meloun }
3514b84206bSMichal Meloun
3524b84206bSMichal Meloun /* Mask all interrupts) */
3534b84206bSMichal Meloun WR4(sc, GICP_SEMR0, 0xFFFFFFFF);
3544b84206bSMichal Meloun WR4(sc, GICP_SEMR1, 0xFFFFFFFF);
3554b84206bSMichal Meloun
3564b84206bSMichal Meloun /* Create all interrupt sources */
3574b84206bSMichal Meloun sc->isrcs = malloc(sizeof(*sc->isrcs) * MV_AP806_SEI_MAX_NIRQS,
3584b84206bSMichal Meloun M_DEVBUF, M_WAITOK | M_ZERO);
3594b84206bSMichal Meloun name = device_get_nameunit(sc->dev);
3604b84206bSMichal Meloun for (irq = 0; irq < MV_AP806_SEI_MAX_NIRQS; irq++) {
3614b84206bSMichal Meloun sc->isrcs[irq].irq = irq;
3624b84206bSMichal Meloun rv = intr_isrc_register(&sc->isrcs[irq].isrc,
3634b84206bSMichal Meloun sc->dev, 0, "%s,%u", name, irq);
3644b84206bSMichal Meloun if (rv != 0)
3654b84206bSMichal Meloun goto fail; /* XXX deregister ISRCs */
3664b84206bSMichal Meloun }
36721cc0918SElliott Mitchell xref = OF_xref_from_node(node);
3684b84206bSMichal Meloun if (intr_pic_register(dev, xref) == NULL) {
3694b84206bSMichal Meloun device_printf(dev, "Cannot register SEI\n");
3704b84206bSMichal Meloun rv = ENXIO;
3714b84206bSMichal Meloun goto fail;
3724b84206bSMichal Meloun }
3734b84206bSMichal Meloun if (bus_setup_intr(dev, sc->irq_res,INTR_TYPE_MISC | INTR_MPSAFE,
3744b84206bSMichal Meloun mv_ap806_sei_intr, NULL, sc, &sc->irq_ih)) {
3754b84206bSMichal Meloun device_printf(dev,
3764b84206bSMichal Meloun "Unable to register interrupt handler\n");
3774b84206bSMichal Meloun rv = ENXIO;
3784b84206bSMichal Meloun goto fail;
3794b84206bSMichal Meloun }
3804b84206bSMichal Meloun
381986bbba9SKornel Duleba /*
382986bbba9SKornel Duleba * Bitmap of all IRQs.
383986bbba9SKornel Duleba * 1 - available, 0 - used.
384986bbba9SKornel Duleba */
385986bbba9SKornel Duleba BIT_FILL(MV_AP806_SEI_CP_SIZE, &sc->msi_bitmap);
386986bbba9SKornel Duleba
3874b84206bSMichal Meloun OF_device_register_xref(xref, dev);
3884b84206bSMichal Meloun return (0);
3894b84206bSMichal Meloun
3904b84206bSMichal Meloun fail:
3914b84206bSMichal Meloun if (sc->irq_ih != NULL)
3924b84206bSMichal Meloun bus_teardown_intr(dev, sc->irq_res, sc->irq_ih);
3934b84206bSMichal Meloun if (sc->irq_res != NULL)
3944b84206bSMichal Meloun bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
3954b84206bSMichal Meloun if (sc->mem_res != NULL)
3964b84206bSMichal Meloun bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
3974b84206bSMichal Meloun MV_AP806_SEI_LOCK_DESTROY(sc);
3984b84206bSMichal Meloun return (ENXIO);
3994b84206bSMichal Meloun }
4004b84206bSMichal Meloun
4014b84206bSMichal Meloun static int
mv_ap806_sei_detach(device_t dev)4024b84206bSMichal Meloun mv_ap806_sei_detach(device_t dev)
4034b84206bSMichal Meloun {
4044b84206bSMichal Meloun
4054b84206bSMichal Meloun return (EBUSY);
4064b84206bSMichal Meloun }
4074b84206bSMichal Meloun
408986bbba9SKornel Duleba static int
mv_ap806_sei_alloc_msi(device_t dev,device_t child,int count,int maxcount,device_t * pic,struct intr_irqsrc ** srcs)409986bbba9SKornel Duleba mv_ap806_sei_alloc_msi(device_t dev, device_t child, int count, int maxcount,
410986bbba9SKornel Duleba device_t *pic, struct intr_irqsrc **srcs)
411986bbba9SKornel Duleba {
412986bbba9SKornel Duleba struct mv_ap806_sei_softc *sc;
413986bbba9SKornel Duleba int i, ret = 0, vector;
414986bbba9SKornel Duleba
415986bbba9SKornel Duleba sc = device_get_softc(dev);
416986bbba9SKornel Duleba
417986bbba9SKornel Duleba for (i = 0; i < count; i++) {
418986bbba9SKornel Duleba /*
419986bbba9SKornel Duleba * Find first available MSI vector represented by first set bit
420986bbba9SKornel Duleba * in the bitmap. BIT_FFS starts the count from 1,
421986bbba9SKornel Duleba * 0 means that nothing was found.
422986bbba9SKornel Duleba */
423986bbba9SKornel Duleba vector = BIT_FFS_AT(MV_AP806_SEI_CP_SIZE, &sc->msi_bitmap, 0);
424986bbba9SKornel Duleba if (vector == 0) {
425986bbba9SKornel Duleba ret = ENOMEM;
426986bbba9SKornel Duleba i--;
427986bbba9SKornel Duleba goto fail;
428986bbba9SKornel Duleba }
429986bbba9SKornel Duleba
430986bbba9SKornel Duleba vector--;
431986bbba9SKornel Duleba BIT_CLR(MV_AP806_SEI_CP_SIZE, vector, &sc->msi_bitmap);
432986bbba9SKornel Duleba vector += MV_AP806_SEI_CP_FIRST;
433986bbba9SKornel Duleba
434986bbba9SKornel Duleba srcs[i] = &sc->isrcs[vector].isrc;
435986bbba9SKornel Duleba }
436986bbba9SKornel Duleba
437986bbba9SKornel Duleba return (ret);
438986bbba9SKornel Duleba fail:
439986bbba9SKornel Duleba mv_ap806_sei_release_msi(dev, child, i + 1, srcs);
440986bbba9SKornel Duleba return (ret);
441986bbba9SKornel Duleba }
442986bbba9SKornel Duleba
443986bbba9SKornel Duleba static int
mv_ap806_sei_release_msi(device_t dev,device_t child,int count,struct intr_irqsrc ** srcs)444986bbba9SKornel Duleba mv_ap806_sei_release_msi(device_t dev, device_t child, int count, struct intr_irqsrc **srcs)
445986bbba9SKornel Duleba {
446986bbba9SKornel Duleba struct mv_ap806_sei_softc *sc;
447986bbba9SKornel Duleba int i;
448986bbba9SKornel Duleba
449986bbba9SKornel Duleba sc = device_get_softc(dev);
450986bbba9SKornel Duleba
451986bbba9SKornel Duleba for (i = 0; i < count; i++) {
452986bbba9SKornel Duleba BIT_SET(MV_AP806_SEI_CP_SIZE,
453986bbba9SKornel Duleba srcs[i]->isrc_irq - MV_AP806_SEI_CP_FIRST,
454986bbba9SKornel Duleba &sc->msi_bitmap);
455986bbba9SKornel Duleba }
456986bbba9SKornel Duleba
457986bbba9SKornel Duleba return (0);
458986bbba9SKornel Duleba }
459986bbba9SKornel Duleba
460986bbba9SKornel Duleba static int
mv_ap806_sei_map_msi(device_t dev,device_t child,struct intr_irqsrc * isrc,uint64_t * addr,uint32_t * data)461986bbba9SKornel Duleba mv_ap806_sei_map_msi(device_t dev, device_t child, struct intr_irqsrc *isrc,
462986bbba9SKornel Duleba uint64_t *addr, uint32_t *data)
463986bbba9SKornel Duleba {
464986bbba9SKornel Duleba struct mv_ap806_sei_softc *sc;
465986bbba9SKornel Duleba
466986bbba9SKornel Duleba sc = device_get_softc(dev);
467986bbba9SKornel Duleba
468986bbba9SKornel Duleba *addr = rman_get_start(sc->mem_res) + MV_AP806_SEI_SETSPI_OFFSET;
469986bbba9SKornel Duleba *data = isrc->isrc_irq;
470986bbba9SKornel Duleba
471986bbba9SKornel Duleba return (0);
472986bbba9SKornel Duleba }
473986bbba9SKornel Duleba
4744b84206bSMichal Meloun static device_method_t mv_ap806_sei_methods[] = {
4754b84206bSMichal Meloun /* Device interface */
4764b84206bSMichal Meloun DEVMETHOD(device_probe, mv_ap806_sei_probe),
4774b84206bSMichal Meloun DEVMETHOD(device_attach, mv_ap806_sei_attach),
4784b84206bSMichal Meloun DEVMETHOD(device_detach, mv_ap806_sei_detach),
4794b84206bSMichal Meloun
4804b84206bSMichal Meloun /* Interrupt controller interface */
4814b84206bSMichal Meloun DEVMETHOD(pic_disable_intr, mv_ap806_sei_disable_intr),
4824b84206bSMichal Meloun DEVMETHOD(pic_enable_intr, mv_ap806_sei_enable_intr),
4834b84206bSMichal Meloun DEVMETHOD(pic_map_intr, mv_ap806_sei_map_intr),
4844b84206bSMichal Meloun DEVMETHOD(pic_setup_intr, mv_ap806_sei_setup_intr),
4854b84206bSMichal Meloun DEVMETHOD(pic_teardown_intr, mv_ap806_sei_teardown_intr),
4864b84206bSMichal Meloun DEVMETHOD(pic_post_filter, mv_ap806_sei_post_filter),
4874b84206bSMichal Meloun DEVMETHOD(pic_post_ithread, mv_ap806_sei_post_ithread),
4884b84206bSMichal Meloun DEVMETHOD(pic_pre_ithread, mv_ap806_sei_pre_ithread),
4894b84206bSMichal Meloun
490986bbba9SKornel Duleba /* MSI interface */
491986bbba9SKornel Duleba DEVMETHOD(msi_alloc_msi, mv_ap806_sei_alloc_msi),
492986bbba9SKornel Duleba DEVMETHOD(msi_release_msi, mv_ap806_sei_release_msi),
493986bbba9SKornel Duleba DEVMETHOD(msi_map_msi, mv_ap806_sei_map_msi),
494986bbba9SKornel Duleba
4954b84206bSMichal Meloun DEVMETHOD_END
4964b84206bSMichal Meloun };
4974b84206bSMichal Meloun
4984b84206bSMichal Meloun static driver_t mv_ap806_sei_driver = {
4994b84206bSMichal Meloun "mv_ap806_sei",
5004b84206bSMichal Meloun mv_ap806_sei_methods,
5014b84206bSMichal Meloun sizeof(struct mv_ap806_sei_softc),
5024b84206bSMichal Meloun };
5034b84206bSMichal Meloun
504a3b866cbSJohn Baldwin EARLY_DRIVER_MODULE(mv_ap806_sei, simplebus, mv_ap806_sei_driver, 0, 0,
505a3b866cbSJohn Baldwin BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
506