[U-Boot] [PATCH 1/2] fdt: Add fdt_translate_address to convert reg node to cpu phys addr

Kumar Gala galak at kernel.crashing.org
Sun Jul 4 20:18:37 CEST 2010


This code is extracted out of the Linux Kernel code from
arch/powerpc/kernel/prom_parse.c.

We maintain some of the same structure to support multiple bus types even
though we only have one in the current code.  In the future we might want
to translate across a PCI bus and thus it will be easier to add that
functionality back in.

Signed-off-by: Kumar Gala <galak at kernel.crashing.org>
---
 common/fdt_support.c  |  264 +++++++++++++++++++++++++++++++++++++++++++++++++
 include/fdt_support.h |    1 +
 2 files changed, 265 insertions(+), 0 deletions(-)

diff --git a/common/fdt_support.c b/common/fdt_support.c
index 4d87135..70ad81d 100644
--- a/common/fdt_support.c
+++ b/common/fdt_support.c
@@ -2,6 +2,8 @@
  * (C) Copyright 2007
  * Gerald Van Baren, Custom IDEAS, vanbaren at cideas.com
  *
+ * Copyright 2010 Freescale Semiconductor, Inc.
+ *
  * See file CREDITS for list of people who contributed to this
  * project.
  *
@@ -861,3 +863,265 @@ void fdt_del_node_and_alias(void *blob, const char *alias)
 	off = fdt_path_offset(blob, "/aliases");
 	fdt_delprop(blob, off, alias);
 }
