144d027bbSEmmanuel Vadot /*-
2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
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
2844d027bbSEmmanuel Vadot #include <sys/param.h>
2944d027bbSEmmanuel Vadot #include <sys/systm.h>
3044d027bbSEmmanuel Vadot #include <sys/bus.h>
3144d027bbSEmmanuel Vadot
32986bbba9SKornel Duleba #include <sys/bitset.h>
3344d027bbSEmmanuel Vadot #include <sys/kernel.h>
3444d027bbSEmmanuel Vadot #include <sys/module.h>
3544d027bbSEmmanuel Vadot #include <sys/rman.h>
3644d027bbSEmmanuel Vadot #include <sys/lock.h>
3744d027bbSEmmanuel Vadot #include <sys/mutex.h>
3844d027bbSEmmanuel Vadot
3944d027bbSEmmanuel Vadot #include <machine/bus.h>
4044d027bbSEmmanuel Vadot #include <machine/resource.h>
4144d027bbSEmmanuel Vadot #include <machine/intr.h>
4244d027bbSEmmanuel Vadot
4344d027bbSEmmanuel Vadot #include <dev/fdt/simplebus.h>
4444d027bbSEmmanuel Vadot
4544d027bbSEmmanuel Vadot #include <dev/ofw/ofw_bus.h>
4644d027bbSEmmanuel Vadot #include <dev/ofw/ofw_bus_subr.h>
4744d027bbSEmmanuel Vadot
48986bbba9SKornel Duleba #include <arm/arm/gic_common.h>
49986bbba9SKornel Duleba
50986bbba9SKornel Duleba #include <dt-bindings/interrupt-controller/irq.h>
51986bbba9SKornel Duleba
52986bbba9SKornel Duleba #include "msi_if.h"
5344d027bbSEmmanuel Vadot #include "pic_if.h"
5444d027bbSEmmanuel Vadot
5544d027bbSEmmanuel Vadot #define MV_AP806_GICP_MAX_NIRQS 207
5644d027bbSEmmanuel Vadot
57986bbba9SKornel Duleba MALLOC_DECLARE(M_GICP);
58986bbba9SKornel Duleba MALLOC_DEFINE(M_GICP, "gicp", "Marvell gicp driver");
59986bbba9SKornel Duleba
6044d027bbSEmmanuel Vadot struct mv_ap806_gicp_softc {
6144d027bbSEmmanuel Vadot device_t dev;
6244d027bbSEmmanuel Vadot device_t parent;
6344d027bbSEmmanuel Vadot struct resource *res;
6444d027bbSEmmanuel Vadot
6544d027bbSEmmanuel Vadot ssize_t spi_ranges_cnt;
6644d027bbSEmmanuel Vadot uint32_t *spi_ranges;
674b84206bSMichal Meloun struct intr_map_data_fdt *parent_map_data;
68986bbba9SKornel Duleba
69986bbba9SKornel Duleba ssize_t msi_bitmap_size; /* Nr of bits in the bitmap. */
70986bbba9SKornel Duleba BITSET_DEFINE_VAR() *msi_bitmap;
7144d027bbSEmmanuel Vadot };
7244d027bbSEmmanuel Vadot
7344d027bbSEmmanuel Vadot static struct ofw_compat_data compat_data[] = {
7444d027bbSEmmanuel Vadot {"marvell,ap806-gicp", 1},
7544d027bbSEmmanuel Vadot {NULL, 0}
7644d027bbSEmmanuel Vadot };
7744d027bbSEmmanuel Vadot
7844d027bbSEmmanuel Vadot #define RD4(sc, reg) bus_read_4((sc)->res, (reg))
7944d027bbSEmmanuel Vadot #define WR4(sc, reg, val) bus_write_4((sc)->res, (reg), (val))
8044d027bbSEmmanuel Vadot
81986bbba9SKornel Duleba static msi_alloc_msi_t mv_ap806_gicp_alloc_msi;
82986bbba9SKornel Duleba static msi_release_msi_t mv_ap806_gicp_release_msi;
83986bbba9SKornel Duleba static msi_map_msi_t mv_ap806_gicp_map_msi;
84986bbba9SKornel Duleba
8544d027bbSEmmanuel Vadot static int
mv_ap806_gicp_probe(device_t dev)8644d027bbSEmmanuel Vadot mv_ap806_gicp_probe(device_t dev)
8744d027bbSEmmanuel Vadot {
8844d027bbSEmmanuel Vadot
8944d027bbSEmmanuel Vadot if (!ofw_bus_status_okay(dev))
9044d027bbSEmmanuel Vadot return (ENXIO);
9144d027bbSEmmanuel Vadot
9244d027bbSEmmanuel Vadot if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
9344d027bbSEmmanuel Vadot return (ENXIO);
9444d027bbSEmmanuel Vadot
9544d027bbSEmmanuel Vadot device_set_desc(dev, "Marvell GICP");
9644d027bbSEmmanuel Vadot return (BUS_PROBE_DEFAULT);
9744d027bbSEmmanuel Vadot }
9844d027bbSEmmanuel Vadot
9944d027bbSEmmanuel Vadot static int
mv_ap806_gicp_attach(device_t dev)10044d027bbSEmmanuel Vadot mv_ap806_gicp_attach(device_t dev)
10144d027bbSEmmanuel Vadot {
10244d027bbSEmmanuel Vadot struct mv_ap806_gicp_softc *sc;
10344d027bbSEmmanuel Vadot phandle_t node, xref, intr_parent;
104986bbba9SKornel Duleba int i, rid;
10544d027bbSEmmanuel Vadot
10644d027bbSEmmanuel Vadot sc = device_get_softc(dev);
10744d027bbSEmmanuel Vadot sc->dev = dev;
10844d027bbSEmmanuel Vadot node = ofw_bus_get_node(dev);
10944d027bbSEmmanuel Vadot
11044d027bbSEmmanuel Vadot /* Look for our parent */
11144d027bbSEmmanuel Vadot if ((intr_parent = ofw_bus_find_iparent(node)) == 0) {
1124b84206bSMichal Meloun device_printf(dev,
1134b84206bSMichal Meloun "Cannot find our parent interrupt controller\n");
11444d027bbSEmmanuel Vadot return (ENXIO);
11544d027bbSEmmanuel Vadot }
11644d027bbSEmmanuel Vadot if ((sc->parent = OF_device_from_xref(intr_parent)) == NULL) {
1174b84206bSMichal Meloun device_printf(dev,
1184b84206bSMichal Meloun "cannot find parent interrupt controller device\n");
11944d027bbSEmmanuel Vadot return (ENXIO);
12044d027bbSEmmanuel Vadot }
12144d027bbSEmmanuel Vadot
122986bbba9SKornel Duleba rid = 0;
123986bbba9SKornel Duleba sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
124986bbba9SKornel Duleba if (sc->res == NULL) {
125986bbba9SKornel Duleba device_printf(dev, "cannot allocate resources for device\n");
126986bbba9SKornel Duleba return (ENXIO);
127986bbba9SKornel Duleba }
128986bbba9SKornel Duleba
129622d17daSZyta Szpak sc->spi_ranges_cnt = OF_getencprop_alloc_multi(node, "marvell,spi-ranges",
130622d17daSZyta Szpak sizeof(*sc->spi_ranges), (void **)&sc->spi_ranges);
13144d027bbSEmmanuel Vadot
132986bbba9SKornel Duleba sc->msi_bitmap_size = 0;
133986bbba9SKornel Duleba for (i = 0; i < sc->spi_ranges_cnt; i += 2)
134986bbba9SKornel Duleba sc->msi_bitmap_size += sc->spi_ranges[i + 1];
135986bbba9SKornel Duleba
136986bbba9SKornel Duleba /*
137986bbba9SKornel Duleba * Create a bitmap of all MSIs that we have.
138986bbba9SKornel Duleba * Each has a correspoding SPI in the GIC.
139986bbba9SKornel Duleba * It will be used to dynamically allocate IRQs when requested.
140986bbba9SKornel Duleba */
141986bbba9SKornel Duleba sc->msi_bitmap = BITSET_ALLOC(sc->msi_bitmap_size, M_GICP, M_WAITOK);
142986bbba9SKornel Duleba BIT_FILL(sc->msi_bitmap_size, sc->msi_bitmap); /* 1 - available, 0 - used. */
143986bbba9SKornel Duleba
14444d027bbSEmmanuel Vadot xref = OF_xref_from_node(node);
14544d027bbSEmmanuel Vadot if (intr_pic_register(dev, xref) == NULL) {
14644d027bbSEmmanuel Vadot device_printf(dev, "Cannot register GICP\n");
14744d027bbSEmmanuel Vadot return (ENXIO);
14844d027bbSEmmanuel Vadot }
1494b84206bSMichal Meloun /* Allocate GIC compatible mapping entry (3 cells) */
1504b84206bSMichal Meloun sc->parent_map_data = (struct intr_map_data_fdt *)intr_alloc_map_data(
1514b84206bSMichal Meloun INTR_MAP_DATA_FDT, sizeof(struct intr_map_data_fdt) +
1524b84206bSMichal Meloun + 3 * sizeof(phandle_t), M_WAITOK | M_ZERO);
15344d027bbSEmmanuel Vadot OF_device_register_xref(xref, dev);
15444d027bbSEmmanuel Vadot
15544d027bbSEmmanuel Vadot return (0);
15644d027bbSEmmanuel Vadot }
15744d027bbSEmmanuel Vadot
15844d027bbSEmmanuel Vadot static int
mv_ap806_gicp_detach(device_t dev)15944d027bbSEmmanuel Vadot mv_ap806_gicp_detach(device_t dev)
16044d027bbSEmmanuel Vadot {
16144d027bbSEmmanuel Vadot
16244d027bbSEmmanuel Vadot return (EBUSY);
16344d027bbSEmmanuel Vadot }
16444d027bbSEmmanuel Vadot
165986bbba9SKornel Duleba static uint32_t
mv_ap806_gicp_msi_to_spi(struct mv_ap806_gicp_softc * sc,int irq)166986bbba9SKornel Duleba mv_ap806_gicp_msi_to_spi(struct mv_ap806_gicp_softc *sc, int irq)
167986bbba9SKornel Duleba {
168986bbba9SKornel Duleba int i;
169986bbba9SKornel Duleba
170986bbba9SKornel Duleba for (i = 0; i < sc->spi_ranges_cnt; i += 2) {
171986bbba9SKornel Duleba if (irq < sc->spi_ranges[i + 1]) {
172986bbba9SKornel Duleba irq += sc->spi_ranges[i];
173986bbba9SKornel Duleba break;
174986bbba9SKornel Duleba }
175986bbba9SKornel Duleba irq -= sc->spi_ranges[i + 1];
176986bbba9SKornel Duleba }
177986bbba9SKornel Duleba
178986bbba9SKornel Duleba return (irq - GIC_FIRST_SPI);
179986bbba9SKornel Duleba }
180986bbba9SKornel Duleba
181986bbba9SKornel Duleba static uint32_t
mv_ap806_gicp_irq_to_msi(struct mv_ap806_gicp_softc * sc,int irq)182986bbba9SKornel Duleba mv_ap806_gicp_irq_to_msi(struct mv_ap806_gicp_softc *sc, int irq)
183986bbba9SKornel Duleba {
184986bbba9SKornel Duleba int i;
185986bbba9SKornel Duleba
186986bbba9SKornel Duleba for (i = 0; i < sc->spi_ranges_cnt; i += 2) {
187986bbba9SKornel Duleba if (irq >= sc->spi_ranges[i] &&
188986bbba9SKornel Duleba irq - sc->spi_ranges[i] < sc->spi_ranges[i + 1]) {
189986bbba9SKornel Duleba irq -= sc->spi_ranges[i];
190986bbba9SKornel Duleba break;
191986bbba9SKornel Duleba }
192986bbba9SKornel Duleba }
193986bbba9SKornel Duleba
194986bbba9SKornel Duleba return (irq);
195986bbba9SKornel Duleba }
196986bbba9SKornel Duleba
1974b84206bSMichal Meloun static struct intr_map_data *
mv_ap806_gicp_convert_map_data(struct mv_ap806_gicp_softc * sc,struct intr_map_data * data)1984b84206bSMichal Meloun mv_ap806_gicp_convert_map_data(struct mv_ap806_gicp_softc *sc,
1994b84206bSMichal Meloun struct intr_map_data *data)
2004b84206bSMichal Meloun {
2014b84206bSMichal Meloun struct intr_map_data_fdt *daf;
202986bbba9SKornel Duleba uint32_t irq_num;
2034b84206bSMichal Meloun
2044b84206bSMichal Meloun daf = (struct intr_map_data_fdt *)data;
2054b84206bSMichal Meloun if (daf->ncells != 2)
2064b84206bSMichal Meloun return (NULL);
2074b84206bSMichal Meloun
2084b84206bSMichal Meloun irq_num = daf->cells[0];
2094b84206bSMichal Meloun if (irq_num >= MV_AP806_GICP_MAX_NIRQS)
2104b84206bSMichal Meloun return (NULL);
2114b84206bSMichal Meloun
2124b84206bSMichal Meloun /* Construct GIC compatible mapping. */
2134b84206bSMichal Meloun sc->parent_map_data->ncells = 3;
2144b84206bSMichal Meloun sc->parent_map_data->cells[0] = 0; /* SPI */
215986bbba9SKornel Duleba sc->parent_map_data->cells[1] = mv_ap806_gicp_msi_to_spi(sc, irq_num);
216986bbba9SKornel Duleba sc->parent_map_data->cells[2] = IRQ_TYPE_LEVEL_HIGH;
2174b84206bSMichal Meloun
2184b84206bSMichal Meloun return ((struct intr_map_data *)sc->parent_map_data);
2194b84206bSMichal Meloun }
2204b84206bSMichal Meloun
22144d027bbSEmmanuel Vadot static int
mv_ap806_gicp_activate_intr(device_t dev,struct intr_irqsrc * isrc,struct resource * res,struct intr_map_data * data)22244d027bbSEmmanuel Vadot mv_ap806_gicp_activate_intr(device_t dev, struct intr_irqsrc *isrc,
22344d027bbSEmmanuel Vadot struct resource *res, struct intr_map_data *data)
22444d027bbSEmmanuel Vadot {
22544d027bbSEmmanuel Vadot struct mv_ap806_gicp_softc *sc;
22644d027bbSEmmanuel Vadot
22744d027bbSEmmanuel Vadot sc = device_get_softc(dev);
2284b84206bSMichal Meloun data = mv_ap806_gicp_convert_map_data(sc, data);
2294b84206bSMichal Meloun if (data == NULL)
2304b84206bSMichal Meloun return (EINVAL);
23144d027bbSEmmanuel Vadot
23244d027bbSEmmanuel Vadot return (PIC_ACTIVATE_INTR(sc->parent, isrc, res, data));
23344d027bbSEmmanuel Vadot }
23444d027bbSEmmanuel Vadot
23544d027bbSEmmanuel Vadot static void
mv_ap806_gicp_enable_intr(device_t dev,struct intr_irqsrc * isrc)23644d027bbSEmmanuel Vadot mv_ap806_gicp_enable_intr(device_t dev, struct intr_irqsrc *isrc)
23744d027bbSEmmanuel Vadot {
23844d027bbSEmmanuel Vadot struct mv_ap806_gicp_softc *sc;
23944d027bbSEmmanuel Vadot
24044d027bbSEmmanuel Vadot sc = device_get_softc(dev);
24144d027bbSEmmanuel Vadot
24244d027bbSEmmanuel Vadot PIC_ENABLE_INTR(sc->parent, isrc);
24344d027bbSEmmanuel Vadot }
24444d027bbSEmmanuel Vadot
24544d027bbSEmmanuel Vadot static void
mv_ap806_gicp_disable_intr(device_t dev,struct intr_irqsrc * isrc)24644d027bbSEmmanuel Vadot mv_ap806_gicp_disable_intr(device_t dev, struct intr_irqsrc *isrc)
24744d027bbSEmmanuel Vadot {
24844d027bbSEmmanuel Vadot struct mv_ap806_gicp_softc *sc;
24944d027bbSEmmanuel Vadot
25044d027bbSEmmanuel Vadot sc = device_get_softc(dev);
25144d027bbSEmmanuel Vadot
25244d027bbSEmmanuel Vadot PIC_DISABLE_INTR(sc->parent, isrc);
25344d027bbSEmmanuel Vadot }
25444d027bbSEmmanuel Vadot
25544d027bbSEmmanuel Vadot static int
mv_ap806_gicp_map_intr(device_t dev,struct intr_map_data * data,struct intr_irqsrc ** isrcp)25644d027bbSEmmanuel Vadot mv_ap806_gicp_map_intr(device_t dev, struct intr_map_data *data,
25744d027bbSEmmanuel Vadot struct intr_irqsrc **isrcp)
25844d027bbSEmmanuel Vadot {
25944d027bbSEmmanuel Vadot
260986bbba9SKornel Duleba panic("%s: MSI interface has to be used to map an interrupt.\n",
261986bbba9SKornel Duleba __func__);
26244d027bbSEmmanuel Vadot }
26344d027bbSEmmanuel Vadot
26444d027bbSEmmanuel Vadot static int
mv_ap806_gicp_deactivate_intr(device_t dev,struct intr_irqsrc * isrc,struct resource * res,struct intr_map_data * data)26544d027bbSEmmanuel Vadot mv_ap806_gicp_deactivate_intr(device_t dev, struct intr_irqsrc *isrc,
26644d027bbSEmmanuel Vadot struct resource *res, struct intr_map_data *data)
26744d027bbSEmmanuel Vadot {
26844d027bbSEmmanuel Vadot struct mv_ap806_gicp_softc *sc;
26944d027bbSEmmanuel Vadot
27044d027bbSEmmanuel Vadot sc = device_get_softc(dev);
27144d027bbSEmmanuel Vadot
2724b84206bSMichal Meloun data = mv_ap806_gicp_convert_map_data(sc, data);
2734b84206bSMichal Meloun if (data == NULL)
2744b84206bSMichal Meloun return (EINVAL);
2754b84206bSMichal Meloun
27644d027bbSEmmanuel Vadot return (PIC_DEACTIVATE_INTR(sc->parent, isrc, res, data));
27744d027bbSEmmanuel Vadot }
27844d027bbSEmmanuel Vadot
27944d027bbSEmmanuel Vadot static int
mv_ap806_gicp_setup_intr(device_t dev,struct intr_irqsrc * isrc,struct resource * res,struct intr_map_data * data)28044d027bbSEmmanuel Vadot mv_ap806_gicp_setup_intr(device_t dev, struct intr_irqsrc *isrc,
28144d027bbSEmmanuel Vadot struct resource *res, struct intr_map_data *data)
28244d027bbSEmmanuel Vadot {
28344d027bbSEmmanuel Vadot struct mv_ap806_gicp_softc *sc;
28444d027bbSEmmanuel Vadot
28544d027bbSEmmanuel Vadot sc = device_get_softc(dev);
2864b84206bSMichal Meloun data = mv_ap806_gicp_convert_map_data(sc, data);
2874b84206bSMichal Meloun if (data == NULL)
2884b84206bSMichal Meloun return (EINVAL);
28944d027bbSEmmanuel Vadot
29044d027bbSEmmanuel Vadot return (PIC_SETUP_INTR(sc->parent, isrc, res, data));
29144d027bbSEmmanuel Vadot }
29244d027bbSEmmanuel Vadot
29344d027bbSEmmanuel Vadot static int
mv_ap806_gicp_teardown_intr(device_t dev,struct intr_irqsrc * isrc,struct resource * res,struct intr_map_data * data)29444d027bbSEmmanuel Vadot mv_ap806_gicp_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
29544d027bbSEmmanuel Vadot struct resource *res, struct intr_map_data *data)
29644d027bbSEmmanuel Vadot {
29744d027bbSEmmanuel Vadot struct mv_ap806_gicp_softc *sc;
29844d027bbSEmmanuel Vadot
29944d027bbSEmmanuel Vadot sc = device_get_softc(dev);
3004b84206bSMichal Meloun data = mv_ap806_gicp_convert_map_data(sc, data);
3014b84206bSMichal Meloun if (data == NULL)
3024b84206bSMichal Meloun return (EINVAL);
30344d027bbSEmmanuel Vadot
30444d027bbSEmmanuel Vadot return (PIC_TEARDOWN_INTR(sc->parent, isrc, res, data));
30544d027bbSEmmanuel Vadot }
30644d027bbSEmmanuel Vadot
30744d027bbSEmmanuel Vadot static void
mv_ap806_gicp_pre_ithread(device_t dev,struct intr_irqsrc * isrc)30844d027bbSEmmanuel Vadot mv_ap806_gicp_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
30944d027bbSEmmanuel Vadot {
31044d027bbSEmmanuel Vadot struct mv_ap806_gicp_softc *sc;
31144d027bbSEmmanuel Vadot
31244d027bbSEmmanuel Vadot sc = device_get_softc(dev);
31344d027bbSEmmanuel Vadot
31444d027bbSEmmanuel Vadot PIC_PRE_ITHREAD(sc->parent, isrc);
31544d027bbSEmmanuel Vadot }
31644d027bbSEmmanuel Vadot
31744d027bbSEmmanuel Vadot static void
mv_ap806_gicp_post_ithread(device_t dev,struct intr_irqsrc * isrc)31844d027bbSEmmanuel Vadot mv_ap806_gicp_post_ithread(device_t dev, struct intr_irqsrc *isrc)
31944d027bbSEmmanuel Vadot {
32044d027bbSEmmanuel Vadot struct mv_ap806_gicp_softc *sc;
32144d027bbSEmmanuel Vadot
32244d027bbSEmmanuel Vadot sc = device_get_softc(dev);
32344d027bbSEmmanuel Vadot
32444d027bbSEmmanuel Vadot PIC_POST_ITHREAD(sc->parent, isrc);
32544d027bbSEmmanuel Vadot }
32644d027bbSEmmanuel Vadot
32744d027bbSEmmanuel Vadot static void
mv_ap806_gicp_post_filter(device_t dev,struct intr_irqsrc * isrc)32844d027bbSEmmanuel Vadot mv_ap806_gicp_post_filter(device_t dev, struct intr_irqsrc *isrc)
32944d027bbSEmmanuel Vadot {
33044d027bbSEmmanuel Vadot struct mv_ap806_gicp_softc *sc;
33144d027bbSEmmanuel Vadot
33244d027bbSEmmanuel Vadot sc = device_get_softc(dev);
33344d027bbSEmmanuel Vadot
33444d027bbSEmmanuel Vadot PIC_POST_FILTER(sc->parent, isrc);
33544d027bbSEmmanuel Vadot }
33644d027bbSEmmanuel Vadot
337986bbba9SKornel Duleba static int
mv_ap806_gicp_alloc_msi(device_t dev,device_t child,int count,int maxcount,device_t * pic,struct intr_irqsrc ** srcs)338986bbba9SKornel Duleba mv_ap806_gicp_alloc_msi(device_t dev, device_t child, int count, int maxcount,
339986bbba9SKornel Duleba device_t *pic, struct intr_irqsrc **srcs)
340986bbba9SKornel Duleba {
341986bbba9SKornel Duleba struct mv_ap806_gicp_softc *sc;
342986bbba9SKornel Duleba int i, ret, vector;
343986bbba9SKornel Duleba
344986bbba9SKornel Duleba sc = device_get_softc(dev);
345986bbba9SKornel Duleba
346986bbba9SKornel Duleba for (i = 0; i < count; i++) {
347986bbba9SKornel Duleba /*
348986bbba9SKornel Duleba * Find first available vector represented by first set bit
349986bbba9SKornel Duleba * in the bitmap. BIT_FFS starts the count from 1, 0 means
350986bbba9SKornel Duleba * that nothing was found.
351986bbba9SKornel Duleba */
352986bbba9SKornel Duleba vector = BIT_FFS(sc->msi_bitmap_size, sc->msi_bitmap);
353986bbba9SKornel Duleba if (vector == 0) {
354986bbba9SKornel Duleba ret = ENOMEM;
355986bbba9SKornel Duleba i--;
356986bbba9SKornel Duleba goto fail;
357986bbba9SKornel Duleba }
358986bbba9SKornel Duleba vector--;
359986bbba9SKornel Duleba BIT_CLR(sc->msi_bitmap_size, vector, sc->msi_bitmap);
360986bbba9SKornel Duleba
361986bbba9SKornel Duleba /* Create GIC compatible SPI interrupt description. */
362986bbba9SKornel Duleba sc->parent_map_data->ncells = 3;
363986bbba9SKornel Duleba sc->parent_map_data->cells[0] = 0; /* SPI */
364986bbba9SKornel Duleba sc->parent_map_data->cells[1] = mv_ap806_gicp_msi_to_spi(sc, vector);
365986bbba9SKornel Duleba sc->parent_map_data->cells[2] = IRQ_TYPE_LEVEL_HIGH;
366986bbba9SKornel Duleba
367986bbba9SKornel Duleba ret = PIC_MAP_INTR(sc->parent,
368986bbba9SKornel Duleba (struct intr_map_data *)sc->parent_map_data,
369986bbba9SKornel Duleba &srcs[i]);
370986bbba9SKornel Duleba if (ret != 0)
371986bbba9SKornel Duleba goto fail;
372986bbba9SKornel Duleba
373986bbba9SKornel Duleba srcs[i]->isrc_dev = dev;
374986bbba9SKornel Duleba }
375986bbba9SKornel Duleba
376986bbba9SKornel Duleba return (0);
377986bbba9SKornel Duleba fail:
378986bbba9SKornel Duleba mv_ap806_gicp_release_msi(dev, child, i + 1, srcs);
379986bbba9SKornel Duleba return (ret);
380986bbba9SKornel Duleba }
381986bbba9SKornel Duleba
382986bbba9SKornel Duleba static int
mv_ap806_gicp_release_msi(device_t dev,device_t child,int count,struct intr_irqsrc ** srcs)383986bbba9SKornel Duleba mv_ap806_gicp_release_msi(device_t dev, device_t child, int count,
384986bbba9SKornel Duleba struct intr_irqsrc **srcs)
385986bbba9SKornel Duleba {
386986bbba9SKornel Duleba struct mv_ap806_gicp_softc *sc;
387986bbba9SKornel Duleba int i;
388986bbba9SKornel Duleba
389986bbba9SKornel Duleba sc = device_get_softc(dev);
390986bbba9SKornel Duleba
391986bbba9SKornel Duleba for (i = 0; i < count; i++) {
392986bbba9SKornel Duleba BIT_SET(sc->msi_bitmap_size,
393986bbba9SKornel Duleba mv_ap806_gicp_irq_to_msi(sc, srcs[i]->isrc_irq),
394986bbba9SKornel Duleba sc->msi_bitmap);
395986bbba9SKornel Duleba }
396986bbba9SKornel Duleba
397986bbba9SKornel Duleba return (0);
398986bbba9SKornel Duleba }
399986bbba9SKornel Duleba
400986bbba9SKornel Duleba static int
mv_ap806_gicp_map_msi(device_t dev,device_t child,struct intr_irqsrc * isrc,uint64_t * addr,uint32_t * data)401986bbba9SKornel Duleba mv_ap806_gicp_map_msi(device_t dev, device_t child, struct intr_irqsrc *isrc,
402986bbba9SKornel Duleba uint64_t *addr, uint32_t *data)
403986bbba9SKornel Duleba {
404986bbba9SKornel Duleba struct mv_ap806_gicp_softc *sc;
405986bbba9SKornel Duleba
406986bbba9SKornel Duleba sc = device_get_softc(dev);
407986bbba9SKornel Duleba
408986bbba9SKornel Duleba *addr = rman_get_start(sc->res);
409986bbba9SKornel Duleba *data = mv_ap806_gicp_irq_to_msi(sc, isrc->isrc_irq);
410986bbba9SKornel Duleba
411986bbba9SKornel Duleba return (0);
412986bbba9SKornel Duleba }
413986bbba9SKornel Duleba
41444d027bbSEmmanuel Vadot static device_method_t mv_ap806_gicp_methods[] = {
41544d027bbSEmmanuel Vadot /* Device interface */
41644d027bbSEmmanuel Vadot DEVMETHOD(device_probe, mv_ap806_gicp_probe),
41744d027bbSEmmanuel Vadot DEVMETHOD(device_attach, mv_ap806_gicp_attach),
41844d027bbSEmmanuel Vadot DEVMETHOD(device_detach, mv_ap806_gicp_detach),
41944d027bbSEmmanuel Vadot
42044d027bbSEmmanuel Vadot /* Interrupt controller interface */
42144d027bbSEmmanuel Vadot DEVMETHOD(pic_activate_intr, mv_ap806_gicp_activate_intr),
42244d027bbSEmmanuel Vadot DEVMETHOD(pic_disable_intr, mv_ap806_gicp_disable_intr),
42344d027bbSEmmanuel Vadot DEVMETHOD(pic_enable_intr, mv_ap806_gicp_enable_intr),
42444d027bbSEmmanuel Vadot DEVMETHOD(pic_map_intr, mv_ap806_gicp_map_intr),
42544d027bbSEmmanuel Vadot DEVMETHOD(pic_deactivate_intr, mv_ap806_gicp_deactivate_intr),
42644d027bbSEmmanuel Vadot DEVMETHOD(pic_setup_intr, mv_ap806_gicp_setup_intr),
42744d027bbSEmmanuel Vadot DEVMETHOD(pic_teardown_intr, mv_ap806_gicp_teardown_intr),
42844d027bbSEmmanuel Vadot DEVMETHOD(pic_post_filter, mv_ap806_gicp_post_filter),
42944d027bbSEmmanuel Vadot DEVMETHOD(pic_post_ithread, mv_ap806_gicp_post_ithread),
43044d027bbSEmmanuel Vadot DEVMETHOD(pic_pre_ithread, mv_ap806_gicp_pre_ithread),
43144d027bbSEmmanuel Vadot
432986bbba9SKornel Duleba /* MSI interface */
433986bbba9SKornel Duleba DEVMETHOD(msi_alloc_msi, mv_ap806_gicp_alloc_msi),
434986bbba9SKornel Duleba DEVMETHOD(msi_release_msi, mv_ap806_gicp_release_msi),
435986bbba9SKornel Duleba DEVMETHOD(msi_map_msi, mv_ap806_gicp_map_msi),
436986bbba9SKornel Duleba
43744d027bbSEmmanuel Vadot DEVMETHOD_END
43844d027bbSEmmanuel Vadot };
43944d027bbSEmmanuel Vadot
44044d027bbSEmmanuel Vadot static driver_t mv_ap806_gicp_driver = {
44144d027bbSEmmanuel Vadot "mv_ap806_gicp",
44244d027bbSEmmanuel Vadot mv_ap806_gicp_methods,
44344d027bbSEmmanuel Vadot sizeof(struct mv_ap806_gicp_softc),
44444d027bbSEmmanuel Vadot };
44544d027bbSEmmanuel Vadot
446a3b866cbSJohn Baldwin EARLY_DRIVER_MODULE(mv_ap806_gicp, simplebus, mv_ap806_gicp_driver, 0, 0,
447a3b866cbSJohn Baldwin BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
448