diff options
Diffstat (limited to 'sys/arch')
-rw-r--r-- | sys/arch/arm/simplebus/simplebus.c | 77 |
1 files changed, 75 insertions, 2 deletions
diff --git a/sys/arch/arm/simplebus/simplebus.c b/sys/arch/arm/simplebus/simplebus.c index 65689d8a859..c818814b62a 100644 --- a/sys/arch/arm/simplebus/simplebus.c +++ b/sys/arch/arm/simplebus/simplebus.c @@ -1,4 +1,4 @@ -/* $OpenBSD: simplebus.c,v 1.6 2016/07/13 20:42:44 patrick Exp $ */ +/* $OpenBSD: simplebus.c,v 1.7 2016/07/18 11:53:32 patrick Exp $ */ /* * Copyright (c) 2016 Patrick Wildt <patrick@blueri.se> * @@ -29,6 +29,7 @@ int simplebus_match(struct device *, void *, void *); void simplebus_attach(struct device *, struct device *, void *); void simplebus_attach_node(struct device *, int); +int simplebus_bs_map(void *, bus_addr_t, bus_size_t, int, bus_space_handle_t *); struct simplebus_softc { struct device sc_dev; @@ -37,6 +38,11 @@ struct simplebus_softc { bus_dma_tag_t sc_dmat; int sc_acells; int sc_scells; + int sc_pacells; + int sc_pscells; + struct bus_space sc_bus; + int *sc_ranges; + int sc_rangeslen; }; struct cfattach simplebus_ca = { @@ -80,6 +86,8 @@ simplebus_attach(struct device *parent, struct device *self, void *aux) fa->fa_acells); sc->sc_scells = OF_getpropint(sc->sc_node, "#size-cells", fa->fa_scells); + sc->sc_pacells = fa->fa_acells; + sc->sc_pscells = fa->fa_scells; if (OF_getprop(sc->sc_node, "name", name, sizeof(name)) > 0) { name[sizeof(name) - 1] = 0; @@ -88,6 +96,17 @@ simplebus_attach(struct device *parent, struct device *self, void *aux) printf("\n"); + memcpy(&sc->sc_bus, sc->sc_iot, sizeof(sc->sc_bus)); + sc->sc_bus.bs_cookie = sc; + sc->sc_bus.bs_map = simplebus_bs_map; + + sc->sc_rangeslen = OF_getproplen(sc->sc_node, "ranges"); + if (sc->sc_rangeslen > 0 && !(sc->sc_rangeslen % sizeof(uint32_t))) { + sc->sc_ranges = malloc(sc->sc_rangeslen, M_TEMP, M_WAITOK); + OF_getpropintarray(sc->sc_node, "ranges", sc->sc_ranges, + sc->sc_rangeslen); + } + /* Scan the whole tree. */ for (node = OF_child(sc->sc_node); node != 0; @@ -118,7 +137,7 @@ simplebus_attach_node(struct device *self, int node) memset(&fa, 0, sizeof(fa)); fa.fa_name = ""; fa.fa_node = node; - fa.fa_iot = sc->sc_iot; + fa.fa_iot = &sc->sc_bus; fa.fa_dmat = sc->sc_dmat; fa.fa_acells = sc->sc_acells; fa.fa_scells = sc->sc_scells; @@ -146,3 +165,57 @@ simplebus_attach_node(struct device *self, int node) free(fa.fa_reg, M_DEVBUF, fa.fa_nreg * sizeof(uint32_t)); free(fa.fa_intr, M_DEVBUF, fa.fa_nintr * sizeof(uint32_t)); } + +/* + * Translate memory address if needed. + */ +int +simplebus_bs_map(void *t, bus_addr_t bpa, bus_size_t size, + int flag, bus_space_handle_t *bshp) +{ + struct simplebus_softc *sc = (struct simplebus_softc *)t; + uint64_t addr, rfrom, rto, rsize; + uint32_t *range; + int parent, rlen, rone; + + addr = bpa; + parent = OF_parent(sc->sc_node); + if (parent == 0) + return bus_space_map(sc->sc_iot, addr, size, flag, bshp); + + if (sc->sc_rangeslen < 0) + return EINVAL; + if (sc->sc_rangeslen == 0) + return bus_space_map(sc->sc_iot, addr, size, flag, bshp); + + rlen = sc->sc_rangeslen / sizeof(uint32_t); + rone = sc->sc_pacells + sc->sc_acells + sc->sc_scells; + + /* For each range. */ + for (range = sc->sc_ranges; rlen >= rone; rlen -= rone, range += rone) { + /* Extract from and size, so we can see if we fit. */ + rfrom = range[0]; + if (sc->sc_acells == 2) + rfrom = (rfrom << 32) + range[1]; + rsize = range[sc->sc_acells + sc->sc_pacells]; + if (sc->sc_scells == 2) + rsize = (rsize << 32) + + range[sc->sc_acells + sc->sc_pacells + 1]; + + /* Try next, if we're not in the range. */ + if (addr < rfrom || (addr + size) > (rfrom + rsize)) + continue; + + /* All good, extract to address and translate. */ + rto = range[sc->sc_acells]; + if (sc->sc_pacells == 2) + rto = (rto << 32) + range[sc->sc_acells + 1]; + + addr -= rfrom; + addr += rto; + + return bus_space_map(sc->sc_iot, addr, size, flag, bshp); + } + + return ESRCH; +} |