1/* 2 * FDT Address translation based on u-boot fdt_support.c which in turn was 3 * based on the kernel unflattened DT address translation code. 4 * 5 * (C) Copyright 2007 6 * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com 7 * 8 * Copyright 2010-2011 Freescale Semiconductor, Inc. 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License as published by 12 * the Free Software Foundation; either version 2, or (at your option) 13 * any later version. 14 */ 15#include <linux/kernel.h> 16#include <linux/libfdt.h> 17#include <linux/of.h> 18#include <linux/of_fdt.h> 19#include <linux/sizes.h> 20 21/* Max address size we deal with */ 22#define OF_MAX_ADDR_CELLS 4 23#define OF_CHECK_COUNTS(na, ns) ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS && \ 24 (ns) > 0) 25 26/* Debug utility */ 27#ifdef DEBUG 28static void __init of_dump_addr(const char *s, const __be32 *addr, int na) 29{ 30 pr_debug("%s", s); 31 while(na--) 32 pr_cont(" %08x", *(addr++)); 33 pr_debug("\n"); 34} 35#else 36static void __init of_dump_addr(const char *s, const __be32 *addr, int na) { } 37#endif 38 39/* Callbacks for bus specific translators */ 40struct of_bus { 41 void (*count_cells)(const void *blob, int parentoffset, 42 int *addrc, int *sizec); 43 u64 (*map)(__be32 *addr, const __be32 *range, 44 int na, int ns, int pna); 45 int (*translate)(__be32 *addr, u64 offset, int na); 46}; 47 48/* Default translator (generic bus) */ 49static void __init fdt_bus_default_count_cells(const void *blob, int parentoffset, 50 int *addrc, int *sizec) 51{ 52 const __be32 *prop; 53 54 if (addrc) { 55 prop = fdt_getprop(blob, parentoffset, "#address-cells", NULL); 56 if (prop) 57 *addrc = be32_to_cpup(prop); 58 else 59 *addrc = dt_root_addr_cells; 60 } 61 62 if (sizec) { 63 prop = fdt_getprop(blob, parentoffset, "#size-cells", NULL); 64 if (prop) 65 *sizec = be32_to_cpup(prop); 66 else 67 *sizec = dt_root_size_cells; 68 } 69} 70 71static u64 __init fdt_bus_default_map(__be32 *addr, const __be32 *range, 72 int na, int ns, int pna) 73{ 74 u64 cp, s, da; 75 76 cp = of_read_number(range, na); 77 s = of_read_number(range + na + pna, ns); 78 da = of_read_number(addr, na); 79 80 pr_debug("FDT: default map, cp=%llx, s=%llx, da=%llx\n", 81 cp, s, da); 82 83 if (da < cp || da >= (cp + s)) 84 return OF_BAD_ADDR; 85 return da - cp; 86} 87 88static int __init fdt_bus_default_translate(__be32 *addr, u64 offset, int na) 89{ 90 u64 a = of_read_number(addr, na); 91 memset(addr, 0, na * 4); 92 a += offset; 93 if (na > 1) 94 addr[na - 2] = cpu_to_fdt32(a >> 32); 95 addr[na - 1] = cpu_to_fdt32(a & 0xffffffffu); 96 97 return 0; 98} 99 100/* Array of bus specific translators */ 101static const struct of_bus of_busses[] __initconst = { 102 /* Default */ 103 { 104 .count_cells = fdt_bus_default_count_cells, 105 .map = fdt_bus_default_map, 106 .translate = fdt_bus_default_translate, 107 }, 108}; 109 110static int __init fdt_translate_one(const void *blob, int parent, 111 const struct of_bus *bus, 112 const struct of_bus *pbus, __be32 *addr, 113 int na, int ns, int pna, const char *rprop) 114{ 115 const __be32 *ranges; 116 int rlen; 117 int rone; 118 u64 offset = OF_BAD_ADDR; 119 120 ranges = fdt_getprop(blob, parent, rprop, &rlen); 121 if (!ranges) 122 return 1; 123 if (rlen == 0) { 124 offset = of_read_number(addr, na); 125 memset(addr, 0, pna * 4); 126 pr_debug("FDT: empty ranges, 1:1 translation\n"); 127 goto finish; 128 } 129 130 pr_debug("FDT: walking ranges...\n"); 131 132 /* Now walk through the ranges */ 133 rlen /= 4; 134 rone = na + pna + ns; 135 for (; rlen >= rone; rlen -= rone, ranges += rone) { 136 offset = bus->map(addr, ranges, na, ns, pna); 137 if (offset != OF_BAD_ADDR) 138 break; 139 } 140 if (offset == OF_BAD_ADDR) { 141 pr_debug("FDT: not found !\n"); 142 return 1; 143 } 144 memcpy(addr, ranges + na, 4 * pna); 145 146 finish: 147 of_dump_addr("FDT: parent translation for:", addr, pna); 148 pr_debug("FDT: with offset: %llx\n", offset); 149 150 /* Translate it into parent bus space */ 151 return pbus->translate(addr, offset, pna); 152} 153 154/* 155 * Translate an address from the device-tree into a CPU physical address, 156 * this walks up the tree and applies the various bus mappings on the 157 * way. 158 * 159 * Note: We consider that crossing any level with #size-cells == 0 to mean 160 * that translation is impossible (that is we are not dealing with a value 161 * that can be mapped to a cpu physical address). This is not really specified 162 * that way, but this is traditionally the way IBM at least do things 163 */ 164u64 __init fdt_translate_address(const void *blob, int node_offset) 165{ 166 int parent, len; 167 const struct of_bus *bus, *pbus; 168 const __be32 *reg; 169 __be32 addr[OF_MAX_ADDR_CELLS]; 170 int na, ns, pna, pns; 171 u64 result = OF_BAD_ADDR; 172 173 pr_debug("FDT: ** translation for device %s **\n", 174 fdt_get_name(blob, node_offset, NULL)); 175 176 reg = fdt_getprop(blob, node_offset, "reg", &len); 177 if (!reg) { 178 pr_err("FDT: warning: device tree node '%s' has no address.\n", 179 fdt_get_name(blob, node_offset, NULL)); 180 goto bail; 181 } 182 183 /* Get parent & match bus type */ 184 parent = fdt_parent_offset(blob, node_offset); 185 if (parent < 0) 186 goto bail; 187 bus = &of_busses[0]; 188 189 /* Cound address cells & copy address locally */ 190 bus->count_cells(blob, parent, &na, &ns); 191 if (!OF_CHECK_COUNTS(na, ns)) { 192 pr_err("FDT: Bad cell count for %s\n", 193 fdt_get_name(blob, node_offset, NULL)); 194 goto bail; 195 } 196 memcpy(addr, reg, na * 4); 197 198 pr_debug("FDT: bus (na=%d, ns=%d) on %s\n", 199 na, ns, fdt_get_name(blob, parent, NULL)); 200 of_dump_addr("OF: translating address:", addr, na); 201 202 /* Translate */ 203 for (;;) { 204 /* Switch to parent bus */ 205 node_offset = parent; 206 parent = fdt_parent_offset(blob, node_offset); 207 208 /* If root, we have finished */ 209 if (parent < 0) { 210 pr_debug("FDT: reached root node\n"); 211 result = of_read_number(addr, na); 212 break; 213 } 214 215 /* Get new parent bus and counts */ 216 pbus = &of_busses[0]; 217 pbus->count_cells(blob, parent, &pna, &pns); 218 if (!OF_CHECK_COUNTS(pna, pns)) { 219 pr_err("FDT: Bad cell count for %s\n", 220 fdt_get_name(blob, node_offset, NULL)); 221 break; 222 } 223 224 pr_debug("FDT: parent bus (na=%d, ns=%d) on %s\n", 225 pna, pns, fdt_get_name(blob, parent, NULL)); 226 227 /* Apply bus translation */ 228 if (fdt_translate_one(blob, node_offset, bus, pbus, 229 addr, na, ns, pna, "ranges")) 230 break; 231 232 /* Complete the move up one level */ 233 na = pna; 234 ns = pns; 235 bus = pbus; 236 237 of_dump_addr("FDT: one level translation:", addr, na); 238 } 239 bail: 240 return result; 241} 242