+
+/* Helper to read a big number; size is in cells (not bytes) */
+static inline u64 of_read_number(const __be32 *cell, int size)
+{
+	u64 r = 0;
+	while (size--)
+		r = (r << 32) | be32_to_cpu(*(cell++));
+	return r;
+}
+
+static int of_n_cells(const void *blob, int nodeoffset, const char *name)
+{
+	int np;
+	const int *ip;
+
+	do {
+		np = fdt_parent_offset(blob, nodeoffset);
+
+		if (np >= 0)
+			nodeoffset = np;
+		ip = (int *)fdt_getprop(blob, nodeoffset, name, NULL);
+		if (ip)
+			return be32_to_cpup(ip);
+	} while (np >= 0);
+
+	/* No #<NAME>-cells property for the root node */
+	return 1;
+}
+
+int of_n_addr_cells(const void *blob, int nodeoffset)
+{
+	return of_n_cells(blob, nodeoffset, "#address-cells");
+}
+
+int of_n_size_cells(const void *blob, int nodeoffset)
+{
+	return of_n_cells(blob, nodeoffset, "#size-cells");
+}
+
+#define PRu64	"%llx"
+
+/* Max address size we deal with */
+#define OF_MAX_ADDR_CELLS	4
+#define OF_BAD_ADDR	((u64)-1)
+#define OF_CHECK_COUNTS(na, ns)	((na) > 0 && (na) <= OF_MAX_ADDR_CELLS && \
+			(ns) > 0)
+
+/* Debug utility */
+#ifdef DEBUG
+static void of_dump_addr(const char *s, const u32 *addr, int na)
+{
+	printf("%s", s);
+	while(na--)
+		printf(" %08x", *(addr++));
+	printf("\n");
+}
+#else
+static void of_dump_addr(const char *s, const u32 *addr, int na) { }
+#endif
+
+/* Callbacks for bus specific translators */
+struct of_bus {
+	const char	*name;
+	const char	*addresses;
+	void		(*count_cells)(void *blob, int offset,
+				int *addrc, int *sizec);
+	u64		(*map)(u32 *addr, const u32 *range,
+				int na, int ns, int pna);
+	int		(*translate)(u32 *addr, u64 offset, int na);
+};
+
+/* Default translator (generic bus) */
+static void of_bus_default_count_cells(void *blob, int offset,
+					int *addrc, int *sizec)
+{
+	if (addrc)
+		*addrc = of_n_addr_cells(blob, offset);
+	if (sizec)
+		*sizec = of_n_size_cells(blob, offset);
+}
+
+static u64 of_bus_default_map(u32 *addr, const u32 *range,
+		int na, int ns, int pna)
+{
+	u64 cp, s, da;
+
+	cp = of_read_number(range, na);
+	s  = of_read_number(range + na + pna, ns);
+	da = of_read_number(addr, na);
+
+	debug("OF: default map, cp="PRu64", s="PRu64", da="PRu64"\n",
+	    cp, s, da);
+
+	if (da < cp || da >= (cp + s))
+		return OF_BAD_ADDR;
+	return da - cp;
+}
+
+static int of_bus_default_translate(u32 *addr, u64 offset, int na)
+{
+	u64 a = of_read_number(addr, na);
+	memset(addr, 0, na * 4);
+	a += offset;
+	if (na > 1)
+		addr[na - 2] = a >> 32;
+	addr[na - 1] = a & 0xffffffffu;
+
+	return 0;
+}
+
+/* Array of bus specific translators */
+static struct of_bus of_busses[] = {
+	/* Default */
+	{
+		.name = "default",
+		.addresses = "reg",
+		.count_cells = of_bus_default_count_cells,
+		.map = of_bus_default_map,
+		.translate = of_bus_default_translate,
+	},
+};
+
+static int of_translate_one(void * blob, int parent, struct of_bus *bus,
+			    struct of_bus *pbus, u32 *addr,
+			    int na, int ns, int pna, const char *rprop)
+{
+	const u32 *ranges;
+	int rlen;
+	int rone;
+	u64 offset = OF_BAD_ADDR;
+
+	/* Normally, an absence of a "ranges" property means we are
+	 * crossing a non-translatable boundary, and thus the addresses
+	 * below the current not cannot be converted to CPU physical ones.
+	 * Unfortunately, while this is very clear in the spec, it's not
+	 * what Apple understood, and they do have things like /uni-n or
+	 * /ht nodes with no "ranges" property and a lot of perfectly
+	 * useable mapped devices below them. Thus we treat the absence of
+	 * "ranges" as equivalent to an empty "ranges" property which means
+	 * a 1:1 translation at that level. It's up to the caller not to try
+	 * to translate addresses that aren't supposed to be translated in
+	 * the first place. --BenH.
+	 */
+	ranges = (u32 *)fdt_getprop(blob, parent, rprop, &rlen);
+	if (ranges == NULL || rlen == 0) {
+		offset = of_read_number(addr, na);
+		memset(addr, 0, pna * 4);
+		debug("OF: no ranges, 1:1 translation\n");
+		goto finish;
+	}
+
+	debug("OF: walking ranges...\n");
+
+	/* Now walk through the ranges */
+	rlen /= 4;
+	rone = na + pna + ns;
+	for (; rlen >= rone; rlen -= rone, ranges += rone) {
+		offset = bus->map(addr, ranges, na, ns, pna);
+		if (offset != OF_BAD_ADDR)
+			break;
+	}
+	if (offset == OF_BAD_ADDR) {
+		debug("OF: not found !\n");
+		return 1;
+	}
+	memcpy(addr, ranges + na, 4 * pna);
+
+ finish:
+	of_dump_addr("OF: parent translation for:", addr, pna);
+	debug("OF: with offset: "PRu64"\n", offset);
+
+	/* Translate it into parent bus space */
+	return pbus->translate(addr, offset, pna);
+}
+
+/*
+ * Translate an address from the device-tree into a CPU physical address,
+ * this walks up the tree and applies the various bus mappings on the
+ * way.
+ *
+ * Note: We consider that crossing any level with #size-cells == 0 to mean
+ * that translation is impossible (that is we are not dealing with a value
+ * that can be mapped to a cpu physical address). This is not really specified
+ * that way, but this is traditionally the way IBM at least do things
+ */
+u64 __of_translate_address(void *blob, int node_offset, const u32 *in_addr,
+			   const char *rprop)
+{
+	int parent;
+	struct of_bus *bus, *pbus;
+	u32 addr[OF_MAX_ADDR_CELLS];
+	int na, ns, pna, pns;
+	u64 result = OF_BAD_ADDR;
+
+	debug("OF: ** translation for device %s **\n",
+		fdt_get_name(blob, node_offset, NULL));
+
+	/* Get parent & match bus type */
+	parent = fdt_parent_offset(blob, node_offset);
+	if (parent < 0)
+		goto bail;
+	bus = &of_busses[0];
+
+	/* Cound address cells & copy address locally */
+	bus->count_cells(blob, node_offset, &na, &ns);
+	if (!OF_CHECK_COUNTS(na, ns)) {
+		printf("%s: Bad cell count for %s\n", __FUNCTION__,
+		       fdt_get_name(blob, node_offset, NULL));
+		goto bail;
+	}
+	memcpy(addr, in_addr, na * 4);
+
+	debug("OF: bus is %s (na=%d, ns=%d) on %s\n",
+	    bus->name, na, ns, fdt_get_name(blob, parent, NULL));
+	of_dump_addr("OF: translating address:", addr, na);
+
+	/* Translate */
+	for (;;) {
+		/* Switch to parent bus */
+		node_offset = parent;
+		parent = fdt_parent_offset(blob, node_offset);
+
+		/* If root, we have finished */
+		if (parent < 0) {
+			debug("OF: reached root node\n");
+			result = of_read_number(addr, na);
+			break;
+		}
+
+		/* Get new parent bus and counts */
+		pbus = &of_busses[0];
+		pbus->count_cells(blob, node_offset, &pna, &pns);
+		if (!OF_CHECK_COUNTS(pna, pns)) {
+			printf("%s: Bad cell count for %s\n", __FUNCTION__,
+				fdt_get_name(blob, node_offset, NULL));
+			break;
+		}
+
+		debug("OF: parent bus is %s (na=%d, ns=%d) on %s\n",
+		    pbus->name, pna, pns, fdt_get_name(blob, parent, NULL));
+
+		/* Apply bus translation */
+		if (of_translate_one(blob, node_offset, bus, pbus,
+					addr, na, ns, pna, rprop))
+			break;
+
+		/* Complete the move up one level */
+		na = pna;
+		ns = pns;
+		bus = pbus;
+
+		of_dump_addr("OF: one level translation:", addr, na);
+	}
+ bail:
+
+	return result;
+}
+
+u64 fdt_translate_address(void *blob, int node_offset, const u32 *in_addr)
+{
+	return __of_translate_address(blob, node_offset, in_addr, "ranges");
+}
diff --git a/include/fdt_support.h b/include/fdt_support.h
index fc16159..78c38b2 100644
--- a/include/fdt_support.h
+++ b/include/fdt_support.h
@@ -83,6 +83,7 @@ int fdt_fixup_nor_flash_size(void *blob, int cs, u32 size);
 
 void fdt_fixup_mtdparts(void *fdt, void *node_info, int node_info_size);
 void fdt_del_node_and_alias(void *blob, const char *alias);
+u64 fdt_translate_address(void *blob, int node_offset, const u32 *in_addr);
 
 #endif /* ifdef CONFIG_OF_LIBFDT */
 #endif /* ifndef __FDT_SUPPORT_H */
-- 
1.6.0.6



More information about the U-Boot mailing list