| Grant Likely | e169cfb | 2009-11-23 14:53:09 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * Functions for working with the Flattened Device Tree data format | 
|  | 3 | * | 
|  | 4 | * Copyright 2009 Benjamin Herrenschmidt, IBM Corp | 
|  | 5 | * benh@kernel.crashing.org | 
|  | 6 | * | 
|  | 7 | * This program is free software; you can redistribute it and/or | 
|  | 8 | * modify it under the terms of the GNU General Public License | 
|  | 9 | * version 2 as published by the Free Software Foundation. | 
|  | 10 | */ | 
|  | 11 |  | 
| Grant Likely | 41f8800 | 2009-11-23 20:07:01 -0700 | [diff] [blame] | 12 | #include <linux/kernel.h> | 
| Grant Likely | f7b3a83 | 2009-11-24 03:26:58 -0700 | [diff] [blame] | 13 | #include <linux/initrd.h> | 
| Grant Likely | e169cfb | 2009-11-23 14:53:09 -0700 | [diff] [blame] | 14 | #include <linux/of.h> | 
|  | 15 | #include <linux/of_fdt.h> | 
| Jeremy Kerr | 4ef7b37 | 2010-02-14 07:13:47 -0700 | [diff] [blame] | 16 | #include <linux/string.h> | 
|  | 17 | #include <linux/errno.h> | 
| Grant Likely | 51975db | 2010-02-01 21:34:14 -0700 | [diff] [blame] | 18 |  | 
| Grant Likely | 86e0322 | 2009-12-10 23:42:21 -0700 | [diff] [blame] | 19 | #ifdef CONFIG_PPC | 
|  | 20 | #include <asm/machdep.h> | 
|  | 21 | #endif /* CONFIG_PPC */ | 
|  | 22 |  | 
| Jeremy Kerr | 4ef7b37 | 2010-02-14 07:13:47 -0700 | [diff] [blame] | 23 | #include <asm/page.h> | 
|  | 24 |  | 
| Grant Likely | f00abd9 | 2009-11-24 03:27:10 -0700 | [diff] [blame] | 25 | int __initdata dt_root_addr_cells; | 
|  | 26 | int __initdata dt_root_size_cells; | 
|  | 27 |  | 
| Grant Likely | e169cfb | 2009-11-23 14:53:09 -0700 | [diff] [blame] | 28 | struct boot_param_header *initial_boot_params; | 
|  | 29 |  | 
|  | 30 | char *find_flat_dt_string(u32 offset) | 
|  | 31 | { | 
|  | 32 | return ((char *)initial_boot_params) + | 
| Jeremy Kerr | 087f79c | 2010-01-30 04:14:19 -0700 | [diff] [blame] | 33 | be32_to_cpu(initial_boot_params->off_dt_strings) + offset; | 
| Grant Likely | e169cfb | 2009-11-23 14:53:09 -0700 | [diff] [blame] | 34 | } | 
| Grant Likely | c8cb7a5 | 2009-11-23 18:54:23 -0700 | [diff] [blame] | 35 |  | 
|  | 36 | /** | 
|  | 37 | * of_scan_flat_dt - scan flattened tree blob and call callback on each. | 
|  | 38 | * @it: callback function | 
|  | 39 | * @data: context data pointer | 
|  | 40 | * | 
|  | 41 | * This function is used to scan the flattened device-tree, it is | 
|  | 42 | * used to extract the memory information at boot before we can | 
|  | 43 | * unflatten the tree | 
|  | 44 | */ | 
|  | 45 | int __init of_scan_flat_dt(int (*it)(unsigned long node, | 
|  | 46 | const char *uname, int depth, | 
|  | 47 | void *data), | 
|  | 48 | void *data) | 
|  | 49 | { | 
|  | 50 | unsigned long p = ((unsigned long)initial_boot_params) + | 
| Jeremy Kerr | 087f79c | 2010-01-30 04:14:19 -0700 | [diff] [blame] | 51 | be32_to_cpu(initial_boot_params->off_dt_struct); | 
| Grant Likely | c8cb7a5 | 2009-11-23 18:54:23 -0700 | [diff] [blame] | 52 | int rc = 0; | 
|  | 53 | int depth = -1; | 
|  | 54 |  | 
|  | 55 | do { | 
| Jeremy Kerr | 3371488 | 2010-01-30 01:45:26 -0700 | [diff] [blame] | 56 | u32 tag = be32_to_cpup((__be32 *)p); | 
| Grant Likely | c8cb7a5 | 2009-11-23 18:54:23 -0700 | [diff] [blame] | 57 | char *pathp; | 
|  | 58 |  | 
|  | 59 | p += 4; | 
|  | 60 | if (tag == OF_DT_END_NODE) { | 
|  | 61 | depth--; | 
|  | 62 | continue; | 
|  | 63 | } | 
|  | 64 | if (tag == OF_DT_NOP) | 
|  | 65 | continue; | 
|  | 66 | if (tag == OF_DT_END) | 
|  | 67 | break; | 
|  | 68 | if (tag == OF_DT_PROP) { | 
| Jeremy Kerr | 3371488 | 2010-01-30 01:45:26 -0700 | [diff] [blame] | 69 | u32 sz = be32_to_cpup((__be32 *)p); | 
| Grant Likely | c8cb7a5 | 2009-11-23 18:54:23 -0700 | [diff] [blame] | 70 | p += 8; | 
| Jeremy Kerr | 087f79c | 2010-01-30 04:14:19 -0700 | [diff] [blame] | 71 | if (be32_to_cpu(initial_boot_params->version) < 0x10) | 
| Grant Likely | c8cb7a5 | 2009-11-23 18:54:23 -0700 | [diff] [blame] | 72 | p = _ALIGN(p, sz >= 8 ? 8 : 4); | 
|  | 73 | p += sz; | 
|  | 74 | p = _ALIGN(p, 4); | 
|  | 75 | continue; | 
|  | 76 | } | 
|  | 77 | if (tag != OF_DT_BEGIN_NODE) { | 
|  | 78 | pr_err("Invalid tag %x in flat device tree!\n", tag); | 
|  | 79 | return -EINVAL; | 
|  | 80 | } | 
|  | 81 | depth++; | 
|  | 82 | pathp = (char *)p; | 
|  | 83 | p = _ALIGN(p + strlen(pathp) + 1, 4); | 
|  | 84 | if ((*pathp) == '/') { | 
|  | 85 | char *lp, *np; | 
|  | 86 | for (lp = NULL, np = pathp; *np; np++) | 
|  | 87 | if ((*np) == '/') | 
|  | 88 | lp = np+1; | 
|  | 89 | if (lp != NULL) | 
|  | 90 | pathp = lp; | 
|  | 91 | } | 
|  | 92 | rc = it(p, pathp, depth, data); | 
|  | 93 | if (rc != 0) | 
|  | 94 | break; | 
|  | 95 | } while (1); | 
|  | 96 |  | 
|  | 97 | return rc; | 
|  | 98 | } | 
| Grant Likely | 819d281 | 2009-11-23 19:44:23 -0700 | [diff] [blame] | 99 |  | 
|  | 100 | /** | 
|  | 101 | * of_get_flat_dt_root - find the root node in the flat blob | 
|  | 102 | */ | 
|  | 103 | unsigned long __init of_get_flat_dt_root(void) | 
|  | 104 | { | 
|  | 105 | unsigned long p = ((unsigned long)initial_boot_params) + | 
| Jeremy Kerr | 087f79c | 2010-01-30 04:14:19 -0700 | [diff] [blame] | 106 | be32_to_cpu(initial_boot_params->off_dt_struct); | 
| Grant Likely | 819d281 | 2009-11-23 19:44:23 -0700 | [diff] [blame] | 107 |  | 
| Jeremy Kerr | 3371488 | 2010-01-30 01:45:26 -0700 | [diff] [blame] | 108 | while (be32_to_cpup((__be32 *)p) == OF_DT_NOP) | 
| Grant Likely | 819d281 | 2009-11-23 19:44:23 -0700 | [diff] [blame] | 109 | p += 4; | 
| Jeremy Kerr | 3371488 | 2010-01-30 01:45:26 -0700 | [diff] [blame] | 110 | BUG_ON(be32_to_cpup((__be32 *)p) != OF_DT_BEGIN_NODE); | 
| Grant Likely | 819d281 | 2009-11-23 19:44:23 -0700 | [diff] [blame] | 111 | p += 4; | 
|  | 112 | return _ALIGN(p + strlen((char *)p) + 1, 4); | 
|  | 113 | } | 
|  | 114 |  | 
| Grant Likely | ca900cf | 2009-11-23 20:06:59 -0700 | [diff] [blame] | 115 | /** | 
|  | 116 | * of_get_flat_dt_prop - Given a node in the flat blob, return the property ptr | 
|  | 117 | * | 
|  | 118 | * This function can be used within scan_flattened_dt callback to get | 
|  | 119 | * access to properties | 
|  | 120 | */ | 
|  | 121 | void *__init of_get_flat_dt_prop(unsigned long node, const char *name, | 
|  | 122 | unsigned long *size) | 
|  | 123 | { | 
|  | 124 | unsigned long p = node; | 
|  | 125 |  | 
|  | 126 | do { | 
| Jeremy Kerr | 3371488 | 2010-01-30 01:45:26 -0700 | [diff] [blame] | 127 | u32 tag = be32_to_cpup((__be32 *)p); | 
| Grant Likely | ca900cf | 2009-11-23 20:06:59 -0700 | [diff] [blame] | 128 | u32 sz, noff; | 
|  | 129 | const char *nstr; | 
|  | 130 |  | 
|  | 131 | p += 4; | 
|  | 132 | if (tag == OF_DT_NOP) | 
|  | 133 | continue; | 
|  | 134 | if (tag != OF_DT_PROP) | 
|  | 135 | return NULL; | 
|  | 136 |  | 
| Jeremy Kerr | 3371488 | 2010-01-30 01:45:26 -0700 | [diff] [blame] | 137 | sz = be32_to_cpup((__be32 *)p); | 
|  | 138 | noff = be32_to_cpup((__be32 *)(p + 4)); | 
| Grant Likely | ca900cf | 2009-11-23 20:06:59 -0700 | [diff] [blame] | 139 | p += 8; | 
| Jeremy Kerr | 087f79c | 2010-01-30 04:14:19 -0700 | [diff] [blame] | 140 | if (be32_to_cpu(initial_boot_params->version) < 0x10) | 
| Grant Likely | ca900cf | 2009-11-23 20:06:59 -0700 | [diff] [blame] | 141 | p = _ALIGN(p, sz >= 8 ? 8 : 4); | 
|  | 142 |  | 
|  | 143 | nstr = find_flat_dt_string(noff); | 
|  | 144 | if (nstr == NULL) { | 
|  | 145 | pr_warning("Can't find property index name !\n"); | 
|  | 146 | return NULL; | 
|  | 147 | } | 
|  | 148 | if (strcmp(name, nstr) == 0) { | 
|  | 149 | if (size) | 
|  | 150 | *size = sz; | 
|  | 151 | return (void *)p; | 
|  | 152 | } | 
|  | 153 | p += sz; | 
|  | 154 | p = _ALIGN(p, 4); | 
|  | 155 | } while (1); | 
|  | 156 | } | 
|  | 157 |  | 
| Grant Likely | 00e38ef | 2009-11-23 20:07:00 -0700 | [diff] [blame] | 158 | /** | 
|  | 159 | * of_flat_dt_is_compatible - Return true if given node has compat in compatible list | 
|  | 160 | * @node: node to test | 
|  | 161 | * @compat: compatible string to compare with compatible list. | 
|  | 162 | */ | 
|  | 163 | int __init of_flat_dt_is_compatible(unsigned long node, const char *compat) | 
|  | 164 | { | 
|  | 165 | const char *cp; | 
|  | 166 | unsigned long cplen, l; | 
|  | 167 |  | 
|  | 168 | cp = of_get_flat_dt_prop(node, "compatible", &cplen); | 
|  | 169 | if (cp == NULL) | 
|  | 170 | return 0; | 
|  | 171 | while (cplen > 0) { | 
|  | 172 | if (strncasecmp(cp, compat, strlen(compat)) == 0) | 
|  | 173 | return 1; | 
|  | 174 | l = strlen(cp) + 1; | 
|  | 175 | cp += l; | 
|  | 176 | cplen -= l; | 
|  | 177 | } | 
|  | 178 |  | 
|  | 179 | return 0; | 
|  | 180 | } | 
|  | 181 |  | 
| Grant Likely | bbd3393 | 2009-11-23 20:07:00 -0700 | [diff] [blame] | 182 | static void *__init unflatten_dt_alloc(unsigned long *mem, unsigned long size, | 
|  | 183 | unsigned long align) | 
|  | 184 | { | 
|  | 185 | void *res; | 
|  | 186 |  | 
|  | 187 | *mem = _ALIGN(*mem, align); | 
|  | 188 | res = (void *)*mem; | 
|  | 189 | *mem += size; | 
|  | 190 |  | 
|  | 191 | return res; | 
|  | 192 | } | 
|  | 193 |  | 
|  | 194 | /** | 
|  | 195 | * unflatten_dt_node - Alloc and populate a device_node from the flat tree | 
|  | 196 | * @p: pointer to node in flat tree | 
|  | 197 | * @dad: Parent struct device_node | 
|  | 198 | * @allnextpp: pointer to ->allnext from last allocated device_node | 
|  | 199 | * @fpsize: Size of the node path up at the current depth. | 
|  | 200 | */ | 
|  | 201 | unsigned long __init unflatten_dt_node(unsigned long mem, | 
|  | 202 | unsigned long *p, | 
|  | 203 | struct device_node *dad, | 
|  | 204 | struct device_node ***allnextpp, | 
|  | 205 | unsigned long fpsize) | 
|  | 206 | { | 
|  | 207 | struct device_node *np; | 
|  | 208 | struct property *pp, **prev_pp = NULL; | 
|  | 209 | char *pathp; | 
|  | 210 | u32 tag; | 
|  | 211 | unsigned int l, allocl; | 
|  | 212 | int has_name = 0; | 
|  | 213 | int new_format = 0; | 
|  | 214 |  | 
| Jeremy Kerr | 3371488 | 2010-01-30 01:45:26 -0700 | [diff] [blame] | 215 | tag = be32_to_cpup((__be32 *)(*p)); | 
| Grant Likely | bbd3393 | 2009-11-23 20:07:00 -0700 | [diff] [blame] | 216 | if (tag != OF_DT_BEGIN_NODE) { | 
|  | 217 | pr_err("Weird tag at start of node: %x\n", tag); | 
|  | 218 | return mem; | 
|  | 219 | } | 
|  | 220 | *p += 4; | 
|  | 221 | pathp = (char *)*p; | 
|  | 222 | l = allocl = strlen(pathp) + 1; | 
|  | 223 | *p = _ALIGN(*p + l, 4); | 
|  | 224 |  | 
|  | 225 | /* version 0x10 has a more compact unit name here instead of the full | 
|  | 226 | * path. we accumulate the full path size using "fpsize", we'll rebuild | 
|  | 227 | * it later. We detect this because the first character of the name is | 
|  | 228 | * not '/'. | 
|  | 229 | */ | 
|  | 230 | if ((*pathp) != '/') { | 
|  | 231 | new_format = 1; | 
|  | 232 | if (fpsize == 0) { | 
|  | 233 | /* root node: special case. fpsize accounts for path | 
|  | 234 | * plus terminating zero. root node only has '/', so | 
|  | 235 | * fpsize should be 2, but we want to avoid the first | 
|  | 236 | * level nodes to have two '/' so we use fpsize 1 here | 
|  | 237 | */ | 
|  | 238 | fpsize = 1; | 
|  | 239 | allocl = 2; | 
|  | 240 | } else { | 
|  | 241 | /* account for '/' and path size minus terminal 0 | 
|  | 242 | * already in 'l' | 
|  | 243 | */ | 
|  | 244 | fpsize += l; | 
|  | 245 | allocl = fpsize; | 
|  | 246 | } | 
|  | 247 | } | 
|  | 248 |  | 
|  | 249 | np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + allocl, | 
|  | 250 | __alignof__(struct device_node)); | 
|  | 251 | if (allnextpp) { | 
|  | 252 | memset(np, 0, sizeof(*np)); | 
|  | 253 | np->full_name = ((char *)np) + sizeof(struct device_node); | 
|  | 254 | if (new_format) { | 
|  | 255 | char *fn = np->full_name; | 
|  | 256 | /* rebuild full path for new format */ | 
|  | 257 | if (dad && dad->parent) { | 
|  | 258 | strcpy(fn, dad->full_name); | 
|  | 259 | #ifdef DEBUG | 
|  | 260 | if ((strlen(fn) + l + 1) != allocl) { | 
|  | 261 | pr_debug("%s: p: %d, l: %d, a: %d\n", | 
|  | 262 | pathp, (int)strlen(fn), | 
|  | 263 | l, allocl); | 
|  | 264 | } | 
|  | 265 | #endif | 
|  | 266 | fn += strlen(fn); | 
|  | 267 | } | 
|  | 268 | *(fn++) = '/'; | 
|  | 269 | memcpy(fn, pathp, l); | 
|  | 270 | } else | 
|  | 271 | memcpy(np->full_name, pathp, l); | 
|  | 272 | prev_pp = &np->properties; | 
|  | 273 | **allnextpp = np; | 
|  | 274 | *allnextpp = &np->allnext; | 
|  | 275 | if (dad != NULL) { | 
|  | 276 | np->parent = dad; | 
|  | 277 | /* we temporarily use the next field as `last_child'*/ | 
|  | 278 | if (dad->next == NULL) | 
|  | 279 | dad->child = np; | 
|  | 280 | else | 
|  | 281 | dad->next->sibling = np; | 
|  | 282 | dad->next = np; | 
|  | 283 | } | 
|  | 284 | kref_init(&np->kref); | 
|  | 285 | } | 
|  | 286 | while (1) { | 
|  | 287 | u32 sz, noff; | 
|  | 288 | char *pname; | 
|  | 289 |  | 
| Jeremy Kerr | 3371488 | 2010-01-30 01:45:26 -0700 | [diff] [blame] | 290 | tag = be32_to_cpup((__be32 *)(*p)); | 
| Grant Likely | bbd3393 | 2009-11-23 20:07:00 -0700 | [diff] [blame] | 291 | if (tag == OF_DT_NOP) { | 
|  | 292 | *p += 4; | 
|  | 293 | continue; | 
|  | 294 | } | 
|  | 295 | if (tag != OF_DT_PROP) | 
|  | 296 | break; | 
|  | 297 | *p += 4; | 
| Jeremy Kerr | 3371488 | 2010-01-30 01:45:26 -0700 | [diff] [blame] | 298 | sz = be32_to_cpup((__be32 *)(*p)); | 
|  | 299 | noff = be32_to_cpup((__be32 *)((*p) + 4)); | 
| Grant Likely | bbd3393 | 2009-11-23 20:07:00 -0700 | [diff] [blame] | 300 | *p += 8; | 
| Jeremy Kerr | 087f79c | 2010-01-30 04:14:19 -0700 | [diff] [blame] | 301 | if (be32_to_cpu(initial_boot_params->version) < 0x10) | 
| Grant Likely | bbd3393 | 2009-11-23 20:07:00 -0700 | [diff] [blame] | 302 | *p = _ALIGN(*p, sz >= 8 ? 8 : 4); | 
|  | 303 |  | 
|  | 304 | pname = find_flat_dt_string(noff); | 
|  | 305 | if (pname == NULL) { | 
|  | 306 | pr_info("Can't find property name in list !\n"); | 
|  | 307 | break; | 
|  | 308 | } | 
|  | 309 | if (strcmp(pname, "name") == 0) | 
|  | 310 | has_name = 1; | 
|  | 311 | l = strlen(pname) + 1; | 
|  | 312 | pp = unflatten_dt_alloc(&mem, sizeof(struct property), | 
|  | 313 | __alignof__(struct property)); | 
|  | 314 | if (allnextpp) { | 
| David Gibson | 04b954a | 2010-02-01 21:34:15 -0700 | [diff] [blame] | 315 | /* We accept flattened tree phandles either in | 
|  | 316 | * ePAPR-style "phandle" properties, or the | 
|  | 317 | * legacy "linux,phandle" properties.  If both | 
|  | 318 | * appear and have different values, things | 
|  | 319 | * will get weird.  Don't do that. */ | 
|  | 320 | if ((strcmp(pname, "phandle") == 0) || | 
|  | 321 | (strcmp(pname, "linux,phandle") == 0)) { | 
| Grant Likely | 6016a36 | 2010-01-28 14:06:53 -0700 | [diff] [blame] | 322 | if (np->phandle == 0) | 
|  | 323 | np->phandle = *((u32 *)*p); | 
| Grant Likely | bbd3393 | 2009-11-23 20:07:00 -0700 | [diff] [blame] | 324 | } | 
| David Gibson | 04b954a | 2010-02-01 21:34:15 -0700 | [diff] [blame] | 325 | /* And we process the "ibm,phandle" property | 
|  | 326 | * used in pSeries dynamic device tree | 
|  | 327 | * stuff */ | 
| Grant Likely | bbd3393 | 2009-11-23 20:07:00 -0700 | [diff] [blame] | 328 | if (strcmp(pname, "ibm,phandle") == 0) | 
| Grant Likely | 6016a36 | 2010-01-28 14:06:53 -0700 | [diff] [blame] | 329 | np->phandle = *((u32 *)*p); | 
| Grant Likely | bbd3393 | 2009-11-23 20:07:00 -0700 | [diff] [blame] | 330 | pp->name = pname; | 
|  | 331 | pp->length = sz; | 
|  | 332 | pp->value = (void *)*p; | 
|  | 333 | *prev_pp = pp; | 
|  | 334 | prev_pp = &pp->next; | 
|  | 335 | } | 
|  | 336 | *p = _ALIGN((*p) + sz, 4); | 
|  | 337 | } | 
|  | 338 | /* with version 0x10 we may not have the name property, recreate | 
|  | 339 | * it here from the unit name if absent | 
|  | 340 | */ | 
|  | 341 | if (!has_name) { | 
|  | 342 | char *p1 = pathp, *ps = pathp, *pa = NULL; | 
|  | 343 | int sz; | 
|  | 344 |  | 
|  | 345 | while (*p1) { | 
|  | 346 | if ((*p1) == '@') | 
|  | 347 | pa = p1; | 
|  | 348 | if ((*p1) == '/') | 
|  | 349 | ps = p1 + 1; | 
|  | 350 | p1++; | 
|  | 351 | } | 
|  | 352 | if (pa < ps) | 
|  | 353 | pa = p1; | 
|  | 354 | sz = (pa - ps) + 1; | 
|  | 355 | pp = unflatten_dt_alloc(&mem, sizeof(struct property) + sz, | 
|  | 356 | __alignof__(struct property)); | 
|  | 357 | if (allnextpp) { | 
|  | 358 | pp->name = "name"; | 
|  | 359 | pp->length = sz; | 
|  | 360 | pp->value = pp + 1; | 
|  | 361 | *prev_pp = pp; | 
|  | 362 | prev_pp = &pp->next; | 
|  | 363 | memcpy(pp->value, ps, sz - 1); | 
|  | 364 | ((char *)pp->value)[sz - 1] = 0; | 
|  | 365 | pr_debug("fixed up name for %s -> %s\n", pathp, | 
|  | 366 | (char *)pp->value); | 
|  | 367 | } | 
|  | 368 | } | 
|  | 369 | if (allnextpp) { | 
|  | 370 | *prev_pp = NULL; | 
|  | 371 | np->name = of_get_property(np, "name", NULL); | 
|  | 372 | np->type = of_get_property(np, "device_type", NULL); | 
|  | 373 |  | 
|  | 374 | if (!np->name) | 
|  | 375 | np->name = "<NULL>"; | 
|  | 376 | if (!np->type) | 
|  | 377 | np->type = "<NULL>"; | 
|  | 378 | } | 
| Jason Gunthorpe | 7f809e1 | 2010-03-26 22:09:56 -0600 | [diff] [blame] | 379 | while (tag == OF_DT_BEGIN_NODE || tag == OF_DT_NOP) { | 
|  | 380 | if (tag == OF_DT_NOP) | 
|  | 381 | *p += 4; | 
|  | 382 | else | 
|  | 383 | mem = unflatten_dt_node(mem, p, np, allnextpp, fpsize); | 
| Jeremy Kerr | 3371488 | 2010-01-30 01:45:26 -0700 | [diff] [blame] | 384 | tag = be32_to_cpup((__be32 *)(*p)); | 
| Grant Likely | bbd3393 | 2009-11-23 20:07:00 -0700 | [diff] [blame] | 385 | } | 
|  | 386 | if (tag != OF_DT_END_NODE) { | 
|  | 387 | pr_err("Weird tag at end of node: %x\n", tag); | 
|  | 388 | return mem; | 
|  | 389 | } | 
|  | 390 | *p += 4; | 
|  | 391 | return mem; | 
|  | 392 | } | 
| Grant Likely | 41f8800 | 2009-11-23 20:07:01 -0700 | [diff] [blame] | 393 |  | 
| Grant Likely | f7b3a83 | 2009-11-24 03:26:58 -0700 | [diff] [blame] | 394 | #ifdef CONFIG_BLK_DEV_INITRD | 
|  | 395 | /** | 
|  | 396 | * early_init_dt_check_for_initrd - Decode initrd location from flat tree | 
|  | 397 | * @node: reference to node containing initrd location ('chosen') | 
|  | 398 | */ | 
|  | 399 | void __init early_init_dt_check_for_initrd(unsigned long node) | 
|  | 400 | { | 
| Jeremy Kerr | 1406bc2 | 2010-01-30 01:31:21 -0700 | [diff] [blame] | 401 | unsigned long start, end, len; | 
| Jeremy Kerr | 3371488 | 2010-01-30 01:45:26 -0700 | [diff] [blame] | 402 | __be32 *prop; | 
| Grant Likely | f7b3a83 | 2009-11-24 03:26:58 -0700 | [diff] [blame] | 403 |  | 
|  | 404 | pr_debug("Looking for initrd properties... "); | 
|  | 405 |  | 
|  | 406 | prop = of_get_flat_dt_prop(node, "linux,initrd-start", &len); | 
| Jeremy Kerr | 1406bc2 | 2010-01-30 01:31:21 -0700 | [diff] [blame] | 407 | if (!prop) | 
|  | 408 | return; | 
|  | 409 | start = of_read_ulong(prop, len/4); | 
| Grant Likely | f7b3a83 | 2009-11-24 03:26:58 -0700 | [diff] [blame] | 410 |  | 
| Jeremy Kerr | 1406bc2 | 2010-01-30 01:31:21 -0700 | [diff] [blame] | 411 | prop = of_get_flat_dt_prop(node, "linux,initrd-end", &len); | 
|  | 412 | if (!prop) | 
|  | 413 | return; | 
|  | 414 | end = of_read_ulong(prop, len/4); | 
| Grant Likely | f7b3a83 | 2009-11-24 03:26:58 -0700 | [diff] [blame] | 415 |  | 
| Jeremy Kerr | 1406bc2 | 2010-01-30 01:31:21 -0700 | [diff] [blame] | 416 | early_init_dt_setup_initrd_arch(start, end); | 
|  | 417 | pr_debug("initrd_start=0x%lx  initrd_end=0x%lx\n", start, end); | 
| Grant Likely | f7b3a83 | 2009-11-24 03:26:58 -0700 | [diff] [blame] | 418 | } | 
|  | 419 | #else | 
|  | 420 | inline void early_init_dt_check_for_initrd(unsigned long node) | 
|  | 421 | { | 
|  | 422 | } | 
|  | 423 | #endif /* CONFIG_BLK_DEV_INITRD */ | 
|  | 424 |  | 
| Grant Likely | 41f8800 | 2009-11-23 20:07:01 -0700 | [diff] [blame] | 425 | /** | 
| Grant Likely | f00abd9 | 2009-11-24 03:27:10 -0700 | [diff] [blame] | 426 | * early_init_dt_scan_root - fetch the top level address and size cells | 
|  | 427 | */ | 
|  | 428 | int __init early_init_dt_scan_root(unsigned long node, const char *uname, | 
|  | 429 | int depth, void *data) | 
|  | 430 | { | 
| Jeremy Kerr | 3371488 | 2010-01-30 01:45:26 -0700 | [diff] [blame] | 431 | __be32 *prop; | 
| Grant Likely | f00abd9 | 2009-11-24 03:27:10 -0700 | [diff] [blame] | 432 |  | 
|  | 433 | if (depth != 0) | 
|  | 434 | return 0; | 
|  | 435 |  | 
| Jeremy Kerr | 3371488 | 2010-01-30 01:45:26 -0700 | [diff] [blame] | 436 | dt_root_size_cells = OF_ROOT_NODE_SIZE_CELLS_DEFAULT; | 
|  | 437 | dt_root_addr_cells = OF_ROOT_NODE_ADDR_CELLS_DEFAULT; | 
|  | 438 |  | 
| Grant Likely | f00abd9 | 2009-11-24 03:27:10 -0700 | [diff] [blame] | 439 | prop = of_get_flat_dt_prop(node, "#size-cells", NULL); | 
| Jeremy Kerr | 3371488 | 2010-01-30 01:45:26 -0700 | [diff] [blame] | 440 | if (prop) | 
|  | 441 | dt_root_size_cells = be32_to_cpup(prop); | 
| Grant Likely | f00abd9 | 2009-11-24 03:27:10 -0700 | [diff] [blame] | 442 | pr_debug("dt_root_size_cells = %x\n", dt_root_size_cells); | 
|  | 443 |  | 
|  | 444 | prop = of_get_flat_dt_prop(node, "#address-cells", NULL); | 
| Jeremy Kerr | 3371488 | 2010-01-30 01:45:26 -0700 | [diff] [blame] | 445 | if (prop) | 
|  | 446 | dt_root_addr_cells = be32_to_cpup(prop); | 
| Grant Likely | f00abd9 | 2009-11-24 03:27:10 -0700 | [diff] [blame] | 447 | pr_debug("dt_root_addr_cells = %x\n", dt_root_addr_cells); | 
|  | 448 |  | 
|  | 449 | /* break now */ | 
|  | 450 | return 1; | 
|  | 451 | } | 
|  | 452 |  | 
| Jeremy Kerr | 2e89e68 | 2010-01-30 01:41:49 -0700 | [diff] [blame] | 453 | u64 __init dt_mem_next_cell(int s, __be32 **cellp) | 
| Grant Likely | 83f7a06 | 2009-11-24 03:37:56 -0700 | [diff] [blame] | 454 | { | 
| Jeremy Kerr | 2e89e68 | 2010-01-30 01:41:49 -0700 | [diff] [blame] | 455 | __be32 *p = *cellp; | 
| Grant Likely | 83f7a06 | 2009-11-24 03:37:56 -0700 | [diff] [blame] | 456 |  | 
|  | 457 | *cellp = p + s; | 
|  | 458 | return of_read_number(p, s); | 
|  | 459 | } | 
|  | 460 |  | 
| Grant Likely | 51975db | 2010-02-01 21:34:14 -0700 | [diff] [blame] | 461 | /** | 
|  | 462 | * early_init_dt_scan_memory - Look for an parse memory nodes | 
|  | 463 | */ | 
|  | 464 | int __init early_init_dt_scan_memory(unsigned long node, const char *uname, | 
|  | 465 | int depth, void *data) | 
|  | 466 | { | 
|  | 467 | char *type = of_get_flat_dt_prop(node, "device_type", NULL); | 
|  | 468 | __be32 *reg, *endp; | 
|  | 469 | unsigned long l; | 
|  | 470 |  | 
|  | 471 | /* We are scanning "memory" nodes only */ | 
|  | 472 | if (type == NULL) { | 
|  | 473 | /* | 
|  | 474 | * The longtrail doesn't have a device_type on the | 
|  | 475 | * /memory node, so look for the node called /memory@0. | 
|  | 476 | */ | 
|  | 477 | if (depth != 1 || strcmp(uname, "memory@0") != 0) | 
|  | 478 | return 0; | 
|  | 479 | } else if (strcmp(type, "memory") != 0) | 
|  | 480 | return 0; | 
|  | 481 |  | 
|  | 482 | reg = of_get_flat_dt_prop(node, "linux,usable-memory", &l); | 
|  | 483 | if (reg == NULL) | 
|  | 484 | reg = of_get_flat_dt_prop(node, "reg", &l); | 
|  | 485 | if (reg == NULL) | 
|  | 486 | return 0; | 
|  | 487 |  | 
|  | 488 | endp = reg + (l / sizeof(__be32)); | 
|  | 489 |  | 
|  | 490 | pr_debug("memory scan node %s, reg size %ld, data: %x %x %x %x,\n", | 
|  | 491 | uname, l, reg[0], reg[1], reg[2], reg[3]); | 
|  | 492 |  | 
|  | 493 | while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) { | 
|  | 494 | u64 base, size; | 
|  | 495 |  | 
|  | 496 | base = dt_mem_next_cell(dt_root_addr_cells, ®); | 
|  | 497 | size = dt_mem_next_cell(dt_root_size_cells, ®); | 
|  | 498 |  | 
|  | 499 | if (size == 0) | 
|  | 500 | continue; | 
|  | 501 | pr_debug(" - %llx ,  %llx\n", (unsigned long long)base, | 
|  | 502 | (unsigned long long)size); | 
|  | 503 |  | 
|  | 504 | early_init_dt_add_memory_arch(base, size); | 
|  | 505 | } | 
|  | 506 |  | 
|  | 507 | return 0; | 
|  | 508 | } | 
|  | 509 |  | 
| Grant Likely | 86e0322 | 2009-12-10 23:42:21 -0700 | [diff] [blame] | 510 | int __init early_init_dt_scan_chosen(unsigned long node, const char *uname, | 
|  | 511 | int depth, void *data) | 
|  | 512 | { | 
|  | 513 | unsigned long l; | 
|  | 514 | char *p; | 
|  | 515 |  | 
|  | 516 | pr_debug("search \"chosen\", depth: %d, uname: %s\n", depth, uname); | 
|  | 517 |  | 
|  | 518 | if (depth != 1 || | 
|  | 519 | (strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0)) | 
|  | 520 | return 0; | 
|  | 521 |  | 
|  | 522 | early_init_dt_check_for_initrd(node); | 
|  | 523 |  | 
|  | 524 | /* Retreive command line */ | 
|  | 525 | p = of_get_flat_dt_prop(node, "bootargs", &l); | 
|  | 526 | if (p != NULL && l > 0) | 
|  | 527 | strlcpy(cmd_line, p, min((int)l, COMMAND_LINE_SIZE)); | 
|  | 528 |  | 
|  | 529 | #ifdef CONFIG_CMDLINE | 
|  | 530 | #ifndef CONFIG_CMDLINE_FORCE | 
|  | 531 | if (p == NULL || l == 0 || (l == 1 && (*p) == 0)) | 
|  | 532 | #endif | 
|  | 533 | strlcpy(cmd_line, CONFIG_CMDLINE, COMMAND_LINE_SIZE); | 
|  | 534 | #endif /* CONFIG_CMDLINE */ | 
|  | 535 |  | 
|  | 536 | early_init_dt_scan_chosen_arch(node); | 
|  | 537 |  | 
|  | 538 | pr_debug("Command line is: %s\n", cmd_line); | 
|  | 539 |  | 
|  | 540 | /* break now */ | 
|  | 541 | return 1; | 
|  | 542 | } | 
|  | 543 |  | 
| Grant Likely | f00abd9 | 2009-11-24 03:27:10 -0700 | [diff] [blame] | 544 | /** | 
| Grant Likely | 41f8800 | 2009-11-23 20:07:01 -0700 | [diff] [blame] | 545 | * unflatten_device_tree - create tree of device_nodes from flat blob | 
|  | 546 | * | 
|  | 547 | * unflattens the device-tree passed by the firmware, creating the | 
|  | 548 | * tree of struct device_node. It also fills the "name" and "type" | 
|  | 549 | * pointers of the nodes so the normal device-tree walking functions | 
|  | 550 | * can be used. | 
|  | 551 | */ | 
|  | 552 | void __init unflatten_device_tree(void) | 
|  | 553 | { | 
|  | 554 | unsigned long start, mem, size; | 
|  | 555 | struct device_node **allnextp = &allnodes; | 
|  | 556 |  | 
|  | 557 | pr_debug(" -> unflatten_device_tree()\n"); | 
|  | 558 |  | 
| Grant Likely | 8bfe9b5 | 2010-04-13 16:12:26 -0700 | [diff] [blame] | 559 | if (!initial_boot_params) { | 
|  | 560 | pr_debug("No device tree pointer\n"); | 
|  | 561 | return; | 
|  | 562 | } | 
|  | 563 |  | 
|  | 564 | pr_debug("Unflattening device tree:\n"); | 
|  | 565 | pr_debug("magic: %08x\n", be32_to_cpu(initial_boot_params->magic)); | 
|  | 566 | pr_debug("size: %08x\n", be32_to_cpu(initial_boot_params->totalsize)); | 
|  | 567 | pr_debug("version: %08x\n", be32_to_cpu(initial_boot_params->version)); | 
|  | 568 |  | 
|  | 569 | if (be32_to_cpu(initial_boot_params->magic) != OF_DT_HEADER) { | 
|  | 570 | pr_err("Invalid device tree blob header\n"); | 
|  | 571 | return; | 
|  | 572 | } | 
|  | 573 |  | 
| Grant Likely | 41f8800 | 2009-11-23 20:07:01 -0700 | [diff] [blame] | 574 | /* First pass, scan for size */ | 
|  | 575 | start = ((unsigned long)initial_boot_params) + | 
| Jeremy Kerr | 087f79c | 2010-01-30 04:14:19 -0700 | [diff] [blame] | 576 | be32_to_cpu(initial_boot_params->off_dt_struct); | 
| Grant Likely | 41f8800 | 2009-11-23 20:07:01 -0700 | [diff] [blame] | 577 | size = unflatten_dt_node(0, &start, NULL, NULL, 0); | 
|  | 578 | size = (size | 3) + 1; | 
|  | 579 |  | 
|  | 580 | pr_debug("  size is %lx, allocating...\n", size); | 
|  | 581 |  | 
|  | 582 | /* Allocate memory for the expanded device tree */ | 
| Jeremy Kerr | 4ef7b37 | 2010-02-14 07:13:47 -0700 | [diff] [blame] | 583 | mem = early_init_dt_alloc_memory_arch(size + 4, | 
|  | 584 | __alignof__(struct device_node)); | 
| Grant Likely | 41f8800 | 2009-11-23 20:07:01 -0700 | [diff] [blame] | 585 | mem = (unsigned long) __va(mem); | 
|  | 586 |  | 
| Jeremy Kerr | 3371488 | 2010-01-30 01:45:26 -0700 | [diff] [blame] | 587 | ((__be32 *)mem)[size / 4] = cpu_to_be32(0xdeadbeef); | 
| Grant Likely | 41f8800 | 2009-11-23 20:07:01 -0700 | [diff] [blame] | 588 |  | 
|  | 589 | pr_debug("  unflattening %lx...\n", mem); | 
|  | 590 |  | 
|  | 591 | /* Second pass, do actual unflattening */ | 
|  | 592 | start = ((unsigned long)initial_boot_params) + | 
| Jeremy Kerr | 087f79c | 2010-01-30 04:14:19 -0700 | [diff] [blame] | 593 | be32_to_cpu(initial_boot_params->off_dt_struct); | 
| Grant Likely | 41f8800 | 2009-11-23 20:07:01 -0700 | [diff] [blame] | 594 | unflatten_dt_node(mem, &start, NULL, &allnextp, 0); | 
| Jeremy Kerr | 3371488 | 2010-01-30 01:45:26 -0700 | [diff] [blame] | 595 | if (be32_to_cpup((__be32 *)start) != OF_DT_END) | 
| Grant Likely | 41f8800 | 2009-11-23 20:07:01 -0700 | [diff] [blame] | 596 | pr_warning("Weird tag at end of tree: %08x\n", *((u32 *)start)); | 
| Jeremy Kerr | 3371488 | 2010-01-30 01:45:26 -0700 | [diff] [blame] | 597 | if (be32_to_cpu(((__be32 *)mem)[size / 4]) != 0xdeadbeef) | 
| Grant Likely | 41f8800 | 2009-11-23 20:07:01 -0700 | [diff] [blame] | 598 | pr_warning("End of tree marker overwritten: %08x\n", | 
| Jeremy Kerr | 3371488 | 2010-01-30 01:45:26 -0700 | [diff] [blame] | 599 | be32_to_cpu(((__be32 *)mem)[size / 4])); | 
| Grant Likely | 41f8800 | 2009-11-23 20:07:01 -0700 | [diff] [blame] | 600 | *allnextp = NULL; | 
|  | 601 |  | 
|  | 602 | /* Get pointer to OF "/chosen" node for use everywhere */ | 
|  | 603 | of_chosen = of_find_node_by_path("/chosen"); | 
|  | 604 | if (of_chosen == NULL) | 
|  | 605 | of_chosen = of_find_node_by_path("/chosen@0"); | 
|  | 606 |  | 
|  | 607 | pr_debug(" <- unflatten_device_tree()\n"); | 
|  | 608 | } |