From 39d0e1dc2812464507bc6e3ad94a104ea5452f4e Mon Sep 17 00:00:00 2001 From: patrick Date: Mon, 18 Jul 2016 11:53:32 +0000 Subject: [PATCH] Some SoCs have a ranges property set in their device trees. This can be used to translate one memory address to another. Currently we just pass the child's memory address to bus space map. If one of the parent busses implements a ranges property, the child's address property is not the real address. This change hooks up a bus space map function that is aware of the ranges property and translates the addresses if needed. ok kettenis@ looks fine jsg@ --- sys/arch/arm/simplebus/simplebus.c | 77 +++++++++++++++++++++++++++++- 1 file 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 * @@ -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; +} -- 2.20.1