u-boot of_translate_address函数
/*
* 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
*/
static u64 __of_translate_address(const void *blob, int node_offset,
const fdt32_t *in_addr, const char *rprop)
{
int parent;
struct of_bus *bus, *pbus;
fdt32_t 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_match_bus(blob, parent);
/* Cound address cells & copy address locally */
bus->count_cells(blob, parent, &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 = fdt_read_number(addr, na);
break;
}
/* Get new parent bus and counts */
pbus = of_match_bus(blob, parent);
pbus->count_cells(blob, parent, &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;
}
int fdt_parent_offset(const void *fdt, int nodeoffset)
{
int nodedepth = fdt_node_depth(fdt, nodeoffset);
if (nodedepth < 0) return nodedepth; return fdt_supernode_atdepth_offset(fdt, nodeoffset, nodedepth - 1, NULL); } int fdt_node_depth(const void *fdt, int nodeoffset) { int nodedepth; int err;
err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth); if (err) return (!fdt_chk_extra() || err < 0) ? err : -FDT_ERR_INTERNAL; return nodedepth; } int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, int supernodedepth, int *nodedepth) { int offset, depth; int supernodeoffset = -FDT_ERR_INTERNAL;
FDT_RO_PROBE(fdt);
if (supernodedepth < 0) return -FDT_ERR_NOTFOUND;
for (offset = 0, depth = 0; (offset >= 0) && (offset <= nodeoffset); offset = fdt_next_node(fdt, offset, &depth)) { if (depth == supernodedepth) supernodeoffset = offset;
if (offset == nodeoffset) { if (nodedepth) *nodedepth = depth;
if (supernodedepth > depth) return -FDT_ERR_NOTFOUND; else return supernodeoffset; } }
if (fdt_chk_extra()) { if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) return -FDT_ERR_BADOFFSET; else if (offset == -FDT_ERR_BADOFFSET) return -FDT_ERR_BADSTRUCTURE; }
return offset; /* error from fdt_next_node() */ } static int of_translate_one(const void *blob, int parent, struct of_bus *bus, struct of_bus *pbus, fdt32_t *addr, int na, int ns, int pna, const char *rprop) { const fdt32_t *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 = fdt_getprop(blob, parent, rprop, &rlen); if (ranges == NULL || rlen == 0) { offset = fdt_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: %llu\n", offset);
/* Translate it into parent bus space */ return pbus->translate(addr, offset, pna); }
if (nodedepth < 0) return nodedepth; return fdt_supernode_atdepth_offset(fdt, nodeoffset, nodedepth - 1, NULL); } int fdt_node_depth(const void *fdt, int nodeoffset) { int nodedepth; int err;
err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth); if (err) return (!fdt_chk_extra() || err < 0) ? err : -FDT_ERR_INTERNAL; return nodedepth; } int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, int supernodedepth, int *nodedepth) { int offset, depth; int supernodeoffset = -FDT_ERR_INTERNAL;
FDT_RO_PROBE(fdt);
if (supernodedepth < 0) return -FDT_ERR_NOTFOUND;
for (offset = 0, depth = 0; (offset >= 0) && (offset <= nodeoffset); offset = fdt_next_node(fdt, offset, &depth)) { if (depth == supernodedepth) supernodeoffset = offset;
if (offset == nodeoffset) { if (nodedepth) *nodedepth = depth;
if (supernodedepth > depth) return -FDT_ERR_NOTFOUND; else return supernodeoffset; } }
if (fdt_chk_extra()) { if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) return -FDT_ERR_BADOFFSET; else if (offset == -FDT_ERR_BADOFFSET) return -FDT_ERR_BADSTRUCTURE; }
return offset; /* error from fdt_next_node() */ } static int of_translate_one(const void *blob, int parent, struct of_bus *bus, struct of_bus *pbus, fdt32_t *addr, int na, int ns, int pna, const char *rprop) { const fdt32_t *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 = fdt_getprop(blob, parent, rprop, &rlen); if (ranges == NULL || rlen == 0) { offset = fdt_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: %llu\n", offset);
/* Translate it into parent bus space */ return pbus->translate(addr, offset, pna); }