aboutsummaryrefslogtreecommitdiff
path: root/core/device.c
diff options
context:
space:
mode:
authorOliver O'Halloran <oohall@gmail.com>2017-03-07 15:16:20 +1100
committerStewart Smith <stewart@linux.vnet.ibm.com>2017-03-07 15:40:32 +1100
commitfb27dd8ac6c4c80511e2ca674a6886cd504e1a2c (patch)
tree9d67ee536d6facbbb886f0c3ddca795ebca2b587 /core/device.c
parent0f28e38bbeaa11bda20b81f2247c82682d93d787 (diff)
downloadskiboot-fb27dd8ac6c4c80511e2ca674a6886cd504e1a2c.zip
skiboot-fb27dd8ac6c4c80511e2ca674a6886cd504e1a2c.tar.gz
skiboot-fb27dd8ac6c4c80511e2ca674a6886cd504e1a2c.tar.bz2
device: implement dt_translate_address() properly
Currently this is implemented by calling dt_get_address() which only works when a device is a child of the root node. This patch implements the functionality to work with nested nodes when all parent nodes have an appropriate "ranges" property. This implementation only works for up to 64 bit addresses. Properly supporting larger addressing schemes is a fair amount of (probably pointless) work, so I'm leaving supporting that until we have an actual a need for it. Signed-off-by: Oliver O'Halloran <oohall@gmail.com> Tested-by: Michael Neuling <mikey@neuling.org> Signed-off-by: Stewart Smith <stewart@linux.vnet.ibm.com>
Diffstat (limited to 'core/device.c')
-rw-r--r--core/device.c65
1 files changed, 63 insertions, 2 deletions
diff --git a/core/device.c b/core/device.c
index 30b31f4..76c2f84 100644
--- a/core/device.c
+++ b/core/device.c
@@ -942,11 +942,72 @@ unsigned int dt_count_addresses(const struct dt_node *node)
return p->len / n;
}
+/* Translates an address from the given bus into its parent's address space */
+static u64 dt_translate_one(const struct dt_node *bus, u64 addr)
+{
+ u32 ranges_count, na, ns, parent_na;
+ const struct dt_property *p;
+ const u32 *ranges;
+ int i, stride;
+
+ assert(bus->parent);
+
+ na = dt_prop_get_u32_def(bus, "#address-cells", 2);
+ ns = dt_prop_get_u32_def(bus, "#size-cells", 2);
+ parent_na = dt_n_address_cells(bus);
+
+ stride = na + ns + parent_na;
+
+ /*
+ * FIXME: We should handle arbitrary length addresses, rather than
+ * limiting it to 64bit. If someone wants/needs that they
+ * can implement the bignum math for it :)
+ */
+ assert(na <= 2);
+ assert(parent_na <= 2);
+
+ /* We should never be trying to translate an address without a ranges */
+ p = dt_require_property(bus, "ranges", -1);
+
+ ranges = (u32 *) &p->prop;
+ ranges_count = (p->len / 4) / (na + parent_na + ns);
+
+ /* An empty ranges property implies 1-1 translation */
+ if (ranges_count == 0)
+ return addr;
+
+ for (i = 0; i < ranges_count; i++, ranges += stride) {
+ /* ranges format: <child base> <parent base> <size> */
+ u64 child_base = dt_get_number(ranges, na);
+ u64 parent_base = dt_get_number(ranges + na, parent_na);
+ u64 size = dt_get_number(ranges + na + parent_na, ns);
+
+ if (addr >= child_base && addr < child_base + size)
+ return (addr - child_base) + parent_base;
+ }
+
+ /* input address was outside the any of our mapped ranges */
+ return 0;
+}
+
u64 dt_translate_address(const struct dt_node *node, unsigned int index,
u64 *out_size)
{
- /* XXX TODO */
- return dt_get_address(node, index, out_size);
+ u64 addr = dt_get_address(node, index, NULL);
+ struct dt_node *bus = node->parent;
+
+ /* FIXME: One day we will probably want to use this, but for now just
+ * force it it to be zero since we only support returning a u64 or u32
+ */
+ assert(!out_size);
+
+ /* apply each translation until we hit the root bus */
+ while (bus->parent) {
+ addr = dt_translate_one(bus, addr);
+ bus = bus->parent;
+ }
+
+ return addr;
}
bool dt_node_is_enabled(struct dt_node *node)