18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* pdt.c: OF PROM device tree support code. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Paul Mackerras August 1996. 58c2ecf20Sopenharmony_ci * Copyright (C) 1996-2005 Paul Mackerras. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Adapted for 64bit PowerPC by Dave Engebretsen and Peter Bergner. 88c2ecf20Sopenharmony_ci * {engebret|bergner}@us.ibm.com 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Adapted for sparc by David S. Miller davem@davemloft.net 118c2ecf20Sopenharmony_ci * Adapted for multiple architectures by Andres Salomon <dilinger@queued.net> 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/errno.h> 178c2ecf20Sopenharmony_ci#include <linux/mutex.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <linux/of.h> 208c2ecf20Sopenharmony_ci#include <linux/of_pdt.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic struct of_pdt_ops *of_pdt_prom_ops __initdata; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#if defined(CONFIG_SPARC) 258c2ecf20Sopenharmony_ciunsigned int of_pdt_unique_id __initdata; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define of_pdt_incr_unique_id(p) do { \ 288c2ecf20Sopenharmony_ci (p)->unique_id = of_pdt_unique_id++; \ 298c2ecf20Sopenharmony_ci} while (0) 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic char * __init of_pdt_build_full_name(struct device_node *dp) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci return build_path_component(dp); 348c2ecf20Sopenharmony_ci} 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#else /* CONFIG_SPARC */ 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic inline void of_pdt_incr_unique_id(void *p) { } 398c2ecf20Sopenharmony_cistatic inline void irq_trans_init(struct device_node *dp) { } 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic char * __init of_pdt_build_full_name(struct device_node *dp) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci static int failsafe_id = 0; /* for generating unique names on failure */ 448c2ecf20Sopenharmony_ci const char *name; 458c2ecf20Sopenharmony_ci char path[256]; 468c2ecf20Sopenharmony_ci char *buf; 478c2ecf20Sopenharmony_ci int len; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci if (!of_pdt_prom_ops->pkg2path(dp->phandle, path, sizeof(path), &len)) { 508c2ecf20Sopenharmony_ci name = kbasename(path); 518c2ecf20Sopenharmony_ci buf = prom_early_alloc(strlen(name) + 1); 528c2ecf20Sopenharmony_ci strcpy(buf, name); 538c2ecf20Sopenharmony_ci return buf; 548c2ecf20Sopenharmony_ci } 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci name = of_get_property(dp, "name", &len); 578c2ecf20Sopenharmony_ci buf = prom_early_alloc(len + 16); 588c2ecf20Sopenharmony_ci sprintf(buf, "%s@unknown%i", name, failsafe_id++); 598c2ecf20Sopenharmony_ci pr_err("%s: pkg2path failed; assigning %s\n", __func__, buf); 608c2ecf20Sopenharmony_ci return buf; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#endif /* !CONFIG_SPARC */ 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic struct property * __init of_pdt_build_one_prop(phandle node, char *prev, 668c2ecf20Sopenharmony_ci char *special_name, 678c2ecf20Sopenharmony_ci void *special_val, 688c2ecf20Sopenharmony_ci int special_len) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci static struct property *tmp = NULL; 718c2ecf20Sopenharmony_ci struct property *p; 728c2ecf20Sopenharmony_ci int err; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if (tmp) { 758c2ecf20Sopenharmony_ci p = tmp; 768c2ecf20Sopenharmony_ci memset(p, 0, sizeof(*p) + 32); 778c2ecf20Sopenharmony_ci tmp = NULL; 788c2ecf20Sopenharmony_ci } else { 798c2ecf20Sopenharmony_ci p = prom_early_alloc(sizeof(struct property) + 32); 808c2ecf20Sopenharmony_ci of_pdt_incr_unique_id(p); 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci p->name = (char *) (p + 1); 848c2ecf20Sopenharmony_ci if (special_name) { 858c2ecf20Sopenharmony_ci strcpy(p->name, special_name); 868c2ecf20Sopenharmony_ci p->length = special_len; 878c2ecf20Sopenharmony_ci p->value = prom_early_alloc(special_len); 888c2ecf20Sopenharmony_ci memcpy(p->value, special_val, special_len); 898c2ecf20Sopenharmony_ci } else { 908c2ecf20Sopenharmony_ci err = of_pdt_prom_ops->nextprop(node, prev, p->name); 918c2ecf20Sopenharmony_ci if (err) { 928c2ecf20Sopenharmony_ci tmp = p; 938c2ecf20Sopenharmony_ci return NULL; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci p->length = of_pdt_prom_ops->getproplen(node, p->name); 968c2ecf20Sopenharmony_ci if (p->length <= 0) { 978c2ecf20Sopenharmony_ci p->length = 0; 988c2ecf20Sopenharmony_ci } else { 998c2ecf20Sopenharmony_ci int len; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci p->value = prom_early_alloc(p->length + 1); 1028c2ecf20Sopenharmony_ci len = of_pdt_prom_ops->getproperty(node, p->name, 1038c2ecf20Sopenharmony_ci p->value, p->length); 1048c2ecf20Sopenharmony_ci if (len <= 0) 1058c2ecf20Sopenharmony_ci p->length = 0; 1068c2ecf20Sopenharmony_ci ((unsigned char *)p->value)[p->length] = '\0'; 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci return p; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic struct property * __init of_pdt_build_prop_list(phandle node) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci struct property *head, *tail; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci head = tail = of_pdt_build_one_prop(node, NULL, 1178c2ecf20Sopenharmony_ci ".node", &node, sizeof(node)); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci tail->next = of_pdt_build_one_prop(node, NULL, NULL, NULL, 0); 1208c2ecf20Sopenharmony_ci tail = tail->next; 1218c2ecf20Sopenharmony_ci while(tail) { 1228c2ecf20Sopenharmony_ci tail->next = of_pdt_build_one_prop(node, tail->name, 1238c2ecf20Sopenharmony_ci NULL, NULL, 0); 1248c2ecf20Sopenharmony_ci tail = tail->next; 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci return head; 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic char * __init of_pdt_get_one_property(phandle node, const char *name) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci char *buf = "<NULL>"; 1338c2ecf20Sopenharmony_ci int len; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci len = of_pdt_prom_ops->getproplen(node, name); 1368c2ecf20Sopenharmony_ci if (len > 0) { 1378c2ecf20Sopenharmony_ci buf = prom_early_alloc(len); 1388c2ecf20Sopenharmony_ci len = of_pdt_prom_ops->getproperty(node, name, buf, len); 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci return buf; 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic struct device_node * __init of_pdt_create_node(phandle node, 1458c2ecf20Sopenharmony_ci struct device_node *parent) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci struct device_node *dp; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if (!node) 1508c2ecf20Sopenharmony_ci return NULL; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci dp = prom_early_alloc(sizeof(*dp)); 1538c2ecf20Sopenharmony_ci of_node_init(dp); 1548c2ecf20Sopenharmony_ci of_pdt_incr_unique_id(dp); 1558c2ecf20Sopenharmony_ci dp->parent = parent; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci dp->name = of_pdt_get_one_property(node, "name"); 1588c2ecf20Sopenharmony_ci dp->phandle = node; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci dp->properties = of_pdt_build_prop_list(node); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci dp->full_name = of_pdt_build_full_name(dp); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci irq_trans_init(dp); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci return dp; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic struct device_node * __init of_pdt_build_tree(struct device_node *parent, 1708c2ecf20Sopenharmony_ci phandle node) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci struct device_node *ret = NULL, *prev_sibling = NULL; 1738c2ecf20Sopenharmony_ci struct device_node *dp; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci while (1) { 1768c2ecf20Sopenharmony_ci dp = of_pdt_create_node(node, parent); 1778c2ecf20Sopenharmony_ci if (!dp) 1788c2ecf20Sopenharmony_ci break; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci if (prev_sibling) 1818c2ecf20Sopenharmony_ci prev_sibling->sibling = dp; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci if (!ret) 1848c2ecf20Sopenharmony_ci ret = dp; 1858c2ecf20Sopenharmony_ci prev_sibling = dp; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci dp->child = of_pdt_build_tree(dp, of_pdt_prom_ops->getchild(node)); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci node = of_pdt_prom_ops->getsibling(node); 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci return ret; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic void * __init kernel_tree_alloc(u64 size, u64 align) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci return prom_early_alloc(size); 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_civoid __init of_pdt_build_devicetree(phandle root_node, struct of_pdt_ops *ops) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci BUG_ON(!ops); 2038c2ecf20Sopenharmony_ci of_pdt_prom_ops = ops; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci of_root = of_pdt_create_node(root_node, NULL); 2068c2ecf20Sopenharmony_ci of_root->full_name = "/"; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci of_root->child = of_pdt_build_tree(of_root, 2098c2ecf20Sopenharmony_ci of_pdt_prom_ops->getchild(of_root->phandle)); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci /* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */ 2128c2ecf20Sopenharmony_ci of_alias_scan(kernel_tree_alloc); 2138c2ecf20Sopenharmony_ci} 214