mirror of
https://github.com/torvalds/linux.git
synced 2024-12-29 22:31:32 +00:00
0829f6d1f6
After the move to having device nodes be proper kobjects the lifecycle of the node needs to be controlled better. At first convert of_add_node() in the unflattened functions to of_init_node() which initializes the kobject so that of_node_get/put work correctly even before of_init is called. Afterwards introduce of_node_is_initialized & of_node_is_attached that query the underlying kobject about the state (attached means kobj is visible in sysfs) Using that make sure the lifecycle of the tree is correct at all times. Signed-off-by: Pantelis Antoniou <panto@antoniou-consulting.com> [grant.likely: moved of_node_init() calls, fixed up locking, and dropped __of_populate() hunks] Signed-off-by: Grant Likely <grant.likely@linaro.org>
255 lines
5.9 KiB
C
255 lines
5.9 KiB
C
/* pdt.c: OF PROM device tree support code.
|
|
*
|
|
* Paul Mackerras August 1996.
|
|
* Copyright (C) 1996-2005 Paul Mackerras.
|
|
*
|
|
* Adapted for 64bit PowerPC by Dave Engebretsen and Peter Bergner.
|
|
* {engebret|bergner}@us.ibm.com
|
|
*
|
|
* Adapted for sparc by David S. Miller davem@davemloft.net
|
|
* Adapted for multiple architectures by Andres Salomon <dilinger@queued.net>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version
|
|
* 2 of the License, or (at your option) any later version.
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_pdt.h>
|
|
|
|
static struct of_pdt_ops *of_pdt_prom_ops __initdata;
|
|
|
|
void __initdata (*of_pdt_build_more)(struct device_node *dp,
|
|
struct device_node ***nextp);
|
|
|
|
#if defined(CONFIG_SPARC)
|
|
unsigned int of_pdt_unique_id __initdata;
|
|
|
|
#define of_pdt_incr_unique_id(p) do { \
|
|
(p)->unique_id = of_pdt_unique_id++; \
|
|
} while (0)
|
|
|
|
static char * __init of_pdt_build_full_name(struct device_node *dp)
|
|
{
|
|
int len, ourlen, plen;
|
|
char *n;
|
|
|
|
dp->path_component_name = build_path_component(dp);
|
|
|
|
plen = strlen(dp->parent->full_name);
|
|
ourlen = strlen(dp->path_component_name);
|
|
len = ourlen + plen + 2;
|
|
|
|
n = prom_early_alloc(len);
|
|
strcpy(n, dp->parent->full_name);
|
|
if (!of_node_is_root(dp->parent)) {
|
|
strcpy(n + plen, "/");
|
|
plen++;
|
|
}
|
|
strcpy(n + plen, dp->path_component_name);
|
|
|
|
return n;
|
|
}
|
|
|
|
#else /* CONFIG_SPARC */
|
|
|
|
static inline void of_pdt_incr_unique_id(void *p) { }
|
|
static inline void irq_trans_init(struct device_node *dp) { }
|
|
|
|
static char * __init of_pdt_build_full_name(struct device_node *dp)
|
|
{
|
|
static int failsafe_id = 0; /* for generating unique names on failure */
|
|
char *buf;
|
|
int len;
|
|
|
|
if (of_pdt_prom_ops->pkg2path(dp->phandle, NULL, 0, &len))
|
|
goto failsafe;
|
|
|
|
buf = prom_early_alloc(len + 1);
|
|
if (of_pdt_prom_ops->pkg2path(dp->phandle, buf, len, &len))
|
|
goto failsafe;
|
|
return buf;
|
|
|
|
failsafe:
|
|
buf = prom_early_alloc(strlen(dp->parent->full_name) +
|
|
strlen(dp->name) + 16);
|
|
sprintf(buf, "%s/%s@unknown%i",
|
|
of_node_is_root(dp->parent) ? "" : dp->parent->full_name,
|
|
dp->name, failsafe_id++);
|
|
pr_err("%s: pkg2path failed; assigning %s\n", __func__, buf);
|
|
return buf;
|
|
}
|
|
|
|
#endif /* !CONFIG_SPARC */
|
|
|
|
static struct property * __init of_pdt_build_one_prop(phandle node, char *prev,
|
|
char *special_name,
|
|
void *special_val,
|
|
int special_len)
|
|
{
|
|
static struct property *tmp = NULL;
|
|
struct property *p;
|
|
int err;
|
|
|
|
if (tmp) {
|
|
p = tmp;
|
|
memset(p, 0, sizeof(*p) + 32);
|
|
tmp = NULL;
|
|
} else {
|
|
p = prom_early_alloc(sizeof(struct property) + 32);
|
|
of_pdt_incr_unique_id(p);
|
|
}
|
|
|
|
p->name = (char *) (p + 1);
|
|
if (special_name) {
|
|
strcpy(p->name, special_name);
|
|
p->length = special_len;
|
|
p->value = prom_early_alloc(special_len);
|
|
memcpy(p->value, special_val, special_len);
|
|
} else {
|
|
err = of_pdt_prom_ops->nextprop(node, prev, p->name);
|
|
if (err) {
|
|
tmp = p;
|
|
return NULL;
|
|
}
|
|
p->length = of_pdt_prom_ops->getproplen(node, p->name);
|
|
if (p->length <= 0) {
|
|
p->length = 0;
|
|
} else {
|
|
int len;
|
|
|
|
p->value = prom_early_alloc(p->length + 1);
|
|
len = of_pdt_prom_ops->getproperty(node, p->name,
|
|
p->value, p->length);
|
|
if (len <= 0)
|
|
p->length = 0;
|
|
((unsigned char *)p->value)[p->length] = '\0';
|
|
}
|
|
}
|
|
return p;
|
|
}
|
|
|
|
static struct property * __init of_pdt_build_prop_list(phandle node)
|
|
{
|
|
struct property *head, *tail;
|
|
|
|
head = tail = of_pdt_build_one_prop(node, NULL,
|
|
".node", &node, sizeof(node));
|
|
|
|
tail->next = of_pdt_build_one_prop(node, NULL, NULL, NULL, 0);
|
|
tail = tail->next;
|
|
while(tail) {
|
|
tail->next = of_pdt_build_one_prop(node, tail->name,
|
|
NULL, NULL, 0);
|
|
tail = tail->next;
|
|
}
|
|
|
|
return head;
|
|
}
|
|
|
|
static char * __init of_pdt_get_one_property(phandle node, const char *name)
|
|
{
|
|
char *buf = "<NULL>";
|
|
int len;
|
|
|
|
len = of_pdt_prom_ops->getproplen(node, name);
|
|
if (len > 0) {
|
|
buf = prom_early_alloc(len);
|
|
len = of_pdt_prom_ops->getproperty(node, name, buf, len);
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
static struct device_node * __init of_pdt_create_node(phandle node,
|
|
struct device_node *parent)
|
|
{
|
|
struct device_node *dp;
|
|
|
|
if (!node)
|
|
return NULL;
|
|
|
|
dp = prom_early_alloc(sizeof(*dp));
|
|
of_node_init(dp);
|
|
of_pdt_incr_unique_id(dp);
|
|
dp->parent = parent;
|
|
|
|
dp->name = of_pdt_get_one_property(node, "name");
|
|
dp->type = of_pdt_get_one_property(node, "device_type");
|
|
dp->phandle = node;
|
|
|
|
dp->properties = of_pdt_build_prop_list(node);
|
|
|
|
irq_trans_init(dp);
|
|
|
|
return dp;
|
|
}
|
|
|
|
static struct device_node * __init of_pdt_build_tree(struct device_node *parent,
|
|
phandle node,
|
|
struct device_node ***nextp)
|
|
{
|
|
struct device_node *ret = NULL, *prev_sibling = NULL;
|
|
struct device_node *dp;
|
|
|
|
while (1) {
|
|
dp = of_pdt_create_node(node, parent);
|
|
if (!dp)
|
|
break;
|
|
|
|
if (prev_sibling)
|
|
prev_sibling->sibling = dp;
|
|
|
|
if (!ret)
|
|
ret = dp;
|
|
prev_sibling = dp;
|
|
|
|
*(*nextp) = dp;
|
|
*nextp = &dp->allnext;
|
|
|
|
dp->full_name = of_pdt_build_full_name(dp);
|
|
|
|
dp->child = of_pdt_build_tree(dp,
|
|
of_pdt_prom_ops->getchild(node), nextp);
|
|
|
|
if (of_pdt_build_more)
|
|
of_pdt_build_more(dp, nextp);
|
|
|
|
node = of_pdt_prom_ops->getsibling(node);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void * __init kernel_tree_alloc(u64 size, u64 align)
|
|
{
|
|
return prom_early_alloc(size);
|
|
}
|
|
|
|
void __init of_pdt_build_devicetree(phandle root_node, struct of_pdt_ops *ops)
|
|
{
|
|
struct device_node **nextp;
|
|
|
|
BUG_ON(!ops);
|
|
of_pdt_prom_ops = ops;
|
|
|
|
of_allnodes = of_pdt_create_node(root_node, NULL);
|
|
#if defined(CONFIG_SPARC)
|
|
of_allnodes->path_component_name = "";
|
|
#endif
|
|
of_allnodes->full_name = "/";
|
|
|
|
nextp = &of_allnodes->allnext;
|
|
of_allnodes->child = of_pdt_build_tree(of_allnodes,
|
|
of_pdt_prom_ops->getchild(of_allnodes->phandle), &nextp);
|
|
|
|
/* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */
|
|
of_alias_scan(kernel_tree_alloc);
|
|
}
|