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 */ 31fad556bfSFrank Rowand static struct device_node *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) { 43fad556bfSFrank Rowand found = 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 */ 56f94823f2SFrank Rowand static phandle live_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 */ 7725e16877SFrank Rowand static void adjust_overlay_phandles(struct device_node *overlay, 787941b27bSPantelis Antoniou int phandle_delta) 797941b27bSPantelis Antoniou { 807941b27bSPantelis Antoniou struct device_node *child; 817941b27bSPantelis Antoniou struct property *prop; 827941b27bSPantelis Antoniou phandle phandle; 837941b27bSPantelis Antoniou 8425e16877SFrank Rowand if (overlay->phandle != 0 && overlay->phandle != OF_PHANDLE_ILLEGAL) 8525e16877SFrank Rowand overlay->phandle += phandle_delta; 867941b27bSPantelis Antoniou 8725e16877SFrank Rowand for_each_property_of_node(overlay, 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 10025e16877SFrank Rowand *(uint32_t *)prop->value = cpu_to_be32(overlay->phandle); 1017941b27bSPantelis Antoniou } 1027941b27bSPantelis Antoniou 10325e16877SFrank Rowand for_each_child_of_node(overlay, child) 104f94823f2SFrank Rowand adjust_overlay_phandles(child, phandle_delta); 1057941b27bSPantelis Antoniou } 1067941b27bSPantelis Antoniou 10725e16877SFrank Rowand static int update_usages_of_a_phandle_reference(struct device_node *overlay, 10825e16877SFrank Rowand struct property *prop_fixup, phandle phandle) 1097941b27bSPantelis Antoniou { 1107941b27bSPantelis Antoniou struct device_node *refnode; 11125e16877SFrank Rowand struct property *prop; 11225e16877SFrank Rowand char *value, *cur, *end, *node_path, *prop_name, *s; 11325e16877SFrank Rowand int offset, len; 1147941b27bSPantelis Antoniou int err = 0; 1157941b27bSPantelis Antoniou 11625e16877SFrank Rowand value = kmalloc(prop_fixup->length, GFP_KERNEL); 11725e16877SFrank Rowand if (!value) 1187941b27bSPantelis Antoniou return -ENOMEM; 11925e16877SFrank Rowand memcpy(value, prop_fixup->value, prop_fixup->length); 1207941b27bSPantelis Antoniou 12125e16877SFrank Rowand end = value + prop_fixup->length; 12225e16877SFrank Rowand for (cur = value; cur < end; cur += len + 1) { 12325e16877SFrank Rowand len = strlen(cur); 1247941b27bSPantelis Antoniou 12525e16877SFrank Rowand node_path = cur; 12625e16877SFrank Rowand s = strchr(cur, ':'); 1277941b27bSPantelis Antoniou if (!s) { 1287941b27bSPantelis Antoniou err = -EINVAL; 1297941b27bSPantelis Antoniou goto err_fail; 1307941b27bSPantelis Antoniou } 1317941b27bSPantelis Antoniou *s++ = '\0'; 1327941b27bSPantelis Antoniou 13325e16877SFrank Rowand prop_name = s; 1347941b27bSPantelis Antoniou s = strchr(s, ':'); 1357941b27bSPantelis Antoniou if (!s) { 1367941b27bSPantelis Antoniou err = -EINVAL; 1377941b27bSPantelis Antoniou goto err_fail; 1387941b27bSPantelis Antoniou } 1397941b27bSPantelis Antoniou 1407941b27bSPantelis Antoniou *s++ = '\0'; 1417941b27bSPantelis Antoniou err = kstrtoint(s, 10, &offset); 1429f27ede4SFrank Rowand if (err) 1437941b27bSPantelis Antoniou goto err_fail; 1447941b27bSPantelis Antoniou 14525e16877SFrank Rowand refnode = find_node_by_full_name(overlay, node_path); 14696d1c8e8SFrank Rowand if (!refnode) 1477941b27bSPantelis Antoniou continue; 1487941b27bSPantelis Antoniou 14925e16877SFrank Rowand for_each_property_of_node(refnode, prop) { 15025e16877SFrank Rowand if (!of_prop_cmp(prop->name, prop_name)) 1517941b27bSPantelis Antoniou break; 1527941b27bSPantelis Antoniou } 15382f68756SAmitoj Kaur Chawla of_node_put(refnode); 1547941b27bSPantelis Antoniou 15525e16877SFrank Rowand if (!prop) { 1567941b27bSPantelis Antoniou err = -ENOENT; 1577941b27bSPantelis Antoniou goto err_fail; 1587941b27bSPantelis Antoniou } 1597941b27bSPantelis Antoniou 16025e16877SFrank Rowand *(__be32 *)(prop->value + offset) = cpu_to_be32(phandle); 1617941b27bSPantelis Antoniou } 1627941b27bSPantelis Antoniou 1637941b27bSPantelis Antoniou err_fail: 16425e16877SFrank Rowand kfree(value); 1657941b27bSPantelis Antoniou return err; 1667941b27bSPantelis Antoniou } 1677941b27bSPantelis Antoniou 168da56d04cSPantelis Antoniou /* compare nodes taking into account that 'name' strips out the @ part */ 169fad556bfSFrank Rowand static int node_name_cmp(const struct device_node *dn1, 170da56d04cSPantelis Antoniou const struct device_node *dn2) 171da56d04cSPantelis Antoniou { 172da56d04cSPantelis Antoniou const char *n1 = strrchr(dn1->full_name, '/') ? : "/"; 173da56d04cSPantelis Antoniou const char *n2 = strrchr(dn2->full_name, '/') ? : "/"; 174da56d04cSPantelis Antoniou 175da56d04cSPantelis Antoniou return of_node_cmp(n1, n2); 176da56d04cSPantelis Antoniou } 177da56d04cSPantelis Antoniou 1787941b27bSPantelis Antoniou /* 1797941b27bSPantelis Antoniou * Adjust the local phandle references by the given phandle delta. 180da56d04cSPantelis Antoniou * Assumes the existances of a __local_fixups__ node at the root. 181da56d04cSPantelis Antoniou * Assumes that __of_verify_tree_phandle_references has been called. 182da56d04cSPantelis Antoniou * Does not take any devtree locks so make sure you call this on a tree 183da56d04cSPantelis Antoniou * which is at the detached state. 1847941b27bSPantelis Antoniou */ 18525e16877SFrank Rowand static int adjust_local_phandle_references(struct device_node *local_fixups, 18625e16877SFrank Rowand struct device_node *overlay, int phandle_delta) 1877941b27bSPantelis Antoniou { 18825e16877SFrank Rowand struct device_node *child, *overlay_child; 18925e16877SFrank Rowand struct property *prop_fix, *prop; 190da56d04cSPantelis Antoniou int err, i, count; 191da56d04cSPantelis Antoniou unsigned int off; 192da56d04cSPantelis Antoniou phandle phandle; 1937941b27bSPantelis Antoniou 19425e16877SFrank Rowand if (!local_fixups) 1957941b27bSPantelis Antoniou return 0; 1967941b27bSPantelis Antoniou 19725e16877SFrank Rowand for_each_property_of_node(local_fixups, prop_fix) { 198da56d04cSPantelis Antoniou 1997941b27bSPantelis Antoniou /* skip properties added automatically */ 20025e16877SFrank Rowand if (!of_prop_cmp(prop_fix->name, "name") || 20125e16877SFrank Rowand !of_prop_cmp(prop_fix->name, "phandle") || 20225e16877SFrank Rowand !of_prop_cmp(prop_fix->name, "linux,phandle")) 2037941b27bSPantelis Antoniou continue; 2047941b27bSPantelis Antoniou 20525e16877SFrank Rowand if ((prop_fix->length % 4) != 0 || prop_fix->length == 0) 206da56d04cSPantelis Antoniou return -EINVAL; 20725e16877SFrank Rowand count = prop_fix->length / sizeof(__be32); 208da56d04cSPantelis Antoniou 20925e16877SFrank Rowand for_each_property_of_node(overlay, prop) { 21025e16877SFrank Rowand if (!of_prop_cmp(prop->name, prop_fix->name)) 211da56d04cSPantelis Antoniou break; 212da56d04cSPantelis Antoniou } 213da56d04cSPantelis Antoniou 21425e16877SFrank Rowand if (!prop) 215da56d04cSPantelis Antoniou return -EINVAL; 216da56d04cSPantelis Antoniou 217da56d04cSPantelis Antoniou for (i = 0; i < count; i++) { 21825e16877SFrank Rowand off = be32_to_cpu(((__be32 *)prop_fix->value)[i]); 21925e16877SFrank Rowand if (off >= prop->length || (off + 4) > prop->length) 220da56d04cSPantelis Antoniou return -EINVAL; 221da56d04cSPantelis Antoniou 222da56d04cSPantelis Antoniou if (phandle_delta) { 22325e16877SFrank Rowand phandle = be32_to_cpu(*(__be32 *)(prop->value + off)); 224da56d04cSPantelis Antoniou phandle += phandle_delta; 22525e16877SFrank Rowand *(__be32 *)(prop->value + off) = cpu_to_be32(phandle); 226da56d04cSPantelis Antoniou } 227da56d04cSPantelis Antoniou } 228da56d04cSPantelis Antoniou } 229da56d04cSPantelis Antoniou 23025e16877SFrank Rowand for_each_child_of_node(local_fixups, child) { 231da56d04cSPantelis Antoniou 23225e16877SFrank Rowand for_each_child_of_node(overlay, overlay_child) 23325e16877SFrank Rowand if (!node_name_cmp(child, overlay_child)) 234da56d04cSPantelis Antoniou break; 235da56d04cSPantelis Antoniou 23625e16877SFrank Rowand if (!overlay_child) 237da56d04cSPantelis Antoniou return -EINVAL; 238da56d04cSPantelis Antoniou 23925e16877SFrank Rowand err = adjust_local_phandle_references(child, overlay_child, 240da56d04cSPantelis Antoniou phandle_delta); 2419f27ede4SFrank Rowand if (err) 2427941b27bSPantelis Antoniou return err; 2437941b27bSPantelis Antoniou } 2447941b27bSPantelis Antoniou 2457941b27bSPantelis Antoniou return 0; 2467941b27bSPantelis Antoniou } 2477941b27bSPantelis Antoniou 2487941b27bSPantelis Antoniou /** 2497941b27bSPantelis Antoniou * of_resolve - Resolve the given node against the live tree. 2507941b27bSPantelis Antoniou * 2517941b27bSPantelis Antoniou * @resolve: Node to resolve 2527941b27bSPantelis Antoniou * 2537941b27bSPantelis Antoniou * Perform dynamic Device Tree resolution against the live tree 2547941b27bSPantelis Antoniou * to the given node to resolve. This depends on the live tree 2557941b27bSPantelis Antoniou * having a __symbols__ node, and the resolve node the __fixups__ & 2567941b27bSPantelis Antoniou * __local_fixups__ nodes (if needed). 2577941b27bSPantelis Antoniou * The result of the operation is a resolve node that it's contents 2587941b27bSPantelis Antoniou * are fit to be inserted or operate upon the live tree. 2597941b27bSPantelis Antoniou * Returns 0 on success or a negative error value on error. 2607941b27bSPantelis Antoniou */ 26125e16877SFrank Rowand int of_resolve_phandles(struct device_node *overlay) 2627941b27bSPantelis Antoniou { 26325e16877SFrank Rowand struct device_node *child, *local_fixups, *refnode; 26425e16877SFrank Rowand struct device_node *tree_symbols, *overlay_symbols, *overlay_fixups; 26525e16877SFrank Rowand struct property *prop; 2667941b27bSPantelis Antoniou const char *refpath; 2677941b27bSPantelis Antoniou phandle phandle, phandle_delta; 2687941b27bSPantelis Antoniou int err; 2697941b27bSPantelis Antoniou 27025e16877SFrank Rowand if (!overlay) 27125e16877SFrank Rowand pr_err("%s: null overlay\n", __func__); 27225e16877SFrank Rowand if (overlay && !of_node_check_flag(overlay, OF_DETACHED)) 2735de3bbc8SMichal Suchanek pr_err("%s: node %s not detached\n", __func__, 27425e16877SFrank Rowand overlay->full_name); 27525e16877SFrank Rowand if (!overlay || !of_node_check_flag(overlay, OF_DETACHED)) 2767941b27bSPantelis Antoniou return -EINVAL; 2777941b27bSPantelis Antoniou 278f94823f2SFrank Rowand phandle_delta = live_tree_max_phandle() + 1; 27925e16877SFrank Rowand adjust_overlay_phandles(overlay, phandle_delta); 280da56d04cSPantelis Antoniou 28125e16877SFrank Rowand local_fixups = NULL; 28225e16877SFrank Rowand for_each_child_of_node(overlay, local_fixups) 28325e16877SFrank Rowand if (!of_node_cmp(local_fixups->name, "__local_fixups__")) 284da56d04cSPantelis Antoniou break; 285da56d04cSPantelis Antoniou 28625e16877SFrank Rowand if (local_fixups != NULL) { 28725e16877SFrank Rowand err = adjust_local_phandle_references(local_fixups, 28825e16877SFrank Rowand overlay, 0); 2899f27ede4SFrank Rowand if (err) 2907941b27bSPantelis Antoniou return err; 2917941b27bSPantelis Antoniou 29225e16877SFrank Rowand BUG_ON(adjust_local_phandle_references(local_fixups, 29325e16877SFrank Rowand overlay, phandle_delta)); 294da56d04cSPantelis Antoniou } 295da56d04cSPantelis Antoniou 29625e16877SFrank Rowand tree_symbols = NULL; 29725e16877SFrank Rowand overlay_symbols = NULL; 29825e16877SFrank Rowand overlay_fixups = NULL; 2997941b27bSPantelis Antoniou 30025e16877SFrank Rowand tree_symbols = of_find_node_by_path("/__symbols__"); 3017941b27bSPantelis Antoniou 30225e16877SFrank Rowand for_each_child_of_node(overlay, child) { 3037941b27bSPantelis Antoniou 30425e16877SFrank Rowand if (!overlay_symbols && !of_node_cmp(child->name, "__symbols__")) 30525e16877SFrank Rowand overlay_symbols = child; 3067941b27bSPantelis Antoniou 30725e16877SFrank Rowand if (!overlay_fixups && !of_node_cmp(child->name, "__fixups__")) 30825e16877SFrank Rowand overlay_fixups = child; 3097941b27bSPantelis Antoniou 31025e16877SFrank Rowand if (overlay_symbols && overlay_fixups) 3117941b27bSPantelis Antoniou break; 3127941b27bSPantelis Antoniou } 3137941b27bSPantelis Antoniou 31425e16877SFrank Rowand if (!overlay_fixups) { 315a67976ecSFrank Rowand err = 0; 3167941b27bSPantelis Antoniou goto out; 3177941b27bSPantelis Antoniou } 3187941b27bSPantelis Antoniou 31925e16877SFrank Rowand if (!tree_symbols) { 3205de3bbc8SMichal Suchanek pr_err("%s: no symbols in root of device tree.\n", __func__); 3217941b27bSPantelis Antoniou err = -EINVAL; 3227941b27bSPantelis Antoniou goto out; 3237941b27bSPantelis Antoniou } 3247941b27bSPantelis Antoniou 32525e16877SFrank Rowand for_each_property_of_node(overlay_fixups, prop) { 3267941b27bSPantelis Antoniou 3277941b27bSPantelis Antoniou /* skip properties added automatically */ 32825e16877SFrank Rowand if (!of_prop_cmp(prop->name, "name")) 3297941b27bSPantelis Antoniou continue; 3307941b27bSPantelis Antoniou 33125e16877SFrank Rowand err = of_property_read_string(tree_symbols, 33225e16877SFrank Rowand prop->name, &refpath); 3339f27ede4SFrank Rowand if (err) 3347941b27bSPantelis Antoniou goto out; 3357941b27bSPantelis Antoniou 3367941b27bSPantelis Antoniou refnode = of_find_node_by_path(refpath); 3377941b27bSPantelis Antoniou if (!refnode) { 3387941b27bSPantelis Antoniou err = -ENOENT; 3397941b27bSPantelis Antoniou goto out; 3407941b27bSPantelis Antoniou } 3417941b27bSPantelis Antoniou 3427941b27bSPantelis Antoniou phandle = refnode->phandle; 3437941b27bSPantelis Antoniou of_node_put(refnode); 3447941b27bSPantelis Antoniou 34525e16877SFrank Rowand err = update_usages_of_a_phandle_reference(overlay, prop, phandle); 3467941b27bSPantelis Antoniou if (err) 3477941b27bSPantelis Antoniou break; 3487941b27bSPantelis Antoniou } 3497941b27bSPantelis Antoniou 3507941b27bSPantelis Antoniou out: 35125e16877SFrank Rowand of_node_put(tree_symbols); 3527941b27bSPantelis Antoniou 3537941b27bSPantelis Antoniou return err; 3547941b27bSPantelis Antoniou } 3557941b27bSPantelis Antoniou EXPORT_SYMBOL_GPL(of_resolve_phandles); 356