1 /* $OpenBSD: mvmbus.c,v 1.4 2021/10/24 17:52:27 mpi 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 <arm/simplebus/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 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 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 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 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 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 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 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 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 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 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 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 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