1e169cfbeSGrant Likely /* 2e169cfbeSGrant Likely * Functions for working with the Flattened Device Tree data format 3e169cfbeSGrant Likely * 4e169cfbeSGrant Likely * Copyright 2009 Benjamin Herrenschmidt, IBM Corp 5e169cfbeSGrant Likely * benh@kernel.crashing.org 6e169cfbeSGrant Likely * 7e169cfbeSGrant Likely * This program is free software; you can redistribute it and/or 8e169cfbeSGrant Likely * modify it under the terms of the GNU General Public License 9e169cfbeSGrant Likely * version 2 as published by the Free Software Foundation. 10e169cfbeSGrant Likely */ 11e169cfbeSGrant Likely 12e169cfbeSGrant Likely #include <linux/of.h> 13e169cfbeSGrant Likely #include <linux/of_fdt.h> 14e169cfbeSGrant Likely 15e169cfbeSGrant Likely struct boot_param_header *initial_boot_params; 16e169cfbeSGrant Likely 17e169cfbeSGrant Likely char *find_flat_dt_string(u32 offset) 18e169cfbeSGrant Likely { 19e169cfbeSGrant Likely return ((char *)initial_boot_params) + 20e169cfbeSGrant Likely initial_boot_params->off_dt_strings + offset; 21e169cfbeSGrant Likely } 22c8cb7a59SGrant Likely 23c8cb7a59SGrant Likely /** 24c8cb7a59SGrant Likely * of_scan_flat_dt - scan flattened tree blob and call callback on each. 25c8cb7a59SGrant Likely * @it: callback function 26c8cb7a59SGrant Likely * @data: context data pointer 27c8cb7a59SGrant Likely * 28c8cb7a59SGrant Likely * This function is used to scan the flattened device-tree, it is 29c8cb7a59SGrant Likely * used to extract the memory information at boot before we can 30c8cb7a59SGrant Likely * unflatten the tree 31c8cb7a59SGrant Likely */ 32c8cb7a59SGrant Likely int __init of_scan_flat_dt(int (*it)(unsigned long node, 33c8cb7a59SGrant Likely const char *uname, int depth, 34c8cb7a59SGrant Likely void *data), 35c8cb7a59SGrant Likely void *data) 36c8cb7a59SGrant Likely { 37c8cb7a59SGrant Likely unsigned long p = ((unsigned long)initial_boot_params) + 38c8cb7a59SGrant Likely initial_boot_params->off_dt_struct; 39c8cb7a59SGrant Likely int rc = 0; 40c8cb7a59SGrant Likely int depth = -1; 41c8cb7a59SGrant Likely 42c8cb7a59SGrant Likely do { 43c8cb7a59SGrant Likely u32 tag = *((u32 *)p); 44c8cb7a59SGrant Likely char *pathp; 45c8cb7a59SGrant Likely 46c8cb7a59SGrant Likely p += 4; 47c8cb7a59SGrant Likely if (tag == OF_DT_END_NODE) { 48c8cb7a59SGrant Likely depth--; 49c8cb7a59SGrant Likely continue; 50c8cb7a59SGrant Likely } 51c8cb7a59SGrant Likely if (tag == OF_DT_NOP) 52c8cb7a59SGrant Likely continue; 53c8cb7a59SGrant Likely if (tag == OF_DT_END) 54c8cb7a59SGrant Likely break; 55c8cb7a59SGrant Likely if (tag == OF_DT_PROP) { 56c8cb7a59SGrant Likely u32 sz = *((u32 *)p); 57c8cb7a59SGrant Likely p += 8; 58c8cb7a59SGrant Likely if (initial_boot_params->version < 0x10) 59c8cb7a59SGrant Likely p = _ALIGN(p, sz >= 8 ? 8 : 4); 60c8cb7a59SGrant Likely p += sz; 61c8cb7a59SGrant Likely p = _ALIGN(p, 4); 62c8cb7a59SGrant Likely continue; 63c8cb7a59SGrant Likely } 64c8cb7a59SGrant Likely if (tag != OF_DT_BEGIN_NODE) { 65c8cb7a59SGrant Likely pr_err("Invalid tag %x in flat device tree!\n", tag); 66c8cb7a59SGrant Likely return -EINVAL; 67c8cb7a59SGrant Likely } 68c8cb7a59SGrant Likely depth++; 69c8cb7a59SGrant Likely pathp = (char *)p; 70c8cb7a59SGrant Likely p = _ALIGN(p + strlen(pathp) + 1, 4); 71c8cb7a59SGrant Likely if ((*pathp) == '/') { 72c8cb7a59SGrant Likely char *lp, *np; 73c8cb7a59SGrant Likely for (lp = NULL, np = pathp; *np; np++) 74c8cb7a59SGrant Likely if ((*np) == '/') 75c8cb7a59SGrant Likely lp = np+1; 76c8cb7a59SGrant Likely if (lp != NULL) 77c8cb7a59SGrant Likely pathp = lp; 78c8cb7a59SGrant Likely } 79c8cb7a59SGrant Likely rc = it(p, pathp, depth, data); 80c8cb7a59SGrant Likely if (rc != 0) 81c8cb7a59SGrant Likely break; 82c8cb7a59SGrant Likely } while (1); 83c8cb7a59SGrant Likely 84c8cb7a59SGrant Likely return rc; 85c8cb7a59SGrant Likely } 86819d2819SGrant Likely 87819d2819SGrant Likely /** 88819d2819SGrant Likely * of_get_flat_dt_root - find the root node in the flat blob 89819d2819SGrant Likely */ 90819d2819SGrant Likely unsigned long __init of_get_flat_dt_root(void) 91819d2819SGrant Likely { 92819d2819SGrant Likely unsigned long p = ((unsigned long)initial_boot_params) + 93819d2819SGrant Likely initial_boot_params->off_dt_struct; 94819d2819SGrant Likely 95819d2819SGrant Likely while (*((u32 *)p) == OF_DT_NOP) 96819d2819SGrant Likely p += 4; 97819d2819SGrant Likely BUG_ON(*((u32 *)p) != OF_DT_BEGIN_NODE); 98819d2819SGrant Likely p += 4; 99819d2819SGrant Likely return _ALIGN(p + strlen((char *)p) + 1, 4); 100819d2819SGrant Likely } 101819d2819SGrant Likely 102ca900cfaSGrant Likely /** 103ca900cfaSGrant Likely * of_get_flat_dt_prop - Given a node in the flat blob, return the property ptr 104ca900cfaSGrant Likely * 105ca900cfaSGrant Likely * This function can be used within scan_flattened_dt callback to get 106ca900cfaSGrant Likely * access to properties 107ca900cfaSGrant Likely */ 108ca900cfaSGrant Likely void *__init of_get_flat_dt_prop(unsigned long node, const char *name, 109ca900cfaSGrant Likely unsigned long *size) 110ca900cfaSGrant Likely { 111ca900cfaSGrant Likely unsigned long p = node; 112ca900cfaSGrant Likely 113ca900cfaSGrant Likely do { 114ca900cfaSGrant Likely u32 tag = *((u32 *)p); 115ca900cfaSGrant Likely u32 sz, noff; 116ca900cfaSGrant Likely const char *nstr; 117ca900cfaSGrant Likely 118ca900cfaSGrant Likely p += 4; 119ca900cfaSGrant Likely if (tag == OF_DT_NOP) 120ca900cfaSGrant Likely continue; 121ca900cfaSGrant Likely if (tag != OF_DT_PROP) 122ca900cfaSGrant Likely return NULL; 123ca900cfaSGrant Likely 124ca900cfaSGrant Likely sz = *((u32 *)p); 125ca900cfaSGrant Likely noff = *((u32 *)(p + 4)); 126ca900cfaSGrant Likely p += 8; 127ca900cfaSGrant Likely if (initial_boot_params->version < 0x10) 128ca900cfaSGrant Likely p = _ALIGN(p, sz >= 8 ? 8 : 4); 129ca900cfaSGrant Likely 130ca900cfaSGrant Likely nstr = find_flat_dt_string(noff); 131ca900cfaSGrant Likely if (nstr == NULL) { 132ca900cfaSGrant Likely pr_warning("Can't find property index name !\n"); 133ca900cfaSGrant Likely return NULL; 134ca900cfaSGrant Likely } 135ca900cfaSGrant Likely if (strcmp(name, nstr) == 0) { 136ca900cfaSGrant Likely if (size) 137ca900cfaSGrant Likely *size = sz; 138ca900cfaSGrant Likely return (void *)p; 139ca900cfaSGrant Likely } 140ca900cfaSGrant Likely p += sz; 141ca900cfaSGrant Likely p = _ALIGN(p, 4); 142ca900cfaSGrant Likely } while (1); 143ca900cfaSGrant Likely } 144ca900cfaSGrant Likely 14500e38efdSGrant Likely /** 14600e38efdSGrant Likely * of_flat_dt_is_compatible - Return true if given node has compat in compatible list 14700e38efdSGrant Likely * @node: node to test 14800e38efdSGrant Likely * @compat: compatible string to compare with compatible list. 14900e38efdSGrant Likely */ 15000e38efdSGrant Likely int __init of_flat_dt_is_compatible(unsigned long node, const char *compat) 15100e38efdSGrant Likely { 15200e38efdSGrant Likely const char *cp; 15300e38efdSGrant Likely unsigned long cplen, l; 15400e38efdSGrant Likely 15500e38efdSGrant Likely cp = of_get_flat_dt_prop(node, "compatible", &cplen); 15600e38efdSGrant Likely if (cp == NULL) 15700e38efdSGrant Likely return 0; 15800e38efdSGrant Likely while (cplen > 0) { 15900e38efdSGrant Likely if (strncasecmp(cp, compat, strlen(compat)) == 0) 16000e38efdSGrant Likely return 1; 16100e38efdSGrant Likely l = strlen(cp) + 1; 16200e38efdSGrant Likely cp += l; 16300e38efdSGrant Likely cplen -= l; 16400e38efdSGrant Likely } 16500e38efdSGrant Likely 16600e38efdSGrant Likely return 0; 16700e38efdSGrant Likely } 16800e38efdSGrant Likely 169bbd33931SGrant Likely static void *__init unflatten_dt_alloc(unsigned long *mem, unsigned long size, 170bbd33931SGrant Likely unsigned long align) 171bbd33931SGrant Likely { 172bbd33931SGrant Likely void *res; 173bbd33931SGrant Likely 174bbd33931SGrant Likely *mem = _ALIGN(*mem, align); 175bbd33931SGrant Likely res = (void *)*mem; 176bbd33931SGrant Likely *mem += size; 177bbd33931SGrant Likely 178bbd33931SGrant Likely return res; 179bbd33931SGrant Likely } 180bbd33931SGrant Likely 181bbd33931SGrant Likely /** 182bbd33931SGrant Likely * unflatten_dt_node - Alloc and populate a device_node from the flat tree 183bbd33931SGrant Likely * @p: pointer to node in flat tree 184bbd33931SGrant Likely * @dad: Parent struct device_node 185bbd33931SGrant Likely * @allnextpp: pointer to ->allnext from last allocated device_node 186bbd33931SGrant Likely * @fpsize: Size of the node path up at the current depth. 187bbd33931SGrant Likely */ 188bbd33931SGrant Likely unsigned long __init unflatten_dt_node(unsigned long mem, 189bbd33931SGrant Likely unsigned long *p, 190bbd33931SGrant Likely struct device_node *dad, 191bbd33931SGrant Likely struct device_node ***allnextpp, 192bbd33931SGrant Likely unsigned long fpsize) 193bbd33931SGrant Likely { 194bbd33931SGrant Likely struct device_node *np; 195bbd33931SGrant Likely struct property *pp, **prev_pp = NULL; 196bbd33931SGrant Likely char *pathp; 197bbd33931SGrant Likely u32 tag; 198bbd33931SGrant Likely unsigned int l, allocl; 199bbd33931SGrant Likely int has_name = 0; 200bbd33931SGrant Likely int new_format = 0; 201bbd33931SGrant Likely 202bbd33931SGrant Likely tag = *((u32 *)(*p)); 203bbd33931SGrant Likely if (tag != OF_DT_BEGIN_NODE) { 204bbd33931SGrant Likely pr_err("Weird tag at start of node: %x\n", tag); 205bbd33931SGrant Likely return mem; 206bbd33931SGrant Likely } 207bbd33931SGrant Likely *p += 4; 208bbd33931SGrant Likely pathp = (char *)*p; 209bbd33931SGrant Likely l = allocl = strlen(pathp) + 1; 210bbd33931SGrant Likely *p = _ALIGN(*p + l, 4); 211bbd33931SGrant Likely 212bbd33931SGrant Likely /* version 0x10 has a more compact unit name here instead of the full 213bbd33931SGrant Likely * path. we accumulate the full path size using "fpsize", we'll rebuild 214bbd33931SGrant Likely * it later. We detect this because the first character of the name is 215bbd33931SGrant Likely * not '/'. 216bbd33931SGrant Likely */ 217bbd33931SGrant Likely if ((*pathp) != '/') { 218bbd33931SGrant Likely new_format = 1; 219bbd33931SGrant Likely if (fpsize == 0) { 220bbd33931SGrant Likely /* root node: special case. fpsize accounts for path 221bbd33931SGrant Likely * plus terminating zero. root node only has '/', so 222bbd33931SGrant Likely * fpsize should be 2, but we want to avoid the first 223bbd33931SGrant Likely * level nodes to have two '/' so we use fpsize 1 here 224bbd33931SGrant Likely */ 225bbd33931SGrant Likely fpsize = 1; 226bbd33931SGrant Likely allocl = 2; 227bbd33931SGrant Likely } else { 228bbd33931SGrant Likely /* account for '/' and path size minus terminal 0 229bbd33931SGrant Likely * already in 'l' 230bbd33931SGrant Likely */ 231bbd33931SGrant Likely fpsize += l; 232bbd33931SGrant Likely allocl = fpsize; 233bbd33931SGrant Likely } 234bbd33931SGrant Likely } 235bbd33931SGrant Likely 236bbd33931SGrant Likely np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + allocl, 237bbd33931SGrant Likely __alignof__(struct device_node)); 238bbd33931SGrant Likely if (allnextpp) { 239bbd33931SGrant Likely memset(np, 0, sizeof(*np)); 240bbd33931SGrant Likely np->full_name = ((char *)np) + sizeof(struct device_node); 241bbd33931SGrant Likely if (new_format) { 242bbd33931SGrant Likely char *fn = np->full_name; 243bbd33931SGrant Likely /* rebuild full path for new format */ 244bbd33931SGrant Likely if (dad && dad->parent) { 245bbd33931SGrant Likely strcpy(fn, dad->full_name); 246bbd33931SGrant Likely #ifdef DEBUG 247bbd33931SGrant Likely if ((strlen(fn) + l + 1) != allocl) { 248bbd33931SGrant Likely pr_debug("%s: p: %d, l: %d, a: %d\n", 249bbd33931SGrant Likely pathp, (int)strlen(fn), 250bbd33931SGrant Likely l, allocl); 251bbd33931SGrant Likely } 252bbd33931SGrant Likely #endif 253bbd33931SGrant Likely fn += strlen(fn); 254bbd33931SGrant Likely } 255bbd33931SGrant Likely *(fn++) = '/'; 256bbd33931SGrant Likely memcpy(fn, pathp, l); 257bbd33931SGrant Likely } else 258bbd33931SGrant Likely memcpy(np->full_name, pathp, l); 259bbd33931SGrant Likely prev_pp = &np->properties; 260bbd33931SGrant Likely **allnextpp = np; 261bbd33931SGrant Likely *allnextpp = &np->allnext; 262bbd33931SGrant Likely if (dad != NULL) { 263bbd33931SGrant Likely np->parent = dad; 264bbd33931SGrant Likely /* we temporarily use the next field as `last_child'*/ 265bbd33931SGrant Likely if (dad->next == NULL) 266bbd33931SGrant Likely dad->child = np; 267bbd33931SGrant Likely else 268bbd33931SGrant Likely dad->next->sibling = np; 269bbd33931SGrant Likely dad->next = np; 270bbd33931SGrant Likely } 271bbd33931SGrant Likely kref_init(&np->kref); 272bbd33931SGrant Likely } 273bbd33931SGrant Likely while (1) { 274bbd33931SGrant Likely u32 sz, noff; 275bbd33931SGrant Likely char *pname; 276bbd33931SGrant Likely 277bbd33931SGrant Likely tag = *((u32 *)(*p)); 278bbd33931SGrant Likely if (tag == OF_DT_NOP) { 279bbd33931SGrant Likely *p += 4; 280bbd33931SGrant Likely continue; 281bbd33931SGrant Likely } 282bbd33931SGrant Likely if (tag != OF_DT_PROP) 283bbd33931SGrant Likely break; 284bbd33931SGrant Likely *p += 4; 285bbd33931SGrant Likely sz = *((u32 *)(*p)); 286bbd33931SGrant Likely noff = *((u32 *)((*p) + 4)); 287bbd33931SGrant Likely *p += 8; 288bbd33931SGrant Likely if (initial_boot_params->version < 0x10) 289bbd33931SGrant Likely *p = _ALIGN(*p, sz >= 8 ? 8 : 4); 290bbd33931SGrant Likely 291bbd33931SGrant Likely pname = find_flat_dt_string(noff); 292bbd33931SGrant Likely if (pname == NULL) { 293bbd33931SGrant Likely pr_info("Can't find property name in list !\n"); 294bbd33931SGrant Likely break; 295bbd33931SGrant Likely } 296bbd33931SGrant Likely if (strcmp(pname, "name") == 0) 297bbd33931SGrant Likely has_name = 1; 298bbd33931SGrant Likely l = strlen(pname) + 1; 299bbd33931SGrant Likely pp = unflatten_dt_alloc(&mem, sizeof(struct property), 300bbd33931SGrant Likely __alignof__(struct property)); 301bbd33931SGrant Likely if (allnextpp) { 302bbd33931SGrant Likely if (strcmp(pname, "linux,phandle") == 0) { 303bbd33931SGrant Likely np->node = *((u32 *)*p); 304bbd33931SGrant Likely if (np->linux_phandle == 0) 305bbd33931SGrant Likely np->linux_phandle = np->node; 306bbd33931SGrant Likely } 307bbd33931SGrant Likely if (strcmp(pname, "ibm,phandle") == 0) 308bbd33931SGrant Likely np->linux_phandle = *((u32 *)*p); 309bbd33931SGrant Likely pp->name = pname; 310bbd33931SGrant Likely pp->length = sz; 311bbd33931SGrant Likely pp->value = (void *)*p; 312bbd33931SGrant Likely *prev_pp = pp; 313bbd33931SGrant Likely prev_pp = &pp->next; 314bbd33931SGrant Likely } 315bbd33931SGrant Likely *p = _ALIGN((*p) + sz, 4); 316bbd33931SGrant Likely } 317bbd33931SGrant Likely /* with version 0x10 we may not have the name property, recreate 318bbd33931SGrant Likely * it here from the unit name if absent 319bbd33931SGrant Likely */ 320bbd33931SGrant Likely if (!has_name) { 321bbd33931SGrant Likely char *p1 = pathp, *ps = pathp, *pa = NULL; 322bbd33931SGrant Likely int sz; 323bbd33931SGrant Likely 324bbd33931SGrant Likely while (*p1) { 325bbd33931SGrant Likely if ((*p1) == '@') 326bbd33931SGrant Likely pa = p1; 327bbd33931SGrant Likely if ((*p1) == '/') 328bbd33931SGrant Likely ps = p1 + 1; 329bbd33931SGrant Likely p1++; 330bbd33931SGrant Likely } 331bbd33931SGrant Likely if (pa < ps) 332bbd33931SGrant Likely pa = p1; 333bbd33931SGrant Likely sz = (pa - ps) + 1; 334bbd33931SGrant Likely pp = unflatten_dt_alloc(&mem, sizeof(struct property) + sz, 335bbd33931SGrant Likely __alignof__(struct property)); 336bbd33931SGrant Likely if (allnextpp) { 337bbd33931SGrant Likely pp->name = "name"; 338bbd33931SGrant Likely pp->length = sz; 339bbd33931SGrant Likely pp->value = pp + 1; 340bbd33931SGrant Likely *prev_pp = pp; 341bbd33931SGrant Likely prev_pp = &pp->next; 342bbd33931SGrant Likely memcpy(pp->value, ps, sz - 1); 343bbd33931SGrant Likely ((char *)pp->value)[sz - 1] = 0; 344bbd33931SGrant Likely pr_debug("fixed up name for %s -> %s\n", pathp, 345bbd33931SGrant Likely (char *)pp->value); 346bbd33931SGrant Likely } 347bbd33931SGrant Likely } 348bbd33931SGrant Likely if (allnextpp) { 349bbd33931SGrant Likely *prev_pp = NULL; 350bbd33931SGrant Likely np->name = of_get_property(np, "name", NULL); 351bbd33931SGrant Likely np->type = of_get_property(np, "device_type", NULL); 352bbd33931SGrant Likely 353bbd33931SGrant Likely if (!np->name) 354bbd33931SGrant Likely np->name = "<NULL>"; 355bbd33931SGrant Likely if (!np->type) 356bbd33931SGrant Likely np->type = "<NULL>"; 357bbd33931SGrant Likely } 358bbd33931SGrant Likely while (tag == OF_DT_BEGIN_NODE) { 359bbd33931SGrant Likely mem = unflatten_dt_node(mem, p, np, allnextpp, fpsize); 360bbd33931SGrant Likely tag = *((u32 *)(*p)); 361bbd33931SGrant Likely } 362bbd33931SGrant Likely if (tag != OF_DT_END_NODE) { 363bbd33931SGrant Likely pr_err("Weird tag at end of node: %x\n", tag); 364bbd33931SGrant Likely return mem; 365bbd33931SGrant Likely } 366bbd33931SGrant Likely *p += 4; 367bbd33931SGrant Likely return mem; 368bbd33931SGrant Likely } 369