xref: /openbsd-src/sys/dev/fdt/mvicu.c (revision 897fc685943471cf985a0fe38ba076ea6fe74fa5)
1 /*	$OpenBSD: mvicu.c,v 1.2 2018/03/29 18:11:55 kettenis Exp $	*/
2 /*
3  * Copyright (c) 2018 Mark Kettenis <kettenis@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/device.h>
21 
22 #include <machine/intr.h>
23 #include <machine/bus.h>
24 #include <machine/fdt.h>
25 
26 #include <dev/ofw/openfirm.h>
27 #include <dev/ofw/fdt.h>
28 
29 /* Registers. */
30 #define ICU_SETSPI_NSR_AL	0x10
31 #define ICU_SETSPI_NSR_AH	0x14
32 #define ICU_CLRSPI_NSR_AL	0x18
33 #define ICU_CLRSPI_NSR_AH	0x1c
34 #define ICU_INT_CFG(x)	(0x100 + (x) * 4)
35 #define  ICU_INT_ENABLE		(1 << 24)
36 #define  ICU_INT_EDGE		(1 << 28)
37 #define  ICU_INT_GROUP_SHIFT	29
38 #define  ICU_INT_MASK		0x3ff
39 
40 #define HREAD4(sc, reg)							\
41 	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
42 #define HWRITE4(sc, reg, val)						\
43 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
44 
45 struct mvicu_softc {
46 	struct device		sc_dev;
47 	bus_space_tag_t		sc_iot;
48 	bus_space_handle_t	sc_ioh;
49 
50 	uint32_t		sc_spi_ranges[4];
51 
52 	struct interrupt_controller sc_ic;
53 	struct interrupt_controller *sc_parent_ic;
54 };
55 
56 int mvicu_match(struct device *, void *, void *);
57 void mvicu_attach(struct device *, struct device *, void *);
58 
59 struct cfattach	mvicu_ca = {
60 	sizeof (struct mvicu_softc), mvicu_match, mvicu_attach
61 };
62 
63 struct cfdriver mvicu_cd = {
64 	NULL, "mvicu", DV_DULL
65 };
66 
67 void	*mvicu_intr_establish(void *, int *, int, int (*)(void *),
68 	    void *, char *);
69 void	mvicu_intr_disestablish(void *);
70 
71 int
72 mvicu_match(struct device *parent, void *match, void *aux)
73 {
74 	struct fdt_attach_args *faa = aux;
75 
76 	return OF_is_compatible(faa->fa_node, "marvell,cp110-icu");
77 }
78 
79 void
80 mvicu_attach(struct device *parent, struct device *self, void *aux)
81 {
82 	struct mvicu_softc *sc = (struct mvicu_softc *)self;
83 	struct fdt_attach_args *faa = aux;
84 	struct interrupt_controller *ic;
85 	bus_addr_t low, high, setspi_addr, clrspi_addr;
86 	uint32_t phandle;
87 	int node;
88 
89 	if (faa->fa_nreg < 1) {
90 		printf(": no registers\n");
91 		return;
92 	}
93 
94 	phandle = OF_getpropint(faa->fa_node, "msi-parent", 0);
95 	node = OF_getnodebyphandle(phandle);
96 	if (node == 0) {
97 		printf(": GICP not found\n");
98 		return;
99 	}
100 	OF_getpropintarray(node, "marvell,spi-ranges", sc->sc_spi_ranges,
101 	    sizeof(sc->sc_spi_ranges));
102 
103 	sc->sc_iot = faa->fa_iot;
104 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
105 	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
106 		printf(": can't map registers\n");
107 		return;
108 	}
109 
110 	/*
111 	 * This driver assumes that the ICU has been configured by the
112 	 * firmware.  Do some (minimal) checks to verify that has
113 	 * indeed been done.
114 	 */
115 	low = HREAD4(sc, ICU_SETSPI_NSR_AL);
116 	high = HREAD4(sc, ICU_SETSPI_NSR_AH);
117 	setspi_addr = (high << 32) | low;
118 	low = HREAD4(sc, ICU_CLRSPI_NSR_AL);
119 	high = HREAD4(sc, ICU_CLRSPI_NSR_AH);
120 	clrspi_addr = (high << 32) | low;
121 	if (setspi_addr == 0 || clrspi_addr == 0) {
122 		printf(": not configured by firmware\n");
123 		return;
124 	}
125 
126 	printf("\n");
127 
128 	extern uint32_t arm_intr_get_parent(int);
129 	phandle = arm_intr_get_parent(node);
130 	extern LIST_HEAD(, interrupt_controller) interrupt_controllers;
131 	LIST_FOREACH(ic, &interrupt_controllers, ic_list) {
132 		if (ic->ic_phandle == phandle)
133 			break;
134 	}
135 	sc->sc_parent_ic = ic;
136 
137 	sc->sc_ic.ic_node = faa->fa_node;
138 	sc->sc_ic.ic_cookie = sc;
139 	sc->sc_ic.ic_establish = mvicu_intr_establish;
140 	sc->sc_ic.ic_disestablish = mvicu_intr_disestablish;
141 	arm_intr_register_fdt(&sc->sc_ic);
142 }
143 
144 void *
145 mvicu_intr_establish(void *cookie, int *cell, int level,
146     int (*func)(void *), void *arg, char *name)
147 {
148 	struct mvicu_softc *sc = cookie;
149 	struct interrupt_controller *ic = sc->sc_parent_ic;
150 	uint32_t group = cell[0];
151 	uint32_t idx = cell[1];
152 	uint32_t interrupt[3];
153 	uint32_t reg;
154 	int i;
155 
156 	if (ic == NULL)
157 		return NULL;
158 
159 	reg = HREAD4(sc, ICU_INT_CFG(idx));
160 	if ((reg & ICU_INT_ENABLE) == 0 ||
161 	    (reg >> ICU_INT_GROUP_SHIFT) != group)
162 		return NULL;
163 
164 	/* Convert to GIC interrupt source. */
165 	idx = reg & ICU_INT_MASK;
166 	for (i = 0; i < nitems(sc->sc_spi_ranges); i += 2) {
167 		if (idx < sc->sc_spi_ranges[i + 1]) {
168 			idx += sc->sc_spi_ranges[i];
169 			break;
170 		}
171 		idx -= sc->sc_spi_ranges[i];
172 	}
173 	if (i == nitems(sc->sc_spi_ranges))
174 		return NULL;
175 	interrupt[0] = 0;
176 	interrupt[1] = idx - 32;
177 	interrupt[2] = cell[2];
178 	return ic->ic_establish(ic->ic_cookie, interrupt, level,
179 	    func, arg, name);
180 }
181 
182 void
183 mvicu_intr_disestablish(void *cookie)
184 {
185 	panic("%s", __func__);
186 }
187