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