1 /* $OpenBSD: mvicu.c,v 1.8 2021/10/24 17:52:26 mpi 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 const 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
mvicu_match(struct device * parent,void * match,void * aux)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
mvicu_attach(struct device * parent,struct device * self,void * aux)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
mvicu_register(struct mvicu_softc * sc,int node,int idx)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 *
mvicu_intr_establish(void * cookie,int * cell,int level,struct cpu_info * ci,int (* func)(void *),void * arg,char * name)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 machine_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
mvicu_intr_disestablish(void * cookie)272 mvicu_intr_disestablish(void *cookie)
273 {
274 panic("%s", __func__);
275 }
276
277 void
mvicu_intr_barrier(void * cookie)278 mvicu_intr_barrier(void *cookie)
279 {
280 struct machine_intr_handle *ih = cookie;
281 struct interrupt_controller *ic = ih->ih_ic;
282
283 ic->ic_barrier(ih->ih_ih);
284 }
285