xref: /openbsd/sys/arch/armv7/marvell/mvmbus.c (revision 94673892)
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, &reg))
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, &reg))
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, &reg) == 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