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 *s++ = '\0'; 140624ab2a4SFrank Rowand 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 22225e16877SFrank Rowand phandle = be32_to_cpu(*(__be32 *)(prop->value + off)); 223da56d04cSPantelis Antoniou phandle += phandle_delta; 22425e16877SFrank Rowand *(__be32 *)(prop->value + off) = cpu_to_be32(phandle); 225da56d04cSPantelis Antoniou } 226da56d04cSPantelis Antoniou } 227da56d04cSPantelis Antoniou 22825e16877SFrank Rowand for_each_child_of_node(local_fixups, child) { 229da56d04cSPantelis Antoniou 23025e16877SFrank Rowand for_each_child_of_node(overlay, overlay_child) 23125e16877SFrank Rowand if (!node_name_cmp(child, overlay_child)) 232da56d04cSPantelis Antoniou break; 233da56d04cSPantelis Antoniou 23425e16877SFrank Rowand if (!overlay_child) 235da56d04cSPantelis Antoniou return -EINVAL; 236da56d04cSPantelis Antoniou 23725e16877SFrank Rowand err = adjust_local_phandle_references(child, overlay_child, 238da56d04cSPantelis Antoniou phandle_delta); 2399f27ede4SFrank Rowand if (err) 2407941b27bSPantelis Antoniou return err; 2417941b27bSPantelis Antoniou } 2427941b27bSPantelis Antoniou 2437941b27bSPantelis Antoniou return 0; 2447941b27bSPantelis Antoniou } 2457941b27bSPantelis Antoniou 2467941b27bSPantelis Antoniou /** 2477941b27bSPantelis Antoniou * of_resolve - Resolve the given node against the live tree. 2487941b27bSPantelis Antoniou * 2497941b27bSPantelis Antoniou * @resolve: Node to resolve 2507941b27bSPantelis Antoniou * 2517941b27bSPantelis Antoniou * Perform dynamic Device Tree resolution against the live tree 2527941b27bSPantelis Antoniou * to the given node to resolve. This depends on the live tree 2537941b27bSPantelis Antoniou * having a __symbols__ node, and the resolve node the __fixups__ & 2547941b27bSPantelis Antoniou * __local_fixups__ nodes (if needed). 2557941b27bSPantelis Antoniou * The result of the operation is a resolve node that it's contents 2567941b27bSPantelis Antoniou * are fit to be inserted or operate upon the live tree. 2577941b27bSPantelis Antoniou * Returns 0 on success or a negative error value on error. 2587941b27bSPantelis Antoniou */ 25925e16877SFrank Rowand int of_resolve_phandles(struct device_node *overlay) 2607941b27bSPantelis Antoniou { 26125e16877SFrank Rowand struct device_node *child, *local_fixups, *refnode; 26225e16877SFrank Rowand struct device_node *tree_symbols, *overlay_symbols, *overlay_fixups; 26325e16877SFrank Rowand struct property *prop; 2647941b27bSPantelis Antoniou const char *refpath; 2657941b27bSPantelis Antoniou phandle phandle, phandle_delta; 2667941b27bSPantelis Antoniou int err; 2677941b27bSPantelis Antoniou 268624ab2a4SFrank Rowand if (!overlay) { 269624ab2a4SFrank Rowand pr_err("null overlay\n"); 2707941b27bSPantelis Antoniou return -EINVAL; 271624ab2a4SFrank Rowand } 272624ab2a4SFrank Rowand if (!of_node_check_flag(overlay, OF_DETACHED)) { 273624ab2a4SFrank Rowand pr_err("overlay not detached\n"); 274624ab2a4SFrank Rowand return -EINVAL; 275624ab2a4SFrank Rowand } 2767941b27bSPantelis Antoniou 277f94823f2SFrank Rowand phandle_delta = live_tree_max_phandle() + 1; 27825e16877SFrank Rowand adjust_overlay_phandles(overlay, phandle_delta); 279da56d04cSPantelis Antoniou 28025e16877SFrank Rowand for_each_child_of_node(overlay, local_fixups) 28125e16877SFrank Rowand if (!of_node_cmp(local_fixups->name, "__local_fixups__")) 282da56d04cSPantelis Antoniou break; 283da56d04cSPantelis Antoniou 284624ab2a4SFrank Rowand err = adjust_local_phandle_references(local_fixups, overlay, phandle_delta); 2859f27ede4SFrank Rowand if (err) 2867941b27bSPantelis Antoniou return err; 2877941b27bSPantelis Antoniou 28825e16877SFrank Rowand overlay_symbols = NULL; 28925e16877SFrank Rowand overlay_fixups = NULL; 2907941b27bSPantelis Antoniou 29125e16877SFrank Rowand tree_symbols = of_find_node_by_path("/__symbols__"); 2927941b27bSPantelis Antoniou 29325e16877SFrank Rowand for_each_child_of_node(overlay, child) { 294624ab2a4SFrank Rowand if (!of_node_cmp(child->name, "__symbols__")) 29525e16877SFrank Rowand overlay_symbols = child; 296624ab2a4SFrank Rowand if (!of_node_cmp(child->name, "__fixups__")) 29725e16877SFrank Rowand overlay_fixups = child; 2987941b27bSPantelis Antoniou } 2997941b27bSPantelis Antoniou 30025e16877SFrank Rowand if (!overlay_fixups) { 301a67976ecSFrank Rowand err = 0; 3027941b27bSPantelis Antoniou goto out; 3037941b27bSPantelis Antoniou } 3047941b27bSPantelis Antoniou 30525e16877SFrank Rowand if (!tree_symbols) { 306624ab2a4SFrank Rowand pr_err("no symbols in root of device tree.\n"); 3077941b27bSPantelis Antoniou err = -EINVAL; 3087941b27bSPantelis Antoniou goto out; 3097941b27bSPantelis Antoniou } 3107941b27bSPantelis Antoniou 31125e16877SFrank Rowand for_each_property_of_node(overlay_fixups, prop) { 3127941b27bSPantelis Antoniou 3137941b27bSPantelis Antoniou /* skip properties added automatically */ 31425e16877SFrank Rowand if (!of_prop_cmp(prop->name, "name")) 3157941b27bSPantelis Antoniou continue; 3167941b27bSPantelis Antoniou 31725e16877SFrank Rowand err = of_property_read_string(tree_symbols, 31825e16877SFrank Rowand prop->name, &refpath); 3199f27ede4SFrank Rowand if (err) 3207941b27bSPantelis Antoniou goto out; 3217941b27bSPantelis Antoniou 3227941b27bSPantelis Antoniou refnode = of_find_node_by_path(refpath); 3237941b27bSPantelis Antoniou if (!refnode) { 3247941b27bSPantelis Antoniou err = -ENOENT; 3257941b27bSPantelis Antoniou goto out; 3267941b27bSPantelis Antoniou } 3277941b27bSPantelis Antoniou 3287941b27bSPantelis Antoniou phandle = refnode->phandle; 3297941b27bSPantelis Antoniou of_node_put(refnode); 3307941b27bSPantelis Antoniou 33125e16877SFrank Rowand err = update_usages_of_a_phandle_reference(overlay, prop, phandle); 3327941b27bSPantelis Antoniou if (err) 3337941b27bSPantelis Antoniou break; 3347941b27bSPantelis Antoniou } 3357941b27bSPantelis Antoniou 3367941b27bSPantelis Antoniou out: 33725e16877SFrank Rowand of_node_put(tree_symbols); 3387941b27bSPantelis Antoniou 3397941b27bSPantelis Antoniou return err; 3407941b27bSPantelis Antoniou } 3417941b27bSPantelis Antoniou EXPORT_SYMBOL_GPL(of_resolve_phandles); 342