2010-10-11 07:42:33 +04:00
/* pdt.c: OF PROM device tree support code.
2010-08-30 07:57:28 +04:00
*
* Paul Mackerras August 1996.
* Copyright ( C ) 1996 - 2005 Paul Mackerras .
*
* Adapted for 64 bit PowerPC by Dave Engebretsen and Peter Bergner .
* { engebret | bergner } @ us . ibm . com
*
* Adapted for sparc by David S . Miller davem @ davemloft . net
2010-10-11 07:42:33 +04:00
* Adapted for multiple architectures by Andres Salomon < dilinger @ queued . net >
2010-08-30 07:57:28 +04:00
*
* 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>
2010-10-11 07:42:33 +04:00
# include <linux/of_pdt.h>
2010-08-30 07:57:28 +04:00
# include <asm/prom.h>
2010-10-11 07:49:45 +04:00
static struct of_pdt_ops * of_pdt_prom_ops __initdata ;
2010-08-30 07:57:28 +04:00
2010-10-11 07:51:25 +04:00
void __initdata ( * of_pdt_build_more ) ( struct device_node * dp ,
2010-10-11 07:42:33 +04:00
struct device_node * * * nextp ) ;
2010-08-30 07:57:28 +04:00
2010-10-11 07:42:33 +04:00
# 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 inline const char * of_pdt_node_name ( struct device_node * dp )
{
return dp - > path_component_name ;
}
# else
static inline void of_pdt_incr_unique_id ( void * p ) { }
static inline void irq_trans_init ( struct device_node * dp ) { }
static inline const char * of_pdt_node_name ( struct device_node * dp )
{
return dp - > name ;
}
# endif /* !CONFIG_SPARC */
2010-08-30 07:57:28 +04:00
2010-10-11 07:51:25 +04:00
static struct property * __init of_pdt_build_one_prop ( phandle node , char * prev ,
2010-08-30 07:57:28 +04:00
char * special_name ,
void * special_val ,
int special_len )
{
static struct property * tmp = NULL ;
struct property * p ;
2010-10-11 07:49:45 +04:00
int err ;
2010-08-30 07:57:28 +04:00
if ( tmp ) {
p = tmp ;
memset ( p , 0 , sizeof ( * p ) + 32 ) ;
tmp = NULL ;
} else {
p = prom_early_alloc ( sizeof ( struct property ) + 32 ) ;
2010-10-11 07:42:33 +04:00
of_pdt_incr_unique_id ( p ) ;
2010-08-30 07:57:28 +04:00
}
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 {
2010-10-11 07:49:45 +04:00
err = of_pdt_prom_ops - > nextprop ( node , prev , p - > name ) ;
if ( err ) {
2010-08-30 07:57:28 +04:00
tmp = p ;
return NULL ;
}
2010-10-11 07:49:45 +04:00
p - > length = of_pdt_prom_ops - > getproplen ( node , p - > name ) ;
2010-08-30 07:57:28 +04:00
if ( p - > length < = 0 ) {
p - > length = 0 ;
} else {
int len ;
p - > value = prom_early_alloc ( p - > length + 1 ) ;
2010-10-11 07:49:45 +04:00
len = of_pdt_prom_ops - > getproperty ( node , p - > name ,
p - > value , p - > length ) ;
2010-08-30 07:57:28 +04:00
if ( len < = 0 )
p - > length = 0 ;
( ( unsigned char * ) p - > value ) [ p - > length ] = ' \0 ' ;
}
}
return p ;
}
2010-10-11 07:51:25 +04:00
static struct property * __init of_pdt_build_prop_list ( phandle node )
2010-08-30 07:57:28 +04:00
{
struct property * head , * tail ;
2010-10-11 07:51:25 +04:00
head = tail = of_pdt_build_one_prop ( node , NULL ,
2010-08-30 07:57:28 +04:00
" .node " , & node , sizeof ( node ) ) ;
2010-10-11 07:51:25 +04:00
tail - > next = of_pdt_build_one_prop ( node , NULL , NULL , NULL , 0 ) ;
2010-08-30 07:57:28 +04:00
tail = tail - > next ;
while ( tail ) {
2010-10-11 07:51:25 +04:00
tail - > next = of_pdt_build_one_prop ( node , tail - > name ,
2010-08-30 07:57:28 +04:00
NULL , NULL , 0 ) ;
tail = tail - > next ;
}
return head ;
}
2010-10-11 07:51:25 +04:00
static char * __init of_pdt_get_one_property ( phandle node , const char * name )
2010-08-30 07:57:28 +04:00
{
char * buf = " <NULL> " ;
int len ;
2010-10-11 07:49:45 +04:00
len = of_pdt_prom_ops - > getproplen ( node , name ) ;
2010-08-30 07:57:28 +04:00
if ( len > 0 ) {
buf = prom_early_alloc ( len ) ;
2010-10-11 07:49:45 +04:00
len = of_pdt_prom_ops - > getproperty ( node , name , buf , len ) ;
2010-08-30 07:57:28 +04:00
}
return buf ;
}
2010-10-11 07:52:57 +04:00
static char * __init of_pdt_try_pkg2path ( phandle node )
{
char * res , * buf = NULL ;
int len ;
if ( ! of_pdt_prom_ops - > pkg2path )
return NULL ;
if ( of_pdt_prom_ops - > pkg2path ( node , buf , 0 , & len ) )
return NULL ;
buf = prom_early_alloc ( len + 1 ) ;
if ( of_pdt_prom_ops - > pkg2path ( node , buf , len , & len ) ) {
pr_err ( " %s: package-to-path failed \n " , __func__ ) ;
return NULL ;
}
res = strrchr ( buf , ' / ' ) ;
if ( ! res ) {
pr_err ( " %s: couldn't find / in %s \n " , __func__ , buf ) ;
return NULL ;
}
return res + 1 ;
}
/*
* When fetching the node ' s name , first try using package - to - path ; if
* that fails ( either because the arch hasn ' t supplied a PROM callback ,
* or some other random failure ) , fall back to just looking at the node ' s
* ' name ' property .
*/
static char * __init of_pdt_build_name ( phandle node )
{
char * buf ;
buf = of_pdt_try_pkg2path ( node ) ;
if ( ! buf )
buf = of_pdt_get_one_property ( node , " name " ) ;
return buf ;
}
2010-10-11 07:51:25 +04:00
static struct device_node * __init of_pdt_create_node ( phandle node ,
2010-08-30 07:57:28 +04:00
struct device_node * parent )
{
struct device_node * dp ;
if ( ! node )
return NULL ;
dp = prom_early_alloc ( sizeof ( * dp ) ) ;
2010-10-11 07:42:33 +04:00
of_pdt_incr_unique_id ( dp ) ;
2010-08-30 07:57:28 +04:00
dp - > parent = parent ;
kref_init ( & dp - > kref ) ;
2010-10-11 07:52:57 +04:00
dp - > name = of_pdt_build_name ( node ) ;
2010-10-11 07:51:25 +04:00
dp - > type = of_pdt_get_one_property ( node , " device_type " ) ;
2010-08-30 07:57:28 +04:00
dp - > phandle = node ;
2010-10-11 07:51:25 +04:00
dp - > properties = of_pdt_build_prop_list ( node ) ;
2010-08-30 07:57:28 +04:00
irq_trans_init ( dp ) ;
return dp ;
}
2010-10-11 07:51:25 +04:00
static char * __init of_pdt_build_full_name ( struct device_node * dp )
2010-08-30 07:57:28 +04:00
{
int len , ourlen , plen ;
char * n ;
plen = strlen ( dp - > parent - > full_name ) ;
2010-10-11 07:42:33 +04:00
ourlen = strlen ( of_pdt_node_name ( dp ) ) ;
2010-08-30 07:57:28 +04:00
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 + + ;
}
2010-10-11 07:42:33 +04:00
strcpy ( n + plen , of_pdt_node_name ( dp ) ) ;
2010-08-30 07:57:28 +04:00
return n ;
}
2010-10-11 07:51:25 +04:00
static struct device_node * __init of_pdt_build_tree ( struct device_node * parent ,
2010-08-30 07:57:28 +04:00
phandle node ,
struct device_node * * * nextp )
{
struct device_node * ret = NULL , * prev_sibling = NULL ;
struct device_node * dp ;
while ( 1 ) {
2010-10-11 07:51:25 +04:00
dp = of_pdt_create_node ( node , parent ) ;
2010-08-30 07:57:28 +04:00
if ( ! dp )
break ;
if ( prev_sibling )
prev_sibling - > sibling = dp ;
if ( ! ret )
ret = dp ;
prev_sibling = dp ;
* ( * nextp ) = dp ;
* nextp = & dp - > allnext ;
2010-10-11 07:42:33 +04:00
# if defined(CONFIG_SPARC)
2010-08-30 07:57:28 +04:00
dp - > path_component_name = build_path_component ( dp ) ;
2010-10-11 07:42:33 +04:00
# endif
2010-10-11 07:51:25 +04:00
dp - > full_name = of_pdt_build_full_name ( dp ) ;
2010-08-30 07:57:28 +04:00
2010-10-11 07:51:25 +04:00
dp - > child = of_pdt_build_tree ( dp ,
2010-10-11 07:49:45 +04:00
of_pdt_prom_ops - > getchild ( node ) , nextp ) ;
2010-08-30 07:57:28 +04:00
2010-10-11 07:51:25 +04:00
if ( of_pdt_build_more )
of_pdt_build_more ( dp , nextp ) ;
2010-08-30 07:57:28 +04:00
2010-10-11 07:49:45 +04:00
node = of_pdt_prom_ops - > getsibling ( node ) ;
2010-08-30 07:57:28 +04:00
}
return ret ;
}
2010-10-11 07:49:45 +04:00
void __init of_pdt_build_devicetree ( phandle root_node , struct of_pdt_ops * ops )
2010-08-30 07:57:28 +04:00
{
struct device_node * * nextp ;
2010-10-11 07:49:45 +04:00
BUG_ON ( ! ops ) ;
of_pdt_prom_ops = ops ;
2010-10-11 07:51:25 +04:00
allnodes = of_pdt_create_node ( root_node , NULL ) ;
2010-10-11 07:42:33 +04:00
# if defined(CONFIG_SPARC)
2010-08-30 07:57:28 +04:00
allnodes - > path_component_name = " " ;
2010-10-11 07:42:33 +04:00
# endif
2010-08-30 07:57:28 +04:00
allnodes - > full_name = " / " ;
nextp = & allnodes - > allnext ;
2010-10-11 07:51:25 +04:00
allnodes - > child = of_pdt_build_tree ( allnodes ,
2010-10-11 07:49:45 +04:00
of_pdt_prom_ops - > getchild ( allnodes - > phandle ) , & nextp ) ;
2010-08-30 07:57:28 +04:00
}