xref: /openbmc/linux/arch/sparc/kernel/prom_common.c (revision 6524036a1e5736a07466208362d83ddf31aae3ac)
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 
28dfa76060SDavid S. Miller struct device_node *of_find_node_by_phandle(phandle handle)
29dfa76060SDavid S. Miller {
30dfa76060SDavid S. Miller 	struct device_node *np;
31dfa76060SDavid S. Miller 
32dfa76060SDavid S. Miller 	for (np = allnodes; np; np = np->allnext)
33dfa76060SDavid S. Miller 		if (np->node == handle)
34dfa76060SDavid S. Miller 			break;
35dfa76060SDavid S. Miller 
36dfa76060SDavid S. Miller 	return np;
37dfa76060SDavid S. Miller }
38dfa76060SDavid S. Miller EXPORT_SYMBOL(of_find_node_by_phandle);
39dfa76060SDavid S. Miller 
40dfa76060SDavid S. Miller int of_getintprop_default(struct device_node *np, const char *name, int def)
41dfa76060SDavid S. Miller {
42dfa76060SDavid S. Miller 	struct property *prop;
43dfa76060SDavid S. Miller 	int len;
44dfa76060SDavid S. Miller 
45dfa76060SDavid S. Miller 	prop = of_find_property(np, name, &len);
46dfa76060SDavid S. Miller 	if (!prop || len != 4)
47dfa76060SDavid S. Miller 		return def;
48dfa76060SDavid S. Miller 
49dfa76060SDavid S. Miller 	return *(int *) prop->value;
50dfa76060SDavid S. Miller }
51dfa76060SDavid S. Miller EXPORT_SYMBOL(of_getintprop_default);
52dfa76060SDavid S. Miller 
53dfa76060SDavid S. Miller DEFINE_MUTEX(of_set_property_mutex);
54dfa76060SDavid S. Miller EXPORT_SYMBOL(of_set_property_mutex);
55dfa76060SDavid S. Miller 
56dfa76060SDavid S. Miller int of_set_property(struct device_node *dp, const char *name, void *val, int len)
57dfa76060SDavid S. Miller {
58dfa76060SDavid S. Miller 	struct property **prevp;
59dfa76060SDavid S. Miller 	void *new_val;
60dfa76060SDavid S. Miller 	int err;
61dfa76060SDavid S. Miller 
62dfa76060SDavid S. Miller 	new_val = kmalloc(len, GFP_KERNEL);
63dfa76060SDavid S. Miller 	if (!new_val)
64dfa76060SDavid S. Miller 		return -ENOMEM;
65dfa76060SDavid S. Miller 
66dfa76060SDavid S. Miller 	memcpy(new_val, val, len);
67dfa76060SDavid S. Miller 
68dfa76060SDavid S. Miller 	err = -ENODEV;
69dfa76060SDavid S. Miller 
70dfa76060SDavid S. Miller 	write_lock(&devtree_lock);
71dfa76060SDavid S. Miller 	prevp = &dp->properties;
72dfa76060SDavid S. Miller 	while (*prevp) {
73dfa76060SDavid S. Miller 		struct property *prop = *prevp;
74dfa76060SDavid S. Miller 
75dfa76060SDavid S. Miller 		if (!strcasecmp(prop->name, name)) {
76dfa76060SDavid S. Miller 			void *old_val = prop->value;
77dfa76060SDavid S. Miller 			int ret;
78dfa76060SDavid S. Miller 
79dfa76060SDavid S. Miller 			mutex_lock(&of_set_property_mutex);
80dfa76060SDavid S. Miller 			ret = prom_setprop(dp->node, name, val, len);
81dfa76060SDavid S. Miller 			mutex_unlock(&of_set_property_mutex);
82dfa76060SDavid S. Miller 
83dfa76060SDavid S. Miller 			err = -EINVAL;
84dfa76060SDavid S. Miller 			if (ret >= 0) {
85dfa76060SDavid S. Miller 				prop->value = new_val;
86dfa76060SDavid S. Miller 				prop->length = len;
87dfa76060SDavid S. Miller 
88dfa76060SDavid S. Miller 				if (OF_IS_DYNAMIC(prop))
89dfa76060SDavid S. Miller 					kfree(old_val);
90dfa76060SDavid S. Miller 
91dfa76060SDavid S. Miller 				OF_MARK_DYNAMIC(prop);
92dfa76060SDavid S. Miller 
93dfa76060SDavid S. Miller 				err = 0;
94dfa76060SDavid S. Miller 			}
95dfa76060SDavid S. Miller 			break;
96dfa76060SDavid S. Miller 		}
97dfa76060SDavid S. Miller 		prevp = &(*prevp)->next;
98dfa76060SDavid S. Miller 	}
99dfa76060SDavid S. Miller 	write_unlock(&devtree_lock);
100dfa76060SDavid S. Miller 
101dfa76060SDavid S. Miller 	/* XXX Upate procfs if necessary... */
102dfa76060SDavid S. Miller 
103dfa76060SDavid S. Miller 	return err;
104dfa76060SDavid S. Miller }
105dfa76060SDavid S. Miller EXPORT_SYMBOL(of_set_property);
106dfa76060SDavid S. Miller 
107dfa76060SDavid S. Miller int of_find_in_proplist(const char *list, const char *match, int len)
108dfa76060SDavid S. Miller {
109dfa76060SDavid S. Miller 	while (len > 0) {
110dfa76060SDavid S. Miller 		int l;
111dfa76060SDavid S. Miller 
112dfa76060SDavid S. Miller 		if (!strcmp(list, match))
113dfa76060SDavid S. Miller 			return 1;
114dfa76060SDavid S. Miller 		l = strlen(list) + 1;
115dfa76060SDavid S. Miller 		list += l;
116dfa76060SDavid S. Miller 		len -= l;
117dfa76060SDavid S. Miller 	}
118dfa76060SDavid S. Miller 	return 0;
119dfa76060SDavid S. Miller }
120dfa76060SDavid S. Miller EXPORT_SYMBOL(of_find_in_proplist);
121dfa76060SDavid S. Miller 
122e5ff0fe3SDavid S. Miller unsigned int prom_unique_id;
123b9e5567cSDavid S. Miller 
124b9e5567cSDavid S. Miller static struct property * __init build_one_prop(phandle node, char *prev,
125b9e5567cSDavid S. Miller 					       char *special_name,
126b9e5567cSDavid S. Miller 					       void *special_val,
127b9e5567cSDavid S. Miller 					       int special_len)
128b9e5567cSDavid S. Miller {
129b9e5567cSDavid S. Miller 	static struct property *tmp = NULL;
130b9e5567cSDavid S. Miller 	struct property *p;
131b9e5567cSDavid S. Miller 	const char *name;
132b9e5567cSDavid S. Miller 
133b9e5567cSDavid S. Miller 	if (tmp) {
134b9e5567cSDavid S. Miller 		p = tmp;
135b9e5567cSDavid S. Miller 		memset(p, 0, sizeof(*p) + 32);
136b9e5567cSDavid S. Miller 		tmp = NULL;
137b9e5567cSDavid S. Miller 	} else {
138b9e5567cSDavid S. Miller 		p = prom_early_alloc(sizeof(struct property) + 32);
139b9e5567cSDavid S. Miller 		p->unique_id = prom_unique_id++;
140b9e5567cSDavid S. Miller 	}
141b9e5567cSDavid S. Miller 
142b9e5567cSDavid S. Miller 	p->name = (char *) (p + 1);
143b9e5567cSDavid S. Miller 	if (special_name) {
144b9e5567cSDavid S. Miller 		strcpy(p->name, special_name);
145b9e5567cSDavid S. Miller 		p->length = special_len;
146b9e5567cSDavid S. Miller 		p->value = prom_early_alloc(special_len);
147b9e5567cSDavid S. Miller 		memcpy(p->value, special_val, special_len);
148b9e5567cSDavid S. Miller 	} else {
149b9e5567cSDavid S. Miller #ifdef CONFIG_SPARC32
150b9e5567cSDavid S. Miller 		if (prev == NULL) {
151b9e5567cSDavid S. Miller 			name = prom_firstprop(node, NULL);
152b9e5567cSDavid S. Miller 		} else {
153b9e5567cSDavid S. Miller 			name = prom_nextprop(node, prev, NULL);
154b9e5567cSDavid S. Miller 		}
155b9e5567cSDavid S. Miller #else
156b9e5567cSDavid S. Miller 		if (prev == NULL) {
157b9e5567cSDavid S. Miller 			prom_firstprop(node, p->name);
158b9e5567cSDavid S. Miller 		} else {
159b9e5567cSDavid S. Miller 			prom_nextprop(node, prev, p->name);
160b9e5567cSDavid S. Miller 		}
161b9e5567cSDavid S. Miller 		name = p->name;
162b9e5567cSDavid S. Miller #endif
163b9e5567cSDavid S. Miller 		if (strlen(name) == 0) {
164b9e5567cSDavid S. Miller 			tmp = p;
165b9e5567cSDavid S. Miller 			return NULL;
166b9e5567cSDavid S. Miller 		}
167b9e5567cSDavid S. Miller #ifdef CONFIG_SPARC32
168b9e5567cSDavid S. Miller 		strcpy(p->name, name);
169b9e5567cSDavid S. Miller #endif
170b9e5567cSDavid S. Miller 		p->length = prom_getproplen(node, p->name);
171b9e5567cSDavid S. Miller 		if (p->length <= 0) {
172b9e5567cSDavid S. Miller 			p->length = 0;
173b9e5567cSDavid S. Miller 		} else {
174b9e5567cSDavid S. Miller 			int len;
175b9e5567cSDavid S. Miller 
176b9e5567cSDavid S. Miller 			p->value = prom_early_alloc(p->length + 1);
177b9e5567cSDavid S. Miller 			len = prom_getproperty(node, p->name, p->value,
178b9e5567cSDavid S. Miller 					       p->length);
179b9e5567cSDavid S. Miller 			if (len <= 0)
180b9e5567cSDavid S. Miller 				p->length = 0;
181b9e5567cSDavid S. Miller 			((unsigned char *)p->value)[p->length] = '\0';
182b9e5567cSDavid S. Miller 		}
183b9e5567cSDavid S. Miller 	}
184b9e5567cSDavid S. Miller 	return p;
185b9e5567cSDavid S. Miller }
186b9e5567cSDavid S. Miller 
1877d9439d5SDavid S. Miller static struct property * __init build_prop_list(phandle node)
188b9e5567cSDavid S. Miller {
189b9e5567cSDavid S. Miller 	struct property *head, *tail;
190b9e5567cSDavid S. Miller 
191b9e5567cSDavid S. Miller 	head = tail = build_one_prop(node, NULL,
192b9e5567cSDavid S. Miller 				     ".node", &node, sizeof(node));
193b9e5567cSDavid S. Miller 
194b9e5567cSDavid S. Miller 	tail->next = build_one_prop(node, NULL, NULL, NULL, 0);
195b9e5567cSDavid S. Miller 	tail = tail->next;
196b9e5567cSDavid S. Miller 	while(tail) {
197b9e5567cSDavid S. Miller 		tail->next = build_one_prop(node, tail->name,
198b9e5567cSDavid S. Miller 					    NULL, NULL, 0);
199b9e5567cSDavid S. Miller 		tail = tail->next;
200b9e5567cSDavid S. Miller 	}
201b9e5567cSDavid S. Miller 
202b9e5567cSDavid S. Miller 	return head;
203b9e5567cSDavid S. Miller }
2047d9439d5SDavid S. Miller 
2057d9439d5SDavid S. Miller static char * __init get_one_property(phandle node, const char *name)
2067d9439d5SDavid S. Miller {
2077d9439d5SDavid S. Miller 	char *buf = "<NULL>";
2087d9439d5SDavid S. Miller 	int len;
2097d9439d5SDavid S. Miller 
2107d9439d5SDavid S. Miller 	len = prom_getproplen(node, name);
2117d9439d5SDavid S. Miller 	if (len > 0) {
2127d9439d5SDavid S. Miller 		buf = prom_early_alloc(len);
2137d9439d5SDavid S. Miller 		len = prom_getproperty(node, name, buf, len);
2147d9439d5SDavid S. Miller 	}
2157d9439d5SDavid S. Miller 
2167d9439d5SDavid S. Miller 	return buf;
2177d9439d5SDavid S. Miller }
2187d9439d5SDavid S. Miller 
219*6524036aSDavid S. Miller struct device_node * __init prom_create_node(phandle node,
2207d9439d5SDavid S. Miller 					     struct device_node *parent)
2217d9439d5SDavid S. Miller {
2227d9439d5SDavid S. Miller 	struct device_node *dp;
2237d9439d5SDavid S. Miller 
2247d9439d5SDavid S. Miller 	if (!node)
2257d9439d5SDavid S. Miller 		return NULL;
2267d9439d5SDavid S. Miller 
2277d9439d5SDavid S. Miller 	dp = prom_early_alloc(sizeof(*dp));
2287d9439d5SDavid S. Miller 	dp->unique_id = prom_unique_id++;
2297d9439d5SDavid S. Miller 	dp->parent = parent;
2307d9439d5SDavid S. Miller 
2317d9439d5SDavid S. Miller 	kref_init(&dp->kref);
2327d9439d5SDavid S. Miller 
2337d9439d5SDavid S. Miller 	dp->name = get_one_property(node, "name");
2347d9439d5SDavid S. Miller 	dp->type = get_one_property(node, "device_type");
2357d9439d5SDavid S. Miller 	dp->node = node;
2367d9439d5SDavid S. Miller 
2377d9439d5SDavid S. Miller 	/* Build interrupts later... */
2387d9439d5SDavid S. Miller 
2397d9439d5SDavid S. Miller 	dp->properties = build_prop_list(node);
2407d9439d5SDavid S. Miller 
2417d9439d5SDavid S. Miller 	return dp;
2427d9439d5SDavid S. Miller }
243*6524036aSDavid S. Miller 
244*6524036aSDavid S. Miller static char * __init build_full_name(struct device_node *dp)
245*6524036aSDavid S. Miller {
246*6524036aSDavid S. Miller 	int len, ourlen, plen;
247*6524036aSDavid S. Miller 	char *n;
248*6524036aSDavid S. Miller 
249*6524036aSDavid S. Miller 	plen = strlen(dp->parent->full_name);
250*6524036aSDavid S. Miller 	ourlen = strlen(dp->path_component_name);
251*6524036aSDavid S. Miller 	len = ourlen + plen + 2;
252*6524036aSDavid S. Miller 
253*6524036aSDavid S. Miller 	n = prom_early_alloc(len);
254*6524036aSDavid S. Miller 	strcpy(n, dp->parent->full_name);
255*6524036aSDavid S. Miller 	if (!is_root_node(dp->parent)) {
256*6524036aSDavid S. Miller 		strcpy(n + plen, "/");
257*6524036aSDavid S. Miller 		plen++;
258*6524036aSDavid S. Miller 	}
259*6524036aSDavid S. Miller 	strcpy(n + plen, dp->path_component_name);
260*6524036aSDavid S. Miller 
261*6524036aSDavid S. Miller 	return n;
262*6524036aSDavid S. Miller }
263*6524036aSDavid S. Miller 
264*6524036aSDavid S. Miller struct device_node * __init prom_build_tree(struct device_node *parent,
265*6524036aSDavid S. Miller 					    phandle node,
266*6524036aSDavid S. Miller 					    struct device_node ***nextp)
267*6524036aSDavid S. Miller {
268*6524036aSDavid S. Miller 	struct device_node *ret = NULL, *prev_sibling = NULL;
269*6524036aSDavid S. Miller 	struct device_node *dp;
270*6524036aSDavid S. Miller 
271*6524036aSDavid S. Miller 	while (1) {
272*6524036aSDavid S. Miller 		dp = prom_create_node(node, parent);
273*6524036aSDavid S. Miller 		if (!dp)
274*6524036aSDavid S. Miller 			break;
275*6524036aSDavid S. Miller 
276*6524036aSDavid S. Miller 		if (prev_sibling)
277*6524036aSDavid S. Miller 			prev_sibling->sibling = dp;
278*6524036aSDavid S. Miller 
279*6524036aSDavid S. Miller 		if (!ret)
280*6524036aSDavid S. Miller 			ret = dp;
281*6524036aSDavid S. Miller 		prev_sibling = dp;
282*6524036aSDavid S. Miller 
283*6524036aSDavid S. Miller 		*(*nextp) = dp;
284*6524036aSDavid S. Miller 		*nextp = &dp->allnext;
285*6524036aSDavid S. Miller 
286*6524036aSDavid S. Miller 		dp->path_component_name = build_path_component(dp);
287*6524036aSDavid S. Miller 		dp->full_name = build_full_name(dp);
288*6524036aSDavid S. Miller 
289*6524036aSDavid S. Miller 		dp->child = prom_build_tree(dp, prom_getchild(node), nextp);
290*6524036aSDavid S. Miller 
291*6524036aSDavid S. Miller 		node = prom_getsibling(node);
292*6524036aSDavid S. Miller 	}
293*6524036aSDavid S. Miller 
294*6524036aSDavid S. Miller 	return ret;
295*6524036aSDavid S. Miller }
296