xref: /freebsd/sys/arm/mv/mv_ap806_gicp.c (revision 986bbba9a6994155ee651ce2df2f6fd9a67e13ea)
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