Some SoCs have a ranges property set in their device trees. This can
authorpatrick <patrick@openbsd.org>
Mon, 18 Jul 2016 11:53:32 +0000 (11:53 +0000)
committerpatrick <patrick@openbsd.org>
Mon, 18 Jul 2016 11:53:32 +0000 (11:53 +0000)
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

index 65689d8..c818814 100644 (file)
@@ -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;
+}