1dfa76060SDavid S. Miller /* prom_common.c: OF device tree support common code. 2dfa76060SDavid S. Miller * 3dfa76060SDavid S. Miller * Paul Mackerras August 1996. 4dfa76060SDavid S. Miller * Copyright (C) 1996-2005 Paul Mackerras. 5dfa76060SDavid S. Miller * 6dfa76060SDavid S. Miller * Adapted for 64bit PowerPC by Dave Engebretsen and Peter Bergner. 7dfa76060SDavid S. Miller * {engebret|bergner}@us.ibm.com 8dfa76060SDavid S. Miller * 9dfa76060SDavid S. Miller * Adapted for sparc by David S. Miller davem@davemloft.net 10dfa76060SDavid S. Miller * 11dfa76060SDavid S. Miller * This program is free software; you can redistribute it and/or 12dfa76060SDavid S. Miller * modify it under the terms of the GNU General Public License 13dfa76060SDavid S. Miller * as published by the Free Software Foundation; either version 14dfa76060SDavid S. Miller * 2 of the License, or (at your option) any later version. 15dfa76060SDavid S. Miller */ 16dfa76060SDavid S. Miller 17dfa76060SDavid S. Miller #include <linux/kernel.h> 18dfa76060SDavid S. Miller #include <linux/module.h> 19dfa76060SDavid S. Miller #include <linux/errno.h> 20dfa76060SDavid S. Miller #include <linux/mutex.h> 21dfa76060SDavid S. Miller #include <linux/slab.h> 22dfa76060SDavid S. Miller #include <linux/of.h> 23dfa76060SDavid S. Miller #include <asm/prom.h> 24dfa76060SDavid S. Miller #include <asm/oplib.h> 25e63829deSKonrad Eisele #include <asm/leon.h> 26dfa76060SDavid S. Miller 27dfa76060SDavid S. Miller #include "prom.h" 28dfa76060SDavid S. Miller 29e63829deSKonrad Eisele void (*prom_build_more)(struct device_node *dp, struct device_node ***nextp); 30e63829deSKonrad Eisele 31ad07aed8SDavid S. Miller struct device_node *of_console_device; 32ad07aed8SDavid S. Miller EXPORT_SYMBOL(of_console_device); 33ad07aed8SDavid S. Miller 34ad07aed8SDavid S. Miller char *of_console_path; 35ad07aed8SDavid S. Miller EXPORT_SYMBOL(of_console_path); 36ad07aed8SDavid S. Miller 37ad07aed8SDavid S. Miller char *of_console_options; 38ad07aed8SDavid S. Miller EXPORT_SYMBOL(of_console_options); 39ad07aed8SDavid S. Miller 40dfa76060SDavid S. Miller struct device_node *of_find_node_by_phandle(phandle handle) 41dfa76060SDavid S. Miller { 42dfa76060SDavid S. Miller struct device_node *np; 43dfa76060SDavid S. Miller 44dfa76060SDavid S. Miller for (np = allnodes; np; np = np->allnext) 45*6016a363SGrant Likely if (np->phandle == handle) 46dfa76060SDavid S. Miller break; 47dfa76060SDavid S. Miller 48dfa76060SDavid S. Miller return np; 49dfa76060SDavid S. Miller } 50dfa76060SDavid S. Miller EXPORT_SYMBOL(of_find_node_by_phandle); 51dfa76060SDavid S. Miller 52dfa76060SDavid S. Miller int of_getintprop_default(struct device_node *np, const char *name, int def) 53dfa76060SDavid S. Miller { 54dfa76060SDavid S. Miller struct property *prop; 55dfa76060SDavid S. Miller int len; 56dfa76060SDavid S. Miller 57dfa76060SDavid S. Miller prop = of_find_property(np, name, &len); 58dfa76060SDavid S. Miller if (!prop || len != 4) 59dfa76060SDavid S. Miller return def; 60dfa76060SDavid S. Miller 61dfa76060SDavid S. Miller return *(int *) prop->value; 62dfa76060SDavid S. Miller } 63dfa76060SDavid S. Miller EXPORT_SYMBOL(of_getintprop_default); 64dfa76060SDavid S. Miller 65dfa76060SDavid S. Miller DEFINE_MUTEX(of_set_property_mutex); 66dfa76060SDavid S. Miller EXPORT_SYMBOL(of_set_property_mutex); 67dfa76060SDavid S. Miller 68dfa76060SDavid S. Miller int of_set_property(struct device_node *dp, const char *name, void *val, int len) 69dfa76060SDavid S. Miller { 70dfa76060SDavid S. Miller struct property **prevp; 71dfa76060SDavid S. Miller void *new_val; 72dfa76060SDavid S. Miller int err; 73dfa76060SDavid S. Miller 74dfa76060SDavid S. Miller new_val = kmalloc(len, GFP_KERNEL); 75dfa76060SDavid S. Miller if (!new_val) 76dfa76060SDavid S. Miller return -ENOMEM; 77dfa76060SDavid S. Miller 78dfa76060SDavid S. Miller memcpy(new_val, val, len); 79dfa76060SDavid S. Miller 80dfa76060SDavid S. Miller err = -ENODEV; 81dfa76060SDavid S. Miller 821c9d80ddSDavid S. Miller mutex_lock(&of_set_property_mutex); 83dfa76060SDavid S. Miller write_lock(&devtree_lock); 84dfa76060SDavid S. Miller prevp = &dp->properties; 85dfa76060SDavid S. Miller while (*prevp) { 86dfa76060SDavid S. Miller struct property *prop = *prevp; 87dfa76060SDavid S. Miller 88dfa76060SDavid S. Miller if (!strcasecmp(prop->name, name)) { 89dfa76060SDavid S. Miller void *old_val = prop->value; 90dfa76060SDavid S. Miller int ret; 91dfa76060SDavid S. Miller 92*6016a363SGrant Likely ret = prom_setprop(dp->phandle, name, val, len); 93dfa76060SDavid S. Miller 94dfa76060SDavid S. Miller err = -EINVAL; 95dfa76060SDavid S. Miller if (ret >= 0) { 96dfa76060SDavid S. Miller prop->value = new_val; 97dfa76060SDavid S. Miller prop->length = len; 98dfa76060SDavid S. Miller 99dfa76060SDavid S. Miller if (OF_IS_DYNAMIC(prop)) 100dfa76060SDavid S. Miller kfree(old_val); 101dfa76060SDavid S. Miller 102dfa76060SDavid S. Miller OF_MARK_DYNAMIC(prop); 103dfa76060SDavid S. Miller 104dfa76060SDavid S. Miller err = 0; 105dfa76060SDavid S. Miller } 106dfa76060SDavid S. Miller break; 107dfa76060SDavid S. Miller } 108dfa76060SDavid S. Miller prevp = &(*prevp)->next; 109dfa76060SDavid S. Miller } 110dfa76060SDavid S. Miller write_unlock(&devtree_lock); 1111c9d80ddSDavid S. Miller mutex_unlock(&of_set_property_mutex); 112dfa76060SDavid S. Miller 113dfa76060SDavid S. Miller /* XXX Upate procfs if necessary... */ 114dfa76060SDavid S. Miller 115dfa76060SDavid S. Miller return err; 116dfa76060SDavid S. Miller } 117dfa76060SDavid S. Miller EXPORT_SYMBOL(of_set_property); 118dfa76060SDavid S. Miller 119dfa76060SDavid S. Miller int of_find_in_proplist(const char *list, const char *match, int len) 120dfa76060SDavid S. Miller { 121dfa76060SDavid S. Miller while (len > 0) { 122dfa76060SDavid S. Miller int l; 123dfa76060SDavid S. Miller 124dfa76060SDavid S. Miller if (!strcmp(list, match)) 125dfa76060SDavid S. Miller return 1; 126dfa76060SDavid S. Miller l = strlen(list) + 1; 127dfa76060SDavid S. Miller list += l; 128dfa76060SDavid S. Miller len -= l; 129dfa76060SDavid S. Miller } 130dfa76060SDavid S. Miller return 0; 131dfa76060SDavid S. Miller } 132dfa76060SDavid S. Miller EXPORT_SYMBOL(of_find_in_proplist); 133dfa76060SDavid S. Miller 134e5ff0fe3SDavid S. Miller unsigned int prom_unique_id; 135b9e5567cSDavid S. Miller 136b9e5567cSDavid S. Miller static struct property * __init build_one_prop(phandle node, char *prev, 137b9e5567cSDavid S. Miller char *special_name, 138b9e5567cSDavid S. Miller void *special_val, 139b9e5567cSDavid S. Miller int special_len) 140b9e5567cSDavid S. Miller { 141b9e5567cSDavid S. Miller static struct property *tmp = NULL; 142b9e5567cSDavid S. Miller struct property *p; 143b9e5567cSDavid S. Miller const char *name; 144b9e5567cSDavid S. Miller 145b9e5567cSDavid S. Miller if (tmp) { 146b9e5567cSDavid S. Miller p = tmp; 147b9e5567cSDavid S. Miller memset(p, 0, sizeof(*p) + 32); 148b9e5567cSDavid S. Miller tmp = NULL; 149b9e5567cSDavid S. Miller } else { 150b9e5567cSDavid S. Miller p = prom_early_alloc(sizeof(struct property) + 32); 151b9e5567cSDavid S. Miller p->unique_id = prom_unique_id++; 152b9e5567cSDavid S. Miller } 153b9e5567cSDavid S. Miller 154b9e5567cSDavid S. Miller p->name = (char *) (p + 1); 155b9e5567cSDavid S. Miller if (special_name) { 156b9e5567cSDavid S. Miller strcpy(p->name, special_name); 157b9e5567cSDavid S. Miller p->length = special_len; 158b9e5567cSDavid S. Miller p->value = prom_early_alloc(special_len); 159b9e5567cSDavid S. Miller memcpy(p->value, special_val, special_len); 160b9e5567cSDavid S. Miller } else { 161b9e5567cSDavid S. Miller if (prev == NULL) { 16247cd5265SJulian Calaby name = prom_firstprop(node, p->name); 163b9e5567cSDavid S. Miller } else { 16447cd5265SJulian Calaby name = prom_nextprop(node, prev, p->name); 165b9e5567cSDavid S. Miller } 16647cd5265SJulian Calaby 167e63829deSKonrad Eisele if (!name || strlen(name) == 0) { 168b9e5567cSDavid S. Miller tmp = p; 169b9e5567cSDavid S. Miller return NULL; 170b9e5567cSDavid S. Miller } 171b9e5567cSDavid S. Miller #ifdef CONFIG_SPARC32 172b9e5567cSDavid S. Miller strcpy(p->name, name); 173b9e5567cSDavid S. Miller #endif 174b9e5567cSDavid S. Miller p->length = prom_getproplen(node, p->name); 175b9e5567cSDavid S. Miller if (p->length <= 0) { 176b9e5567cSDavid S. Miller p->length = 0; 177b9e5567cSDavid S. Miller } else { 178b9e5567cSDavid S. Miller int len; 179b9e5567cSDavid S. Miller 180b9e5567cSDavid S. Miller p->value = prom_early_alloc(p->length + 1); 181b9e5567cSDavid S. Miller len = prom_getproperty(node, p->name, p->value, 182b9e5567cSDavid S. Miller p->length); 183b9e5567cSDavid S. Miller if (len <= 0) 184b9e5567cSDavid S. Miller p->length = 0; 185b9e5567cSDavid S. Miller ((unsigned char *)p->value)[p->length] = '\0'; 186b9e5567cSDavid S. Miller } 187b9e5567cSDavid S. Miller } 188b9e5567cSDavid S. Miller return p; 189b9e5567cSDavid S. Miller } 190b9e5567cSDavid S. Miller 1917d9439d5SDavid S. Miller static struct property * __init build_prop_list(phandle node) 192b9e5567cSDavid S. Miller { 193b9e5567cSDavid S. Miller struct property *head, *tail; 194b9e5567cSDavid S. Miller 195b9e5567cSDavid S. Miller head = tail = build_one_prop(node, NULL, 196b9e5567cSDavid S. Miller ".node", &node, sizeof(node)); 197b9e5567cSDavid S. Miller 198b9e5567cSDavid S. Miller tail->next = build_one_prop(node, NULL, NULL, NULL, 0); 199b9e5567cSDavid S. Miller tail = tail->next; 200b9e5567cSDavid S. Miller while(tail) { 201b9e5567cSDavid S. Miller tail->next = build_one_prop(node, tail->name, 202b9e5567cSDavid S. Miller NULL, NULL, 0); 203b9e5567cSDavid S. Miller tail = tail->next; 204b9e5567cSDavid S. Miller } 205b9e5567cSDavid S. Miller 206b9e5567cSDavid S. Miller return head; 207b9e5567cSDavid S. Miller } 2087d9439d5SDavid S. Miller 2097d9439d5SDavid S. Miller static char * __init get_one_property(phandle node, const char *name) 2107d9439d5SDavid S. Miller { 2117d9439d5SDavid S. Miller char *buf = "<NULL>"; 2127d9439d5SDavid S. Miller int len; 2137d9439d5SDavid S. Miller 2147d9439d5SDavid S. Miller len = prom_getproplen(node, name); 2157d9439d5SDavid S. Miller if (len > 0) { 2167d9439d5SDavid S. Miller buf = prom_early_alloc(len); 2177d9439d5SDavid S. Miller len = prom_getproperty(node, name, buf, len); 2187d9439d5SDavid S. Miller } 2197d9439d5SDavid S. Miller 2207d9439d5SDavid S. Miller return buf; 2217d9439d5SDavid S. Miller } 2227d9439d5SDavid S. Miller 22323dc758eSDavid S. Miller static struct device_node * __init prom_create_node(phandle node, 2247d9439d5SDavid S. Miller struct device_node *parent) 2257d9439d5SDavid S. Miller { 2267d9439d5SDavid S. Miller struct device_node *dp; 2277d9439d5SDavid S. Miller 2287d9439d5SDavid S. Miller if (!node) 2297d9439d5SDavid S. Miller return NULL; 2307d9439d5SDavid S. Miller 2317d9439d5SDavid S. Miller dp = prom_early_alloc(sizeof(*dp)); 2327d9439d5SDavid S. Miller dp->unique_id = prom_unique_id++; 2337d9439d5SDavid S. Miller dp->parent = parent; 2347d9439d5SDavid S. Miller 2357d9439d5SDavid S. Miller kref_init(&dp->kref); 2367d9439d5SDavid S. Miller 2377d9439d5SDavid S. Miller dp->name = get_one_property(node, "name"); 2387d9439d5SDavid S. Miller dp->type = get_one_property(node, "device_type"); 239*6016a363SGrant Likely dp->phandle = node; 2407d9439d5SDavid S. Miller 2417d9439d5SDavid S. Miller dp->properties = build_prop_list(node); 2427d9439d5SDavid S. Miller 243bf944c37SDavid S. Miller irq_trans_init(dp); 244bf944c37SDavid S. Miller 2457d9439d5SDavid S. Miller return dp; 2467d9439d5SDavid S. Miller } 2476524036aSDavid S. Miller 248e63829deSKonrad Eisele char * __init build_full_name(struct device_node *dp) 2496524036aSDavid S. Miller { 2506524036aSDavid S. Miller int len, ourlen, plen; 2516524036aSDavid S. Miller char *n; 2526524036aSDavid S. Miller 2536524036aSDavid S. Miller plen = strlen(dp->parent->full_name); 2546524036aSDavid S. Miller ourlen = strlen(dp->path_component_name); 2556524036aSDavid S. Miller len = ourlen + plen + 2; 2566524036aSDavid S. Miller 2576524036aSDavid S. Miller n = prom_early_alloc(len); 2586524036aSDavid S. Miller strcpy(n, dp->parent->full_name); 2596524036aSDavid S. Miller if (!is_root_node(dp->parent)) { 2606524036aSDavid S. Miller strcpy(n + plen, "/"); 2616524036aSDavid S. Miller plen++; 2626524036aSDavid S. Miller } 2636524036aSDavid S. Miller strcpy(n + plen, dp->path_component_name); 2646524036aSDavid S. Miller 2656524036aSDavid S. Miller return n; 2666524036aSDavid S. Miller } 2676524036aSDavid S. Miller 26823dc758eSDavid S. Miller static struct device_node * __init prom_build_tree(struct device_node *parent, 2696524036aSDavid S. Miller phandle node, 2706524036aSDavid S. Miller struct device_node ***nextp) 2716524036aSDavid S. Miller { 2726524036aSDavid S. Miller struct device_node *ret = NULL, *prev_sibling = NULL; 2736524036aSDavid S. Miller struct device_node *dp; 2746524036aSDavid S. Miller 2756524036aSDavid S. Miller while (1) { 2766524036aSDavid S. Miller dp = prom_create_node(node, parent); 2776524036aSDavid S. Miller if (!dp) 2786524036aSDavid S. Miller break; 2796524036aSDavid S. Miller 2806524036aSDavid S. Miller if (prev_sibling) 2816524036aSDavid S. Miller prev_sibling->sibling = dp; 2826524036aSDavid S. Miller 2836524036aSDavid S. Miller if (!ret) 2846524036aSDavid S. Miller ret = dp; 2856524036aSDavid S. Miller prev_sibling = dp; 2866524036aSDavid S. Miller 2876524036aSDavid S. Miller *(*nextp) = dp; 2886524036aSDavid S. Miller *nextp = &dp->allnext; 2896524036aSDavid S. Miller 2906524036aSDavid S. Miller dp->path_component_name = build_path_component(dp); 2916524036aSDavid S. Miller dp->full_name = build_full_name(dp); 2926524036aSDavid S. Miller 2936524036aSDavid S. Miller dp->child = prom_build_tree(dp, prom_getchild(node), nextp); 2946524036aSDavid S. Miller 295e63829deSKonrad Eisele if (prom_build_more) 296e63829deSKonrad Eisele prom_build_more(dp, nextp); 297e63829deSKonrad Eisele 2986524036aSDavid S. Miller node = prom_getsibling(node); 2996524036aSDavid S. Miller } 3006524036aSDavid S. Miller 3016524036aSDavid S. Miller return ret; 3026524036aSDavid S. Miller } 30323dc758eSDavid S. Miller 30423dc758eSDavid S. Miller unsigned int prom_early_allocated __initdata; 30523dc758eSDavid S. Miller 30623dc758eSDavid S. Miller void __init prom_build_devicetree(void) 30723dc758eSDavid S. Miller { 30823dc758eSDavid S. Miller struct device_node **nextp; 30923dc758eSDavid S. Miller 31023dc758eSDavid S. Miller allnodes = prom_create_node(prom_root_node, NULL); 31123dc758eSDavid S. Miller allnodes->path_component_name = ""; 31223dc758eSDavid S. Miller allnodes->full_name = "/"; 31323dc758eSDavid S. Miller 31423dc758eSDavid S. Miller nextp = &allnodes->allnext; 31523dc758eSDavid S. Miller allnodes->child = prom_build_tree(allnodes, 316*6016a363SGrant Likely prom_getchild(allnodes->phandle), 31723dc758eSDavid S. Miller &nextp); 31823dc758eSDavid S. Miller of_console_init(); 31923dc758eSDavid S. Miller 32023dc758eSDavid S. Miller printk("PROM: Built device tree with %u bytes of memory.\n", 32123dc758eSDavid S. Miller prom_early_allocated); 32223dc758eSDavid S. Miller } 323