xref: /freebsd/sys/arm/mv/mv_cp110_icu.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
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 
3244d027bbSEmmanuel Vadot #include <sys/kernel.h>
3344d027bbSEmmanuel Vadot #include <sys/module.h>
3444d027bbSEmmanuel Vadot #include <sys/rman.h>
3544d027bbSEmmanuel Vadot #include <sys/lock.h>
3644d027bbSEmmanuel Vadot #include <sys/mutex.h>
3744d027bbSEmmanuel Vadot 
3844d027bbSEmmanuel Vadot #include <machine/bus.h>
3944d027bbSEmmanuel Vadot #include <machine/resource.h>
4044d027bbSEmmanuel Vadot #include <machine/intr.h>
4144d027bbSEmmanuel Vadot 
4244d027bbSEmmanuel Vadot #include <dev/fdt/simplebus.h>
4344d027bbSEmmanuel Vadot 
4444d027bbSEmmanuel Vadot #include <dev/ofw/ofw_bus.h>
4544d027bbSEmmanuel Vadot #include <dev/ofw/ofw_bus_subr.h>
4644d027bbSEmmanuel Vadot 
478a7a4683SEmmanuel Vadot #include <dt-bindings/interrupt-controller/irq.h>
48986bbba9SKornel Duleba 
4944d027bbSEmmanuel Vadot #include "pic_if.h"
50986bbba9SKornel Duleba #include "msi_if.h"
51986bbba9SKornel Duleba 
52986bbba9SKornel Duleba #define	ICU_TYPE_NSR		1
53986bbba9SKornel Duleba #define	ICU_TYPE_SEI		2
5444d027bbSEmmanuel Vadot 
5544d027bbSEmmanuel Vadot #define	ICU_GRP_NSR		0x0
5644d027bbSEmmanuel Vadot #define	ICU_GRP_SR		0x1
5744d027bbSEmmanuel Vadot #define	ICU_GRP_SEI		0x4
5844d027bbSEmmanuel Vadot #define	ICU_GRP_REI		0x5
5944d027bbSEmmanuel Vadot 
6044d027bbSEmmanuel Vadot #define	ICU_SETSPI_NSR_AL	0x10
6144d027bbSEmmanuel Vadot #define	ICU_SETSPI_NSR_AH	0x14
6244d027bbSEmmanuel Vadot #define	ICU_CLRSPI_NSR_AL	0x18
6344d027bbSEmmanuel Vadot #define	ICU_CLRSPI_NSR_AH	0x1c
64986bbba9SKornel Duleba #define	ICU_SETSPI_SEI_AL	0x50
65986bbba9SKornel Duleba #define	ICU_SETSPI_SEI_AH	0x54
6644d027bbSEmmanuel Vadot #define	ICU_INT_CFG(x)	(0x100 + (x) * 4)
6744d027bbSEmmanuel Vadot #define	 ICU_INT_ENABLE		(1 << 24)
6844d027bbSEmmanuel Vadot #define	 ICU_INT_EDGE		(1 << 28)
6944d027bbSEmmanuel Vadot #define	 ICU_INT_GROUP_SHIFT	29
7044d027bbSEmmanuel Vadot #define	 ICU_INT_MASK		0x3ff
7144d027bbSEmmanuel Vadot 
72986bbba9SKornel Duleba #define	ICU_INT_SATA0		109
73986bbba9SKornel Duleba #define	ICU_INT_SATA1		107
74986bbba9SKornel Duleba 
7544d027bbSEmmanuel Vadot #define	MV_CP110_ICU_MAX_NIRQS	207
7644d027bbSEmmanuel Vadot 
77986bbba9SKornel Duleba #define	MV_CP110_ICU_CLRSPI_OFFSET	0x8
78986bbba9SKornel Duleba 
7944d027bbSEmmanuel Vadot struct mv_cp110_icu_softc {
8044d027bbSEmmanuel Vadot 	device_t		dev;
8144d027bbSEmmanuel Vadot 	device_t		parent;
8244d027bbSEmmanuel Vadot 	struct resource		*res;
834b84206bSMichal Meloun 	struct intr_map_data_fdt *parent_map_data;
84986bbba9SKornel Duleba 	bool			initialized;
85986bbba9SKornel Duleba 	int			type;
8644d027bbSEmmanuel Vadot };
8744d027bbSEmmanuel Vadot 
8844d027bbSEmmanuel Vadot static struct resource_spec mv_cp110_icu_res_spec[] = {
8944d027bbSEmmanuel Vadot 	{ SYS_RES_MEMORY,	0,	RF_ACTIVE | RF_SHAREABLE },
9044d027bbSEmmanuel Vadot 	{ -1, 0 }
9144d027bbSEmmanuel Vadot };
9244d027bbSEmmanuel Vadot 
9344d027bbSEmmanuel Vadot static struct ofw_compat_data compat_data[] = {
94986bbba9SKornel Duleba 	{"marvell,cp110-icu-nsr",	ICU_TYPE_NSR},
95986bbba9SKornel Duleba 	{"marvell,cp110-icu-sei",	ICU_TYPE_SEI},
9644d027bbSEmmanuel Vadot 	{NULL,				0}
9744d027bbSEmmanuel Vadot };
9844d027bbSEmmanuel Vadot 
9944d027bbSEmmanuel Vadot #define	RD4(sc, reg)		bus_read_4((sc)->res, (reg))
10044d027bbSEmmanuel Vadot #define	WR4(sc, reg, val)	bus_write_4((sc)->res, (reg), (val))
10144d027bbSEmmanuel Vadot 
10244d027bbSEmmanuel Vadot static int
mv_cp110_icu_probe(device_t dev)10344d027bbSEmmanuel Vadot mv_cp110_icu_probe(device_t dev)
10444d027bbSEmmanuel Vadot {
10544d027bbSEmmanuel Vadot 
10644d027bbSEmmanuel Vadot 	if (!ofw_bus_status_okay(dev))
10744d027bbSEmmanuel Vadot 		return (ENXIO);
10844d027bbSEmmanuel Vadot 
10944d027bbSEmmanuel Vadot 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
11044d027bbSEmmanuel Vadot 		return (ENXIO);
11144d027bbSEmmanuel Vadot 
11244d027bbSEmmanuel Vadot 	device_set_desc(dev, "Marvell Interrupt Consolidation Unit");
11344d027bbSEmmanuel Vadot 	return (BUS_PROBE_DEFAULT);
11444d027bbSEmmanuel Vadot }
11544d027bbSEmmanuel Vadot 
11644d027bbSEmmanuel Vadot static int
mv_cp110_icu_attach(device_t dev)11744d027bbSEmmanuel Vadot mv_cp110_icu_attach(device_t dev)
11844d027bbSEmmanuel Vadot {
11944d027bbSEmmanuel Vadot 	struct mv_cp110_icu_softc *sc;
12044d027bbSEmmanuel Vadot 	phandle_t node, msi_parent;
121986bbba9SKornel Duleba 	uint32_t reg, icu_grp;
122986bbba9SKornel Duleba 	int i;
12344d027bbSEmmanuel Vadot 
12444d027bbSEmmanuel Vadot 	sc = device_get_softc(dev);
12544d027bbSEmmanuel Vadot 	sc->dev = dev;
12644d027bbSEmmanuel Vadot 	node = ofw_bus_get_node(dev);
127986bbba9SKornel Duleba 	sc->type = (int)ofw_bus_search_compatible(dev, compat_data)->ocd_data;
128986bbba9SKornel Duleba 	sc->initialized = false;
12944d027bbSEmmanuel Vadot 
13044d027bbSEmmanuel Vadot 	if (OF_getencprop(node, "msi-parent", &msi_parent,
13144d027bbSEmmanuel Vadot 	    sizeof(phandle_t)) <= 0) {
13244d027bbSEmmanuel Vadot 		device_printf(dev, "cannot find msi-parent property\n");
13344d027bbSEmmanuel Vadot 		return (ENXIO);
13444d027bbSEmmanuel Vadot 	}
13544d027bbSEmmanuel Vadot 
13644d027bbSEmmanuel Vadot 	if ((sc->parent = OF_device_from_xref(msi_parent)) == NULL) {
13744d027bbSEmmanuel Vadot 		device_printf(dev, "cannot find msi-parent device\n");
13844d027bbSEmmanuel Vadot 		return (ENXIO);
13944d027bbSEmmanuel Vadot 	}
14044d027bbSEmmanuel Vadot 	if (bus_alloc_resources(dev, mv_cp110_icu_res_spec, &sc->res) != 0) {
14144d027bbSEmmanuel Vadot 		device_printf(dev, "cannot allocate resources for device\n");
14244d027bbSEmmanuel Vadot 		return (ENXIO);
14344d027bbSEmmanuel Vadot 	}
14444d027bbSEmmanuel Vadot 
14544d027bbSEmmanuel Vadot 	if (intr_pic_register(dev, OF_xref_from_node(node)) == NULL) {
14644d027bbSEmmanuel Vadot 		device_printf(dev, "Cannot register ICU\n");
14744d027bbSEmmanuel Vadot 		goto fail;
14844d027bbSEmmanuel Vadot 	}
1494b84206bSMichal Meloun 
150986bbba9SKornel Duleba 	/* Allocate GICP/SEI compatible mapping entry (2 cells) */
1514b84206bSMichal Meloun 	sc->parent_map_data = (struct intr_map_data_fdt *)intr_alloc_map_data(
1524b84206bSMichal Meloun 	    INTR_MAP_DATA_FDT, sizeof(struct intr_map_data_fdt) +
1534b84206bSMichal Meloun 	    + 3 * sizeof(phandle_t), M_WAITOK | M_ZERO);
154986bbba9SKornel Duleba 
155986bbba9SKornel Duleba 	/* Clear any previous mapping done by firmware. */
156986bbba9SKornel Duleba 	for (i = 0; i < MV_CP110_ICU_MAX_NIRQS; i++) {
157986bbba9SKornel Duleba 		reg = RD4(sc, ICU_INT_CFG(i));
158986bbba9SKornel Duleba 		icu_grp = reg >> ICU_INT_GROUP_SHIFT;
159986bbba9SKornel Duleba 
160986bbba9SKornel Duleba 		if (icu_grp == ICU_GRP_NSR || icu_grp == ICU_GRP_SEI)
161986bbba9SKornel Duleba 			WR4(sc, ICU_INT_CFG(i), 0);
162986bbba9SKornel Duleba 	}
163986bbba9SKornel Duleba 
16444d027bbSEmmanuel Vadot 	return (0);
16544d027bbSEmmanuel Vadot 
16644d027bbSEmmanuel Vadot fail:
16744d027bbSEmmanuel Vadot 	bus_release_resources(dev, mv_cp110_icu_res_spec, &sc->res);
16844d027bbSEmmanuel Vadot 	return (ENXIO);
16944d027bbSEmmanuel Vadot }
17044d027bbSEmmanuel Vadot 
1714b84206bSMichal Meloun static struct intr_map_data *
mv_cp110_icu_convert_map_data(struct mv_cp110_icu_softc * sc,struct intr_map_data * data)1724b84206bSMichal Meloun mv_cp110_icu_convert_map_data(struct mv_cp110_icu_softc *sc, struct intr_map_data *data)
1734b84206bSMichal Meloun {
1744b84206bSMichal Meloun 	struct intr_map_data_fdt *daf;
1754b84206bSMichal Meloun 	uint32_t reg, irq_no, irq_type;
1764b84206bSMichal Meloun 
1774b84206bSMichal Meloun 	daf = (struct intr_map_data_fdt *)data;
1784b84206bSMichal Meloun 	if (daf->ncells != 2)
1794b84206bSMichal Meloun 		return (NULL);
180986bbba9SKornel Duleba 
1814b84206bSMichal Meloun 	irq_no = daf->cells[0];
1824b84206bSMichal Meloun 	if (irq_no >= MV_CP110_ICU_MAX_NIRQS)
1834b84206bSMichal Meloun 		return (NULL);
184986bbba9SKornel Duleba 
185986bbba9SKornel Duleba 	irq_type = daf->cells[1];
1864b84206bSMichal Meloun 	if (irq_type != IRQ_TYPE_LEVEL_HIGH &&
1874b84206bSMichal Meloun 	    irq_type != IRQ_TYPE_EDGE_RISING)
1884b84206bSMichal Meloun 		return (NULL);
1894b84206bSMichal Meloun 
190986bbba9SKornel Duleba 	/* ICU -> GICP/SEI mapping is set in mv_cp110_icu_map_intr. */
1914b84206bSMichal Meloun 	reg = RD4(sc, ICU_INT_CFG(irq_no));
1924b84206bSMichal Meloun 
1934b84206bSMichal Meloun 	/* Construct GICP compatible mapping. */
1944b84206bSMichal Meloun 	sc->parent_map_data->ncells = 2;
1954b84206bSMichal Meloun 	sc->parent_map_data->cells[0] = reg & ICU_INT_MASK;
1964b84206bSMichal Meloun 	sc->parent_map_data->cells[1] = irq_type;
1974b84206bSMichal Meloun 
1984b84206bSMichal Meloun 	return ((struct intr_map_data *)sc->parent_map_data);
1994b84206bSMichal Meloun }
2004b84206bSMichal Meloun 
20144d027bbSEmmanuel Vadot static int
mv_cp110_icu_detach(device_t dev)20244d027bbSEmmanuel Vadot mv_cp110_icu_detach(device_t dev)
20344d027bbSEmmanuel Vadot {
20444d027bbSEmmanuel Vadot 
20544d027bbSEmmanuel Vadot 	return (EBUSY);
20644d027bbSEmmanuel Vadot }
20744d027bbSEmmanuel Vadot 
20844d027bbSEmmanuel Vadot static int
mv_cp110_icu_activate_intr(device_t dev,struct intr_irqsrc * isrc,struct resource * res,struct intr_map_data * data)20944d027bbSEmmanuel Vadot mv_cp110_icu_activate_intr(device_t dev, struct intr_irqsrc *isrc,
21044d027bbSEmmanuel Vadot     struct resource *res, struct intr_map_data *data)
21144d027bbSEmmanuel Vadot {
21244d027bbSEmmanuel Vadot 	struct mv_cp110_icu_softc *sc;
21344d027bbSEmmanuel Vadot 
21444d027bbSEmmanuel Vadot 	sc = device_get_softc(dev);
2154b84206bSMichal Meloun 	data = mv_cp110_icu_convert_map_data(sc, data);
2164b84206bSMichal Meloun 	if (data == NULL)
2174b84206bSMichal Meloun 		return (EINVAL);
21844d027bbSEmmanuel Vadot 	return (PIC_ACTIVATE_INTR(sc->parent, isrc, res, data));
21944d027bbSEmmanuel Vadot }
22044d027bbSEmmanuel Vadot 
22144d027bbSEmmanuel Vadot static void
mv_cp110_icu_enable_intr(device_t dev,struct intr_irqsrc * isrc)22244d027bbSEmmanuel Vadot mv_cp110_icu_enable_intr(device_t dev, struct intr_irqsrc *isrc)
22344d027bbSEmmanuel Vadot {
22444d027bbSEmmanuel Vadot 	struct mv_cp110_icu_softc *sc;
22544d027bbSEmmanuel Vadot 	sc = device_get_softc(dev);
22644d027bbSEmmanuel Vadot 
22744d027bbSEmmanuel Vadot 	PIC_ENABLE_INTR(sc->parent, isrc);
22844d027bbSEmmanuel Vadot }
22944d027bbSEmmanuel Vadot 
23044d027bbSEmmanuel Vadot static void
mv_cp110_icu_disable_intr(device_t dev,struct intr_irqsrc * isrc)23144d027bbSEmmanuel Vadot mv_cp110_icu_disable_intr(device_t dev, struct intr_irqsrc *isrc)
23244d027bbSEmmanuel Vadot {
23344d027bbSEmmanuel Vadot 	struct mv_cp110_icu_softc *sc;
23444d027bbSEmmanuel Vadot 
23544d027bbSEmmanuel Vadot 	sc = device_get_softc(dev);
23644d027bbSEmmanuel Vadot 
23744d027bbSEmmanuel Vadot 	PIC_DISABLE_INTR(sc->parent, isrc);
23844d027bbSEmmanuel Vadot }
23944d027bbSEmmanuel Vadot 
240986bbba9SKornel Duleba static void
mv_cp110_icu_init(struct mv_cp110_icu_softc * sc,uint64_t addr)241986bbba9SKornel Duleba mv_cp110_icu_init(struct mv_cp110_icu_softc *sc, uint64_t addr)
242986bbba9SKornel Duleba {
243986bbba9SKornel Duleba 
244986bbba9SKornel Duleba 	if (sc->initialized)
245986bbba9SKornel Duleba 		return;
246986bbba9SKornel Duleba 
247986bbba9SKornel Duleba 	switch (sc->type) {
248986bbba9SKornel Duleba 	case ICU_TYPE_NSR:
249986bbba9SKornel Duleba 		WR4(sc, ICU_SETSPI_NSR_AL, addr & UINT32_MAX);
250986bbba9SKornel Duleba 		WR4(sc, ICU_SETSPI_NSR_AH, (addr >> 32) & UINT32_MAX);
251986bbba9SKornel Duleba 		addr += MV_CP110_ICU_CLRSPI_OFFSET;
252986bbba9SKornel Duleba 		WR4(sc, ICU_CLRSPI_NSR_AL, addr & UINT32_MAX);
253986bbba9SKornel Duleba 		WR4(sc, ICU_CLRSPI_NSR_AH, (addr >> 32) & UINT32_MAX);
254986bbba9SKornel Duleba 		break;
255986bbba9SKornel Duleba 	case ICU_TYPE_SEI:
256986bbba9SKornel Duleba 		WR4(sc, ICU_SETSPI_SEI_AL, addr & UINT32_MAX);
257986bbba9SKornel Duleba 		WR4(sc, ICU_SETSPI_SEI_AH, (addr >> 32) & UINT32_MAX);
258986bbba9SKornel Duleba 		break;
259986bbba9SKornel Duleba 	default:
260986bbba9SKornel Duleba 		panic("Unkown ICU type.");
261986bbba9SKornel Duleba 	}
262986bbba9SKornel Duleba 
263986bbba9SKornel Duleba 	sc->initialized = true;
264986bbba9SKornel Duleba }
265986bbba9SKornel Duleba 
26644d027bbSEmmanuel Vadot static int
mv_cp110_icu_map_intr(device_t dev,struct intr_map_data * data,struct intr_irqsrc ** isrcp)26744d027bbSEmmanuel Vadot mv_cp110_icu_map_intr(device_t dev, struct intr_map_data *data,
26844d027bbSEmmanuel Vadot     struct intr_irqsrc **isrcp)
26944d027bbSEmmanuel Vadot {
27044d027bbSEmmanuel Vadot 	struct mv_cp110_icu_softc *sc;
27144d027bbSEmmanuel Vadot 	struct intr_map_data_fdt *daf;
272986bbba9SKornel Duleba 	uint32_t vector, irq_no, irq_type;
273986bbba9SKornel Duleba 	uint64_t addr;
2744b84206bSMichal Meloun 	int ret;
27544d027bbSEmmanuel Vadot 
27644d027bbSEmmanuel Vadot 	sc = device_get_softc(dev);
27744d027bbSEmmanuel Vadot 
27844d027bbSEmmanuel Vadot 	if (data->type != INTR_MAP_DATA_FDT)
27944d027bbSEmmanuel Vadot 		return (ENOTSUP);
28044d027bbSEmmanuel Vadot 
2814b84206bSMichal Meloun 	/* Parse original */
28244d027bbSEmmanuel Vadot 	daf = (struct intr_map_data_fdt *)data;
2834b84206bSMichal Meloun 	if (daf->ncells != 2)
2844b84206bSMichal Meloun 		return (EINVAL);
285986bbba9SKornel Duleba 
2864b84206bSMichal Meloun 	irq_no = daf->cells[0];
287986bbba9SKornel Duleba 	if (irq_no >= MV_CP110_ICU_MAX_NIRQS)
28844d027bbSEmmanuel Vadot 		return (EINVAL);
28944d027bbSEmmanuel Vadot 
290986bbba9SKornel Duleba 	irq_type = daf->cells[1];
291986bbba9SKornel Duleba 	if (irq_type != IRQ_TYPE_LEVEL_HIGH &&
292986bbba9SKornel Duleba 	    irq_type != IRQ_TYPE_EDGE_RISING)
293986bbba9SKornel Duleba 		return (EINVAL);
29444d027bbSEmmanuel Vadot 
295986bbba9SKornel Duleba 	/*
296986bbba9SKornel Duleba 	 * Allocate MSI vector.
297986bbba9SKornel Duleba 	 * We don't use intr_alloc_msi wrapper, since it registers a new irq
298986bbba9SKornel Duleba 	 * in the kernel. In our case irq was already added by the ofw code.
299986bbba9SKornel Duleba 	 */
300986bbba9SKornel Duleba 	ret = MSI_ALLOC_MSI(sc->parent, dev, 1, 1, NULL, isrcp);
301986bbba9SKornel Duleba 	if (ret != 0)
302986bbba9SKornel Duleba 		return (ret);
303986bbba9SKornel Duleba 
304986bbba9SKornel Duleba 	ret = MSI_MAP_MSI(sc->parent, dev, *isrcp, &addr, &vector);
305986bbba9SKornel Duleba 	if (ret != 0)
306986bbba9SKornel Duleba 		goto fail;
307986bbba9SKornel Duleba 
308986bbba9SKornel Duleba 	mv_cp110_icu_init(sc, addr);
309986bbba9SKornel Duleba 	vector |= ICU_INT_ENABLE;
310986bbba9SKornel Duleba 
311986bbba9SKornel Duleba 	if (sc->type == ICU_TYPE_NSR)
312986bbba9SKornel Duleba 		vector |= ICU_GRP_NSR << ICU_INT_GROUP_SHIFT;
313986bbba9SKornel Duleba 	else
314986bbba9SKornel Duleba 		vector |= ICU_GRP_SEI << ICU_INT_GROUP_SHIFT;
315986bbba9SKornel Duleba 
316986bbba9SKornel Duleba 	if (irq_type & IRQ_TYPE_EDGE_BOTH)
317986bbba9SKornel Duleba 		vector |= ICU_INT_EDGE;
318986bbba9SKornel Duleba 
319986bbba9SKornel Duleba 	WR4(sc, ICU_INT_CFG(irq_no), vector);
320986bbba9SKornel Duleba 
321986bbba9SKornel Duleba 	/*
322986bbba9SKornel Duleba 	 * SATA controller has two ports, each gets its own interrupt.
323986bbba9SKornel Duleba 	 * The problem is that only one irq is described in dts.
324986bbba9SKornel Duleba 	 * Also ahci_generic driver supports only one irq per controller.
325986bbba9SKornel Duleba 	 * As a workaround map both interrupts when one of them is allocated.
326986bbba9SKornel Duleba 	 * This allows us to use both SATA ports.
327986bbba9SKornel Duleba 	 */
328986bbba9SKornel Duleba 	if (irq_no == ICU_INT_SATA0)
329986bbba9SKornel Duleba 		WR4(sc, ICU_INT_CFG(ICU_INT_SATA1), vector);
330986bbba9SKornel Duleba 	if (irq_no == ICU_INT_SATA1)
331986bbba9SKornel Duleba 		WR4(sc, ICU_INT_CFG(ICU_INT_SATA0), vector);
332986bbba9SKornel Duleba 
3334b84206bSMichal Meloun 	(*isrcp)->isrc_dev = sc->dev;
3344b84206bSMichal Meloun 	return (ret);
335986bbba9SKornel Duleba 
336986bbba9SKornel Duleba fail:
337986bbba9SKornel Duleba 	if (*isrcp != NULL)
338986bbba9SKornel Duleba 		MSI_RELEASE_MSI(sc->parent, dev, 1, isrcp);
339986bbba9SKornel Duleba 
340986bbba9SKornel Duleba 	return (ret);
34144d027bbSEmmanuel Vadot }
34244d027bbSEmmanuel Vadot 
34344d027bbSEmmanuel Vadot static int
mv_cp110_icu_deactivate_intr(device_t dev,struct intr_irqsrc * isrc,struct resource * res,struct intr_map_data * data)34444d027bbSEmmanuel Vadot mv_cp110_icu_deactivate_intr(device_t dev, struct intr_irqsrc *isrc,
34544d027bbSEmmanuel Vadot     struct resource *res, struct intr_map_data *data)
34644d027bbSEmmanuel Vadot {
34744d027bbSEmmanuel Vadot 	struct mv_cp110_icu_softc *sc;
348986bbba9SKornel Duleba 	struct intr_map_data_fdt *daf;
349986bbba9SKornel Duleba 	int irq_no, ret;
350986bbba9SKornel Duleba 
351986bbba9SKornel Duleba 	if (data->type != INTR_MAP_DATA_FDT)
352986bbba9SKornel Duleba 		return (ENOTSUP);
35344d027bbSEmmanuel Vadot 
35444d027bbSEmmanuel Vadot 	sc = device_get_softc(dev);
355986bbba9SKornel Duleba 	daf = (struct intr_map_data_fdt *)data;
356986bbba9SKornel Duleba 	if (daf->ncells != 2)
357986bbba9SKornel Duleba 		return (EINVAL);
358986bbba9SKornel Duleba 
359986bbba9SKornel Duleba 	irq_no = daf->cells[0];
3604b84206bSMichal Meloun 	data = mv_cp110_icu_convert_map_data(sc, data);
3614b84206bSMichal Meloun 	if (data == NULL)
3624b84206bSMichal Meloun 		return (EINVAL);
36344d027bbSEmmanuel Vadot 
364986bbba9SKornel Duleba 	/* Clear the mapping. */
365986bbba9SKornel Duleba 	WR4(sc, ICU_INT_CFG(irq_no), 0);
366986bbba9SKornel Duleba 
367986bbba9SKornel Duleba 	ret = PIC_DEACTIVATE_INTR(sc->parent, isrc, res, data);
368986bbba9SKornel Duleba 	if (ret != 0)
369986bbba9SKornel Duleba 		return (ret);
370986bbba9SKornel Duleba 
371986bbba9SKornel Duleba 	return (MSI_RELEASE_MSI(sc->parent, dev, 1, &isrc));
37244d027bbSEmmanuel Vadot }
37344d027bbSEmmanuel Vadot 
37444d027bbSEmmanuel Vadot static int
mv_cp110_icu_setup_intr(device_t dev,struct intr_irqsrc * isrc,struct resource * res,struct intr_map_data * data)37544d027bbSEmmanuel Vadot mv_cp110_icu_setup_intr(device_t dev, struct intr_irqsrc *isrc,
37644d027bbSEmmanuel Vadot     struct resource *res, struct intr_map_data *data)
37744d027bbSEmmanuel Vadot {
37844d027bbSEmmanuel Vadot 	struct mv_cp110_icu_softc *sc;
37944d027bbSEmmanuel Vadot 
38044d027bbSEmmanuel Vadot 	sc = device_get_softc(dev);
3814b84206bSMichal Meloun 	data = mv_cp110_icu_convert_map_data(sc, data);
3824b84206bSMichal Meloun 	if (data == NULL)
3834b84206bSMichal Meloun 		return (EINVAL);
38444d027bbSEmmanuel Vadot 
38544d027bbSEmmanuel Vadot 	return (PIC_SETUP_INTR(sc->parent, isrc, res, data));
38644d027bbSEmmanuel Vadot }
38744d027bbSEmmanuel Vadot 
38844d027bbSEmmanuel Vadot static int
mv_cp110_icu_teardown_intr(device_t dev,struct intr_irqsrc * isrc,struct resource * res,struct intr_map_data * data)38944d027bbSEmmanuel Vadot mv_cp110_icu_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
39044d027bbSEmmanuel Vadot     struct resource *res, struct intr_map_data *data)
39144d027bbSEmmanuel Vadot {
39244d027bbSEmmanuel Vadot 	struct mv_cp110_icu_softc *sc;
39344d027bbSEmmanuel Vadot 
39444d027bbSEmmanuel Vadot 	sc = device_get_softc(dev);
3954b84206bSMichal Meloun 	data = mv_cp110_icu_convert_map_data(sc, data);
3964b84206bSMichal Meloun 	if (data == NULL)
3974b84206bSMichal Meloun 		return (EINVAL);
39844d027bbSEmmanuel Vadot 
39944d027bbSEmmanuel Vadot 	return (PIC_TEARDOWN_INTR(sc->parent, isrc, res, data));
40044d027bbSEmmanuel Vadot }
40144d027bbSEmmanuel Vadot 
40244d027bbSEmmanuel Vadot static void
mv_cp110_icu_pre_ithread(device_t dev,struct intr_irqsrc * isrc)40344d027bbSEmmanuel Vadot mv_cp110_icu_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
40444d027bbSEmmanuel Vadot {
40544d027bbSEmmanuel Vadot 	struct mv_cp110_icu_softc *sc;
40644d027bbSEmmanuel Vadot 
40744d027bbSEmmanuel Vadot 	sc = device_get_softc(dev);
40844d027bbSEmmanuel Vadot 
40944d027bbSEmmanuel Vadot 	PIC_PRE_ITHREAD(sc->parent, isrc);
41044d027bbSEmmanuel Vadot }
41144d027bbSEmmanuel Vadot 
41244d027bbSEmmanuel Vadot static void
mv_cp110_icu_post_ithread(device_t dev,struct intr_irqsrc * isrc)41344d027bbSEmmanuel Vadot mv_cp110_icu_post_ithread(device_t dev, struct intr_irqsrc *isrc)
41444d027bbSEmmanuel Vadot {
41544d027bbSEmmanuel Vadot 	struct mv_cp110_icu_softc *sc;
41644d027bbSEmmanuel Vadot 
41744d027bbSEmmanuel Vadot 	sc = device_get_softc(dev);
41844d027bbSEmmanuel Vadot 
41944d027bbSEmmanuel Vadot 	PIC_POST_ITHREAD(sc->parent, isrc);
42044d027bbSEmmanuel Vadot }
42144d027bbSEmmanuel Vadot 
42244d027bbSEmmanuel Vadot static void
mv_cp110_icu_post_filter(device_t dev,struct intr_irqsrc * isrc)42344d027bbSEmmanuel Vadot mv_cp110_icu_post_filter(device_t dev, struct intr_irqsrc *isrc)
42444d027bbSEmmanuel Vadot {
42544d027bbSEmmanuel Vadot 	struct mv_cp110_icu_softc *sc;
42644d027bbSEmmanuel Vadot 
42744d027bbSEmmanuel Vadot 	sc = device_get_softc(dev);
42844d027bbSEmmanuel Vadot 
42944d027bbSEmmanuel Vadot 	PIC_POST_FILTER(sc->parent, isrc);
43044d027bbSEmmanuel Vadot }
43144d027bbSEmmanuel Vadot 
43244d027bbSEmmanuel Vadot static device_method_t mv_cp110_icu_methods[] = {
43344d027bbSEmmanuel Vadot 	/* Device interface */
43444d027bbSEmmanuel Vadot 	DEVMETHOD(device_probe,		mv_cp110_icu_probe),
43544d027bbSEmmanuel Vadot 	DEVMETHOD(device_attach,	mv_cp110_icu_attach),
43644d027bbSEmmanuel Vadot 	DEVMETHOD(device_detach,	mv_cp110_icu_detach),
43744d027bbSEmmanuel Vadot 
43844d027bbSEmmanuel Vadot 	/* Interrupt controller interface */
43944d027bbSEmmanuel Vadot 	DEVMETHOD(pic_activate_intr,	mv_cp110_icu_activate_intr),
44044d027bbSEmmanuel Vadot 	DEVMETHOD(pic_disable_intr,	mv_cp110_icu_disable_intr),
44144d027bbSEmmanuel Vadot 	DEVMETHOD(pic_enable_intr,	mv_cp110_icu_enable_intr),
44244d027bbSEmmanuel Vadot 	DEVMETHOD(pic_map_intr,		mv_cp110_icu_map_intr),
44344d027bbSEmmanuel Vadot 	DEVMETHOD(pic_deactivate_intr,	mv_cp110_icu_deactivate_intr),
44444d027bbSEmmanuel Vadot 	DEVMETHOD(pic_setup_intr,	mv_cp110_icu_setup_intr),
44544d027bbSEmmanuel Vadot 	DEVMETHOD(pic_teardown_intr,	mv_cp110_icu_teardown_intr),
44644d027bbSEmmanuel Vadot 	DEVMETHOD(pic_post_filter,	mv_cp110_icu_post_filter),
44744d027bbSEmmanuel Vadot 	DEVMETHOD(pic_post_ithread,	mv_cp110_icu_post_ithread),
44844d027bbSEmmanuel Vadot 	DEVMETHOD(pic_pre_ithread,	mv_cp110_icu_pre_ithread),
44944d027bbSEmmanuel Vadot 
45044d027bbSEmmanuel Vadot 	DEVMETHOD_END
45144d027bbSEmmanuel Vadot };
45244d027bbSEmmanuel Vadot 
45344d027bbSEmmanuel Vadot static driver_t mv_cp110_icu_driver = {
45444d027bbSEmmanuel Vadot 	"mv_cp110_icu",
45544d027bbSEmmanuel Vadot 	mv_cp110_icu_methods,
45644d027bbSEmmanuel Vadot 	sizeof(struct mv_cp110_icu_softc),
45744d027bbSEmmanuel Vadot };
45844d027bbSEmmanuel Vadot 
459a3b866cbSJohn Baldwin EARLY_DRIVER_MODULE(mv_cp110_icu, mv_cp110_icu_bus, mv_cp110_icu_driver, 0, 0,
460a3b866cbSJohn Baldwin     BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LAST);
461