xref: /freebsd/sys/arm/mv/mv_cp110_icu.c (revision a3b866cb2991f32b57c135937c31b46c9605c97f)
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 
3744d027bbSEmmanuel Vadot #include <sys/kernel.h>
3844d027bbSEmmanuel Vadot #include <sys/module.h>
3944d027bbSEmmanuel Vadot #include <sys/rman.h>
4044d027bbSEmmanuel Vadot #include <sys/lock.h>
4144d027bbSEmmanuel Vadot #include <sys/mutex.h>
4244d027bbSEmmanuel Vadot 
4344d027bbSEmmanuel Vadot #include <machine/bus.h>
4444d027bbSEmmanuel Vadot #include <machine/resource.h>
4544d027bbSEmmanuel Vadot #include <machine/intr.h>
4644d027bbSEmmanuel Vadot 
4744d027bbSEmmanuel Vadot #include <dev/fdt/simplebus.h>
4844d027bbSEmmanuel Vadot 
4944d027bbSEmmanuel Vadot #include <dev/ofw/ofw_bus.h>
5044d027bbSEmmanuel Vadot #include <dev/ofw/ofw_bus_subr.h>
5144d027bbSEmmanuel Vadot 
528a7a4683SEmmanuel Vadot #include <dt-bindings/interrupt-controller/irq.h>
53986bbba9SKornel Duleba 
5444d027bbSEmmanuel Vadot #include "pic_if.h"
55986bbba9SKornel Duleba #include "msi_if.h"
56986bbba9SKornel Duleba 
57986bbba9SKornel Duleba #define	ICU_TYPE_NSR		1
58986bbba9SKornel Duleba #define	ICU_TYPE_SEI		2
5944d027bbSEmmanuel Vadot 
6044d027bbSEmmanuel Vadot #define	ICU_GRP_NSR		0x0
6144d027bbSEmmanuel Vadot #define	ICU_GRP_SR		0x1
6244d027bbSEmmanuel Vadot #define	ICU_GRP_SEI		0x4
6344d027bbSEmmanuel Vadot #define	ICU_GRP_REI		0x5
6444d027bbSEmmanuel Vadot 
6544d027bbSEmmanuel Vadot #define	ICU_SETSPI_NSR_AL	0x10
6644d027bbSEmmanuel Vadot #define	ICU_SETSPI_NSR_AH	0x14
6744d027bbSEmmanuel Vadot #define	ICU_CLRSPI_NSR_AL	0x18
6844d027bbSEmmanuel Vadot #define	ICU_CLRSPI_NSR_AH	0x1c
69986bbba9SKornel Duleba #define	ICU_SETSPI_SEI_AL	0x50
70986bbba9SKornel Duleba #define	ICU_SETSPI_SEI_AH	0x54
7144d027bbSEmmanuel Vadot #define	ICU_INT_CFG(x)	(0x100 + (x) * 4)
7244d027bbSEmmanuel Vadot #define	 ICU_INT_ENABLE		(1 << 24)
7344d027bbSEmmanuel Vadot #define	 ICU_INT_EDGE		(1 << 28)
7444d027bbSEmmanuel Vadot #define	 ICU_INT_GROUP_SHIFT	29
7544d027bbSEmmanuel Vadot #define	 ICU_INT_MASK		0x3ff
7644d027bbSEmmanuel Vadot 
77986bbba9SKornel Duleba #define	ICU_INT_SATA0		109
78986bbba9SKornel Duleba #define	ICU_INT_SATA1		107
79986bbba9SKornel Duleba 
8044d027bbSEmmanuel Vadot #define	MV_CP110_ICU_MAX_NIRQS	207
8144d027bbSEmmanuel Vadot 
82986bbba9SKornel Duleba #define	MV_CP110_ICU_CLRSPI_OFFSET	0x8
83986bbba9SKornel Duleba 
8444d027bbSEmmanuel Vadot struct mv_cp110_icu_softc {
8544d027bbSEmmanuel Vadot 	device_t		dev;
8644d027bbSEmmanuel Vadot 	device_t		parent;
8744d027bbSEmmanuel Vadot 	struct resource		*res;
884b84206bSMichal Meloun 	struct intr_map_data_fdt *parent_map_data;
89986bbba9SKornel Duleba 	bool			initialized;
90986bbba9SKornel Duleba 	int			type;
9144d027bbSEmmanuel Vadot };
9244d027bbSEmmanuel Vadot 
9344d027bbSEmmanuel Vadot static struct resource_spec mv_cp110_icu_res_spec[] = {
9444d027bbSEmmanuel Vadot 	{ SYS_RES_MEMORY,	0,	RF_ACTIVE | RF_SHAREABLE },
9544d027bbSEmmanuel Vadot 	{ -1, 0 }
9644d027bbSEmmanuel Vadot };
9744d027bbSEmmanuel Vadot 
9844d027bbSEmmanuel Vadot static struct ofw_compat_data compat_data[] = {
99986bbba9SKornel Duleba 	{"marvell,cp110-icu-nsr",	ICU_TYPE_NSR},
100986bbba9SKornel Duleba 	{"marvell,cp110-icu-sei",	ICU_TYPE_SEI},
10144d027bbSEmmanuel Vadot 	{NULL,				0}
10244d027bbSEmmanuel Vadot };
10344d027bbSEmmanuel Vadot 
10444d027bbSEmmanuel Vadot #define	RD4(sc, reg)		bus_read_4((sc)->res, (reg))
10544d027bbSEmmanuel Vadot #define	WR4(sc, reg, val)	bus_write_4((sc)->res, (reg), (val))
10644d027bbSEmmanuel Vadot 
10744d027bbSEmmanuel Vadot static int
10844d027bbSEmmanuel Vadot mv_cp110_icu_probe(device_t dev)
10944d027bbSEmmanuel Vadot {
11044d027bbSEmmanuel Vadot 
11144d027bbSEmmanuel Vadot 	if (!ofw_bus_status_okay(dev))
11244d027bbSEmmanuel Vadot 		return (ENXIO);
11344d027bbSEmmanuel Vadot 
11444d027bbSEmmanuel Vadot 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
11544d027bbSEmmanuel Vadot 		return (ENXIO);
11644d027bbSEmmanuel Vadot 
11744d027bbSEmmanuel Vadot 	device_set_desc(dev, "Marvell Interrupt Consolidation Unit");
11844d027bbSEmmanuel Vadot 	return (BUS_PROBE_DEFAULT);
11944d027bbSEmmanuel Vadot }
12044d027bbSEmmanuel Vadot 
12144d027bbSEmmanuel Vadot static int
12244d027bbSEmmanuel Vadot mv_cp110_icu_attach(device_t dev)
12344d027bbSEmmanuel Vadot {
12444d027bbSEmmanuel Vadot 	struct mv_cp110_icu_softc *sc;
12544d027bbSEmmanuel Vadot 	phandle_t node, msi_parent;
126986bbba9SKornel Duleba 	uint32_t reg, icu_grp;
127986bbba9SKornel Duleba 	int i;
12844d027bbSEmmanuel Vadot 
12944d027bbSEmmanuel Vadot 	sc = device_get_softc(dev);
13044d027bbSEmmanuel Vadot 	sc->dev = dev;
13144d027bbSEmmanuel Vadot 	node = ofw_bus_get_node(dev);
132986bbba9SKornel Duleba 	sc->type = (int)ofw_bus_search_compatible(dev, compat_data)->ocd_data;
133986bbba9SKornel Duleba 	sc->initialized = false;
13444d027bbSEmmanuel Vadot 
13544d027bbSEmmanuel Vadot 	if (OF_getencprop(node, "msi-parent", &msi_parent,
13644d027bbSEmmanuel Vadot 	    sizeof(phandle_t)) <= 0) {
13744d027bbSEmmanuel Vadot 		device_printf(dev, "cannot find msi-parent property\n");
13844d027bbSEmmanuel Vadot 		return (ENXIO);
13944d027bbSEmmanuel Vadot 	}
14044d027bbSEmmanuel Vadot 
14144d027bbSEmmanuel Vadot 	if ((sc->parent = OF_device_from_xref(msi_parent)) == NULL) {
14244d027bbSEmmanuel Vadot 		device_printf(dev, "cannot find msi-parent device\n");
14344d027bbSEmmanuel Vadot 		return (ENXIO);
14444d027bbSEmmanuel Vadot 	}
14544d027bbSEmmanuel Vadot 	if (bus_alloc_resources(dev, mv_cp110_icu_res_spec, &sc->res) != 0) {
14644d027bbSEmmanuel Vadot 		device_printf(dev, "cannot allocate resources for device\n");
14744d027bbSEmmanuel Vadot 		return (ENXIO);
14844d027bbSEmmanuel Vadot 	}
14944d027bbSEmmanuel Vadot 
15044d027bbSEmmanuel Vadot 	if (intr_pic_register(dev, OF_xref_from_node(node)) == NULL) {
15144d027bbSEmmanuel Vadot 		device_printf(dev, "Cannot register ICU\n");
15244d027bbSEmmanuel Vadot 		goto fail;
15344d027bbSEmmanuel Vadot 	}
1544b84206bSMichal Meloun 
155986bbba9SKornel Duleba 	/* Allocate GICP/SEI compatible mapping entry (2 cells) */
1564b84206bSMichal Meloun 	sc->parent_map_data = (struct intr_map_data_fdt *)intr_alloc_map_data(
1574b84206bSMichal Meloun 	    INTR_MAP_DATA_FDT, sizeof(struct intr_map_data_fdt) +
1584b84206bSMichal Meloun 	    + 3 * sizeof(phandle_t), M_WAITOK | M_ZERO);
159986bbba9SKornel Duleba 
160986bbba9SKornel Duleba 	/* Clear any previous mapping done by firmware. */
161986bbba9SKornel Duleba 	for (i = 0; i < MV_CP110_ICU_MAX_NIRQS; i++) {
162986bbba9SKornel Duleba 		reg = RD4(sc, ICU_INT_CFG(i));
163986bbba9SKornel Duleba 		icu_grp = reg >> ICU_INT_GROUP_SHIFT;
164986bbba9SKornel Duleba 
165986bbba9SKornel Duleba 		if (icu_grp == ICU_GRP_NSR || icu_grp == ICU_GRP_SEI)
166986bbba9SKornel Duleba 			WR4(sc, ICU_INT_CFG(i), 0);
167986bbba9SKornel Duleba 	}
168986bbba9SKornel Duleba 
16944d027bbSEmmanuel Vadot 	return (0);
17044d027bbSEmmanuel Vadot 
17144d027bbSEmmanuel Vadot fail:
17244d027bbSEmmanuel Vadot 	bus_release_resources(dev, mv_cp110_icu_res_spec, &sc->res);
17344d027bbSEmmanuel Vadot 	return (ENXIO);
17444d027bbSEmmanuel Vadot }
17544d027bbSEmmanuel Vadot 
1764b84206bSMichal Meloun static struct intr_map_data *
1774b84206bSMichal Meloun mv_cp110_icu_convert_map_data(struct mv_cp110_icu_softc *sc, struct intr_map_data *data)
1784b84206bSMichal Meloun {
1794b84206bSMichal Meloun 	struct intr_map_data_fdt *daf;
1804b84206bSMichal Meloun 	uint32_t reg, irq_no, irq_type;
1814b84206bSMichal Meloun 
1824b84206bSMichal Meloun 	daf = (struct intr_map_data_fdt *)data;
1834b84206bSMichal Meloun 	if (daf->ncells != 2)
1844b84206bSMichal Meloun 		return (NULL);
185986bbba9SKornel Duleba 
1864b84206bSMichal Meloun 	irq_no = daf->cells[0];
1874b84206bSMichal Meloun 	if (irq_no >= MV_CP110_ICU_MAX_NIRQS)
1884b84206bSMichal Meloun 		return (NULL);
189986bbba9SKornel Duleba 
190986bbba9SKornel Duleba 	irq_type = daf->cells[1];
1914b84206bSMichal Meloun 	if (irq_type != IRQ_TYPE_LEVEL_HIGH &&
1924b84206bSMichal Meloun 	    irq_type != IRQ_TYPE_EDGE_RISING)
1934b84206bSMichal Meloun 		return (NULL);
1944b84206bSMichal Meloun 
195986bbba9SKornel Duleba 	/* ICU -> GICP/SEI mapping is set in mv_cp110_icu_map_intr. */
1964b84206bSMichal Meloun 	reg = RD4(sc, ICU_INT_CFG(irq_no));
1974b84206bSMichal Meloun 
1984b84206bSMichal Meloun 	/* Construct GICP compatible mapping. */
1994b84206bSMichal Meloun 	sc->parent_map_data->ncells = 2;
2004b84206bSMichal Meloun 	sc->parent_map_data->cells[0] = reg & ICU_INT_MASK;
2014b84206bSMichal Meloun 	sc->parent_map_data->cells[1] = irq_type;
2024b84206bSMichal Meloun 
2034b84206bSMichal Meloun 	return ((struct intr_map_data *)sc->parent_map_data);
2044b84206bSMichal Meloun }
2054b84206bSMichal Meloun 
20644d027bbSEmmanuel Vadot static int
20744d027bbSEmmanuel Vadot mv_cp110_icu_detach(device_t dev)
20844d027bbSEmmanuel Vadot {
20944d027bbSEmmanuel Vadot 
21044d027bbSEmmanuel Vadot 	return (EBUSY);
21144d027bbSEmmanuel Vadot }
21244d027bbSEmmanuel Vadot 
21344d027bbSEmmanuel Vadot static int
21444d027bbSEmmanuel Vadot mv_cp110_icu_activate_intr(device_t dev, struct intr_irqsrc *isrc,
21544d027bbSEmmanuel Vadot     struct resource *res, struct intr_map_data *data)
21644d027bbSEmmanuel Vadot {
21744d027bbSEmmanuel Vadot 	struct mv_cp110_icu_softc *sc;
21844d027bbSEmmanuel Vadot 
21944d027bbSEmmanuel Vadot 	sc = device_get_softc(dev);
2204b84206bSMichal Meloun 	data = mv_cp110_icu_convert_map_data(sc, data);
2214b84206bSMichal Meloun 	if (data == NULL)
2224b84206bSMichal Meloun 		return (EINVAL);
22344d027bbSEmmanuel Vadot 	return (PIC_ACTIVATE_INTR(sc->parent, isrc, res, data));
22444d027bbSEmmanuel Vadot }
22544d027bbSEmmanuel Vadot 
22644d027bbSEmmanuel Vadot static void
22744d027bbSEmmanuel Vadot mv_cp110_icu_enable_intr(device_t dev, struct intr_irqsrc *isrc)
22844d027bbSEmmanuel Vadot {
22944d027bbSEmmanuel Vadot 	struct mv_cp110_icu_softc *sc;
23044d027bbSEmmanuel Vadot 	sc = device_get_softc(dev);
23144d027bbSEmmanuel Vadot 
23244d027bbSEmmanuel Vadot 	PIC_ENABLE_INTR(sc->parent, isrc);
23344d027bbSEmmanuel Vadot }
23444d027bbSEmmanuel Vadot 
23544d027bbSEmmanuel Vadot static void
23644d027bbSEmmanuel Vadot mv_cp110_icu_disable_intr(device_t dev, struct intr_irqsrc *isrc)
23744d027bbSEmmanuel Vadot {
23844d027bbSEmmanuel Vadot 	struct mv_cp110_icu_softc *sc;
23944d027bbSEmmanuel Vadot 
24044d027bbSEmmanuel Vadot 	sc = device_get_softc(dev);
24144d027bbSEmmanuel Vadot 
24244d027bbSEmmanuel Vadot 	PIC_DISABLE_INTR(sc->parent, isrc);
24344d027bbSEmmanuel Vadot }
24444d027bbSEmmanuel Vadot 
245986bbba9SKornel Duleba static void
246986bbba9SKornel Duleba mv_cp110_icu_init(struct mv_cp110_icu_softc *sc, uint64_t addr)
247986bbba9SKornel Duleba {
248986bbba9SKornel Duleba 
249986bbba9SKornel Duleba 	if (sc->initialized)
250986bbba9SKornel Duleba 		return;
251986bbba9SKornel Duleba 
252986bbba9SKornel Duleba 	switch (sc->type) {
253986bbba9SKornel Duleba 	case ICU_TYPE_NSR:
254986bbba9SKornel Duleba 		WR4(sc, ICU_SETSPI_NSR_AL, addr & UINT32_MAX);
255986bbba9SKornel Duleba 		WR4(sc, ICU_SETSPI_NSR_AH, (addr >> 32) & UINT32_MAX);
256986bbba9SKornel Duleba 		addr += MV_CP110_ICU_CLRSPI_OFFSET;
257986bbba9SKornel Duleba 		WR4(sc, ICU_CLRSPI_NSR_AL, addr & UINT32_MAX);
258986bbba9SKornel Duleba 		WR4(sc, ICU_CLRSPI_NSR_AH, (addr >> 32) & UINT32_MAX);
259986bbba9SKornel Duleba 		break;
260986bbba9SKornel Duleba 	case ICU_TYPE_SEI:
261986bbba9SKornel Duleba 		WR4(sc, ICU_SETSPI_SEI_AL, addr & UINT32_MAX);
262986bbba9SKornel Duleba 		WR4(sc, ICU_SETSPI_SEI_AH, (addr >> 32) & UINT32_MAX);
263986bbba9SKornel Duleba 		break;
264986bbba9SKornel Duleba 	default:
265986bbba9SKornel Duleba 		panic("Unkown ICU type.");
266986bbba9SKornel Duleba 	}
267986bbba9SKornel Duleba 
268986bbba9SKornel Duleba 	sc->initialized = true;
269986bbba9SKornel Duleba }
270986bbba9SKornel Duleba 
27144d027bbSEmmanuel Vadot static int
27244d027bbSEmmanuel Vadot mv_cp110_icu_map_intr(device_t dev, struct intr_map_data *data,
27344d027bbSEmmanuel Vadot     struct intr_irqsrc **isrcp)
27444d027bbSEmmanuel Vadot {
27544d027bbSEmmanuel Vadot 	struct mv_cp110_icu_softc *sc;
27644d027bbSEmmanuel Vadot 	struct intr_map_data_fdt *daf;
277986bbba9SKornel Duleba 	uint32_t vector, irq_no, irq_type;
278986bbba9SKornel Duleba 	uint64_t addr;
2794b84206bSMichal Meloun 	int ret;
28044d027bbSEmmanuel Vadot 
28144d027bbSEmmanuel Vadot 	sc = device_get_softc(dev);
28244d027bbSEmmanuel Vadot 
28344d027bbSEmmanuel Vadot 	if (data->type != INTR_MAP_DATA_FDT)
28444d027bbSEmmanuel Vadot 		return (ENOTSUP);
28544d027bbSEmmanuel Vadot 
2864b84206bSMichal Meloun 	/* Parse original */
28744d027bbSEmmanuel Vadot 	daf = (struct intr_map_data_fdt *)data;
2884b84206bSMichal Meloun 	if (daf->ncells != 2)
2894b84206bSMichal Meloun 		return (EINVAL);
290986bbba9SKornel Duleba 
2914b84206bSMichal Meloun 	irq_no = daf->cells[0];
292986bbba9SKornel Duleba 	if (irq_no >= MV_CP110_ICU_MAX_NIRQS)
29344d027bbSEmmanuel Vadot 		return (EINVAL);
29444d027bbSEmmanuel Vadot 
295986bbba9SKornel Duleba 	irq_type = daf->cells[1];
296986bbba9SKornel Duleba 	if (irq_type != IRQ_TYPE_LEVEL_HIGH &&
297986bbba9SKornel Duleba 	    irq_type != IRQ_TYPE_EDGE_RISING)
298986bbba9SKornel Duleba 		return (EINVAL);
29944d027bbSEmmanuel Vadot 
300986bbba9SKornel Duleba 	/*
301986bbba9SKornel Duleba 	 * Allocate MSI vector.
302986bbba9SKornel Duleba 	 * We don't use intr_alloc_msi wrapper, since it registers a new irq
303986bbba9SKornel Duleba 	 * in the kernel. In our case irq was already added by the ofw code.
304986bbba9SKornel Duleba 	 */
305986bbba9SKornel Duleba 	ret = MSI_ALLOC_MSI(sc->parent, dev, 1, 1, NULL, isrcp);
306986bbba9SKornel Duleba 	if (ret != 0)
307986bbba9SKornel Duleba 		return (ret);
308986bbba9SKornel Duleba 
309986bbba9SKornel Duleba 	ret = MSI_MAP_MSI(sc->parent, dev, *isrcp, &addr, &vector);
310986bbba9SKornel Duleba 	if (ret != 0)
311986bbba9SKornel Duleba 		goto fail;
312986bbba9SKornel Duleba 
313986bbba9SKornel Duleba 	mv_cp110_icu_init(sc, addr);
314986bbba9SKornel Duleba 	vector |= ICU_INT_ENABLE;
315986bbba9SKornel Duleba 
316986bbba9SKornel Duleba 	if (sc->type == ICU_TYPE_NSR)
317986bbba9SKornel Duleba 		vector |= ICU_GRP_NSR << ICU_INT_GROUP_SHIFT;
318986bbba9SKornel Duleba 	else
319986bbba9SKornel Duleba 		vector |= ICU_GRP_SEI << ICU_INT_GROUP_SHIFT;
320986bbba9SKornel Duleba 
321986bbba9SKornel Duleba 	if (irq_type & IRQ_TYPE_EDGE_BOTH)
322986bbba9SKornel Duleba 		vector |= ICU_INT_EDGE;
323986bbba9SKornel Duleba 
324986bbba9SKornel Duleba 	WR4(sc, ICU_INT_CFG(irq_no), vector);
325986bbba9SKornel Duleba 
326986bbba9SKornel Duleba 	/*
327986bbba9SKornel Duleba 	 * SATA controller has two ports, each gets its own interrupt.
328986bbba9SKornel Duleba 	 * The problem is that only one irq is described in dts.
329986bbba9SKornel Duleba 	 * Also ahci_generic driver supports only one irq per controller.
330986bbba9SKornel Duleba 	 * As a workaround map both interrupts when one of them is allocated.
331986bbba9SKornel Duleba 	 * This allows us to use both SATA ports.
332986bbba9SKornel Duleba 	 */
333986bbba9SKornel Duleba 	if (irq_no == ICU_INT_SATA0)
334986bbba9SKornel Duleba 		WR4(sc, ICU_INT_CFG(ICU_INT_SATA1), vector);
335986bbba9SKornel Duleba 	if (irq_no == ICU_INT_SATA1)
336986bbba9SKornel Duleba 		WR4(sc, ICU_INT_CFG(ICU_INT_SATA0), vector);
337986bbba9SKornel Duleba 
3384b84206bSMichal Meloun 	(*isrcp)->isrc_dev = sc->dev;
3394b84206bSMichal Meloun 	return (ret);
340986bbba9SKornel Duleba 
341986bbba9SKornel Duleba fail:
342986bbba9SKornel Duleba 	if (*isrcp != NULL)
343986bbba9SKornel Duleba 		MSI_RELEASE_MSI(sc->parent, dev, 1, isrcp);
344986bbba9SKornel Duleba 
345986bbba9SKornel Duleba 	return (ret);
34644d027bbSEmmanuel Vadot }
34744d027bbSEmmanuel Vadot 
34844d027bbSEmmanuel Vadot static int
34944d027bbSEmmanuel Vadot mv_cp110_icu_deactivate_intr(device_t dev, struct intr_irqsrc *isrc,
35044d027bbSEmmanuel Vadot     struct resource *res, struct intr_map_data *data)
35144d027bbSEmmanuel Vadot {
35244d027bbSEmmanuel Vadot 	struct mv_cp110_icu_softc *sc;
353986bbba9SKornel Duleba 	struct intr_map_data_fdt *daf;
354986bbba9SKornel Duleba 	int irq_no, ret;
355986bbba9SKornel Duleba 
356986bbba9SKornel Duleba 	if (data->type != INTR_MAP_DATA_FDT)
357986bbba9SKornel Duleba 		return (ENOTSUP);
35844d027bbSEmmanuel Vadot 
35944d027bbSEmmanuel Vadot 	sc = device_get_softc(dev);
360986bbba9SKornel Duleba 	daf = (struct intr_map_data_fdt *)data;
361986bbba9SKornel Duleba 	if (daf->ncells != 2)
362986bbba9SKornel Duleba 		return (EINVAL);
363986bbba9SKornel Duleba 
364986bbba9SKornel Duleba 	irq_no = daf->cells[0];
3654b84206bSMichal Meloun 	data = mv_cp110_icu_convert_map_data(sc, data);
3664b84206bSMichal Meloun 	if (data == NULL)
3674b84206bSMichal Meloun 		return (EINVAL);
36844d027bbSEmmanuel Vadot 
369986bbba9SKornel Duleba 	/* Clear the mapping. */
370986bbba9SKornel Duleba 	WR4(sc, ICU_INT_CFG(irq_no), 0);
371986bbba9SKornel Duleba 
372986bbba9SKornel Duleba 	ret = PIC_DEACTIVATE_INTR(sc->parent, isrc, res, data);
373986bbba9SKornel Duleba 	if (ret != 0)
374986bbba9SKornel Duleba 		return (ret);
375986bbba9SKornel Duleba 
376986bbba9SKornel Duleba 	return (MSI_RELEASE_MSI(sc->parent, dev, 1, &isrc));
37744d027bbSEmmanuel Vadot }
37844d027bbSEmmanuel Vadot 
37944d027bbSEmmanuel Vadot static int
38044d027bbSEmmanuel Vadot mv_cp110_icu_setup_intr(device_t dev, struct intr_irqsrc *isrc,
38144d027bbSEmmanuel Vadot     struct resource *res, struct intr_map_data *data)
38244d027bbSEmmanuel Vadot {
38344d027bbSEmmanuel Vadot 	struct mv_cp110_icu_softc *sc;
38444d027bbSEmmanuel Vadot 
38544d027bbSEmmanuel Vadot 	sc = device_get_softc(dev);
3864b84206bSMichal Meloun 	data = mv_cp110_icu_convert_map_data(sc, data);
3874b84206bSMichal Meloun 	if (data == NULL)
3884b84206bSMichal Meloun 		return (EINVAL);
38944d027bbSEmmanuel Vadot 
39044d027bbSEmmanuel Vadot 	return (PIC_SETUP_INTR(sc->parent, isrc, res, data));
39144d027bbSEmmanuel Vadot }
39244d027bbSEmmanuel Vadot 
39344d027bbSEmmanuel Vadot static int
39444d027bbSEmmanuel Vadot mv_cp110_icu_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
39544d027bbSEmmanuel Vadot     struct resource *res, struct intr_map_data *data)
39644d027bbSEmmanuel Vadot {
39744d027bbSEmmanuel Vadot 	struct mv_cp110_icu_softc *sc;
39844d027bbSEmmanuel Vadot 
39944d027bbSEmmanuel Vadot 	sc = device_get_softc(dev);
4004b84206bSMichal Meloun 	data = mv_cp110_icu_convert_map_data(sc, data);
4014b84206bSMichal Meloun 	if (data == NULL)
4024b84206bSMichal Meloun 		return (EINVAL);
40344d027bbSEmmanuel Vadot 
40444d027bbSEmmanuel Vadot 	return (PIC_TEARDOWN_INTR(sc->parent, isrc, res, data));
40544d027bbSEmmanuel Vadot }
40644d027bbSEmmanuel Vadot 
40744d027bbSEmmanuel Vadot static void
40844d027bbSEmmanuel Vadot mv_cp110_icu_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
40944d027bbSEmmanuel Vadot {
41044d027bbSEmmanuel Vadot 	struct mv_cp110_icu_softc *sc;
41144d027bbSEmmanuel Vadot 
41244d027bbSEmmanuel Vadot 	sc = device_get_softc(dev);
41344d027bbSEmmanuel Vadot 
41444d027bbSEmmanuel Vadot 	PIC_PRE_ITHREAD(sc->parent, isrc);
41544d027bbSEmmanuel Vadot }
41644d027bbSEmmanuel Vadot 
41744d027bbSEmmanuel Vadot static void
41844d027bbSEmmanuel Vadot mv_cp110_icu_post_ithread(device_t dev, struct intr_irqsrc *isrc)
41944d027bbSEmmanuel Vadot {
42044d027bbSEmmanuel Vadot 	struct mv_cp110_icu_softc *sc;
42144d027bbSEmmanuel Vadot 
42244d027bbSEmmanuel Vadot 	sc = device_get_softc(dev);
42344d027bbSEmmanuel Vadot 
42444d027bbSEmmanuel Vadot 	PIC_POST_ITHREAD(sc->parent, isrc);
42544d027bbSEmmanuel Vadot }
42644d027bbSEmmanuel Vadot 
42744d027bbSEmmanuel Vadot static void
42844d027bbSEmmanuel Vadot mv_cp110_icu_post_filter(device_t dev, struct intr_irqsrc *isrc)
42944d027bbSEmmanuel Vadot {
43044d027bbSEmmanuel Vadot 	struct mv_cp110_icu_softc *sc;
43144d027bbSEmmanuel Vadot 
43244d027bbSEmmanuel Vadot 	sc = device_get_softc(dev);
43344d027bbSEmmanuel Vadot 
43444d027bbSEmmanuel Vadot 	PIC_POST_FILTER(sc->parent, isrc);
43544d027bbSEmmanuel Vadot }
43644d027bbSEmmanuel Vadot 
43744d027bbSEmmanuel Vadot static device_method_t mv_cp110_icu_methods[] = {
43844d027bbSEmmanuel Vadot 	/* Device interface */
43944d027bbSEmmanuel Vadot 	DEVMETHOD(device_probe,		mv_cp110_icu_probe),
44044d027bbSEmmanuel Vadot 	DEVMETHOD(device_attach,	mv_cp110_icu_attach),
44144d027bbSEmmanuel Vadot 	DEVMETHOD(device_detach,	mv_cp110_icu_detach),
44244d027bbSEmmanuel Vadot 
44344d027bbSEmmanuel Vadot 	/* Interrupt controller interface */
44444d027bbSEmmanuel Vadot 	DEVMETHOD(pic_activate_intr,	mv_cp110_icu_activate_intr),
44544d027bbSEmmanuel Vadot 	DEVMETHOD(pic_disable_intr,	mv_cp110_icu_disable_intr),
44644d027bbSEmmanuel Vadot 	DEVMETHOD(pic_enable_intr,	mv_cp110_icu_enable_intr),
44744d027bbSEmmanuel Vadot 	DEVMETHOD(pic_map_intr,		mv_cp110_icu_map_intr),
44844d027bbSEmmanuel Vadot 	DEVMETHOD(pic_deactivate_intr,	mv_cp110_icu_deactivate_intr),
44944d027bbSEmmanuel Vadot 	DEVMETHOD(pic_setup_intr,	mv_cp110_icu_setup_intr),
45044d027bbSEmmanuel Vadot 	DEVMETHOD(pic_teardown_intr,	mv_cp110_icu_teardown_intr),
45144d027bbSEmmanuel Vadot 	DEVMETHOD(pic_post_filter,	mv_cp110_icu_post_filter),
45244d027bbSEmmanuel Vadot 	DEVMETHOD(pic_post_ithread,	mv_cp110_icu_post_ithread),
45344d027bbSEmmanuel Vadot 	DEVMETHOD(pic_pre_ithread,	mv_cp110_icu_pre_ithread),
45444d027bbSEmmanuel Vadot 
45544d027bbSEmmanuel Vadot 	DEVMETHOD_END
45644d027bbSEmmanuel Vadot };
45744d027bbSEmmanuel Vadot 
45844d027bbSEmmanuel Vadot static driver_t mv_cp110_icu_driver = {
45944d027bbSEmmanuel Vadot 	"mv_cp110_icu",
46044d027bbSEmmanuel Vadot 	mv_cp110_icu_methods,
46144d027bbSEmmanuel Vadot 	sizeof(struct mv_cp110_icu_softc),
46244d027bbSEmmanuel Vadot };
46344d027bbSEmmanuel Vadot 
464*a3b866cbSJohn Baldwin EARLY_DRIVER_MODULE(mv_cp110_icu, mv_cp110_icu_bus, mv_cp110_icu_driver, 0, 0,
465*a3b866cbSJohn Baldwin     BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LAST);
466