xref: /openbsd/sys/dev/fdt/mvicu.c (revision 905646f0)
1 /*	$OpenBSD: mvicu.c,v 1.6 2020/07/17 08:07:34 patrick 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 #include <sys/malloc.h>
22 
23 #include <machine/intr.h>
24 #include <machine/bus.h>
25 #include <machine/fdt.h>
26 
27 #include <dev/ofw/openfirm.h>
28 #include <dev/ofw/fdt.h>
29 
30 /* Registers. */
31 #define ICU_SETSPI_NSR_AL	0x10
32 #define ICU_SETSPI_NSR_AH	0x14
33 #define ICU_CLRSPI_NSR_AL	0x18
34 #define ICU_CLRSPI_NSR_AH	0x1c
35 #define ICU_SET_SEI_AL		0x50
36 #define ICU_SET_SEI_AH		0x54
37 #define ICU_CLR_SEI_AL		0x58
38 #define ICU_CLR_SEI_AH		0x5c
39 #define ICU_INT_CFG(x)	(0x100 + (x) * 4)
40 #define  ICU_INT_ENABLE		(1 << 24)
41 #define  ICU_INT_EDGE		(1 << 28)
42 #define  ICU_INT_GROUP_SHIFT	29
43 #define  ICU_INT_MASK		0x3ff
44 
45 #define GICP_SETSPI_NSR		0x00
46 #define GICP_CLRSPI_NSR		0x08
47 
48 /* Devices */
49 #define ICU_DEVICE_SATA0	109
50 #define ICU_DEVICE_SATA1	107
51 #define ICU_DEVICE_NIRQ		207
52 
53 /* Groups. */
54 #define ICU_GRP_NSR		0x0
55 #define ICU_GRP_SR		0x1
56 #define ICU_GRP_SEI		0x4
57 #define ICU_GRP_REI		0x5
58 
59 #define HREAD4(sc, reg)							\
60 	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
61 #define HWRITE4(sc, reg, val)						\
62 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
63 
64 struct mvicu_softc;
65 struct mvicu_subnode {
66 	struct mvicu_softc	*sn_sc;
67 	int			sn_group;
68 	struct interrupt_controller sn_ic;
69 	struct interrupt_controller *sn_parent_ic;
70 };
71 
72 struct mvicu_softc {
73 	struct device		sc_dev;
74 	bus_space_tag_t		sc_iot;
75 	bus_space_handle_t	sc_ioh;
76 
77 	uint64_t		sc_nsr_addr;
78 	uint64_t		sc_sei_addr;
79 
80 	int			sc_legacy;
81 	struct mvicu_subnode	*sc_nodes;
82 };
83 
84 int mvicu_match(struct device *, void *, void *);
85 void mvicu_attach(struct device *, struct device *, void *);
86 
87 struct cfattach	mvicu_ca = {
88 	sizeof (struct mvicu_softc), mvicu_match, mvicu_attach
89 };
90 
91 struct cfdriver mvicu_cd = {
92 	NULL, "mvicu", DV_DULL
93 };
94 
95 void	mvicu_register(struct mvicu_softc *, int, int);
96 void	*mvicu_intr_establish(void *, int *, int, struct cpu_info *,
97 	    int (*)(void *), void *, char *);
98 void	mvicu_intr_disestablish(void *);
99 void	mvicu_intr_barrier(void *);
100 
101 int
102 mvicu_match(struct device *parent, void *match, void *aux)
103 {
104 	struct fdt_attach_args *faa = aux;
105 
106 	return OF_is_compatible(faa->fa_node, "marvell,cp110-icu");
107 }
108 
109 void
110 mvicu_attach(struct device *parent, struct device *self, void *aux)
111 {
112 	struct mvicu_softc *sc = (struct mvicu_softc *)self;
113 	struct fdt_attach_args *faa = aux;
114 	int i, node, nchildren;
115 
116 	if (faa->fa_nreg < 1) {
117 		printf(": no registers\n");
118 		return;
119 	}
120 
121 	sc->sc_iot = faa->fa_iot;
122 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
123 	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
124 		printf(": can't map registers\n");
125 		return;
126 	}
127 
128 	printf("\n");
129 
130 	if (OF_child(faa->fa_node) == 0) {
131 		sc->sc_legacy = 1;
132 		sc->sc_nodes = mallocarray(1, sizeof(*sc->sc_nodes),
133 		    M_DEVBUF, M_WAITOK | M_ZERO);
134 		mvicu_register(sc, faa->fa_node, 0);
135 	} else {
136 		for (node = OF_child(faa->fa_node), nchildren = 0;
137 		    node; node = OF_peer(node))
138 			nchildren++;
139 		sc->sc_nodes = mallocarray(nchildren, sizeof(*sc->sc_nodes),
140 		    M_DEVBUF, M_WAITOK | M_ZERO);
141 		for (node = OF_child(faa->fa_node), i = 0; node;
142 		    node = OF_peer(node))
143 			mvicu_register(sc, node, i++);
144 	}
145 }
146 
147 void
148 mvicu_register(struct mvicu_softc *sc, int node, int idx)
149 {
150 	struct mvicu_subnode *sn = &sc->sc_nodes[idx];
151 	struct interrupt_controller *ic;
152 	uint32_t phandle = 0;
153 	uint32_t group;
154 	int i;
155 
156 	sn->sn_group = -1;
157 	if (OF_is_compatible(node, "marvell,cp110-icu") ||
158 	    OF_is_compatible(node, "marvell,cp110-icu-nsr"))
159 		sn->sn_group = ICU_GRP_NSR;
160 	if (OF_is_compatible(node, "marvell,cp110-icu-sei"))
161 		sn->sn_group = ICU_GRP_SEI;
162 
163 	for (i = 0; i < ICU_DEVICE_NIRQ; i++) {
164 		group = HREAD4(sc, ICU_INT_CFG(i)) >> ICU_INT_GROUP_SHIFT;
165 		if ((sn->sn_group == ICU_GRP_NSR && group == ICU_GRP_NSR) ||
166 		    (sn->sn_group == ICU_GRP_SEI && group == ICU_GRP_SEI))
167 			HWRITE4(sc, ICU_INT_CFG(i), 0);
168 	}
169 
170 	sn->sn_sc = sc;
171 	sn->sn_ic.ic_node = node;
172 	sn->sn_ic.ic_cookie = sn;
173 	sn->sn_ic.ic_establish = mvicu_intr_establish;
174 	sn->sn_ic.ic_disestablish = mvicu_intr_disestablish;
175 	sn->sn_ic.ic_barrier = mvicu_intr_barrier;
176 
177 	while (node && !phandle) {
178 		phandle = OF_getpropint(node, "msi-parent", 0);
179 		node = OF_parent(node);
180 	}
181 	if (phandle == 0)
182 		return;
183 
184 	extern LIST_HEAD(, interrupt_controller) interrupt_controllers;
185 	LIST_FOREACH(ic, &interrupt_controllers, ic_list) {
186 		if (ic->ic_phandle == phandle)
187 			break;
188 	}
189 	if (ic == NULL)
190 		return;
191 
192 	sn->sn_parent_ic = ic;
193 	fdt_intr_register(&sn->sn_ic);
194 }
195 
196 void *
197 mvicu_intr_establish(void *cookie, int *cell, int level,
198     struct cpu_info *ci, int (*func)(void *), void *arg, char *name)
199 {
200 	struct mvicu_subnode *sn = cookie;
201 	struct mvicu_softc *sc = sn->sn_sc;
202 	struct interrupt_controller *ic = sn->sn_parent_ic;
203 	struct arm_intr_handle *ih;
204 	uint32_t idx, flags;
205 	uint64_t addr, data;
206 	int edge = 0;
207 
208 	if (sc->sc_legacy) {
209 		if (cell[0] != ICU_GRP_NSR)
210 			return NULL;
211 		idx = cell[1];
212 		flags = cell[2];
213 		edge = ((flags & 0xf) == 0x1);
214 	} else if (sn->sn_group == ICU_GRP_NSR) {
215 		idx = cell[0];
216 		flags = cell[1];
217 		edge = ((flags & 0xf) == 0x1);
218 	} else if (sn->sn_group == ICU_GRP_SEI) {
219 		idx = cell[0];
220 		flags = cell[1];
221 		edge = 1;
222 	} else {
223 		return NULL;
224 	}
225 
226 	data = flags;
227 	cookie = ic->ic_establish_msi(ic->ic_cookie, &addr, &data,
228 	    level, ci, func, arg, name);
229 	if (cookie == NULL)
230 		return NULL;
231 
232 	if (sn->sn_group == ICU_GRP_NSR && !sc->sc_nsr_addr) {
233 		sc->sc_nsr_addr = addr;
234 		HWRITE4(sc, ICU_SETSPI_NSR_AL,
235 		    (addr + GICP_SETSPI_NSR) & 0xffffffff);
236 		HWRITE4(sc, ICU_SETSPI_NSR_AH,
237 		    (addr + GICP_SETSPI_NSR) >> 32);
238 		HWRITE4(sc, ICU_CLRSPI_NSR_AL,
239 		    (addr + GICP_CLRSPI_NSR) & 0xffffffff);
240 		HWRITE4(sc, ICU_CLRSPI_NSR_AH,
241 		    (addr + GICP_CLRSPI_NSR) >> 32);
242 	}
243 
244 	if (sn->sn_group == ICU_GRP_SEI && !sc->sc_sei_addr) {
245 		sc->sc_sei_addr = addr;
246 		HWRITE4(sc, ICU_SET_SEI_AL, addr & 0xffffffff);
247 		HWRITE4(sc, ICU_SET_SEI_AH, addr >> 32);
248 	}
249 
250 	/* Configure ICU. */
251 	HWRITE4(sc, ICU_INT_CFG(idx), data | ICU_INT_ENABLE |
252 	    (sn->sn_group << ICU_INT_GROUP_SHIFT) | (edge ? ICU_INT_EDGE : 0));
253 
254 	/* Need to configure interrupt for both SATA ports. */
255 	if (idx == ICU_DEVICE_SATA0 || idx == ICU_DEVICE_SATA1) {
256 		HWRITE4(sc, ICU_INT_CFG(ICU_DEVICE_SATA0), data |
257 		    ICU_INT_ENABLE | (sn->sn_group << ICU_INT_GROUP_SHIFT) |
258 		    (edge ? ICU_INT_EDGE : 0));
259 		HWRITE4(sc, ICU_INT_CFG(ICU_DEVICE_SATA1), data |
260 		    ICU_INT_ENABLE | (sn->sn_group << ICU_INT_GROUP_SHIFT) |
261 		    (edge ? ICU_INT_EDGE : 0));
262 	}
263 
264 	ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK);
265 	ih->ih_ic = ic;
266 	ih->ih_ih = cookie;
267 
268 	return ih;
269 }
270 
271 void
272 mvicu_intr_disestablish(void *cookie)
273 {
274 	panic("%s", __func__);
275 }
276 
277 void
278 mvicu_intr_barrier(void *cookie)
279 {
280 	struct arm_intr_handle *ih = cookie;
281 	struct interrupt_controller *ic = ih->ih_ic;
282 
283 	ic->ic_barrier(ih->ih_ih);
284 }
285