144d027bbSEmmanuel Vadot /*- 244d027bbSEmmanuel Vadot * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 344d027bbSEmmanuel Vadot * 444d027bbSEmmanuel Vadot * Copyright (c) 2018 Rubicon Communications, LLC (Netgate) 544d027bbSEmmanuel Vadot * 644d027bbSEmmanuel Vadot * Redistribution and use in source and binary forms, with or without 744d027bbSEmmanuel Vadot * modification, are permitted provided that the following conditions 844d027bbSEmmanuel Vadot * are met: 944d027bbSEmmanuel Vadot * 1. Redistributions of source code must retain the above copyright 1044d027bbSEmmanuel Vadot * notice, this list of conditions and the following disclaimer. 1144d027bbSEmmanuel Vadot * 2. Redistributions in binary form must reproduce the above copyright 1244d027bbSEmmanuel Vadot * notice, this list of conditions and the following disclaimer in the 1344d027bbSEmmanuel Vadot * documentation and/or other materials provided with the distribution. 1444d027bbSEmmanuel Vadot * 1544d027bbSEmmanuel Vadot * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1644d027bbSEmmanuel Vadot * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1744d027bbSEmmanuel Vadot * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1844d027bbSEmmanuel Vadot * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1944d027bbSEmmanuel Vadot * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2044d027bbSEmmanuel Vadot * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2144d027bbSEmmanuel Vadot * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2244d027bbSEmmanuel Vadot * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2344d027bbSEmmanuel Vadot * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2444d027bbSEmmanuel Vadot * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2544d027bbSEmmanuel Vadot * SUCH DAMAGE. 2644d027bbSEmmanuel Vadot * 2744d027bbSEmmanuel Vadot * $FreeBSD$ 2844d027bbSEmmanuel Vadot */ 2944d027bbSEmmanuel Vadot 3044d027bbSEmmanuel Vadot #include <sys/cdefs.h> 3144d027bbSEmmanuel Vadot __FBSDID("$FreeBSD$"); 3244d027bbSEmmanuel Vadot 3344d027bbSEmmanuel Vadot #include <sys/param.h> 3444d027bbSEmmanuel Vadot #include <sys/systm.h> 3544d027bbSEmmanuel Vadot #include <sys/bus.h> 3644d027bbSEmmanuel Vadot 37*986bbba9SKornel Duleba #include <sys/bitset.h> 3844d027bbSEmmanuel Vadot #include <sys/kernel.h> 3944d027bbSEmmanuel Vadot #include <sys/module.h> 4044d027bbSEmmanuel Vadot #include <sys/rman.h> 4144d027bbSEmmanuel Vadot #include <sys/lock.h> 4244d027bbSEmmanuel Vadot #include <sys/mutex.h> 4344d027bbSEmmanuel Vadot 4444d027bbSEmmanuel Vadot #include <machine/bus.h> 4544d027bbSEmmanuel Vadot #include <machine/resource.h> 4644d027bbSEmmanuel Vadot #include <machine/intr.h> 4744d027bbSEmmanuel Vadot 4844d027bbSEmmanuel Vadot #include <dev/fdt/simplebus.h> 4944d027bbSEmmanuel Vadot 5044d027bbSEmmanuel Vadot #include <dev/ofw/ofw_bus.h> 5144d027bbSEmmanuel Vadot #include <dev/ofw/ofw_bus_subr.h> 5244d027bbSEmmanuel Vadot 53*986bbba9SKornel Duleba #include <arm/arm/gic_common.h> 54*986bbba9SKornel Duleba 55*986bbba9SKornel Duleba #include <dt-bindings/interrupt-controller/irq.h> 56*986bbba9SKornel Duleba 57*986bbba9SKornel Duleba #include "msi_if.h" 5844d027bbSEmmanuel Vadot #include "pic_if.h" 5944d027bbSEmmanuel Vadot 6044d027bbSEmmanuel Vadot #define MV_AP806_GICP_MAX_NIRQS 207 6144d027bbSEmmanuel Vadot 62*986bbba9SKornel Duleba MALLOC_DECLARE(M_GICP); 63*986bbba9SKornel Duleba MALLOC_DEFINE(M_GICP, "gicp", "Marvell gicp driver"); 64*986bbba9SKornel Duleba 6544d027bbSEmmanuel Vadot struct mv_ap806_gicp_softc { 6644d027bbSEmmanuel Vadot device_t dev; 6744d027bbSEmmanuel Vadot device_t parent; 6844d027bbSEmmanuel Vadot struct resource *res; 6944d027bbSEmmanuel Vadot 7044d027bbSEmmanuel Vadot ssize_t spi_ranges_cnt; 7144d027bbSEmmanuel Vadot uint32_t *spi_ranges; 724b84206bSMichal Meloun struct intr_map_data_fdt *parent_map_data; 73*986bbba9SKornel Duleba 74*986bbba9SKornel Duleba ssize_t msi_bitmap_size; /* Nr of bits in the bitmap. */ 75*986bbba9SKornel Duleba BITSET_DEFINE_VAR() *msi_bitmap; 7644d027bbSEmmanuel Vadot }; 7744d027bbSEmmanuel Vadot 7844d027bbSEmmanuel Vadot static struct ofw_compat_data compat_data[] = { 7944d027bbSEmmanuel Vadot {"marvell,ap806-gicp", 1}, 8044d027bbSEmmanuel Vadot {NULL, 0} 8144d027bbSEmmanuel Vadot }; 8244d027bbSEmmanuel Vadot 8344d027bbSEmmanuel Vadot #define RD4(sc, reg) bus_read_4((sc)->res, (reg)) 8444d027bbSEmmanuel Vadot #define WR4(sc, reg, val) bus_write_4((sc)->res, (reg), (val)) 8544d027bbSEmmanuel Vadot 86*986bbba9SKornel Duleba static msi_alloc_msi_t mv_ap806_gicp_alloc_msi; 87*986bbba9SKornel Duleba static msi_release_msi_t mv_ap806_gicp_release_msi; 88*986bbba9SKornel Duleba static msi_map_msi_t mv_ap806_gicp_map_msi; 89*986bbba9SKornel Duleba 9044d027bbSEmmanuel Vadot static int 9144d027bbSEmmanuel Vadot mv_ap806_gicp_probe(device_t dev) 9244d027bbSEmmanuel Vadot { 9344d027bbSEmmanuel Vadot 9444d027bbSEmmanuel Vadot if (!ofw_bus_status_okay(dev)) 9544d027bbSEmmanuel Vadot return (ENXIO); 9644d027bbSEmmanuel Vadot 9744d027bbSEmmanuel Vadot if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 9844d027bbSEmmanuel Vadot return (ENXIO); 9944d027bbSEmmanuel Vadot 10044d027bbSEmmanuel Vadot device_set_desc(dev, "Marvell GICP"); 10144d027bbSEmmanuel Vadot return (BUS_PROBE_DEFAULT); 10244d027bbSEmmanuel Vadot } 10344d027bbSEmmanuel Vadot 10444d027bbSEmmanuel Vadot static int 10544d027bbSEmmanuel Vadot mv_ap806_gicp_attach(device_t dev) 10644d027bbSEmmanuel Vadot { 10744d027bbSEmmanuel Vadot struct mv_ap806_gicp_softc *sc; 10844d027bbSEmmanuel Vadot phandle_t node, xref, intr_parent; 109*986bbba9SKornel Duleba int i, rid; 11044d027bbSEmmanuel Vadot 11144d027bbSEmmanuel Vadot sc = device_get_softc(dev); 11244d027bbSEmmanuel Vadot sc->dev = dev; 11344d027bbSEmmanuel Vadot node = ofw_bus_get_node(dev); 11444d027bbSEmmanuel Vadot 11544d027bbSEmmanuel Vadot /* Look for our parent */ 11644d027bbSEmmanuel Vadot if ((intr_parent = ofw_bus_find_iparent(node)) == 0) { 1174b84206bSMichal Meloun device_printf(dev, 1184b84206bSMichal Meloun "Cannot find our parent interrupt controller\n"); 11944d027bbSEmmanuel Vadot return (ENXIO); 12044d027bbSEmmanuel Vadot } 12144d027bbSEmmanuel Vadot if ((sc->parent = OF_device_from_xref(intr_parent)) == NULL) { 1224b84206bSMichal Meloun device_printf(dev, 1234b84206bSMichal Meloun "cannot find parent interrupt controller device\n"); 12444d027bbSEmmanuel Vadot return (ENXIO); 12544d027bbSEmmanuel Vadot } 12644d027bbSEmmanuel Vadot 127*986bbba9SKornel Duleba rid = 0; 128*986bbba9SKornel Duleba sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); 129*986bbba9SKornel Duleba if (sc->res == NULL) { 130*986bbba9SKornel Duleba device_printf(dev, "cannot allocate resources for device\n"); 131*986bbba9SKornel Duleba return (ENXIO); 132*986bbba9SKornel Duleba } 133*986bbba9SKornel Duleba 134622d17daSZyta Szpak sc->spi_ranges_cnt = OF_getencprop_alloc_multi(node, "marvell,spi-ranges", 135622d17daSZyta Szpak sizeof(*sc->spi_ranges), (void **)&sc->spi_ranges); 13644d027bbSEmmanuel Vadot 137*986bbba9SKornel Duleba sc->msi_bitmap_size = 0; 138*986bbba9SKornel Duleba for (i = 0; i < sc->spi_ranges_cnt; i += 2) 139*986bbba9SKornel Duleba sc->msi_bitmap_size += sc->spi_ranges[i + 1]; 140*986bbba9SKornel Duleba 141*986bbba9SKornel Duleba /* 142*986bbba9SKornel Duleba * Create a bitmap of all MSIs that we have. 143*986bbba9SKornel Duleba * Each has a correspoding SPI in the GIC. 144*986bbba9SKornel Duleba * It will be used to dynamically allocate IRQs when requested. 145*986bbba9SKornel Duleba */ 146*986bbba9SKornel Duleba sc->msi_bitmap = BITSET_ALLOC(sc->msi_bitmap_size, M_GICP, M_WAITOK); 147*986bbba9SKornel Duleba BIT_FILL(sc->msi_bitmap_size, sc->msi_bitmap); /* 1 - available, 0 - used. */ 148*986bbba9SKornel Duleba 14944d027bbSEmmanuel Vadot xref = OF_xref_from_node(node); 15044d027bbSEmmanuel Vadot if (intr_pic_register(dev, xref) == NULL) { 15144d027bbSEmmanuel Vadot device_printf(dev, "Cannot register GICP\n"); 15244d027bbSEmmanuel Vadot return (ENXIO); 15344d027bbSEmmanuel Vadot } 1544b84206bSMichal Meloun /* Allocate GIC compatible mapping entry (3 cells) */ 1554b84206bSMichal Meloun sc->parent_map_data = (struct intr_map_data_fdt *)intr_alloc_map_data( 1564b84206bSMichal Meloun INTR_MAP_DATA_FDT, sizeof(struct intr_map_data_fdt) + 1574b84206bSMichal Meloun + 3 * sizeof(phandle_t), M_WAITOK | M_ZERO); 15844d027bbSEmmanuel Vadot OF_device_register_xref(xref, dev); 15944d027bbSEmmanuel Vadot 16044d027bbSEmmanuel Vadot return (0); 16144d027bbSEmmanuel Vadot } 16244d027bbSEmmanuel Vadot 16344d027bbSEmmanuel Vadot static int 16444d027bbSEmmanuel Vadot mv_ap806_gicp_detach(device_t dev) 16544d027bbSEmmanuel Vadot { 16644d027bbSEmmanuel Vadot 16744d027bbSEmmanuel Vadot return (EBUSY); 16844d027bbSEmmanuel Vadot } 16944d027bbSEmmanuel Vadot 170*986bbba9SKornel Duleba static uint32_t 171*986bbba9SKornel Duleba mv_ap806_gicp_msi_to_spi(struct mv_ap806_gicp_softc *sc, int irq) 172*986bbba9SKornel Duleba { 173*986bbba9SKornel Duleba int i; 174*986bbba9SKornel Duleba 175*986bbba9SKornel Duleba for (i = 0; i < sc->spi_ranges_cnt; i += 2) { 176*986bbba9SKornel Duleba if (irq < sc->spi_ranges[i + 1]) { 177*986bbba9SKornel Duleba irq += sc->spi_ranges[i]; 178*986bbba9SKornel Duleba break; 179*986bbba9SKornel Duleba } 180*986bbba9SKornel Duleba irq -= sc->spi_ranges[i + 1]; 181*986bbba9SKornel Duleba } 182*986bbba9SKornel Duleba 183*986bbba9SKornel Duleba return (irq - GIC_FIRST_SPI); 184*986bbba9SKornel Duleba } 185*986bbba9SKornel Duleba 186*986bbba9SKornel Duleba static uint32_t 187*986bbba9SKornel Duleba mv_ap806_gicp_irq_to_msi(struct mv_ap806_gicp_softc *sc, int irq) 188*986bbba9SKornel Duleba { 189*986bbba9SKornel Duleba int i; 190*986bbba9SKornel Duleba 191*986bbba9SKornel Duleba for (i = 0; i < sc->spi_ranges_cnt; i += 2) { 192*986bbba9SKornel Duleba if (irq >= sc->spi_ranges[i] && 193*986bbba9SKornel Duleba irq - sc->spi_ranges[i] < sc->spi_ranges[i + 1]) { 194*986bbba9SKornel Duleba irq -= sc->spi_ranges[i]; 195*986bbba9SKornel Duleba break; 196*986bbba9SKornel Duleba } 197*986bbba9SKornel Duleba } 198*986bbba9SKornel Duleba 199*986bbba9SKornel Duleba return (irq); 200*986bbba9SKornel Duleba } 201*986bbba9SKornel Duleba 2024b84206bSMichal Meloun static struct intr_map_data * 2034b84206bSMichal Meloun mv_ap806_gicp_convert_map_data(struct mv_ap806_gicp_softc *sc, 2044b84206bSMichal Meloun struct intr_map_data *data) 2054b84206bSMichal Meloun { 2064b84206bSMichal Meloun struct intr_map_data_fdt *daf; 207*986bbba9SKornel Duleba uint32_t irq_num; 2084b84206bSMichal Meloun 2094b84206bSMichal Meloun daf = (struct intr_map_data_fdt *)data; 2104b84206bSMichal Meloun if (daf->ncells != 2) 2114b84206bSMichal Meloun return (NULL); 2124b84206bSMichal Meloun 2134b84206bSMichal Meloun irq_num = daf->cells[0]; 2144b84206bSMichal Meloun if (irq_num >= MV_AP806_GICP_MAX_NIRQS) 2154b84206bSMichal Meloun return (NULL); 2164b84206bSMichal Meloun 2174b84206bSMichal Meloun /* Construct GIC compatible mapping. */ 2184b84206bSMichal Meloun sc->parent_map_data->ncells = 3; 2194b84206bSMichal Meloun sc->parent_map_data->cells[0] = 0; /* SPI */ 220*986bbba9SKornel Duleba sc->parent_map_data->cells[1] = mv_ap806_gicp_msi_to_spi(sc, irq_num); 221*986bbba9SKornel Duleba sc->parent_map_data->cells[2] = IRQ_TYPE_LEVEL_HIGH; 2224b84206bSMichal Meloun 2234b84206bSMichal Meloun return ((struct intr_map_data *)sc->parent_map_data); 2244b84206bSMichal Meloun } 2254b84206bSMichal Meloun 22644d027bbSEmmanuel Vadot static int 22744d027bbSEmmanuel Vadot mv_ap806_gicp_activate_intr(device_t dev, struct intr_irqsrc *isrc, 22844d027bbSEmmanuel Vadot struct resource *res, struct intr_map_data *data) 22944d027bbSEmmanuel Vadot { 23044d027bbSEmmanuel Vadot struct mv_ap806_gicp_softc *sc; 23144d027bbSEmmanuel Vadot 23244d027bbSEmmanuel Vadot sc = device_get_softc(dev); 2334b84206bSMichal Meloun data = mv_ap806_gicp_convert_map_data(sc, data); 2344b84206bSMichal Meloun if (data == NULL) 2354b84206bSMichal Meloun return (EINVAL); 23644d027bbSEmmanuel Vadot 23744d027bbSEmmanuel Vadot return (PIC_ACTIVATE_INTR(sc->parent, isrc, res, data)); 23844d027bbSEmmanuel Vadot } 23944d027bbSEmmanuel Vadot 24044d027bbSEmmanuel Vadot static void 24144d027bbSEmmanuel Vadot mv_ap806_gicp_enable_intr(device_t dev, struct intr_irqsrc *isrc) 24244d027bbSEmmanuel Vadot { 24344d027bbSEmmanuel Vadot struct mv_ap806_gicp_softc *sc; 24444d027bbSEmmanuel Vadot 24544d027bbSEmmanuel Vadot sc = device_get_softc(dev); 24644d027bbSEmmanuel Vadot 24744d027bbSEmmanuel Vadot PIC_ENABLE_INTR(sc->parent, isrc); 24844d027bbSEmmanuel Vadot } 24944d027bbSEmmanuel Vadot 25044d027bbSEmmanuel Vadot static void 25144d027bbSEmmanuel Vadot mv_ap806_gicp_disable_intr(device_t dev, struct intr_irqsrc *isrc) 25244d027bbSEmmanuel Vadot { 25344d027bbSEmmanuel Vadot struct mv_ap806_gicp_softc *sc; 25444d027bbSEmmanuel Vadot 25544d027bbSEmmanuel Vadot sc = device_get_softc(dev); 25644d027bbSEmmanuel Vadot 25744d027bbSEmmanuel Vadot PIC_DISABLE_INTR(sc->parent, isrc); 25844d027bbSEmmanuel Vadot } 25944d027bbSEmmanuel Vadot 26044d027bbSEmmanuel Vadot static int 26144d027bbSEmmanuel Vadot mv_ap806_gicp_map_intr(device_t dev, struct intr_map_data *data, 26244d027bbSEmmanuel Vadot struct intr_irqsrc **isrcp) 26344d027bbSEmmanuel Vadot { 26444d027bbSEmmanuel Vadot 265*986bbba9SKornel Duleba panic("%s: MSI interface has to be used to map an interrupt.\n", 266*986bbba9SKornel Duleba __func__); 26744d027bbSEmmanuel Vadot } 26844d027bbSEmmanuel Vadot 26944d027bbSEmmanuel Vadot static int 27044d027bbSEmmanuel Vadot mv_ap806_gicp_deactivate_intr(device_t dev, struct intr_irqsrc *isrc, 27144d027bbSEmmanuel Vadot struct resource *res, struct intr_map_data *data) 27244d027bbSEmmanuel Vadot { 27344d027bbSEmmanuel Vadot struct mv_ap806_gicp_softc *sc; 27444d027bbSEmmanuel Vadot 27544d027bbSEmmanuel Vadot sc = device_get_softc(dev); 27644d027bbSEmmanuel Vadot 2774b84206bSMichal Meloun data = mv_ap806_gicp_convert_map_data(sc, data); 2784b84206bSMichal Meloun if (data == NULL) 2794b84206bSMichal Meloun return (EINVAL); 2804b84206bSMichal Meloun 28144d027bbSEmmanuel Vadot return (PIC_DEACTIVATE_INTR(sc->parent, isrc, res, data)); 28244d027bbSEmmanuel Vadot } 28344d027bbSEmmanuel Vadot 28444d027bbSEmmanuel Vadot static int 28544d027bbSEmmanuel Vadot mv_ap806_gicp_setup_intr(device_t dev, struct intr_irqsrc *isrc, 28644d027bbSEmmanuel Vadot struct resource *res, struct intr_map_data *data) 28744d027bbSEmmanuel Vadot { 28844d027bbSEmmanuel Vadot struct mv_ap806_gicp_softc *sc; 28944d027bbSEmmanuel Vadot 29044d027bbSEmmanuel Vadot sc = device_get_softc(dev); 2914b84206bSMichal Meloun data = mv_ap806_gicp_convert_map_data(sc, data); 2924b84206bSMichal Meloun if (data == NULL) 2934b84206bSMichal Meloun return (EINVAL); 29444d027bbSEmmanuel Vadot 29544d027bbSEmmanuel Vadot return (PIC_SETUP_INTR(sc->parent, isrc, res, data)); 29644d027bbSEmmanuel Vadot } 29744d027bbSEmmanuel Vadot 29844d027bbSEmmanuel Vadot static int 29944d027bbSEmmanuel Vadot mv_ap806_gicp_teardown_intr(device_t dev, struct intr_irqsrc *isrc, 30044d027bbSEmmanuel Vadot struct resource *res, struct intr_map_data *data) 30144d027bbSEmmanuel Vadot { 30244d027bbSEmmanuel Vadot struct mv_ap806_gicp_softc *sc; 30344d027bbSEmmanuel Vadot 30444d027bbSEmmanuel Vadot sc = device_get_softc(dev); 3054b84206bSMichal Meloun data = mv_ap806_gicp_convert_map_data(sc, data); 3064b84206bSMichal Meloun if (data == NULL) 3074b84206bSMichal Meloun return (EINVAL); 30844d027bbSEmmanuel Vadot 30944d027bbSEmmanuel Vadot return (PIC_TEARDOWN_INTR(sc->parent, isrc, res, data)); 31044d027bbSEmmanuel Vadot } 31144d027bbSEmmanuel Vadot 31244d027bbSEmmanuel Vadot static void 31344d027bbSEmmanuel Vadot mv_ap806_gicp_pre_ithread(device_t dev, struct intr_irqsrc *isrc) 31444d027bbSEmmanuel Vadot { 31544d027bbSEmmanuel Vadot struct mv_ap806_gicp_softc *sc; 31644d027bbSEmmanuel Vadot 31744d027bbSEmmanuel Vadot sc = device_get_softc(dev); 31844d027bbSEmmanuel Vadot 31944d027bbSEmmanuel Vadot PIC_PRE_ITHREAD(sc->parent, isrc); 32044d027bbSEmmanuel Vadot } 32144d027bbSEmmanuel Vadot 32244d027bbSEmmanuel Vadot static void 32344d027bbSEmmanuel Vadot mv_ap806_gicp_post_ithread(device_t dev, struct intr_irqsrc *isrc) 32444d027bbSEmmanuel Vadot { 32544d027bbSEmmanuel Vadot struct mv_ap806_gicp_softc *sc; 32644d027bbSEmmanuel Vadot 32744d027bbSEmmanuel Vadot sc = device_get_softc(dev); 32844d027bbSEmmanuel Vadot 32944d027bbSEmmanuel Vadot PIC_POST_ITHREAD(sc->parent, isrc); 33044d027bbSEmmanuel Vadot } 33144d027bbSEmmanuel Vadot 33244d027bbSEmmanuel Vadot static void 33344d027bbSEmmanuel Vadot mv_ap806_gicp_post_filter(device_t dev, struct intr_irqsrc *isrc) 33444d027bbSEmmanuel Vadot { 33544d027bbSEmmanuel Vadot struct mv_ap806_gicp_softc *sc; 33644d027bbSEmmanuel Vadot 33744d027bbSEmmanuel Vadot sc = device_get_softc(dev); 33844d027bbSEmmanuel Vadot 33944d027bbSEmmanuel Vadot PIC_POST_FILTER(sc->parent, isrc); 34044d027bbSEmmanuel Vadot } 34144d027bbSEmmanuel Vadot 342*986bbba9SKornel Duleba static int 343*986bbba9SKornel Duleba mv_ap806_gicp_alloc_msi(device_t dev, device_t child, int count, int maxcount, 344*986bbba9SKornel Duleba device_t *pic, struct intr_irqsrc **srcs) 345*986bbba9SKornel Duleba { 346*986bbba9SKornel Duleba struct mv_ap806_gicp_softc *sc; 347*986bbba9SKornel Duleba int i, ret, vector; 348*986bbba9SKornel Duleba 349*986bbba9SKornel Duleba sc = device_get_softc(dev); 350*986bbba9SKornel Duleba 351*986bbba9SKornel Duleba for (i = 0; i < count; i++) { 352*986bbba9SKornel Duleba /* 353*986bbba9SKornel Duleba * Find first available vector represented by first set bit 354*986bbba9SKornel Duleba * in the bitmap. BIT_FFS starts the count from 1, 0 means 355*986bbba9SKornel Duleba * that nothing was found. 356*986bbba9SKornel Duleba */ 357*986bbba9SKornel Duleba vector = BIT_FFS(sc->msi_bitmap_size, sc->msi_bitmap); 358*986bbba9SKornel Duleba if (vector == 0) { 359*986bbba9SKornel Duleba ret = ENOMEM; 360*986bbba9SKornel Duleba i--; 361*986bbba9SKornel Duleba goto fail; 362*986bbba9SKornel Duleba } 363*986bbba9SKornel Duleba vector--; 364*986bbba9SKornel Duleba BIT_CLR(sc->msi_bitmap_size, vector, sc->msi_bitmap); 365*986bbba9SKornel Duleba 366*986bbba9SKornel Duleba /* Create GIC compatible SPI interrupt description. */ 367*986bbba9SKornel Duleba sc->parent_map_data->ncells = 3; 368*986bbba9SKornel Duleba sc->parent_map_data->cells[0] = 0; /* SPI */ 369*986bbba9SKornel Duleba sc->parent_map_data->cells[1] = mv_ap806_gicp_msi_to_spi(sc, vector); 370*986bbba9SKornel Duleba sc->parent_map_data->cells[2] = IRQ_TYPE_LEVEL_HIGH; 371*986bbba9SKornel Duleba 372*986bbba9SKornel Duleba ret = PIC_MAP_INTR(sc->parent, 373*986bbba9SKornel Duleba (struct intr_map_data *)sc->parent_map_data, 374*986bbba9SKornel Duleba &srcs[i]); 375*986bbba9SKornel Duleba if (ret != 0) 376*986bbba9SKornel Duleba goto fail; 377*986bbba9SKornel Duleba 378*986bbba9SKornel Duleba srcs[i]->isrc_dev = dev; 379*986bbba9SKornel Duleba } 380*986bbba9SKornel Duleba 381*986bbba9SKornel Duleba return (0); 382*986bbba9SKornel Duleba fail: 383*986bbba9SKornel Duleba mv_ap806_gicp_release_msi(dev, child, i + 1, srcs); 384*986bbba9SKornel Duleba return (ret); 385*986bbba9SKornel Duleba } 386*986bbba9SKornel Duleba 387*986bbba9SKornel Duleba static int 388*986bbba9SKornel Duleba mv_ap806_gicp_release_msi(device_t dev, device_t child, int count, 389*986bbba9SKornel Duleba struct intr_irqsrc **srcs) 390*986bbba9SKornel Duleba { 391*986bbba9SKornel Duleba struct mv_ap806_gicp_softc *sc; 392*986bbba9SKornel Duleba int i; 393*986bbba9SKornel Duleba 394*986bbba9SKornel Duleba sc = device_get_softc(dev); 395*986bbba9SKornel Duleba 396*986bbba9SKornel Duleba for (i = 0; i < count; i++) { 397*986bbba9SKornel Duleba BIT_SET(sc->msi_bitmap_size, 398*986bbba9SKornel Duleba mv_ap806_gicp_irq_to_msi(sc, srcs[i]->isrc_irq), 399*986bbba9SKornel Duleba sc->msi_bitmap); 400*986bbba9SKornel Duleba } 401*986bbba9SKornel Duleba 402*986bbba9SKornel Duleba return (0); 403*986bbba9SKornel Duleba } 404*986bbba9SKornel Duleba 405*986bbba9SKornel Duleba static int 406*986bbba9SKornel Duleba mv_ap806_gicp_map_msi(device_t dev, device_t child, struct intr_irqsrc *isrc, 407*986bbba9SKornel Duleba uint64_t *addr, uint32_t *data) 408*986bbba9SKornel Duleba { 409*986bbba9SKornel Duleba struct mv_ap806_gicp_softc *sc; 410*986bbba9SKornel Duleba 411*986bbba9SKornel Duleba sc = device_get_softc(dev); 412*986bbba9SKornel Duleba 413*986bbba9SKornel Duleba *addr = rman_get_start(sc->res); 414*986bbba9SKornel Duleba *data = mv_ap806_gicp_irq_to_msi(sc, isrc->isrc_irq); 415*986bbba9SKornel Duleba 416*986bbba9SKornel Duleba return (0); 417*986bbba9SKornel Duleba } 418*986bbba9SKornel Duleba 41944d027bbSEmmanuel Vadot static device_method_t mv_ap806_gicp_methods[] = { 42044d027bbSEmmanuel Vadot /* Device interface */ 42144d027bbSEmmanuel Vadot DEVMETHOD(device_probe, mv_ap806_gicp_probe), 42244d027bbSEmmanuel Vadot DEVMETHOD(device_attach, mv_ap806_gicp_attach), 42344d027bbSEmmanuel Vadot DEVMETHOD(device_detach, mv_ap806_gicp_detach), 42444d027bbSEmmanuel Vadot 42544d027bbSEmmanuel Vadot /* Interrupt controller interface */ 42644d027bbSEmmanuel Vadot DEVMETHOD(pic_activate_intr, mv_ap806_gicp_activate_intr), 42744d027bbSEmmanuel Vadot DEVMETHOD(pic_disable_intr, mv_ap806_gicp_disable_intr), 42844d027bbSEmmanuel Vadot DEVMETHOD(pic_enable_intr, mv_ap806_gicp_enable_intr), 42944d027bbSEmmanuel Vadot DEVMETHOD(pic_map_intr, mv_ap806_gicp_map_intr), 43044d027bbSEmmanuel Vadot DEVMETHOD(pic_deactivate_intr, mv_ap806_gicp_deactivate_intr), 43144d027bbSEmmanuel Vadot DEVMETHOD(pic_setup_intr, mv_ap806_gicp_setup_intr), 43244d027bbSEmmanuel Vadot DEVMETHOD(pic_teardown_intr, mv_ap806_gicp_teardown_intr), 43344d027bbSEmmanuel Vadot DEVMETHOD(pic_post_filter, mv_ap806_gicp_post_filter), 43444d027bbSEmmanuel Vadot DEVMETHOD(pic_post_ithread, mv_ap806_gicp_post_ithread), 43544d027bbSEmmanuel Vadot DEVMETHOD(pic_pre_ithread, mv_ap806_gicp_pre_ithread), 43644d027bbSEmmanuel Vadot 437*986bbba9SKornel Duleba /* MSI interface */ 438*986bbba9SKornel Duleba DEVMETHOD(msi_alloc_msi, mv_ap806_gicp_alloc_msi), 439*986bbba9SKornel Duleba DEVMETHOD(msi_release_msi, mv_ap806_gicp_release_msi), 440*986bbba9SKornel Duleba DEVMETHOD(msi_map_msi, mv_ap806_gicp_map_msi), 441*986bbba9SKornel Duleba 44244d027bbSEmmanuel Vadot DEVMETHOD_END 44344d027bbSEmmanuel Vadot }; 44444d027bbSEmmanuel Vadot 44544d027bbSEmmanuel Vadot static devclass_t mv_ap806_gicp_devclass; 44644d027bbSEmmanuel Vadot 44744d027bbSEmmanuel Vadot static driver_t mv_ap806_gicp_driver = { 44844d027bbSEmmanuel Vadot "mv_ap806_gicp", 44944d027bbSEmmanuel Vadot mv_ap806_gicp_methods, 45044d027bbSEmmanuel Vadot sizeof(struct mv_ap806_gicp_softc), 45144d027bbSEmmanuel Vadot }; 45244d027bbSEmmanuel Vadot 45344d027bbSEmmanuel Vadot EARLY_DRIVER_MODULE(mv_ap806_gicp, simplebus, mv_ap806_gicp_driver, 45444d027bbSEmmanuel Vadot mv_ap806_gicp_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); 455