[PATCH] of: add ranges address translation for default bus

Sebastian Hesselbarth sebastian.hesselbarth at gmail.com
Sun May 12 19:09:06 EDT 2013


This patch adds address translation for default bus types. It has been
shamelessly ported from Linux device tree address translation with the
following exceptions:
- only default bus map and translate are supported
- of_bus has not been ported
- check for #size-cells > 0 has been removed

Signed-off-by: Sebastian Hesselbarth <sebastian.hesselbarth at gmail.com>
---
Cc: Thomas Petazzoni <thomas.petazzoni at free-electrons.com>
Cc: barebox at lists.infradead.org
---
 drivers/of/base.c |  131 ++++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 114 insertions(+), 17 deletions(-)

diff --git a/drivers/of/base.c b/drivers/of/base.c
index 8383549..cad7baa 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -28,6 +28,12 @@
 #include <linux/amba/bus.h>
 #include <linux/err.h>
 
+/* Max address size we deal with */
+#define OF_MAX_ADDR_CELLS	4
+#define OF_CHECK_ADDR_COUNT(na)	((na) > 0 && (na) <= OF_MAX_ADDR_CELLS)
+/* do not check for #size-cells > 0 as no size cells is perfectly valid */
+#define OF_CHECK_COUNTS(na, ns)	(OF_CHECK_ADDR_COUNT(na))
+
 /**
  * struct alias_prop - Alias property in 'aliases' node
  * @link:	List node to link the structure in aliases_lookup list
@@ -212,28 +218,119 @@ int of_alias_get_id(struct device_node *np, const char *stem)
 }
 EXPORT_SYMBOL_GPL(of_alias_get_id);
 
-u64 of_translate_address(struct device_node *node, const __be32 *in_addr)
+static u64 of_bus_default_map(__be32 *addr, const __be32 *range,
+			      int na, int ns, int pna)
 {
-	struct property *p;
-	u64 addr = be32_to_cpu(*in_addr);
+	u64 cp, s, da;
+
+	cp = of_read_number(range, na);
+	s = of_read_number(range + na + pna, ns);
+	da = of_read_number(addr, na);
+
+	/*
+	 * If #address-cells > 2 we assume the mapping does not specify a
+	 * physical address. Rather, the address specifies an identifier
+	 * that must match exactly.
+	 */
+	if (na > 2 && memcmp(range, addr, 4 * na) != 0)
+		return OF_BAD_ADDR;
+
+	/* check if address is outside mapping range */
+	if (da < cp || da >= (cp + s))
+		return OF_BAD_ADDR;
+	return da - cp;
+}
+
+static int of_bus_default_translate(__be32 *addr, u64 offset, int na)
+{
+	u64 a = of_read_number(addr, na);
+	memset(addr, 0, 4 * na);
+	a += offset;
+	if (na > 1)
+		addr[na - 2] = cpu_to_be32(a >> 32);
+	addr[na - 1] = cpu_to_be32(a & 0xffffffffu);
+	return 0;
+}
+
+static int of_translate_one(struct device_node *parent,
+			    __be32 *addr, int na, int ns, int pna,
+			    const char *rprop)
+{
+	const __be32 *ranges;
+	unsigned int rlen;
+	int rone;
+	u64 offset = OF_BAD_ADDR;
+
+	ranges = of_get_property(parent, rprop, &rlen);
+	if (!ranges)
+		return 1;
+
+	/* walk through ranges */
+	rlen /= 4;
+	rone = na + pna + ns;
+	for (; rlen >= rone; rlen -= rone, ranges += rone) {
+		offset = of_bus_default_map(addr, ranges, na, ns, pna);
+		if (offset != OF_BAD_ADDR)
+			break;
+	}
+	if (offset == OF_BAD_ADDR)
+		return 1;
+
+	memcpy(addr, ranges + na, 4 * pna);
+	/* translate into parent address space */
+	return of_bus_default_translate(addr, offset, pna);
+}
+
+u64 __of_translate_address(struct device_node *node,
+			   const __be32 *in_addr, const char *rprop)
+{
+	struct device_node *parent = node->parent;
+	__be32 addr[OF_MAX_ADDR_CELLS];
+	int na, ns, pna, pns;
+	u64 result = OF_BAD_ADDR;
+
+	/* count address cells & copy address locally */
+	of_bus_count_cells(node, &na, &ns);
+	if (!OF_CHECK_COUNTS(na, ns)) {
+		printk(KERN_ERR "of: bad cell count (%d, %d) for %s\n",
+		       na, ns, node->full_name);
+		return OF_BAD_ADDR;
+	}
+	memcpy(addr, in_addr, na * 4);
 
 	while (1) {
-		int na, nc;
-
-		if (!node->parent)
-			return addr;
-
-		node = node->parent;
-		p = of_find_property(node, "ranges");
-		if (!p && node->parent)
-			return OF_BAD_ADDR;
-		of_bus_count_cells(node, &na, &nc);
-		if (na != 1 || nc != 1) {
-			printk("%s: #size-cells != 1 or #address-cells != 1 "
-					"currently not supported\n", node->name);
-			return OF_BAD_ADDR;
+		node = parent;
+		parent = node->parent;
+
+		/* exit at root node */
+		if (!parent) {
+			result = of_read_number(addr, na);
+			break;
 		}
+
+		/* get new parent bus and counts */
+		of_bus_count_cells(node, &pna, &pns);
+		if (!OF_CHECK_COUNTS(pna, pns)) {
+			printk(KERN_ERR "of: bad cell count (%d, %d) for %s\n",
+			       pna, pns, node->full_name);
+			break;
+		}
+
+		/* apply bus translation */
+		if (of_translate_one(node, addr, na, ns, pna, rprop))
+			break;
+
+		/* ascend device tree */
+		na = pna;
+		ns = pns;
 	}
+
+	return result;
+}
+
+u64 of_translate_address(struct device_node *node, const __be32 *in_addr)
+{
+	return __of_translate_address(node, in_addr, "ranges");
 }
 EXPORT_SYMBOL(of_translate_address);
 
-- 
1.7.10.4




More information about the barebox mailing list