17941b27bSPantelis Antoniou /* 27941b27bSPantelis Antoniou * Functions for dealing with DT resolution 37941b27bSPantelis Antoniou * 47941b27bSPantelis Antoniou * Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com> 57941b27bSPantelis Antoniou * Copyright (C) 2012 Texas Instruments Inc. 67941b27bSPantelis Antoniou * 77941b27bSPantelis Antoniou * This program is free software; you can redistribute it and/or 87941b27bSPantelis Antoniou * modify it under the terms of the GNU General Public License 97941b27bSPantelis Antoniou * version 2 as published by the Free Software Foundation. 107941b27bSPantelis Antoniou */ 117941b27bSPantelis Antoniou 12606ad42aSRob Herring #define pr_fmt(fmt) "OF: resolver: " fmt 13606ad42aSRob Herring 147941b27bSPantelis Antoniou #include <linux/kernel.h> 157941b27bSPantelis Antoniou #include <linux/module.h> 167941b27bSPantelis Antoniou #include <linux/of.h> 177941b27bSPantelis Antoniou #include <linux/of_device.h> 187941b27bSPantelis Antoniou #include <linux/string.h> 197941b27bSPantelis Antoniou #include <linux/ctype.h> 207941b27bSPantelis Antoniou #include <linux/errno.h> 217941b27bSPantelis Antoniou #include <linux/string.h> 227941b27bSPantelis Antoniou #include <linux/slab.h> 237941b27bSPantelis Antoniou 247941b27bSPantelis Antoniou /* illegal phandle value (set when unresolved) */ 257941b27bSPantelis Antoniou #define OF_PHANDLE_ILLEGAL 0xdeadbeef 267941b27bSPantelis Antoniou 277941b27bSPantelis Antoniou /** 287941b27bSPantelis Antoniou * Find a node with the give full name by recursively following any of 297941b27bSPantelis Antoniou * the child node links. 307941b27bSPantelis Antoniou */ 317941b27bSPantelis Antoniou static struct device_node *__of_find_node_by_full_name(struct device_node *node, 327941b27bSPantelis Antoniou const char *full_name) 337941b27bSPantelis Antoniou { 347941b27bSPantelis Antoniou struct device_node *child, *found; 357941b27bSPantelis Antoniou 369f27ede4SFrank Rowand if (!node) 377941b27bSPantelis Antoniou return NULL; 387941b27bSPantelis Antoniou 399f27ede4SFrank Rowand if (!of_node_cmp(node->full_name, full_name)) 4082f68756SAmitoj Kaur Chawla return of_node_get(node); 417941b27bSPantelis Antoniou 427941b27bSPantelis Antoniou for_each_child_of_node(node, child) { 437941b27bSPantelis Antoniou found = __of_find_node_by_full_name(child, full_name); 4482f68756SAmitoj Kaur Chawla if (found != NULL) { 4582f68756SAmitoj Kaur Chawla of_node_put(child); 467941b27bSPantelis Antoniou return found; 477941b27bSPantelis Antoniou } 4882f68756SAmitoj Kaur Chawla } 497941b27bSPantelis Antoniou 507941b27bSPantelis Antoniou return NULL; 517941b27bSPantelis Antoniou } 527941b27bSPantelis Antoniou 537941b27bSPantelis Antoniou /* 547941b27bSPantelis Antoniou * Find live tree's maximum phandle value. 557941b27bSPantelis Antoniou */ 567941b27bSPantelis Antoniou static phandle of_get_tree_max_phandle(void) 577941b27bSPantelis Antoniou { 587941b27bSPantelis Antoniou struct device_node *node; 597941b27bSPantelis Antoniou phandle phandle; 607941b27bSPantelis Antoniou unsigned long flags; 617941b27bSPantelis Antoniou 627941b27bSPantelis Antoniou raw_spin_lock_irqsave(&devtree_lock, flags); 637941b27bSPantelis Antoniou phandle = 0; 647941b27bSPantelis Antoniou for_each_of_allnodes(node) { 657941b27bSPantelis Antoniou if (node->phandle != OF_PHANDLE_ILLEGAL && 667941b27bSPantelis Antoniou node->phandle > phandle) 677941b27bSPantelis Antoniou phandle = node->phandle; 687941b27bSPantelis Antoniou } 697941b27bSPantelis Antoniou raw_spin_unlock_irqrestore(&devtree_lock, flags); 707941b27bSPantelis Antoniou 717941b27bSPantelis Antoniou return phandle; 727941b27bSPantelis Antoniou } 737941b27bSPantelis Antoniou 747941b27bSPantelis Antoniou /* 757941b27bSPantelis Antoniou * Adjust a subtree's phandle values by a given delta. 767941b27bSPantelis Antoniou */ 777941b27bSPantelis Antoniou static void __of_adjust_tree_phandles(struct device_node *node, 787941b27bSPantelis Antoniou int phandle_delta) 797941b27bSPantelis Antoniou { 807941b27bSPantelis Antoniou struct device_node *child; 817941b27bSPantelis Antoniou struct property *prop; 827941b27bSPantelis Antoniou phandle phandle; 837941b27bSPantelis Antoniou 847941b27bSPantelis Antoniou if (node->phandle != 0 && node->phandle != OF_PHANDLE_ILLEGAL) 857941b27bSPantelis Antoniou node->phandle += phandle_delta; 867941b27bSPantelis Antoniou 877941b27bSPantelis Antoniou for_each_property_of_node(node, prop) { 887941b27bSPantelis Antoniou 899f27ede4SFrank Rowand if (of_prop_cmp(prop->name, "phandle") && 909f27ede4SFrank Rowand of_prop_cmp(prop->name, "linux,phandle")) 917941b27bSPantelis Antoniou continue; 927941b27bSPantelis Antoniou 937941b27bSPantelis Antoniou if (prop->length < 4) 947941b27bSPantelis Antoniou continue; 957941b27bSPantelis Antoniou 967941b27bSPantelis Antoniou phandle = be32_to_cpup(prop->value); 97a67976ecSFrank Rowand if (phandle == OF_PHANDLE_ILLEGAL) 987941b27bSPantelis Antoniou continue; 997941b27bSPantelis Antoniou 1007941b27bSPantelis Antoniou *(uint32_t *)prop->value = cpu_to_be32(node->phandle); 1017941b27bSPantelis Antoniou } 1027941b27bSPantelis Antoniou 1037941b27bSPantelis Antoniou for_each_child_of_node(node, child) 1047941b27bSPantelis Antoniou __of_adjust_tree_phandles(child, phandle_delta); 1057941b27bSPantelis Antoniou } 1067941b27bSPantelis Antoniou 107da56d04cSPantelis Antoniou static int __of_adjust_phandle_ref(struct device_node *node, 108da56d04cSPantelis Antoniou struct property *rprop, int value) 1097941b27bSPantelis Antoniou { 1107941b27bSPantelis Antoniou phandle phandle; 1117941b27bSPantelis Antoniou struct device_node *refnode; 1127941b27bSPantelis Antoniou struct property *sprop; 1137941b27bSPantelis Antoniou char *propval, *propcur, *propend, *nodestr, *propstr, *s; 1147941b27bSPantelis Antoniou int offset, propcurlen; 1157941b27bSPantelis Antoniou int err = 0; 1167941b27bSPantelis Antoniou 1177941b27bSPantelis Antoniou propval = kmalloc(rprop->length, GFP_KERNEL); 11896d1c8e8SFrank Rowand if (!propval) 1197941b27bSPantelis Antoniou return -ENOMEM; 1207941b27bSPantelis Antoniou memcpy(propval, rprop->value, rprop->length); 1217941b27bSPantelis Antoniou 1227941b27bSPantelis Antoniou propend = propval + rprop->length; 1237941b27bSPantelis Antoniou for (propcur = propval; propcur < propend; propcur += propcurlen + 1) { 1247941b27bSPantelis Antoniou propcurlen = strlen(propcur); 1257941b27bSPantelis Antoniou 1267941b27bSPantelis Antoniou nodestr = propcur; 1277941b27bSPantelis Antoniou s = strchr(propcur, ':'); 1287941b27bSPantelis Antoniou if (!s) { 1297941b27bSPantelis Antoniou err = -EINVAL; 1307941b27bSPantelis Antoniou goto err_fail; 1317941b27bSPantelis Antoniou } 1327941b27bSPantelis Antoniou *s++ = '\0'; 1337941b27bSPantelis Antoniou 1347941b27bSPantelis Antoniou propstr = s; 1357941b27bSPantelis Antoniou s = strchr(s, ':'); 1367941b27bSPantelis Antoniou if (!s) { 1377941b27bSPantelis Antoniou err = -EINVAL; 1387941b27bSPantelis Antoniou goto err_fail; 1397941b27bSPantelis Antoniou } 1407941b27bSPantelis Antoniou 1417941b27bSPantelis Antoniou *s++ = '\0'; 1427941b27bSPantelis Antoniou err = kstrtoint(s, 10, &offset); 1439f27ede4SFrank Rowand if (err) 1447941b27bSPantelis Antoniou goto err_fail; 1457941b27bSPantelis Antoniou 1467941b27bSPantelis Antoniou refnode = __of_find_node_by_full_name(node, nodestr); 14796d1c8e8SFrank Rowand if (!refnode) 1487941b27bSPantelis Antoniou continue; 1497941b27bSPantelis Antoniou 1507941b27bSPantelis Antoniou for_each_property_of_node(refnode, sprop) { 1519f27ede4SFrank Rowand if (!of_prop_cmp(sprop->name, propstr)) 1527941b27bSPantelis Antoniou break; 1537941b27bSPantelis Antoniou } 15482f68756SAmitoj Kaur Chawla of_node_put(refnode); 1557941b27bSPantelis Antoniou 1567941b27bSPantelis Antoniou if (!sprop) { 1577941b27bSPantelis Antoniou err = -ENOENT; 1587941b27bSPantelis Antoniou goto err_fail; 1597941b27bSPantelis Antoniou } 1607941b27bSPantelis Antoniou 161da56d04cSPantelis Antoniou phandle = value; 1627941b27bSPantelis Antoniou *(__be32 *)(sprop->value + offset) = cpu_to_be32(phandle); 1637941b27bSPantelis Antoniou } 1647941b27bSPantelis Antoniou 1657941b27bSPantelis Antoniou err_fail: 1667941b27bSPantelis Antoniou kfree(propval); 1677941b27bSPantelis Antoniou return err; 1687941b27bSPantelis Antoniou } 1697941b27bSPantelis Antoniou 170da56d04cSPantelis Antoniou /* compare nodes taking into account that 'name' strips out the @ part */ 171da56d04cSPantelis Antoniou static int __of_node_name_cmp(const struct device_node *dn1, 172da56d04cSPantelis Antoniou const struct device_node *dn2) 173da56d04cSPantelis Antoniou { 174da56d04cSPantelis Antoniou const char *n1 = strrchr(dn1->full_name, '/') ? : "/"; 175da56d04cSPantelis Antoniou const char *n2 = strrchr(dn2->full_name, '/') ? : "/"; 176da56d04cSPantelis Antoniou 177da56d04cSPantelis Antoniou return of_node_cmp(n1, n2); 178da56d04cSPantelis Antoniou } 179da56d04cSPantelis Antoniou 1807941b27bSPantelis Antoniou /* 1817941b27bSPantelis Antoniou * Adjust the local phandle references by the given phandle delta. 182da56d04cSPantelis Antoniou * Assumes the existances of a __local_fixups__ node at the root. 183da56d04cSPantelis Antoniou * Assumes that __of_verify_tree_phandle_references has been called. 184da56d04cSPantelis Antoniou * Does not take any devtree locks so make sure you call this on a tree 185da56d04cSPantelis Antoniou * which is at the detached state. 1867941b27bSPantelis Antoniou */ 1877941b27bSPantelis Antoniou static int __of_adjust_tree_phandle_references(struct device_node *node, 188da56d04cSPantelis Antoniou struct device_node *target, int phandle_delta) 1897941b27bSPantelis Antoniou { 190da56d04cSPantelis Antoniou struct device_node *child, *childtarget; 191da56d04cSPantelis Antoniou struct property *rprop, *sprop; 192da56d04cSPantelis Antoniou int err, i, count; 193da56d04cSPantelis Antoniou unsigned int off; 194da56d04cSPantelis Antoniou phandle phandle; 1957941b27bSPantelis Antoniou 1969f27ede4SFrank Rowand if (!node) 1977941b27bSPantelis Antoniou return 0; 1987941b27bSPantelis Antoniou 199da56d04cSPantelis Antoniou for_each_property_of_node(node, rprop) { 200da56d04cSPantelis Antoniou 2017941b27bSPantelis Antoniou /* skip properties added automatically */ 2029f27ede4SFrank Rowand if (!of_prop_cmp(rprop->name, "name") || 2039f27ede4SFrank Rowand !of_prop_cmp(rprop->name, "phandle") || 2049f27ede4SFrank Rowand !of_prop_cmp(rprop->name, "linux,phandle")) 2057941b27bSPantelis Antoniou continue; 2067941b27bSPantelis Antoniou 20796d1c8e8SFrank Rowand if ((rprop->length % 4) != 0 || rprop->length == 0) 208da56d04cSPantelis Antoniou return -EINVAL; 209da56d04cSPantelis Antoniou count = rprop->length / sizeof(__be32); 210da56d04cSPantelis Antoniou 211da56d04cSPantelis Antoniou for_each_property_of_node(target, sprop) { 2129f27ede4SFrank Rowand if (!of_prop_cmp(sprop->name, rprop->name)) 213da56d04cSPantelis Antoniou break; 214da56d04cSPantelis Antoniou } 215da56d04cSPantelis Antoniou 2169f27ede4SFrank Rowand if (!sprop) 217da56d04cSPantelis Antoniou return -EINVAL; 218da56d04cSPantelis Antoniou 219da56d04cSPantelis Antoniou for (i = 0; i < count; i++) { 220da56d04cSPantelis Antoniou off = be32_to_cpu(((__be32 *)rprop->value)[i]); 22196d1c8e8SFrank Rowand if (off >= sprop->length || (off + 4) > sprop->length) 222da56d04cSPantelis Antoniou return -EINVAL; 223da56d04cSPantelis Antoniou 224da56d04cSPantelis Antoniou if (phandle_delta) { 225da56d04cSPantelis Antoniou phandle = be32_to_cpu(*(__be32 *)(sprop->value + off)); 226da56d04cSPantelis Antoniou phandle += phandle_delta; 227da56d04cSPantelis Antoniou *(__be32 *)(sprop->value + off) = cpu_to_be32(phandle); 228da56d04cSPantelis Antoniou } 229da56d04cSPantelis Antoniou } 230da56d04cSPantelis Antoniou } 231da56d04cSPantelis Antoniou 232da56d04cSPantelis Antoniou for_each_child_of_node(node, child) { 233da56d04cSPantelis Antoniou 234da56d04cSPantelis Antoniou for_each_child_of_node(target, childtarget) 2359f27ede4SFrank Rowand if (!__of_node_name_cmp(child, childtarget)) 236da56d04cSPantelis Antoniou break; 237da56d04cSPantelis Antoniou 23896d1c8e8SFrank Rowand if (!childtarget) 239da56d04cSPantelis Antoniou return -EINVAL; 240da56d04cSPantelis Antoniou 241da56d04cSPantelis Antoniou err = __of_adjust_tree_phandle_references(child, childtarget, 242da56d04cSPantelis Antoniou phandle_delta); 2439f27ede4SFrank Rowand if (err) 2447941b27bSPantelis Antoniou return err; 2457941b27bSPantelis Antoniou } 2467941b27bSPantelis Antoniou 2477941b27bSPantelis Antoniou return 0; 2487941b27bSPantelis Antoniou } 2497941b27bSPantelis Antoniou 2507941b27bSPantelis Antoniou /** 2517941b27bSPantelis Antoniou * of_resolve - Resolve the given node against the live tree. 2527941b27bSPantelis Antoniou * 2537941b27bSPantelis Antoniou * @resolve: Node to resolve 2547941b27bSPantelis Antoniou * 2557941b27bSPantelis Antoniou * Perform dynamic Device Tree resolution against the live tree 2567941b27bSPantelis Antoniou * to the given node to resolve. This depends on the live tree 2577941b27bSPantelis Antoniou * having a __symbols__ node, and the resolve node the __fixups__ & 2587941b27bSPantelis Antoniou * __local_fixups__ nodes (if needed). 2597941b27bSPantelis Antoniou * The result of the operation is a resolve node that it's contents 2607941b27bSPantelis Antoniou * are fit to be inserted or operate upon the live tree. 2617941b27bSPantelis Antoniou * Returns 0 on success or a negative error value on error. 2627941b27bSPantelis Antoniou */ 2637941b27bSPantelis Antoniou int of_resolve_phandles(struct device_node *resolve) 2647941b27bSPantelis Antoniou { 265da56d04cSPantelis Antoniou struct device_node *child, *childroot, *refnode; 2667941b27bSPantelis Antoniou struct device_node *root_sym, *resolve_sym, *resolve_fix; 2677941b27bSPantelis Antoniou struct property *rprop; 2687941b27bSPantelis Antoniou const char *refpath; 2697941b27bSPantelis Antoniou phandle phandle, phandle_delta; 2707941b27bSPantelis Antoniou int err; 2717941b27bSPantelis Antoniou 2725de3bbc8SMichal Suchanek if (!resolve) 2735de3bbc8SMichal Suchanek pr_err("%s: null node\n", __func__); 2745de3bbc8SMichal Suchanek if (resolve && !of_node_check_flag(resolve, OF_DETACHED)) 2755de3bbc8SMichal Suchanek pr_err("%s: node %s not detached\n", __func__, 2765de3bbc8SMichal Suchanek resolve->full_name); 2777941b27bSPantelis Antoniou if (!resolve || !of_node_check_flag(resolve, OF_DETACHED)) 2787941b27bSPantelis Antoniou return -EINVAL; 2797941b27bSPantelis Antoniou 2807941b27bSPantelis Antoniou phandle_delta = of_get_tree_max_phandle() + 1; 2817941b27bSPantelis Antoniou __of_adjust_tree_phandles(resolve, phandle_delta); 282da56d04cSPantelis Antoniou 283da56d04cSPantelis Antoniou childroot = NULL; 284da56d04cSPantelis Antoniou for_each_child_of_node(resolve, childroot) 2859f27ede4SFrank Rowand if (!of_node_cmp(childroot->name, "__local_fixups__")) 286da56d04cSPantelis Antoniou break; 287da56d04cSPantelis Antoniou 288da56d04cSPantelis Antoniou if (childroot != NULL) { 289da56d04cSPantelis Antoniou err = __of_adjust_tree_phandle_references(childroot, 290da56d04cSPantelis Antoniou resolve, 0); 2919f27ede4SFrank Rowand if (err) 2927941b27bSPantelis Antoniou return err; 2937941b27bSPantelis Antoniou 294da56d04cSPantelis Antoniou BUG_ON(__of_adjust_tree_phandle_references(childroot, 295da56d04cSPantelis Antoniou resolve, phandle_delta)); 296da56d04cSPantelis Antoniou } 297da56d04cSPantelis Antoniou 2987941b27bSPantelis Antoniou root_sym = NULL; 2997941b27bSPantelis Antoniou resolve_sym = NULL; 3007941b27bSPantelis Antoniou resolve_fix = NULL; 3017941b27bSPantelis Antoniou 3027941b27bSPantelis Antoniou root_sym = of_find_node_by_path("/__symbols__"); 3037941b27bSPantelis Antoniou 3047941b27bSPantelis Antoniou for_each_child_of_node(resolve, child) { 3057941b27bSPantelis Antoniou 3069f27ede4SFrank Rowand if (!resolve_sym && !of_node_cmp(child->name, "__symbols__")) 3077941b27bSPantelis Antoniou resolve_sym = child; 3087941b27bSPantelis Antoniou 3099f27ede4SFrank Rowand if (!resolve_fix && !of_node_cmp(child->name, "__fixups__")) 3107941b27bSPantelis Antoniou resolve_fix = child; 3117941b27bSPantelis Antoniou 3127941b27bSPantelis Antoniou if (resolve_sym && resolve_fix) 3137941b27bSPantelis Antoniou break; 3147941b27bSPantelis Antoniou } 3157941b27bSPantelis Antoniou 3167941b27bSPantelis Antoniou if (!resolve_fix) { 317a67976ecSFrank Rowand err = 0; 3187941b27bSPantelis Antoniou goto out; 3197941b27bSPantelis Antoniou } 3207941b27bSPantelis Antoniou 3217941b27bSPantelis Antoniou if (!root_sym) { 3225de3bbc8SMichal Suchanek pr_err("%s: no symbols in root of device tree.\n", __func__); 3237941b27bSPantelis Antoniou err = -EINVAL; 3247941b27bSPantelis Antoniou goto out; 3257941b27bSPantelis Antoniou } 3267941b27bSPantelis Antoniou 3277941b27bSPantelis Antoniou for_each_property_of_node(resolve_fix, rprop) { 3287941b27bSPantelis Antoniou 3297941b27bSPantelis Antoniou /* skip properties added automatically */ 3309f27ede4SFrank Rowand if (!of_prop_cmp(rprop->name, "name")) 3317941b27bSPantelis Antoniou continue; 3327941b27bSPantelis Antoniou 3337941b27bSPantelis Antoniou err = of_property_read_string(root_sym, 3347941b27bSPantelis Antoniou rprop->name, &refpath); 3359f27ede4SFrank Rowand if (err) 3367941b27bSPantelis Antoniou goto out; 3377941b27bSPantelis Antoniou 3387941b27bSPantelis Antoniou refnode = of_find_node_by_path(refpath); 3397941b27bSPantelis Antoniou if (!refnode) { 3407941b27bSPantelis Antoniou err = -ENOENT; 3417941b27bSPantelis Antoniou goto out; 3427941b27bSPantelis Antoniou } 3437941b27bSPantelis Antoniou 3447941b27bSPantelis Antoniou phandle = refnode->phandle; 3457941b27bSPantelis Antoniou of_node_put(refnode); 3467941b27bSPantelis Antoniou 347da56d04cSPantelis Antoniou err = __of_adjust_phandle_ref(resolve, rprop, phandle); 3487941b27bSPantelis Antoniou if (err) 3497941b27bSPantelis Antoniou break; 3507941b27bSPantelis Antoniou } 3517941b27bSPantelis Antoniou 3527941b27bSPantelis Antoniou out: 3537941b27bSPantelis Antoniou of_node_put(root_sym); 3547941b27bSPantelis Antoniou 3557941b27bSPantelis Antoniou return err; 3567941b27bSPantelis Antoniou } 3577941b27bSPantelis Antoniou EXPORT_SYMBOL_GPL(of_resolve_phandles); 358