xref: /openbsd-src/sys/dev/fdt/mvicu.c (revision 99fd087599a8791921855f21bd7e36130f39aadc)
1 /*	$OpenBSD: mvicu.c,v 1.4 2019/02/03 14:03:36 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, int (*)(void *),
97 	    void *, char *);
98 void	mvicu_intr_disestablish(void *);
99 
100 int
101 mvicu_match(struct device *parent, void *match, void *aux)
102 {
103 	struct fdt_attach_args *faa = aux;
104 
105 	return OF_is_compatible(faa->fa_node, "marvell,cp110-icu");
106 }
107 
108 void
109 mvicu_attach(struct device *parent, struct device *self, void *aux)
110 {
111 	struct mvicu_softc *sc = (struct mvicu_softc *)self;
112 	struct fdt_attach_args *faa = aux;
113 	int i, node, nchildren;
114 
115 	if (faa->fa_nreg < 1) {
116 		printf(": no registers\n");
117 		return;
118 	}
119 
120 	sc->sc_iot = faa->fa_iot;
121 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
122 	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
123 		printf(": can't map registers\n");
124 		return;
125 	}
126 
127 	printf("\n");
128 
129 	if (OF_child(faa->fa_node) == 0) {
130 		sc->sc_legacy = 1;
131 		sc->sc_nodes = mallocarray(1, sizeof(*sc->sc_nodes),
132 		    M_DEVBUF, M_WAITOK | M_ZERO);
133 		mvicu_register(sc, faa->fa_node, 0);
134 	} else {
135 		for (node = OF_child(faa->fa_node), nchildren = 0;
136 		    node; node = OF_peer(node))
137 			nchildren++;
138 		sc->sc_nodes = mallocarray(nchildren, sizeof(*sc->sc_nodes),
139 		    M_DEVBUF, M_WAITOK | M_ZERO);
140 		for (node = OF_child(faa->fa_node), i = 0; node;
141 		    node = OF_peer(node))
142 			mvicu_register(sc, node, i++);
143 	}
144 }
145 
146 void
147 mvicu_register(struct mvicu_softc *sc, int node, int idx)
148 {
149 	struct mvicu_subnode *sn = &sc->sc_nodes[idx];
150 	struct interrupt_controller *ic;
151 	uint32_t phandle = 0;
152 	uint32_t group;
153 	int i;
154 
155 	sn->sn_group = -1;
156 	if (OF_is_compatible(node, "marvell,cp110-icu") ||
157 	    OF_is_compatible(node, "marvell,cp110-icu-nsr"))
158 		sn->sn_group = ICU_GRP_NSR;
159 	if (OF_is_compatible(node, "marvell,cp110-icu-sei"))
160 		sn->sn_group = ICU_GRP_SEI;
161 
162 	for (i = 0; i < ICU_DEVICE_NIRQ; i++) {
163 		group = HREAD4(sc, ICU_INT_CFG(i)) >> ICU_INT_GROUP_SHIFT;
164 		if ((sn->sn_group == ICU_GRP_NSR && group == ICU_GRP_NSR) ||
165 		    (sn->sn_group == ICU_GRP_SEI && group == ICU_GRP_SEI))
166 			HWRITE4(sc, ICU_INT_CFG(i), 0);
167 	}
168 
169 	sn->sn_sc = sc;
170 	sn->sn_ic.ic_node = node;
171 	sn->sn_ic.ic_cookie = sn;
172 	sn->sn_ic.ic_establish = mvicu_intr_establish;
173 	sn->sn_ic.ic_disestablish = mvicu_intr_disestablish;
174 
175 	while (node && !phandle) {
176 		phandle = OF_getpropint(node, "msi-parent", 0);
177 		node = OF_parent(node);
178 	}
179 	if (phandle == 0)
180 		return;
181 
182 	extern LIST_HEAD(, interrupt_controller) interrupt_controllers;
183 	LIST_FOREACH(ic, &interrupt_controllers, ic_list) {
184 		if (ic->ic_phandle == phandle)
185 			break;
186 	}
187 	if (ic == NULL)
188 		return;
189 
190 	sn->sn_parent_ic = ic;
191 	fdt_intr_register(&sn->sn_ic);
192 }
193 
194 void *
195 mvicu_intr_establish(void *cookie, int *cell, int level,
196     int (*func)(void *), void *arg, char *name)
197 {
198 	struct mvicu_subnode *sn = cookie;
199 	struct mvicu_softc *sc = sn->sn_sc;
200 	struct interrupt_controller *ic = sn->sn_parent_ic;
201 	uint32_t idx, flags;
202 	uint64_t addr, data;
203 	int edge = 0;
204 
205 	if (sc->sc_legacy) {
206 		if (cell[0] != ICU_GRP_NSR)
207 			return NULL;
208 		idx = cell[1];
209 		flags = cell[2];
210 		edge = ((flags & 0xf) == 0x1);
211 	} else if (sn->sn_group == ICU_GRP_NSR) {
212 		idx = cell[0];
213 		flags = cell[1];
214 		edge = ((flags & 0xf) == 0x1);
215 	} else if (sn->sn_group == ICU_GRP_SEI) {
216 		idx = cell[0];
217 		flags = cell[1];
218 		edge = 1;
219 	} else {
220 		return NULL;
221 	}
222 
223 	data = flags;
224 	cookie = ic->ic_establish_msi(ic->ic_cookie, &addr, &data,
225 	    level, func, arg, name);
226 	if (cookie == NULL)
227 		return NULL;
228 
229 	if (sn->sn_group == ICU_GRP_NSR && !sc->sc_nsr_addr) {
230 		sc->sc_nsr_addr = addr;
231 		HWRITE4(sc, ICU_SETSPI_NSR_AL,
232 		    (addr + GICP_SETSPI_NSR) & 0xffffffff);
233 		HWRITE4(sc, ICU_SETSPI_NSR_AH,
234 		    (addr + GICP_SETSPI_NSR) >> 32);
235 		HWRITE4(sc, ICU_CLRSPI_NSR_AL,
236 		    (addr + GICP_CLRSPI_NSR) & 0xffffffff);
237 		HWRITE4(sc, ICU_CLRSPI_NSR_AH,
238 		    (addr + GICP_CLRSPI_NSR) >> 32);
239 	}
240 
241 	if (sn->sn_group == ICU_GRP_SEI && !sc->sc_sei_addr) {
242 		sc->sc_sei_addr = addr;
243 		HWRITE4(sc, ICU_SET_SEI_AL, addr & 0xffffffff);
244 		HWRITE4(sc, ICU_SET_SEI_AH, addr >> 32);
245 	}
246 
247 	/* Configure ICU. */
248 	HWRITE4(sc, ICU_INT_CFG(idx), data | ICU_INT_ENABLE |
249 	    (sn->sn_group << ICU_INT_GROUP_SHIFT) | (edge ? ICU_INT_EDGE : 0));
250 
251 	/* Need to configure interrupt for both SATA ports. */
252 	if (idx == ICU_DEVICE_SATA0 || idx == ICU_DEVICE_SATA1) {
253 		HWRITE4(sc, ICU_INT_CFG(ICU_DEVICE_SATA0), data |
254 		    ICU_INT_ENABLE | (sn->sn_group << ICU_INT_GROUP_SHIFT) |
255 		    (edge ? ICU_INT_EDGE : 0));
256 		HWRITE4(sc, ICU_INT_CFG(ICU_DEVICE_SATA1), data |
257 		    ICU_INT_ENABLE | (sn->sn_group << ICU_INT_GROUP_SHIFT) |
258 		    (edge ? ICU_INT_EDGE : 0));
259 	}
260 
261 	return cookie;
262 }
263 
264 void
265 mvicu_intr_disestablish(void *cookie)
266 {
267 	panic("%s", __func__);
268 }
269