1 /* $OpenBSD: mvmbus.c,v 1.5 2023/09/22 01:10:43 jsg Exp $ */
2 /*
3 * Copyright (c) 2016 Patrick Wildt <patrick@blueri.se>
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 #include <machine/bus.h>
23 #include <machine/fdt.h>
24
25 #include <machine/simplebusvar.h>
26 #include <armv7/marvell/mvmbusvar.h>
27
28 #include <dev/ofw/openfirm.h>
29 #include <dev/ofw/fdt.h>
30
31 #define MVMBUS_XP_WINDOW(i) (((i) < 8) ? i << 4 : 0x90 + (((i) - 8) << 3))
32
33 #define MVMBUS_WINDOW_CTRL 0x0000
34 #define MVMBUS_WINDOW_CTRL_ENABLE (1 << 0)
35 #define MVMBUS_WINDOW_CTRL_TARGET_MASK 0xf0
36 #define MVMBUS_WINDOW_CTRL_TARGET_SHIFT 4
37 #define MVMBUS_WINDOW_CTRL_ATTR_MASK 0xff00
38 #define MVMBUS_WINDOW_CTRL_ATTR_SHIFT 8
39 #define MVMBUS_WINDOW_CTRL_SIZE_MASK 0xffff0000
40 #define MVMBUS_WINDOW_CTRL_SIZE_SHIFT 16
41 #define MVMBUS_WINDOW_BASE 0x0004
42 #define MVMBUS_WINDOW_BASE_LOW 0xffff0000
43 #define MVMBUS_WINDOW_BASE_HIGH 0xf
44 #define MVMBUS_WINDOW_REMAP_LO 0x0008
45 #define MVMBUS_WINDOW_REMAP_LO_LOW 0xffff0000
46 #define MVMBUS_WINDOW_REMAP_HI 0x000c
47
48 #define MVMBUS_SDRAM_BASE(i) (0x0000 + ((i) << 3))
49 #define MVMBUS_SDRAM_BASE_HIGH_MASK 0xf
50 #define MVMBUS_SDRAM_BASE_LOW_MASK 0xff000000
51 #define MVMBUS_SDRAM_SIZE(i) (0x0004 + ((i) << 3))
52 #define MVMBUS_SDRAM_SIZE_ENABLED (1 << 0)
53 #define MVMBUS_SDRAM_SIZE_MASK 0xff000000
54
55 struct mvmbus_softc {
56 struct simplebus_softc sc_sbus;
57 bus_space_tag_t sc_iot;
58 bus_space_handle_t sc_mbus_ioh;
59 bus_space_handle_t sc_sdram_ioh;
60 bus_space_handle_t sc_bridge_ioh;
61
62 int sc_num_wins;
63 int sc_num_remappable_wins;
64
65 struct mbus_dram_info sc_dram_info;
66 struct mbus_window {
67 int enabled;
68 paddr_t base;
69 size_t size;
70 uint8_t target;
71 uint8_t attr;
72 } *sc_windows;
73 };
74
75 int mvmbus_match(struct device *, void *, void *);
76 void mvmbus_attach(struct device *, struct device *, void *);
77
78 void mvmbus_parse_ranges(struct mvmbus_softc *, struct fdt_attach_args *);
79 void mvmbus_alloc_window(struct mvmbus_softc *, paddr_t, size_t, paddr_t,
80 uint8_t, uint8_t);
81 void mvmbus_setup_window(struct mvmbus_softc *, int, paddr_t, size_t,
82 paddr_t, uint8_t, uint8_t);
83 void mvmbus_disable_window(struct mvmbus_softc *, int);
84 int mvmbus_window_conflicts(struct mvmbus_softc *, paddr_t, size_t);
85 int mvmbus_window_is_free(struct mvmbus_softc *, int);
86 int mvmbus_find_window(struct mvmbus_softc *, paddr_t, size_t);
87 void mvmbus_parse_dram_info(struct mvmbus_softc *);
88
89 struct mvmbus_softc *mvmbus_sc;
90 struct mbus_dram_info *mvmbus_dram_info;
91 uint32_t mvmbus_pcie_mem_aperture[2];
92 uint32_t mvmbus_pcie_io_aperture[2];
93
94 const struct cfattach mvmbus_ca = {
95 sizeof (struct mvmbus_softc), mvmbus_match, mvmbus_attach
96 };
97
98 struct cfdriver mvmbus_cd = {
99 NULL, "mvmbus", DV_DULL
100 };
101
102 int
mvmbus_match(struct device * parent,void * cfdata,void * aux)103 mvmbus_match(struct device *parent, void *cfdata, void *aux)
104 {
105 struct fdt_attach_args *faa = aux;
106
107 if (OF_is_compatible(faa->fa_node, "marvell,armada370-mbus") ||
108 OF_is_compatible(faa->fa_node, "marvell,armada380-mbus") ||
109 OF_is_compatible(faa->fa_node, "marvell,armadaxp-mbus"))
110 return 10;
111
112 return 0;
113 }
114
115 void
mvmbus_attach(struct device * parent,struct device * self,void * args)116 mvmbus_attach(struct device *parent, struct device *self, void *args)
117 {
118 struct mvmbus_softc *sc = (struct mvmbus_softc *)self;
119 struct fdt_attach_args *faa = args;
120 struct fdt_reg reg;
121 void *mbusc;
122 int i;
123
124 mvmbus_sc = sc;
125 sc->sc_iot = faa->fa_iot;
126
127 /* The registers are in the controller itself, find it. */
128 mbusc = fdt_find_phandle(OF_getpropint(faa->fa_node, "controller", 0));
129 if (mbusc == NULL)
130 panic("%s: cannot find mbus controller", __func__);
131
132 if (fdt_get_reg(mbusc, 0, ®))
133 panic("%s: could not extract memory data from FDT",
134 __func__);
135
136 if (bus_space_map(sc->sc_iot, reg.addr, reg.size, 0, &sc->sc_mbus_ioh))
137 panic("%s: bus_space_map failed!", __func__);
138
139 if (fdt_get_reg(mbusc, 1, ®))
140 panic("%s: could not extract memory data from FDT",
141 __func__);
142
143 if (bus_space_map(sc->sc_iot, reg.addr, reg.size, 0, &sc->sc_sdram_ioh))
144 panic("%s: bus_space_map failed!", __func__);
145
146 /* Bridge mapping is optional. */
147 if (fdt_get_reg(mbusc, 2, ®) == 0)
148 if (bus_space_map(sc->sc_iot, reg.addr, reg.size, 0,
149 &sc->sc_bridge_ioh))
150 sc->sc_bridge_ioh = 0;
151
152 OF_getpropintarray(faa->fa_node, "pcie-mem-aperture",
153 mvmbus_pcie_mem_aperture, sizeof(mvmbus_pcie_mem_aperture));
154 OF_getpropintarray(faa->fa_node, "pcie-io-aperture",
155 mvmbus_pcie_io_aperture, sizeof(mvmbus_pcie_io_aperture));
156
157 /* TODO: support more than the armada 370/380/xp */
158 sc->sc_num_wins = 20;
159 sc->sc_num_remappable_wins = 8;
160
161 sc->sc_windows = mallocarray(sc->sc_num_wins, sizeof(*sc->sc_windows),
162 M_DEVBUF, M_ZERO | M_WAITOK);
163
164 for (i = 0; i < sc->sc_num_wins; i++)
165 mvmbus_disable_window(sc, i);
166
167 mvmbus_parse_dram_info(sc);
168 mvmbus_dram_info = &sc->sc_dram_info;
169
170 mvmbus_parse_ranges(sc, faa);
171
172 /* We are simple-bus compatible, so just attach it. */
173 simplebus_attach(parent, &sc->sc_sbus.sc_dev, faa);
174 }
175
176 void
mvmbus_parse_ranges(struct mvmbus_softc * sc,struct fdt_attach_args * faa)177 mvmbus_parse_ranges(struct mvmbus_softc *sc, struct fdt_attach_args *faa)
178 {
179 int pac, psc, cac, csc, rlen, rone, *range;
180 uint32_t *ranges;
181 int rangeslen;
182
183 rangeslen = OF_getproplen(faa->fa_node, "ranges");
184 if (rangeslen <= 0 || (rangeslen % sizeof(uint32_t)))
185 panic("%s: unsupported ranges format",
186 sc->sc_sbus.sc_dev.dv_xname);
187
188 ranges = malloc(rangeslen, M_TEMP, M_WAITOK);
189 OF_getpropintarray(faa->fa_node, "ranges", ranges, rangeslen);
190
191 cac = OF_getpropint(faa->fa_node, "#address-cells",
192 faa->fa_acells);
193 csc = OF_getpropint(faa->fa_node, "#size-cells",
194 faa->fa_scells);
195 pac = faa->fa_acells;
196 psc = faa->fa_scells;
197
198 /* Must have at least one range. */
199 rone = pac + cac + csc;
200 rlen = rangeslen / sizeof(uint32_t);
201 if (rlen < rone)
202 return;
203
204 /* For each range. */
205 for (range = ranges; rlen >= rone; rlen -= rone, range += rone) {
206 uint32_t window = range[0];
207 if (window & 0xf0000000)
208 continue;
209
210 uint32_t size = range[cac + pac];
211 if (csc == 2)
212 size = range[cac + pac + 1];
213
214 /* All good, extract to address and translate. */
215 uint32_t base = range[cac];
216 if (pac == 2)
217 base = range[cac + 1];
218
219 uint8_t target = (window & 0x0f000000) >> 24;
220 uint8_t attr = (window & 0x00ff0000) >> 16;
221
222 mvmbus_alloc_window(sc, base, size, MVMBUS_NO_REMAP,
223 target, attr);
224 }
225 }
226
227 int
mvmbus_window_is_free(struct mvmbus_softc * sc,int window)228 mvmbus_window_is_free(struct mvmbus_softc *sc, int window)
229 {
230 /*
231 * On Armada XP systems, window 13 is a remappable window. For this
232 * window to work we need to configure the remap register. Since we
233 * don't do this yet, make sure we don't use this window.
234 */
235 if (window == 13)
236 return 0;
237 return !sc->sc_windows[window].enabled;
238 }
239
240 void
mvmbus_alloc_window(struct mvmbus_softc * sc,paddr_t base,size_t size,paddr_t remap,uint8_t target,uint8_t attr)241 mvmbus_alloc_window(struct mvmbus_softc *sc, paddr_t base, size_t size,
242 paddr_t remap, uint8_t target, uint8_t attr)
243 {
244 int win;
245
246 if (mvmbus_window_conflicts(sc, base, size)) {
247 printf("%s: window conflicts with another window\n",
248 sc->sc_sbus.sc_dev.dv_xname);
249 return;
250 }
251
252 /* If no remap is wanted, use unremappable windows. */
253 if (remap == MVMBUS_NO_REMAP)
254 for (win = sc->sc_num_remappable_wins;
255 win < sc->sc_num_wins; win++)
256 if (mvmbus_window_is_free(sc, win))
257 return mvmbus_setup_window(sc, win, base, size,
258 remap, target, attr);
259
260 for (win = 0; win < sc->sc_num_wins; win++)
261 if (mvmbus_window_is_free(sc, win))
262 return mvmbus_setup_window(sc, win, base, size,
263 remap, target, attr);
264
265 printf("%s: no free window available\n",
266 sc->sc_sbus.sc_dev.dv_xname);
267 }
268
269 void
mvmbus_setup_window(struct mvmbus_softc * sc,int window,paddr_t base,size_t size,paddr_t remap,uint8_t target,uint8_t attr)270 mvmbus_setup_window(struct mvmbus_softc *sc, int window, paddr_t base,
271 size_t size, paddr_t remap, uint8_t target, uint8_t attr)
272 {
273 bus_space_write_4(sc->sc_iot, sc->sc_mbus_ioh,
274 MVMBUS_XP_WINDOW(window) + MVMBUS_WINDOW_BASE,
275 base & MVMBUS_WINDOW_BASE_LOW);
276 bus_space_write_4(sc->sc_iot, sc->sc_mbus_ioh,
277 MVMBUS_XP_WINDOW(window) + MVMBUS_WINDOW_CTRL,
278 ((size - 1) & MVMBUS_WINDOW_CTRL_SIZE_MASK) |
279 attr << MVMBUS_WINDOW_CTRL_ATTR_SHIFT |
280 target << MVMBUS_WINDOW_CTRL_TARGET_SHIFT |
281 MVMBUS_WINDOW_CTRL_ENABLE);
282
283 if (window < sc->sc_num_remappable_wins) {
284 if (remap == MVMBUS_NO_REMAP)
285 remap = base;
286 bus_space_write_4(sc->sc_iot, sc->sc_mbus_ioh,
287 MVMBUS_XP_WINDOW(window) + MVMBUS_WINDOW_REMAP_LO,
288 remap & MVMBUS_WINDOW_REMAP_LO_LOW);
289 bus_space_write_4(sc->sc_iot, sc->sc_mbus_ioh,
290 MVMBUS_XP_WINDOW(window) + MVMBUS_WINDOW_REMAP_HI, 0);
291 }
292
293 sc->sc_windows[window].enabled = 1;
294 sc->sc_windows[window].base = base;
295 sc->sc_windows[window].size = size;
296 sc->sc_windows[window].target = target;
297 sc->sc_windows[window].attr = attr;
298 }
299
300 void
mvmbus_disable_window(struct mvmbus_softc * sc,int window)301 mvmbus_disable_window(struct mvmbus_softc *sc, int window)
302 {
303 bus_space_write_4(sc->sc_iot, sc->sc_mbus_ioh,
304 MVMBUS_XP_WINDOW(window) + MVMBUS_WINDOW_BASE, 0);
305 bus_space_write_4(sc->sc_iot, sc->sc_mbus_ioh,
306 MVMBUS_XP_WINDOW(window) + MVMBUS_WINDOW_CTRL, 0);
307
308 if (window < sc->sc_num_remappable_wins) {
309 bus_space_write_4(sc->sc_iot, sc->sc_mbus_ioh,
310 MVMBUS_XP_WINDOW(window) + MVMBUS_WINDOW_REMAP_LO, 0);
311 bus_space_write_4(sc->sc_iot, sc->sc_mbus_ioh,
312 MVMBUS_XP_WINDOW(window) + MVMBUS_WINDOW_REMAP_HI, 0);
313 }
314
315 sc->sc_windows[window].enabled = 0;
316 }
317
318 int
mvmbus_window_conflicts(struct mvmbus_softc * sc,paddr_t base,size_t size)319 mvmbus_window_conflicts(struct mvmbus_softc *sc, paddr_t base, size_t size)
320 {
321 int i;
322
323 for (i = 0; i < sc->sc_num_wins; i++) {
324 if (!sc->sc_windows[i].enabled)
325 continue;
326
327 if (base < (sc->sc_windows[i].base + sc->sc_windows[i].size) &&
328 (base + size) > sc->sc_windows[i].base)
329 return 1;
330 }
331
332 return 0;
333 }
334
335 int
mvmbus_find_window(struct mvmbus_softc * sc,paddr_t base,size_t size)336 mvmbus_find_window(struct mvmbus_softc *sc, paddr_t base, size_t size)
337 {
338 int win;
339
340 for (win = 0; win < sc->sc_num_wins; win++) {
341 if (!sc->sc_windows[win].enabled)
342 continue;
343 if (sc->sc_windows[win].base != base)
344 continue;
345 if (sc->sc_windows[win].size != size)
346 continue;
347 break;
348 }
349
350 if (win == sc->sc_num_wins)
351 return -1;
352 return win;
353 }
354
355 void
mvmbus_add_window(paddr_t base,size_t size,paddr_t remap,uint8_t target,uint8_t attr)356 mvmbus_add_window(paddr_t base, size_t size, paddr_t remap,
357 uint8_t target, uint8_t attr)
358 {
359 struct mvmbus_softc *sc = mvmbus_sc;
360
361 KASSERT(sc != NULL);
362 mvmbus_alloc_window(sc, base, size, remap, target, attr);
363 }
364
365 void
mvmbus_del_window(paddr_t base,size_t size)366 mvmbus_del_window(paddr_t base, size_t size)
367 {
368 struct mvmbus_softc *sc = mvmbus_sc;
369 int win;
370
371 KASSERT(sc != NULL);
372 win = mvmbus_find_window(sc, base, size);
373 if (win >= 0)
374 mvmbus_disable_window(sc, win);
375 }
376
377 void
mvmbus_parse_dram_info(struct mvmbus_softc * sc)378 mvmbus_parse_dram_info(struct mvmbus_softc *sc)
379 {
380 int i, cs = 0;
381
382 sc->sc_dram_info.targetid = 0; /* DDR */
383
384 for (i = 0; i < 4; i++) {
385 uint32_t base = bus_space_read_4(sc->sc_iot, sc->sc_sdram_ioh,
386 MVMBUS_SDRAM_BASE(i));
387 uint32_t size = bus_space_read_4(sc->sc_iot, sc->sc_sdram_ioh,
388 MVMBUS_SDRAM_SIZE(i));
389
390 if (!(size & MVMBUS_SDRAM_SIZE_ENABLED))
391 continue;
392
393 if (base & MVMBUS_SDRAM_BASE_HIGH_MASK)
394 continue;
395
396 struct mbus_dram_window *win = &sc->sc_dram_info.cs[cs++];
397 win->index = i;
398 win->attr = 0xf & ~(1 << i); /* XXX: coherency? */
399 win->base = base & MVMBUS_SDRAM_BASE_LOW_MASK;
400 win->size = (size | MVMBUS_SDRAM_SIZE_MASK) + 1;
401 }
402
403 sc->sc_dram_info.numcs = cs;
404 }
405