162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* pdt.c: OF PROM device tree support code. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Paul Mackerras August 1996. 562306a36Sopenharmony_ci * Copyright (C) 1996-2005 Paul Mackerras. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Adapted for 64bit PowerPC by Dave Engebretsen and Peter Bergner. 862306a36Sopenharmony_ci * {engebret|bergner}@us.ibm.com 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Adapted for sparc by David S. Miller davem@davemloft.net 1162306a36Sopenharmony_ci * Adapted for multiple architectures by Andres Salomon <dilinger@queued.net> 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/errno.h> 1762306a36Sopenharmony_ci#include <linux/mutex.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci#include <linux/of.h> 2062306a36Sopenharmony_ci#include <linux/of_pdt.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic struct of_pdt_ops *of_pdt_prom_ops __initdata; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#if defined(CONFIG_SPARC) 2562306a36Sopenharmony_ciunsigned int of_pdt_unique_id __initdata; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define of_pdt_incr_unique_id(p) do { \ 2862306a36Sopenharmony_ci (p)->unique_id = of_pdt_unique_id++; \ 2962306a36Sopenharmony_ci} while (0) 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic char * __init of_pdt_build_full_name(struct device_node *dp) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci return build_path_component(dp); 3462306a36Sopenharmony_ci} 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#else /* CONFIG_SPARC */ 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic inline void of_pdt_incr_unique_id(void *p) { } 3962306a36Sopenharmony_cistatic inline void irq_trans_init(struct device_node *dp) { } 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic char * __init of_pdt_build_full_name(struct device_node *dp) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci static int failsafe_id = 0; /* for generating unique names on failure */ 4462306a36Sopenharmony_ci const char *name; 4562306a36Sopenharmony_ci char path[256]; 4662306a36Sopenharmony_ci char *buf; 4762306a36Sopenharmony_ci int len; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci if (!of_pdt_prom_ops->pkg2path(dp->phandle, path, sizeof(path), &len)) { 5062306a36Sopenharmony_ci name = kbasename(path); 5162306a36Sopenharmony_ci buf = prom_early_alloc(strlen(name) + 1); 5262306a36Sopenharmony_ci strcpy(buf, name); 5362306a36Sopenharmony_ci return buf; 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci name = of_get_property(dp, "name", &len); 5762306a36Sopenharmony_ci buf = prom_early_alloc(len + 16); 5862306a36Sopenharmony_ci sprintf(buf, "%s@unknown%i", name, failsafe_id++); 5962306a36Sopenharmony_ci pr_err("%s: pkg2path failed; assigning %s\n", __func__, buf); 6062306a36Sopenharmony_ci return buf; 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci#endif /* !CONFIG_SPARC */ 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic struct property * __init of_pdt_build_one_prop(phandle node, char *prev, 6662306a36Sopenharmony_ci char *special_name, 6762306a36Sopenharmony_ci void *special_val, 6862306a36Sopenharmony_ci int special_len) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci static struct property *tmp = NULL; 7162306a36Sopenharmony_ci struct property *p; 7262306a36Sopenharmony_ci int err; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (tmp) { 7562306a36Sopenharmony_ci p = tmp; 7662306a36Sopenharmony_ci memset(p, 0, sizeof(*p) + 32); 7762306a36Sopenharmony_ci tmp = NULL; 7862306a36Sopenharmony_ci } else { 7962306a36Sopenharmony_ci p = prom_early_alloc(sizeof(struct property) + 32); 8062306a36Sopenharmony_ci of_pdt_incr_unique_id(p); 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci p->name = (char *) (p + 1); 8462306a36Sopenharmony_ci if (special_name) { 8562306a36Sopenharmony_ci strcpy(p->name, special_name); 8662306a36Sopenharmony_ci p->length = special_len; 8762306a36Sopenharmony_ci p->value = prom_early_alloc(special_len); 8862306a36Sopenharmony_ci memcpy(p->value, special_val, special_len); 8962306a36Sopenharmony_ci } else { 9062306a36Sopenharmony_ci err = of_pdt_prom_ops->nextprop(node, prev, p->name); 9162306a36Sopenharmony_ci if (err) { 9262306a36Sopenharmony_ci tmp = p; 9362306a36Sopenharmony_ci return NULL; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci p->length = of_pdt_prom_ops->getproplen(node, p->name); 9662306a36Sopenharmony_ci if (p->length <= 0) { 9762306a36Sopenharmony_ci p->length = 0; 9862306a36Sopenharmony_ci } else { 9962306a36Sopenharmony_ci int len; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci p->value = prom_early_alloc(p->length + 1); 10262306a36Sopenharmony_ci len = of_pdt_prom_ops->getproperty(node, p->name, 10362306a36Sopenharmony_ci p->value, p->length); 10462306a36Sopenharmony_ci if (len <= 0) 10562306a36Sopenharmony_ci p->length = 0; 10662306a36Sopenharmony_ci ((unsigned char *)p->value)[p->length] = '\0'; 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci return p; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic struct property * __init of_pdt_build_prop_list(phandle node) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci struct property *head, *tail; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci head = tail = of_pdt_build_one_prop(node, NULL, 11762306a36Sopenharmony_ci ".node", &node, sizeof(node)); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci tail->next = of_pdt_build_one_prop(node, NULL, NULL, NULL, 0); 12062306a36Sopenharmony_ci tail = tail->next; 12162306a36Sopenharmony_ci while(tail) { 12262306a36Sopenharmony_ci tail->next = of_pdt_build_one_prop(node, tail->name, 12362306a36Sopenharmony_ci NULL, NULL, 0); 12462306a36Sopenharmony_ci tail = tail->next; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci return head; 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic char * __init of_pdt_get_one_property(phandle node, const char *name) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci char *buf = "<NULL>"; 13362306a36Sopenharmony_ci int len; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci len = of_pdt_prom_ops->getproplen(node, name); 13662306a36Sopenharmony_ci if (len > 0) { 13762306a36Sopenharmony_ci buf = prom_early_alloc(len); 13862306a36Sopenharmony_ci len = of_pdt_prom_ops->getproperty(node, name, buf, len); 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci return buf; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic struct device_node * __init of_pdt_create_node(phandle node, 14562306a36Sopenharmony_ci struct device_node *parent) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci struct device_node *dp; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (!node) 15062306a36Sopenharmony_ci return NULL; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci dp = prom_early_alloc(sizeof(*dp)); 15362306a36Sopenharmony_ci of_node_init(dp); 15462306a36Sopenharmony_ci of_pdt_incr_unique_id(dp); 15562306a36Sopenharmony_ci dp->parent = parent; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci dp->name = of_pdt_get_one_property(node, "name"); 15862306a36Sopenharmony_ci dp->phandle = node; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci dp->properties = of_pdt_build_prop_list(node); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci dp->full_name = of_pdt_build_full_name(dp); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci irq_trans_init(dp); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci return dp; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic struct device_node * __init of_pdt_build_tree(struct device_node *parent, 17062306a36Sopenharmony_ci phandle node) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci struct device_node *ret = NULL, *prev_sibling = NULL; 17362306a36Sopenharmony_ci struct device_node *dp; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci while (1) { 17662306a36Sopenharmony_ci dp = of_pdt_create_node(node, parent); 17762306a36Sopenharmony_ci if (!dp) 17862306a36Sopenharmony_ci break; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci if (prev_sibling) 18162306a36Sopenharmony_ci prev_sibling->sibling = dp; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci if (!ret) 18462306a36Sopenharmony_ci ret = dp; 18562306a36Sopenharmony_ci prev_sibling = dp; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci dp->child = of_pdt_build_tree(dp, of_pdt_prom_ops->getchild(node)); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci node = of_pdt_prom_ops->getsibling(node); 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci return ret; 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic void * __init kernel_tree_alloc(u64 size, u64 align) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci return prom_early_alloc(size); 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_civoid __init of_pdt_build_devicetree(phandle root_node, struct of_pdt_ops *ops) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci BUG_ON(!ops); 20362306a36Sopenharmony_ci of_pdt_prom_ops = ops; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci of_root = of_pdt_create_node(root_node, NULL); 20662306a36Sopenharmony_ci of_root->full_name = "/"; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci of_root->child = of_pdt_build_tree(of_root, 20962306a36Sopenharmony_ci of_pdt_prom_ops->getchild(of_root->phandle)); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci /* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */ 21262306a36Sopenharmony_ci of_alias_scan(kernel_tree_alloc); 21362306a36Sopenharmony_ci} 214