/* $OpenBSD: mvmbus.c,v 1.4 2021/10/24 17:52:27 mpi Exp $ */ /* * Copyright (c) 2016 Patrick Wildt * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #define MVMBUS_XP_WINDOW(i) (((i) < 8) ? i << 4 : 0x90 + (((i) - 8) << 3)) #define MVMBUS_WINDOW_CTRL 0x0000 #define MVMBUS_WINDOW_CTRL_ENABLE (1 << 0) #define MVMBUS_WINDOW_CTRL_TARGET_MASK 0xf0 #define MVMBUS_WINDOW_CTRL_TARGET_SHIFT 4 #define MVMBUS_WINDOW_CTRL_ATTR_MASK 0xff00 #define MVMBUS_WINDOW_CTRL_ATTR_SHIFT 8 #define MVMBUS_WINDOW_CTRL_SIZE_MASK 0xffff0000 #define MVMBUS_WINDOW_CTRL_SIZE_SHIFT 16 #define MVMBUS_WINDOW_BASE 0x0004 #define MVMBUS_WINDOW_BASE_LOW 0xffff0000 #define MVMBUS_WINDOW_BASE_HIGH 0xf #define MVMBUS_WINDOW_REMAP_LO 0x0008 #define MVMBUS_WINDOW_REMAP_LO_LOW 0xffff0000 #define MVMBUS_WINDOW_REMAP_HI 0x000c #define MVMBUS_SDRAM_BASE(i) (0x0000 + ((i) << 3)) #define MVMBUS_SDRAM_BASE_HIGH_MASK 0xf #define MVMBUS_SDRAM_BASE_LOW_MASK 0xff000000 #define MVMBUS_SDRAM_SIZE(i) (0x0004 + ((i) << 3)) #define MVMBUS_SDRAM_SIZE_ENABLED (1 << 0) #define MVMBUS_SDRAM_SIZE_MASK 0xff000000 struct mvmbus_softc { struct simplebus_softc sc_sbus; bus_space_tag_t sc_iot; bus_space_handle_t sc_mbus_ioh; bus_space_handle_t sc_sdram_ioh; bus_space_handle_t sc_bridge_ioh; int sc_num_wins; int sc_num_remappable_wins; struct mbus_dram_info sc_dram_info; struct mbus_window { int enabled; paddr_t base; size_t size; uint8_t target; uint8_t attr; } *sc_windows; }; int mvmbus_match(struct device *, void *, void *); void mvmbus_attach(struct device *, struct device *, void *); void mvmbus_parse_ranges(struct mvmbus_softc *, struct fdt_attach_args *); void mvmbus_alloc_window(struct mvmbus_softc *, paddr_t, size_t, paddr_t, uint8_t, uint8_t); void mvmbus_setup_window(struct mvmbus_softc *, int, paddr_t, size_t, paddr_t, uint8_t, uint8_t); void mvmbus_disable_window(struct mvmbus_softc *, int); int mvmbus_window_conflicts(struct mvmbus_softc *, paddr_t, size_t); int mvmbus_window_is_free(struct mvmbus_softc *, int); int mvmbus_find_window(struct mvmbus_softc *, paddr_t, size_t); void mvmbus_parse_dram_info(struct mvmbus_softc *); struct mvmbus_softc *mvmbus_sc; struct mbus_dram_info *mvmbus_dram_info; uint32_t mvmbus_pcie_mem_aperture[2]; uint32_t mvmbus_pcie_io_aperture[2]; const struct cfattach mvmbus_ca = { sizeof (struct mvmbus_softc), mvmbus_match, mvmbus_attach }; struct cfdriver mvmbus_cd = { NULL, "mvmbus", DV_DULL }; int mvmbus_match(struct device *parent, void *cfdata, void *aux) { struct fdt_attach_args *faa = aux; if (OF_is_compatible(faa->fa_node, "marvell,armada370-mbus") || OF_is_compatible(faa->fa_node, "marvell,armada380-mbus") || OF_is_compatible(faa->fa_node, "marvell,armadaxp-mbus")) return 10; return 0; } void mvmbus_attach(struct device *parent, struct device *self, void *args) { struct mvmbus_softc *sc = (struct mvmbus_softc *)self; struct fdt_attach_args *faa = args; struct fdt_reg reg; void *mbusc; int i; mvmbus_sc = sc; sc->sc_iot = faa->fa_iot; /* The registers are in the controller itself, find it. */ mbusc = fdt_find_phandle(OF_getpropint(faa->fa_node, "controller", 0)); if (mbusc == NULL) panic("%s: cannot find mbus controller", __func__); if (fdt_get_reg(mbusc, 0, ®)) panic("%s: could not extract memory data from FDT", __func__); if (bus_space_map(sc->sc_iot, reg.addr, reg.size, 0, &sc->sc_mbus_ioh)) panic("%s: bus_space_map failed!", __func__); if (fdt_get_reg(mbusc, 1, ®)) panic("%s: could not extract memory data from FDT", __func__); if (bus_space_map(sc->sc_iot, reg.addr, reg.size, 0, &sc->sc_sdram_ioh)) panic("%s: bus_space_map failed!", __func__); /* Bridge mapping is optional. */ if (fdt_get_reg(mbusc, 2, ®) == 0) if (bus_space_map(sc->sc_iot, reg.addr, reg.size, 0, &sc->sc_bridge_ioh)) sc->sc_bridge_ioh = 0; OF_getpropintarray(faa->fa_node, "pcie-mem-aperture", mvmbus_pcie_mem_aperture, sizeof(mvmbus_pcie_mem_aperture)); OF_getpropintarray(faa->fa_node, "pcie-io-aperture", mvmbus_pcie_io_aperture, sizeof(mvmbus_pcie_io_aperture)); /* TODO: support more than the armada 370/380/xp */ sc->sc_num_wins = 20; sc->sc_num_remappable_wins = 8; sc->sc_windows = mallocarray(sc->sc_num_wins, sizeof(*sc->sc_windows), M_DEVBUF, M_ZERO | M_WAITOK); for (i = 0; i < sc->sc_num_wins; i++) mvmbus_disable_window(sc, i); mvmbus_parse_dram_info(sc); mvmbus_dram_info = &sc->sc_dram_info; mvmbus_parse_ranges(sc, faa); /* We are simple-bus compatible, so just attach it. */ simplebus_attach(parent, &sc->sc_sbus.sc_dev, faa); } void mvmbus_parse_ranges(struct mvmbus_softc *sc, struct fdt_attach_args *faa) { int pac, psc, cac, csc, rlen, rone, *range; uint32_t *ranges; int rangeslen; rangeslen = OF_getproplen(faa->fa_node, "ranges"); if (rangeslen <= 0 || (rangeslen % sizeof(uint32_t))) panic("%s: unsupported ranges format", sc->sc_sbus.sc_dev.dv_xname); ranges = malloc(rangeslen, M_TEMP, M_WAITOK); OF_getpropintarray(faa->fa_node, "ranges", ranges, rangeslen); cac = OF_getpropint(faa->fa_node, "#address-cells", faa->fa_acells); csc = OF_getpropint(faa->fa_node, "#size-cells", faa->fa_scells); pac = faa->fa_acells; psc = faa->fa_scells; /* Must have at least one range. */ rone = pac + cac + csc; rlen = rangeslen / sizeof(uint32_t); if (rlen < rone) return; /* For each range. */ for (range = ranges; rlen >= rone; rlen -= rone, range += rone) { uint32_t window = range[0]; if (window & 0xf0000000) continue; uint32_t size = range[cac + pac]; if (csc == 2) size = range[cac + pac + 1]; /* All good, extract to address and translate. */ uint32_t base = range[cac]; if (pac == 2) base = range[cac + 1]; uint8_t target = (window & 0x0f000000) >> 24; uint8_t attr = (window & 0x00ff0000) >> 16; mvmbus_alloc_window(sc, base, size, MVMBUS_NO_REMAP, target, attr); } } int mvmbus_window_is_free(struct mvmbus_softc *sc, int window) { /* * On Armada XP systems, window 13 is a remappable window. For this * window to work we need to configure the remap register. Since we * don't do this yet, make sure we don't use this window. */ if (window == 13) return 0; return !sc->sc_windows[window].enabled; } void mvmbus_alloc_window(struct mvmbus_softc *sc, paddr_t base, size_t size, paddr_t remap, uint8_t target, uint8_t attr) { int win; if (mvmbus_window_conflicts(sc, base, size)) { printf("%s: window conflicts with another window\n", sc->sc_sbus.sc_dev.dv_xname); return; } /* If no remap is wanted, use unremappable windows. */ if (remap == MVMBUS_NO_REMAP) for (win = sc->sc_num_remappable_wins; win < sc->sc_num_wins; win++) if (mvmbus_window_is_free(sc, win)) return mvmbus_setup_window(sc, win, base, size, remap, target, attr); for (win = 0; win < sc->sc_num_wins; win++) if (mvmbus_window_is_free(sc, win)) return mvmbus_setup_window(sc, win, base, size, remap, target, attr); printf("%s: no free window available\n", sc->sc_sbus.sc_dev.dv_xname); } 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) { bus_space_write_4(sc->sc_iot, sc->sc_mbus_ioh, MVMBUS_XP_WINDOW(window) + MVMBUS_WINDOW_BASE, base & MVMBUS_WINDOW_BASE_LOW); bus_space_write_4(sc->sc_iot, sc->sc_mbus_ioh, MVMBUS_XP_WINDOW(window) + MVMBUS_WINDOW_CTRL, ((size - 1) & MVMBUS_WINDOW_CTRL_SIZE_MASK) | attr << MVMBUS_WINDOW_CTRL_ATTR_SHIFT | target << MVMBUS_WINDOW_CTRL_TARGET_SHIFT | MVMBUS_WINDOW_CTRL_ENABLE); if (window < sc->sc_num_remappable_wins) { if (remap == MVMBUS_NO_REMAP) remap = base; bus_space_write_4(sc->sc_iot, sc->sc_mbus_ioh, MVMBUS_XP_WINDOW(window) + MVMBUS_WINDOW_REMAP_LO, remap & MVMBUS_WINDOW_REMAP_LO_LOW); bus_space_write_4(sc->sc_iot, sc->sc_mbus_ioh, MVMBUS_XP_WINDOW(window) + MVMBUS_WINDOW_REMAP_HI, 0); } sc->sc_windows[window].enabled = 1; sc->sc_windows[window].base = base; sc->sc_windows[window].size = size; sc->sc_windows[window].target = target; sc->sc_windows[window].attr = attr; } void mvmbus_disable_window(struct mvmbus_softc *sc, int window) { bus_space_write_4(sc->sc_iot, sc->sc_mbus_ioh, MVMBUS_XP_WINDOW(window) + MVMBUS_WINDOW_BASE, 0); bus_space_write_4(sc->sc_iot, sc->sc_mbus_ioh, MVMBUS_XP_WINDOW(window) + MVMBUS_WINDOW_CTRL, 0); if (window < sc->sc_num_remappable_wins) { bus_space_write_4(sc->sc_iot, sc->sc_mbus_ioh, MVMBUS_XP_WINDOW(window) + MVMBUS_WINDOW_REMAP_LO, 0); bus_space_write_4(sc->sc_iot, sc->sc_mbus_ioh, MVMBUS_XP_WINDOW(window) + MVMBUS_WINDOW_REMAP_HI, 0); } sc->sc_windows[window].enabled = 0; } int mvmbus_window_conflicts(struct mvmbus_softc *sc, paddr_t base, size_t size) { int i; for (i = 0; i < sc->sc_num_wins; i++) { if (!sc->sc_windows[i].enabled) continue; if (base < (sc->sc_windows[i].base + sc->sc_windows[i].size) && (base + size) > sc->sc_windows[i].base) return 1; } return 0; } int mvmbus_find_window(struct mvmbus_softc *sc, paddr_t base, size_t size) { int win; for (win = 0; win < sc->sc_num_wins; win++) { if (!sc->sc_windows[win].enabled) continue; if (sc->sc_windows[win].base != base) continue; if (sc->sc_windows[win].size != size) continue; break; } if (win == sc->sc_num_wins) return -1; return win; } void mvmbus_add_window(paddr_t base, size_t size, paddr_t remap, uint8_t target, uint8_t attr) { struct mvmbus_softc *sc = mvmbus_sc; KASSERT(sc != NULL); mvmbus_alloc_window(sc, base, size, remap, target, attr); } void mvmbus_del_window(paddr_t base, size_t size) { struct mvmbus_softc *sc = mvmbus_sc; int win; KASSERT(sc != NULL); win = mvmbus_find_window(sc, base, size); if (win >= 0) mvmbus_disable_window(sc, win); } void mvmbus_parse_dram_info(struct mvmbus_softc *sc) { int i, cs = 0; sc->sc_dram_info.targetid = 0; /* DDR */ for (i = 0; i < 4; i++) { uint32_t base = bus_space_read_4(sc->sc_iot, sc->sc_sdram_ioh, MVMBUS_SDRAM_BASE(i)); uint32_t size = bus_space_read_4(sc->sc_iot, sc->sc_sdram_ioh, MVMBUS_SDRAM_SIZE(i)); if (!(size & MVMBUS_SDRAM_SIZE_ENABLED)) continue; if (base & MVMBUS_SDRAM_BASE_HIGH_MASK) continue; struct mbus_dram_window *win = &sc->sc_dram_info.cs[cs++]; win->index = i; win->attr = 0xf & ~(1 << i); /* XXX: coherency? */ win->base = base & MVMBUS_SDRAM_BASE_LOW_MASK; win->size = (size | MVMBUS_SDRAM_SIZE_MASK) + 1; } sc->sc_dram_info.numcs = cs; }