xref: /openbmc/linux/arch/sparc/kernel/prom_common.c (revision ad07aed8ca2023bcfe224a5e3e55bafec2c741d0)
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>
25dfa76060SDavid S. Miller 
26dfa76060SDavid S. Miller #include "prom.h"
27dfa76060SDavid S. Miller 
28*ad07aed8SDavid S. Miller struct device_node *of_console_device;
29*ad07aed8SDavid S. Miller EXPORT_SYMBOL(of_console_device);
30*ad07aed8SDavid S. Miller 
31*ad07aed8SDavid S. Miller char *of_console_path;
32*ad07aed8SDavid S. Miller EXPORT_SYMBOL(of_console_path);
33*ad07aed8SDavid S. Miller 
34*ad07aed8SDavid S. Miller char *of_console_options;
35*ad07aed8SDavid S. Miller EXPORT_SYMBOL(of_console_options);
36*ad07aed8SDavid S. Miller 
37dfa76060SDavid S. Miller struct device_node *of_find_node_by_phandle(phandle handle)
38dfa76060SDavid S. Miller {
39dfa76060SDavid S. Miller 	struct device_node *np;
40dfa76060SDavid S. Miller 
41dfa76060SDavid S. Miller 	for (np = allnodes; np; np = np->allnext)
42dfa76060SDavid S. Miller 		if (np->node == handle)
43dfa76060SDavid S. Miller 			break;
44dfa76060SDavid S. Miller 
45dfa76060SDavid S. Miller 	return np;
46dfa76060SDavid S. Miller }
47dfa76060SDavid S. Miller EXPORT_SYMBOL(of_find_node_by_phandle);
48dfa76060SDavid S. Miller 
49dfa76060SDavid S. Miller int of_getintprop_default(struct device_node *np, const char *name, int def)
50dfa76060SDavid S. Miller {
51dfa76060SDavid S. Miller 	struct property *prop;
52dfa76060SDavid S. Miller 	int len;
53dfa76060SDavid S. Miller 
54dfa76060SDavid S. Miller 	prop = of_find_property(np, name, &len);
55dfa76060SDavid S. Miller 	if (!prop || len != 4)
56dfa76060SDavid S. Miller 		return def;
57dfa76060SDavid S. Miller 
58dfa76060SDavid S. Miller 	return *(int *) prop->value;
59dfa76060SDavid S. Miller }
60dfa76060SDavid S. Miller EXPORT_SYMBOL(of_getintprop_default);
61dfa76060SDavid S. Miller 
62dfa76060SDavid S. Miller DEFINE_MUTEX(of_set_property_mutex);
63dfa76060SDavid S. Miller EXPORT_SYMBOL(of_set_property_mutex);
64dfa76060SDavid S. Miller 
65dfa76060SDavid S. Miller int of_set_property(struct device_node *dp, const char *name, void *val, int len)
66dfa76060SDavid S. Miller {
67dfa76060SDavid S. Miller 	struct property **prevp;
68dfa76060SDavid S. Miller 	void *new_val;
69dfa76060SDavid S. Miller 	int err;
70dfa76060SDavid S. Miller 
71dfa76060SDavid S. Miller 	new_val = kmalloc(len, GFP_KERNEL);
72dfa76060SDavid S. Miller 	if (!new_val)
73dfa76060SDavid S. Miller 		return -ENOMEM;
74dfa76060SDavid S. Miller 
75dfa76060SDavid S. Miller 	memcpy(new_val, val, len);
76dfa76060SDavid S. Miller 
77dfa76060SDavid S. Miller 	err = -ENODEV;
78dfa76060SDavid S. Miller 
79dfa76060SDavid S. Miller 	write_lock(&devtree_lock);
80dfa76060SDavid S. Miller 	prevp = &dp->properties;
81dfa76060SDavid S. Miller 	while (*prevp) {
82dfa76060SDavid S. Miller 		struct property *prop = *prevp;
83dfa76060SDavid S. Miller 
84dfa76060SDavid S. Miller 		if (!strcasecmp(prop->name, name)) {
85dfa76060SDavid S. Miller 			void *old_val = prop->value;
86dfa76060SDavid S. Miller 			int ret;
87dfa76060SDavid S. Miller 
88dfa76060SDavid S. Miller 			mutex_lock(&of_set_property_mutex);
89dfa76060SDavid S. Miller 			ret = prom_setprop(dp->node, name, val, len);
90dfa76060SDavid S. Miller 			mutex_unlock(&of_set_property_mutex);
91dfa76060SDavid S. Miller 
92dfa76060SDavid S. Miller 			err = -EINVAL;
93dfa76060SDavid S. Miller 			if (ret >= 0) {
94dfa76060SDavid S. Miller 				prop->value = new_val;
95dfa76060SDavid S. Miller 				prop->length = len;
96dfa76060SDavid S. Miller 
97dfa76060SDavid S. Miller 				if (OF_IS_DYNAMIC(prop))
98dfa76060SDavid S. Miller 					kfree(old_val);
99dfa76060SDavid S. Miller 
100dfa76060SDavid S. Miller 				OF_MARK_DYNAMIC(prop);
101dfa76060SDavid S. Miller 
102dfa76060SDavid S. Miller 				err = 0;
103dfa76060SDavid S. Miller 			}
104dfa76060SDavid S. Miller 			break;
105dfa76060SDavid S. Miller 		}
106dfa76060SDavid S. Miller 		prevp = &(*prevp)->next;
107dfa76060SDavid S. Miller 	}
108dfa76060SDavid S. Miller 	write_unlock(&devtree_lock);
109dfa76060SDavid S. Miller 
110dfa76060SDavid S. Miller 	/* XXX Upate procfs if necessary... */
111dfa76060SDavid S. Miller 
112dfa76060SDavid S. Miller 	return err;
113dfa76060SDavid S. Miller }
114dfa76060SDavid S. Miller EXPORT_SYMBOL(of_set_property);
115dfa76060SDavid S. Miller 
116dfa76060SDavid S. Miller int of_find_in_proplist(const char *list, const char *match, int len)
117dfa76060SDavid S. Miller {
118dfa76060SDavid S. Miller 	while (len > 0) {
119dfa76060SDavid S. Miller 		int l;
120dfa76060SDavid S. Miller 
121dfa76060SDavid S. Miller 		if (!strcmp(list, match))
122dfa76060SDavid S. Miller 			return 1;
123dfa76060SDavid S. Miller 		l = strlen(list) + 1;
124dfa76060SDavid S. Miller 		list += l;
125dfa76060SDavid S. Miller 		len -= l;
126dfa76060SDavid S. Miller 	}
127dfa76060SDavid S. Miller 	return 0;
128dfa76060SDavid S. Miller }
129dfa76060SDavid S. Miller EXPORT_SYMBOL(of_find_in_proplist);
130dfa76060SDavid S. Miller 
131e5ff0fe3SDavid S. Miller unsigned int prom_unique_id;
132b9e5567cSDavid S. Miller 
133b9e5567cSDavid S. Miller static struct property * __init build_one_prop(phandle node, char *prev,
134b9e5567cSDavid S. Miller 					       char *special_name,
135b9e5567cSDavid S. Miller 					       void *special_val,
136b9e5567cSDavid S. Miller 					       int special_len)
137b9e5567cSDavid S. Miller {
138b9e5567cSDavid S. Miller 	static struct property *tmp = NULL;
139b9e5567cSDavid S. Miller 	struct property *p;
140b9e5567cSDavid S. Miller 	const char *name;
141b9e5567cSDavid S. Miller 
142b9e5567cSDavid S. Miller 	if (tmp) {
143b9e5567cSDavid S. Miller 		p = tmp;
144b9e5567cSDavid S. Miller 		memset(p, 0, sizeof(*p) + 32);
145b9e5567cSDavid S. Miller 		tmp = NULL;
146b9e5567cSDavid S. Miller 	} else {
147b9e5567cSDavid S. Miller 		p = prom_early_alloc(sizeof(struct property) + 32);
148b9e5567cSDavid S. Miller 		p->unique_id = prom_unique_id++;
149b9e5567cSDavid S. Miller 	}
150b9e5567cSDavid S. Miller 
151b9e5567cSDavid S. Miller 	p->name = (char *) (p + 1);
152b9e5567cSDavid S. Miller 	if (special_name) {
153b9e5567cSDavid S. Miller 		strcpy(p->name, special_name);
154b9e5567cSDavid S. Miller 		p->length = special_len;
155b9e5567cSDavid S. Miller 		p->value = prom_early_alloc(special_len);
156b9e5567cSDavid S. Miller 		memcpy(p->value, special_val, special_len);
157b9e5567cSDavid S. Miller 	} else {
158b9e5567cSDavid S. Miller #ifdef CONFIG_SPARC32
159b9e5567cSDavid S. Miller 		if (prev == NULL) {
160b9e5567cSDavid S. Miller 			name = prom_firstprop(node, NULL);
161b9e5567cSDavid S. Miller 		} else {
162b9e5567cSDavid S. Miller 			name = prom_nextprop(node, prev, NULL);
163b9e5567cSDavid S. Miller 		}
164b9e5567cSDavid S. Miller #else
165b9e5567cSDavid S. Miller 		if (prev == NULL) {
166b9e5567cSDavid S. Miller 			prom_firstprop(node, p->name);
167b9e5567cSDavid S. Miller 		} else {
168b9e5567cSDavid S. Miller 			prom_nextprop(node, prev, p->name);
169b9e5567cSDavid S. Miller 		}
170b9e5567cSDavid S. Miller 		name = p->name;
171b9e5567cSDavid S. Miller #endif
172b9e5567cSDavid S. Miller 		if (strlen(name) == 0) {
173b9e5567cSDavid S. Miller 			tmp = p;
174b9e5567cSDavid S. Miller 			return NULL;
175b9e5567cSDavid S. Miller 		}
176b9e5567cSDavid S. Miller #ifdef CONFIG_SPARC32
177b9e5567cSDavid S. Miller 		strcpy(p->name, name);
178b9e5567cSDavid S. Miller #endif
179b9e5567cSDavid S. Miller 		p->length = prom_getproplen(node, p->name);
180b9e5567cSDavid S. Miller 		if (p->length <= 0) {
181b9e5567cSDavid S. Miller 			p->length = 0;
182b9e5567cSDavid S. Miller 		} else {
183b9e5567cSDavid S. Miller 			int len;
184b9e5567cSDavid S. Miller 
185b9e5567cSDavid S. Miller 			p->value = prom_early_alloc(p->length + 1);
186b9e5567cSDavid S. Miller 			len = prom_getproperty(node, p->name, p->value,
187b9e5567cSDavid S. Miller 					       p->length);
188b9e5567cSDavid S. Miller 			if (len <= 0)
189b9e5567cSDavid S. Miller 				p->length = 0;
190b9e5567cSDavid S. Miller 			((unsigned char *)p->value)[p->length] = '\0';
191b9e5567cSDavid S. Miller 		}
192b9e5567cSDavid S. Miller 	}
193b9e5567cSDavid S. Miller 	return p;
194b9e5567cSDavid S. Miller }
195b9e5567cSDavid S. Miller 
1967d9439d5SDavid S. Miller static struct property * __init build_prop_list(phandle node)
197b9e5567cSDavid S. Miller {
198b9e5567cSDavid S. Miller 	struct property *head, *tail;
199b9e5567cSDavid S. Miller 
200b9e5567cSDavid S. Miller 	head = tail = build_one_prop(node, NULL,
201b9e5567cSDavid S. Miller 				     ".node", &node, sizeof(node));
202b9e5567cSDavid S. Miller 
203b9e5567cSDavid S. Miller 	tail->next = build_one_prop(node, NULL, NULL, NULL, 0);
204b9e5567cSDavid S. Miller 	tail = tail->next;
205b9e5567cSDavid S. Miller 	while(tail) {
206b9e5567cSDavid S. Miller 		tail->next = build_one_prop(node, tail->name,
207b9e5567cSDavid S. Miller 					    NULL, NULL, 0);
208b9e5567cSDavid S. Miller 		tail = tail->next;
209b9e5567cSDavid S. Miller 	}
210b9e5567cSDavid S. Miller 
211b9e5567cSDavid S. Miller 	return head;
212b9e5567cSDavid S. Miller }
2137d9439d5SDavid S. Miller 
2147d9439d5SDavid S. Miller static char * __init get_one_property(phandle node, const char *name)
2157d9439d5SDavid S. Miller {
2167d9439d5SDavid S. Miller 	char *buf = "<NULL>";
2177d9439d5SDavid S. Miller 	int len;
2187d9439d5SDavid S. Miller 
2197d9439d5SDavid S. Miller 	len = prom_getproplen(node, name);
2207d9439d5SDavid S. Miller 	if (len > 0) {
2217d9439d5SDavid S. Miller 		buf = prom_early_alloc(len);
2227d9439d5SDavid S. Miller 		len = prom_getproperty(node, name, buf, len);
2237d9439d5SDavid S. Miller 	}
2247d9439d5SDavid S. Miller 
2257d9439d5SDavid S. Miller 	return buf;
2267d9439d5SDavid S. Miller }
2277d9439d5SDavid S. Miller 
22823dc758eSDavid S. Miller static struct device_node * __init prom_create_node(phandle node,
2297d9439d5SDavid S. Miller 						    struct device_node *parent)
2307d9439d5SDavid S. Miller {
2317d9439d5SDavid S. Miller 	struct device_node *dp;
2327d9439d5SDavid S. Miller 
2337d9439d5SDavid S. Miller 	if (!node)
2347d9439d5SDavid S. Miller 		return NULL;
2357d9439d5SDavid S. Miller 
2367d9439d5SDavid S. Miller 	dp = prom_early_alloc(sizeof(*dp));
2377d9439d5SDavid S. Miller 	dp->unique_id = prom_unique_id++;
2387d9439d5SDavid S. Miller 	dp->parent = parent;
2397d9439d5SDavid S. Miller 
2407d9439d5SDavid S. Miller 	kref_init(&dp->kref);
2417d9439d5SDavid S. Miller 
2427d9439d5SDavid S. Miller 	dp->name = get_one_property(node, "name");
2437d9439d5SDavid S. Miller 	dp->type = get_one_property(node, "device_type");
2447d9439d5SDavid S. Miller 	dp->node = node;
2457d9439d5SDavid S. Miller 
2467d9439d5SDavid S. Miller 	/* Build interrupts later... */
2477d9439d5SDavid S. Miller 
2487d9439d5SDavid S. Miller 	dp->properties = build_prop_list(node);
2497d9439d5SDavid S. Miller 
2507d9439d5SDavid S. Miller 	return dp;
2517d9439d5SDavid S. Miller }
2526524036aSDavid S. Miller 
2536524036aSDavid S. Miller static char * __init build_full_name(struct device_node *dp)
2546524036aSDavid S. Miller {
2556524036aSDavid S. Miller 	int len, ourlen, plen;
2566524036aSDavid S. Miller 	char *n;
2576524036aSDavid S. Miller 
2586524036aSDavid S. Miller 	plen = strlen(dp->parent->full_name);
2596524036aSDavid S. Miller 	ourlen = strlen(dp->path_component_name);
2606524036aSDavid S. Miller 	len = ourlen + plen + 2;
2616524036aSDavid S. Miller 
2626524036aSDavid S. Miller 	n = prom_early_alloc(len);
2636524036aSDavid S. Miller 	strcpy(n, dp->parent->full_name);
2646524036aSDavid S. Miller 	if (!is_root_node(dp->parent)) {
2656524036aSDavid S. Miller 		strcpy(n + plen, "/");
2666524036aSDavid S. Miller 		plen++;
2676524036aSDavid S. Miller 	}
2686524036aSDavid S. Miller 	strcpy(n + plen, dp->path_component_name);
2696524036aSDavid S. Miller 
2706524036aSDavid S. Miller 	return n;
2716524036aSDavid S. Miller }
2726524036aSDavid S. Miller 
27323dc758eSDavid S. Miller static struct device_node * __init prom_build_tree(struct device_node *parent,
2746524036aSDavid S. Miller 						   phandle node,
2756524036aSDavid S. Miller 						   struct device_node ***nextp)
2766524036aSDavid S. Miller {
2776524036aSDavid S. Miller 	struct device_node *ret = NULL, *prev_sibling = NULL;
2786524036aSDavid S. Miller 	struct device_node *dp;
2796524036aSDavid S. Miller 
2806524036aSDavid S. Miller 	while (1) {
2816524036aSDavid S. Miller 		dp = prom_create_node(node, parent);
2826524036aSDavid S. Miller 		if (!dp)
2836524036aSDavid S. Miller 			break;
2846524036aSDavid S. Miller 
2856524036aSDavid S. Miller 		if (prev_sibling)
2866524036aSDavid S. Miller 			prev_sibling->sibling = dp;
2876524036aSDavid S. Miller 
2886524036aSDavid S. Miller 		if (!ret)
2896524036aSDavid S. Miller 			ret = dp;
2906524036aSDavid S. Miller 		prev_sibling = dp;
2916524036aSDavid S. Miller 
2926524036aSDavid S. Miller 		*(*nextp) = dp;
2936524036aSDavid S. Miller 		*nextp = &dp->allnext;
2946524036aSDavid S. Miller 
2956524036aSDavid S. Miller 		dp->path_component_name = build_path_component(dp);
2966524036aSDavid S. Miller 		dp->full_name = build_full_name(dp);
2976524036aSDavid S. Miller 
2986524036aSDavid S. Miller 		dp->child = prom_build_tree(dp, prom_getchild(node), nextp);
2996524036aSDavid S. Miller 
3006524036aSDavid S. Miller 		node = prom_getsibling(node);
3016524036aSDavid S. Miller 	}
3026524036aSDavid S. Miller 
3036524036aSDavid S. Miller 	return ret;
3046524036aSDavid S. Miller }
30523dc758eSDavid S. Miller 
30623dc758eSDavid S. Miller unsigned int prom_early_allocated __initdata;
30723dc758eSDavid S. Miller 
30823dc758eSDavid S. Miller void __init prom_build_devicetree(void)
30923dc758eSDavid S. Miller {
31023dc758eSDavid S. Miller 	struct device_node **nextp;
31123dc758eSDavid S. Miller 
31223dc758eSDavid S. Miller 	allnodes = prom_create_node(prom_root_node, NULL);
31323dc758eSDavid S. Miller 	allnodes->path_component_name = "";
31423dc758eSDavid S. Miller 	allnodes->full_name = "/";
31523dc758eSDavid S. Miller 
31623dc758eSDavid S. Miller 	nextp = &allnodes->allnext;
31723dc758eSDavid S. Miller 	allnodes->child = prom_build_tree(allnodes,
31823dc758eSDavid S. Miller 					  prom_getchild(allnodes->node),
31923dc758eSDavid S. Miller 					  &nextp);
32023dc758eSDavid S. Miller 	of_console_init();
32123dc758eSDavid S. Miller 
32223dc758eSDavid S. Miller 	printk("PROM: Built device tree with %u bytes of memory.\n",
32323dc758eSDavid S. Miller 	       prom_early_allocated);
32423dc758eSDavid S. Miller 
32523dc758eSDavid S. Miller 	of_fill_in_cpu_data();
32623dc758eSDavid S. Miller }
327