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 53f94823f2SFrank Rowand static phandle live_tree_max_phandle(void) 547941b27bSPantelis Antoniou { 557941b27bSPantelis Antoniou struct device_node *node; 567941b27bSPantelis Antoniou phandle phandle; 577941b27bSPantelis Antoniou unsigned long flags; 587941b27bSPantelis Antoniou 597941b27bSPantelis Antoniou raw_spin_lock_irqsave(&devtree_lock, flags); 607941b27bSPantelis Antoniou phandle = 0; 617941b27bSPantelis Antoniou for_each_of_allnodes(node) { 627941b27bSPantelis Antoniou if (node->phandle != OF_PHANDLE_ILLEGAL && 637941b27bSPantelis Antoniou node->phandle > phandle) 647941b27bSPantelis Antoniou phandle = node->phandle; 657941b27bSPantelis Antoniou } 667941b27bSPantelis Antoniou raw_spin_unlock_irqrestore(&devtree_lock, flags); 677941b27bSPantelis Antoniou 687941b27bSPantelis Antoniou return phandle; 697941b27bSPantelis Antoniou } 707941b27bSPantelis Antoniou 7125e16877SFrank Rowand static void adjust_overlay_phandles(struct device_node *overlay, 727941b27bSPantelis Antoniou int phandle_delta) 737941b27bSPantelis Antoniou { 747941b27bSPantelis Antoniou struct device_node *child; 757941b27bSPantelis Antoniou struct property *prop; 767941b27bSPantelis Antoniou phandle phandle; 777941b27bSPantelis Antoniou 78269f1a67SFrank Rowand /* adjust node's phandle in node */ 7925e16877SFrank Rowand if (overlay->phandle != 0 && overlay->phandle != OF_PHANDLE_ILLEGAL) 8025e16877SFrank Rowand overlay->phandle += phandle_delta; 817941b27bSPantelis Antoniou 82269f1a67SFrank Rowand /* copy adjusted phandle into *phandle properties */ 8325e16877SFrank Rowand for_each_property_of_node(overlay, prop) { 847941b27bSPantelis Antoniou 859f27ede4SFrank Rowand if (of_prop_cmp(prop->name, "phandle") && 869f27ede4SFrank Rowand of_prop_cmp(prop->name, "linux,phandle")) 877941b27bSPantelis Antoniou continue; 887941b27bSPantelis Antoniou 897941b27bSPantelis Antoniou if (prop->length < 4) 907941b27bSPantelis Antoniou continue; 917941b27bSPantelis Antoniou 927941b27bSPantelis Antoniou phandle = be32_to_cpup(prop->value); 93a67976ecSFrank Rowand if (phandle == OF_PHANDLE_ILLEGAL) 947941b27bSPantelis Antoniou continue; 957941b27bSPantelis Antoniou 9625e16877SFrank Rowand *(uint32_t *)prop->value = cpu_to_be32(overlay->phandle); 977941b27bSPantelis Antoniou } 987941b27bSPantelis Antoniou 9925e16877SFrank Rowand for_each_child_of_node(overlay, child) 100f94823f2SFrank Rowand adjust_overlay_phandles(child, phandle_delta); 1017941b27bSPantelis Antoniou } 1027941b27bSPantelis Antoniou 10325e16877SFrank Rowand static int update_usages_of_a_phandle_reference(struct device_node *overlay, 10425e16877SFrank Rowand struct property *prop_fixup, phandle phandle) 1057941b27bSPantelis Antoniou { 1067941b27bSPantelis Antoniou struct device_node *refnode; 10725e16877SFrank Rowand struct property *prop; 10825e16877SFrank Rowand char *value, *cur, *end, *node_path, *prop_name, *s; 10925e16877SFrank Rowand int offset, len; 1107941b27bSPantelis Antoniou int err = 0; 1117941b27bSPantelis Antoniou 11225e16877SFrank Rowand value = kmalloc(prop_fixup->length, GFP_KERNEL); 11325e16877SFrank Rowand if (!value) 1147941b27bSPantelis Antoniou return -ENOMEM; 11525e16877SFrank Rowand memcpy(value, prop_fixup->value, prop_fixup->length); 1167941b27bSPantelis Antoniou 117269f1a67SFrank Rowand /* prop_fixup contains a list of tuples of path:property_name:offset */ 11825e16877SFrank Rowand end = value + prop_fixup->length; 11925e16877SFrank Rowand for (cur = value; cur < end; cur += len + 1) { 12025e16877SFrank Rowand len = strlen(cur); 1217941b27bSPantelis Antoniou 12225e16877SFrank Rowand node_path = cur; 12325e16877SFrank Rowand s = strchr(cur, ':'); 1247941b27bSPantelis Antoniou if (!s) { 1257941b27bSPantelis Antoniou err = -EINVAL; 1267941b27bSPantelis Antoniou goto err_fail; 1277941b27bSPantelis Antoniou } 1287941b27bSPantelis Antoniou *s++ = '\0'; 1297941b27bSPantelis Antoniou 13025e16877SFrank Rowand prop_name = s; 1317941b27bSPantelis Antoniou s = strchr(s, ':'); 1327941b27bSPantelis Antoniou if (!s) { 1337941b27bSPantelis Antoniou err = -EINVAL; 1347941b27bSPantelis Antoniou goto err_fail; 1357941b27bSPantelis Antoniou } 1367941b27bSPantelis Antoniou *s++ = '\0'; 137624ab2a4SFrank Rowand 1387941b27bSPantelis Antoniou err = kstrtoint(s, 10, &offset); 1399f27ede4SFrank Rowand if (err) 1407941b27bSPantelis Antoniou goto err_fail; 1417941b27bSPantelis Antoniou 14225e16877SFrank Rowand refnode = find_node_by_full_name(overlay, node_path); 14396d1c8e8SFrank Rowand if (!refnode) 1447941b27bSPantelis Antoniou continue; 1457941b27bSPantelis Antoniou 14625e16877SFrank Rowand for_each_property_of_node(refnode, prop) { 14725e16877SFrank Rowand if (!of_prop_cmp(prop->name, prop_name)) 1487941b27bSPantelis Antoniou break; 1497941b27bSPantelis Antoniou } 15082f68756SAmitoj Kaur Chawla of_node_put(refnode); 1517941b27bSPantelis Antoniou 15225e16877SFrank Rowand if (!prop) { 1537941b27bSPantelis Antoniou err = -ENOENT; 1547941b27bSPantelis Antoniou goto err_fail; 1557941b27bSPantelis Antoniou } 1567941b27bSPantelis Antoniou 15725e16877SFrank Rowand *(__be32 *)(prop->value + offset) = cpu_to_be32(phandle); 1587941b27bSPantelis Antoniou } 1597941b27bSPantelis Antoniou 1607941b27bSPantelis Antoniou err_fail: 16125e16877SFrank Rowand kfree(value); 1627941b27bSPantelis Antoniou return err; 1637941b27bSPantelis Antoniou } 1647941b27bSPantelis Antoniou 165da56d04cSPantelis Antoniou /* compare nodes taking into account that 'name' strips out the @ part */ 166fad556bfSFrank Rowand static int node_name_cmp(const struct device_node *dn1, 167da56d04cSPantelis Antoniou const struct device_node *dn2) 168da56d04cSPantelis Antoniou { 169da56d04cSPantelis Antoniou const char *n1 = strrchr(dn1->full_name, '/') ? : "/"; 170da56d04cSPantelis Antoniou const char *n2 = strrchr(dn2->full_name, '/') ? : "/"; 171da56d04cSPantelis Antoniou 172da56d04cSPantelis Antoniou return of_node_cmp(n1, n2); 173da56d04cSPantelis Antoniou } 174da56d04cSPantelis Antoniou 1757941b27bSPantelis Antoniou /* 1767941b27bSPantelis Antoniou * Adjust the local phandle references by the given phandle delta. 177269f1a67SFrank Rowand * 178269f1a67SFrank Rowand * Subtree @local_fixups, which is overlay node __local_fixups__, 179269f1a67SFrank Rowand * mirrors the fragment node structure at the root of the overlay. 180269f1a67SFrank Rowand * 181269f1a67SFrank Rowand * For each property in the fragments that contains a phandle reference, 182269f1a67SFrank Rowand * @local_fixups has a property of the same name that contains a list 183269f1a67SFrank Rowand * of offsets of the phandle reference(s) within the respective property 184269f1a67SFrank Rowand * value(s). The values at these offsets will be fixed up. 1857941b27bSPantelis Antoniou */ 18625e16877SFrank Rowand static int adjust_local_phandle_references(struct device_node *local_fixups, 18725e16877SFrank Rowand struct device_node *overlay, int phandle_delta) 1887941b27bSPantelis Antoniou { 18925e16877SFrank Rowand struct device_node *child, *overlay_child; 19025e16877SFrank Rowand struct property *prop_fix, *prop; 191da56d04cSPantelis Antoniou int err, i, count; 192da56d04cSPantelis Antoniou unsigned int off; 193da56d04cSPantelis Antoniou phandle phandle; 1947941b27bSPantelis Antoniou 19525e16877SFrank Rowand if (!local_fixups) 1967941b27bSPantelis Antoniou return 0; 1977941b27bSPantelis Antoniou 19825e16877SFrank Rowand for_each_property_of_node(local_fixups, prop_fix) { 199da56d04cSPantelis Antoniou 2007941b27bSPantelis Antoniou /* skip properties added automatically */ 20125e16877SFrank Rowand if (!of_prop_cmp(prop_fix->name, "name") || 20225e16877SFrank Rowand !of_prop_cmp(prop_fix->name, "phandle") || 20325e16877SFrank Rowand !of_prop_cmp(prop_fix->name, "linux,phandle")) 2047941b27bSPantelis Antoniou continue; 2057941b27bSPantelis Antoniou 20625e16877SFrank Rowand if ((prop_fix->length % 4) != 0 || prop_fix->length == 0) 207da56d04cSPantelis Antoniou return -EINVAL; 20825e16877SFrank Rowand count = prop_fix->length / sizeof(__be32); 209da56d04cSPantelis Antoniou 21025e16877SFrank Rowand for_each_property_of_node(overlay, prop) { 21125e16877SFrank Rowand if (!of_prop_cmp(prop->name, prop_fix->name)) 212da56d04cSPantelis Antoniou break; 213da56d04cSPantelis Antoniou } 214da56d04cSPantelis Antoniou 21525e16877SFrank Rowand if (!prop) 216da56d04cSPantelis Antoniou return -EINVAL; 217da56d04cSPantelis Antoniou 218da56d04cSPantelis Antoniou for (i = 0; i < count; i++) { 21925e16877SFrank Rowand off = be32_to_cpu(((__be32 *)prop_fix->value)[i]); 220ea8229b7SFrank Rowand if ((off + 4) > prop->length) 221da56d04cSPantelis Antoniou return -EINVAL; 222da56d04cSPantelis Antoniou 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 229269f1a67SFrank Rowand /* 230269f1a67SFrank Rowand * These nested loops recurse down two subtrees in parallel, where the 231269f1a67SFrank Rowand * node names in the two subtrees match. 232269f1a67SFrank Rowand * 233269f1a67SFrank Rowand * The roots of the subtrees are the overlay's __local_fixups__ node 234269f1a67SFrank Rowand * and the overlay's root node. 235269f1a67SFrank Rowand */ 23625e16877SFrank Rowand for_each_child_of_node(local_fixups, child) { 237da56d04cSPantelis Antoniou 23825e16877SFrank Rowand for_each_child_of_node(overlay, overlay_child) 23925e16877SFrank Rowand if (!node_name_cmp(child, overlay_child)) 240da56d04cSPantelis Antoniou break; 241da56d04cSPantelis Antoniou 24225e16877SFrank Rowand if (!overlay_child) 243da56d04cSPantelis Antoniou return -EINVAL; 244da56d04cSPantelis Antoniou 24525e16877SFrank Rowand err = adjust_local_phandle_references(child, overlay_child, 246da56d04cSPantelis Antoniou phandle_delta); 2479f27ede4SFrank Rowand if (err) 2487941b27bSPantelis Antoniou return err; 2497941b27bSPantelis Antoniou } 2507941b27bSPantelis Antoniou 2517941b27bSPantelis Antoniou return 0; 2527941b27bSPantelis Antoniou } 2537941b27bSPantelis Antoniou 2547941b27bSPantelis Antoniou /** 255269f1a67SFrank Rowand * of_resolve_phandles - Relocate and resolve overlay against live tree 2567941b27bSPantelis Antoniou * 257269f1a67SFrank Rowand * @overlay: Pointer to devicetree overlay to relocate and resolve 2587941b27bSPantelis Antoniou * 259269f1a67SFrank Rowand * Modify (relocate) values of local phandles in @overlay to a range that 260269f1a67SFrank Rowand * does not conflict with the live expanded devicetree. Update references 261269f1a67SFrank Rowand * to the local phandles in @overlay. Update (resolve) phandle references 262269f1a67SFrank Rowand * in @overlay that refer to the live expanded devicetree. 263269f1a67SFrank Rowand * 264269f1a67SFrank Rowand * Phandle values in the live tree are in the range of 265269f1a67SFrank Rowand * 1 .. live_tree_max_phandle(). The range of phandle values in the overlay 266269f1a67SFrank Rowand * also begin with at 1. Adjust the phandle values in the overlay to begin 267269f1a67SFrank Rowand * at live_tree_max_phandle() + 1. Update references to the phandles to 268269f1a67SFrank Rowand * the adjusted phandle values. 269269f1a67SFrank Rowand * 270269f1a67SFrank Rowand * The name of each property in the "__fixups__" node in the overlay matches 271269f1a67SFrank Rowand * the name of a symbol (a label) in the live tree. The values of each 272269f1a67SFrank Rowand * property in the "__fixups__" node is a list of the property values in the 273269f1a67SFrank Rowand * overlay that need to be updated to contain the phandle reference 274269f1a67SFrank Rowand * corresponding to that symbol in the live tree. Update the references in 275269f1a67SFrank Rowand * the overlay with the phandle values in the live tree. 276269f1a67SFrank Rowand * 277269f1a67SFrank Rowand * @overlay must be detached. 278269f1a67SFrank Rowand * 279269f1a67SFrank Rowand * Resolving and applying @overlay to the live expanded devicetree must be 280269f1a67SFrank Rowand * protected by a mechanism to ensure that multiple overlays are processed 281269f1a67SFrank Rowand * in a single threaded manner so that multiple overlays will not relocate 282269f1a67SFrank Rowand * phandles to overlapping ranges. The mechanism to enforce this is not 283269f1a67SFrank Rowand * yet implemented. 284269f1a67SFrank Rowand * 285269f1a67SFrank Rowand * Return: %0 on success or a negative error value on error. 2867941b27bSPantelis Antoniou */ 28725e16877SFrank Rowand int of_resolve_phandles(struct device_node *overlay) 2887941b27bSPantelis Antoniou { 28925e16877SFrank Rowand struct device_node *child, *local_fixups, *refnode; 2905581a95fSFrank Rowand struct device_node *tree_symbols, *overlay_fixups; 29125e16877SFrank Rowand struct property *prop; 2927941b27bSPantelis Antoniou const char *refpath; 2937941b27bSPantelis Antoniou phandle phandle, phandle_delta; 2947941b27bSPantelis Antoniou int err; 2957941b27bSPantelis Antoniou 296d9181b20SFrank Rowand tree_symbols = NULL; 297d9181b20SFrank Rowand 298624ab2a4SFrank Rowand if (!overlay) { 299624ab2a4SFrank Rowand pr_err("null overlay\n"); 300d9181b20SFrank Rowand err = -EINVAL; 301d9181b20SFrank Rowand goto err_out; 302624ab2a4SFrank Rowand } 303624ab2a4SFrank Rowand if (!of_node_check_flag(overlay, OF_DETACHED)) { 304624ab2a4SFrank Rowand pr_err("overlay not detached\n"); 305d9181b20SFrank Rowand err = -EINVAL; 306d9181b20SFrank Rowand goto err_out; 307624ab2a4SFrank Rowand } 3087941b27bSPantelis Antoniou 309f94823f2SFrank Rowand phandle_delta = live_tree_max_phandle() + 1; 31025e16877SFrank Rowand adjust_overlay_phandles(overlay, phandle_delta); 311da56d04cSPantelis Antoniou 31225e16877SFrank Rowand for_each_child_of_node(overlay, local_fixups) 31325e16877SFrank Rowand if (!of_node_cmp(local_fixups->name, "__local_fixups__")) 314da56d04cSPantelis Antoniou break; 315da56d04cSPantelis Antoniou 316624ab2a4SFrank Rowand err = adjust_local_phandle_references(local_fixups, overlay, phandle_delta); 3179f27ede4SFrank Rowand if (err) 318d9181b20SFrank Rowand goto err_out; 3197941b27bSPantelis Antoniou 32025e16877SFrank Rowand overlay_fixups = NULL; 3217941b27bSPantelis Antoniou 32225e16877SFrank Rowand for_each_child_of_node(overlay, child) { 323624ab2a4SFrank Rowand if (!of_node_cmp(child->name, "__fixups__")) 32425e16877SFrank Rowand overlay_fixups = child; 3257941b27bSPantelis Antoniou } 3267941b27bSPantelis Antoniou 32725e16877SFrank Rowand if (!overlay_fixups) { 328a67976ecSFrank Rowand err = 0; 3297941b27bSPantelis Antoniou goto out; 3307941b27bSPantelis Antoniou } 3317941b27bSPantelis Antoniou 3324458db4cSFrank Rowand tree_symbols = of_find_node_by_path("/__symbols__"); 33325e16877SFrank Rowand if (!tree_symbols) { 334624ab2a4SFrank Rowand pr_err("no symbols in root of device tree.\n"); 3357941b27bSPantelis Antoniou err = -EINVAL; 336d9181b20SFrank Rowand goto err_out; 3377941b27bSPantelis Antoniou } 3387941b27bSPantelis Antoniou 33925e16877SFrank Rowand for_each_property_of_node(overlay_fixups, prop) { 3407941b27bSPantelis Antoniou 3417941b27bSPantelis Antoniou /* skip properties added automatically */ 34225e16877SFrank Rowand if (!of_prop_cmp(prop->name, "name")) 3437941b27bSPantelis Antoniou continue; 3447941b27bSPantelis Antoniou 34525e16877SFrank Rowand err = of_property_read_string(tree_symbols, 34625e16877SFrank Rowand prop->name, &refpath); 3479f27ede4SFrank Rowand if (err) 348d9181b20SFrank Rowand goto err_out; 3497941b27bSPantelis Antoniou 3507941b27bSPantelis Antoniou refnode = of_find_node_by_path(refpath); 3517941b27bSPantelis Antoniou if (!refnode) { 3527941b27bSPantelis Antoniou err = -ENOENT; 353d9181b20SFrank Rowand goto err_out; 3547941b27bSPantelis Antoniou } 3557941b27bSPantelis Antoniou 3567941b27bSPantelis Antoniou phandle = refnode->phandle; 3577941b27bSPantelis Antoniou of_node_put(refnode); 3587941b27bSPantelis Antoniou 35925e16877SFrank Rowand err = update_usages_of_a_phandle_reference(overlay, prop, phandle); 3607941b27bSPantelis Antoniou if (err) 3617941b27bSPantelis Antoniou break; 3627941b27bSPantelis Antoniou } 3637941b27bSPantelis Antoniou 364d9181b20SFrank Rowand err_out: 365d9181b20SFrank Rowand pr_err("overlay phandle fixup failed: %d\n", err); 3667941b27bSPantelis Antoniou out: 36725e16877SFrank Rowand of_node_put(tree_symbols); 3687941b27bSPantelis Antoniou 3697941b27bSPantelis Antoniou return err; 3707941b27bSPantelis Antoniou } 3717941b27bSPantelis Antoniou EXPORT_SYMBOL_GPL(of_resolve_phandles); 372