Commit a0342c08 authored by Kumar Gala's avatar Kumar Gala
Browse files

fdt: Add fdt_translate_address to convert reg node to cpu phys addr

This code is extracted out of the Linux Kernel code from

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: default avatarKumar Gala <>
Acked-by: default avatarGerald Van Baren <>
parent 46f3e385
......@@ -2,6 +2,8 @@
* (C) Copyright 2007
* Gerald Van Baren, Custom IDEAS,
* 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_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);
printf(" %08x", *(addr++));
static void of_dump_addr(const char *s, const u32 *addr, int na) { }
/* 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)
if (offset == OF_BAD_ADDR) {
debug("OF: not found !\n");
return 1;
memcpy(addr, ranges + na, 4 * pna);
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);
/* 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));
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))
/* Complete the move up one level */
na = pna;
ns = pns;
bus = pbus;
of_dump_addr("OF: one level translation:", addr, na);
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");
......@@ -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 */
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment