xref: /openbmc/linux/arch/sparc/kernel/prom_common.c (revision 1c9d80ddc60f8ac26344ec3db9830e5f8016c16d)
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)
45dfa76060SDavid S. Miller 		if (np->node == 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 
82*1c9d80ddSDavid 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 
92dfa76060SDavid S. Miller 			ret = prom_setprop(dp->node, 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);
111*1c9d80ddSDavid 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");
2397d9439d5SDavid S. Miller 	dp->node = 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,
31623dc758eSDavid S. Miller 					  prom_getchild(allnodes->node),
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