2007-05-01 10:26:07 +04:00
/*
* Procedures for creating , accessing and interpreting the device tree .
*
* 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 and sparc64 by David S . Miller davem @ davemloft . net
*
2009-10-15 20:58:09 +04:00
* Reconsolidated from arch / x / kernel / prom . c by Stephen Rothwell and
* Grant Likely .
2007-05-01 10:26:07 +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/module.h>
# include <linux/of.h>
2007-04-24 10:46:53 +04:00
# include <linux/spinlock.h>
2007-04-24 11:57:33 +04:00
struct device_node * allnodes ;
2007-04-24 10:46:53 +04:00
/* use when traversing tree through the allnext, child, sibling,
* or parent members of struct device_node .
*/
DEFINE_RWLOCK ( devtree_lock ) ;
2007-05-01 10:26:07 +04:00
int of_n_addr_cells ( struct device_node * np )
{
const int * ip ;
do {
if ( np - > parent )
np = np - > parent ;
ip = of_get_property ( np , " #address-cells " , NULL ) ;
if ( ip )
return * ip ;
} while ( np - > parent ) ;
/* No #address-cells property for the root node */
return OF_ROOT_NODE_ADDR_CELLS_DEFAULT ;
}
EXPORT_SYMBOL ( of_n_addr_cells ) ;
int of_n_size_cells ( struct device_node * np )
{
const int * ip ;
do {
if ( np - > parent )
np = np - > parent ;
ip = of_get_property ( np , " #size-cells " , NULL ) ;
if ( ip )
return * ip ;
} while ( np - > parent ) ;
/* No #size-cells property for the root node */
return OF_ROOT_NODE_SIZE_CELLS_DEFAULT ;
}
EXPORT_SYMBOL ( of_n_size_cells ) ;
2010-01-28 23:52:53 +03:00
# if !defined(CONFIG_SPARC) /* SPARC doesn't do ref counting (yet) */
/**
* of_node_get - Increment refcount of a node
* @ node : Node to inc refcount , NULL is supported to
* simplify writing of callers
*
* Returns node .
*/
struct device_node * of_node_get ( struct device_node * node )
{
if ( node )
kref_get ( & node - > kref ) ;
return node ;
}
EXPORT_SYMBOL ( of_node_get ) ;
static inline struct device_node * kref_to_device_node ( struct kref * kref )
{
return container_of ( kref , struct device_node , kref ) ;
}
/**
* of_node_release - release a dynamically allocated node
* @ kref : kref element of the node to be released
*
* In of_node_put ( ) this function is passed to kref_put ( )
* as the destructor .
*/
static void of_node_release ( struct kref * kref )
{
struct device_node * node = kref_to_device_node ( kref ) ;
struct property * prop = node - > properties ;
/* We should never be releasing nodes that haven't been detached. */
if ( ! of_node_check_flag ( node , OF_DETACHED ) ) {
pr_err ( " ERROR: Bad of_node_put() on %s \n " , node - > full_name ) ;
dump_stack ( ) ;
kref_init ( & node - > kref ) ;
return ;
}
if ( ! of_node_check_flag ( node , OF_DYNAMIC ) )
return ;
while ( prop ) {
struct property * next = prop - > next ;
kfree ( prop - > name ) ;
kfree ( prop - > value ) ;
kfree ( prop ) ;
prop = next ;
if ( ! prop ) {
prop = node - > deadprops ;
node - > deadprops = NULL ;
}
}
kfree ( node - > full_name ) ;
kfree ( node - > data ) ;
kfree ( node ) ;
}
/**
* of_node_put - Decrement refcount of a node
* @ node : Node to dec refcount , NULL is supported to
* simplify writing of callers
*
*/
void of_node_put ( struct device_node * node )
{
if ( node )
kref_put ( & node - > kref , of_node_release ) ;
}
EXPORT_SYMBOL ( of_node_put ) ;
# endif /* !CONFIG_SPARC */
2007-04-24 10:46:53 +04:00
struct property * of_find_property ( const struct device_node * np ,
const char * name ,
int * lenp )
{
struct property * pp ;
2008-05-07 23:19:59 +04:00
if ( ! np )
return NULL ;
2007-04-24 10:46:53 +04:00
read_lock ( & devtree_lock ) ;
for ( pp = np - > properties ; pp ! = 0 ; pp = pp - > next ) {
if ( of_prop_cmp ( pp - > name , name ) = = 0 ) {
if ( lenp ! = 0 )
* lenp = pp - > length ;
break ;
}
}
read_unlock ( & devtree_lock ) ;
return pp ;
}
EXPORT_SYMBOL ( of_find_property ) ;
2009-10-15 20:58:09 +04:00
/**
* of_find_all_nodes - Get next node in global list
* @ prev : Previous node or NULL to start iteration
* of_node_put ( ) will be called on it
*
* Returns a node pointer with refcount incremented , use
* of_node_put ( ) on it when done .
*/
struct device_node * of_find_all_nodes ( struct device_node * prev )
{
struct device_node * np ;
read_lock ( & devtree_lock ) ;
np = prev ? prev - > allnext : allnodes ;
for ( ; np ! = NULL ; np = np - > allnext )
if ( of_node_get ( np ) )
break ;
of_node_put ( prev ) ;
read_unlock ( & devtree_lock ) ;
return np ;
}
EXPORT_SYMBOL ( of_find_all_nodes ) ;
2007-05-01 10:26:07 +04:00
/*
* Find a property with a given name for a given node
* and return the value .
*/
const void * of_get_property ( const struct device_node * np , const char * name ,
int * lenp )
{
struct property * pp = of_find_property ( np , name , lenp ) ;
return pp ? pp - > value : NULL ;
}
EXPORT_SYMBOL ( of_get_property ) ;
2007-05-01 10:29:19 +04:00
/** Checks if the given "compat" string matches one of the strings in
* the device ' s " compatible " property
*/
int of_device_is_compatible ( const struct device_node * device ,
const char * compat )
{
const char * cp ;
int cplen , l ;
cp = of_get_property ( device , " compatible " , & cplen ) ;
if ( cp = = NULL )
return 0 ;
while ( cplen > 0 ) {
if ( of_compat_cmp ( cp , compat , strlen ( compat ) ) = = 0 )
return 1 ;
l = strlen ( cp ) + 1 ;
cp + = l ;
cplen - = l ;
}
return 0 ;
}
EXPORT_SYMBOL ( of_device_is_compatible ) ;
2007-04-24 11:16:16 +04:00
2010-01-28 23:47:25 +03:00
/**
* machine_is_compatible - Test root of device tree for a given compatible value
* @ compat : compatible string to look for in root node ' s compatible property .
*
* Returns true if the root node has the given value in its
* compatible property .
*/
int machine_is_compatible ( const char * compat )
{
struct device_node * root ;
int rc = 0 ;
root = of_find_node_by_path ( " / " ) ;
if ( root ) {
rc = of_device_is_compatible ( root , compat ) ;
of_node_put ( root ) ;
}
return rc ;
}
EXPORT_SYMBOL ( machine_is_compatible ) ;
2008-03-26 16:33:14 +03:00
/**
* of_device_is_available - check if a device is available for use
*
* @ device : Node to check for availability
*
* Returns 1 if the status property is absent or set to " okay " or " ok " ,
* 0 otherwise
*/
int of_device_is_available ( const struct device_node * device )
{
const char * status ;
int statlen ;
status = of_get_property ( device , " status " , & statlen ) ;
if ( status = = NULL )
return 1 ;
if ( statlen > 0 ) {
if ( ! strcmp ( status , " okay " ) | | ! strcmp ( status , " ok " ) )
return 1 ;
}
return 0 ;
}
EXPORT_SYMBOL ( of_device_is_available ) ;
2007-04-24 11:16:16 +04:00
/**
* of_get_parent - Get a node ' s parent if any
* @ node : Node to get parent
*
* Returns a node pointer with refcount incremented , use
* of_node_put ( ) on it when done .
*/
struct device_node * of_get_parent ( const struct device_node * node )
{
struct device_node * np ;
if ( ! node )
return NULL ;
read_lock ( & devtree_lock ) ;
np = of_node_get ( node - > parent ) ;
read_unlock ( & devtree_lock ) ;
return np ;
}
EXPORT_SYMBOL ( of_get_parent ) ;
2007-04-24 11:21:29 +04:00
2007-10-26 10:54:31 +04:00
/**
* of_get_next_parent - Iterate to a node ' s parent
* @ node : Node to get parent of
*
* This is like of_get_parent ( ) except that it drops the
* refcount on the passed node , making it suitable for iterating
* through a node ' s parents .
*
* Returns a node pointer with refcount incremented , use
* of_node_put ( ) on it when done .
*/
struct device_node * of_get_next_parent ( struct device_node * node )
{
struct device_node * parent ;
if ( ! node )
return NULL ;
read_lock ( & devtree_lock ) ;
parent = of_node_get ( node - > parent ) ;
of_node_put ( node ) ;
read_unlock ( & devtree_lock ) ;
return parent ;
}
2007-04-24 11:21:29 +04:00
/**
* of_get_next_child - Iterate a node childs
* @ node : parent node
* @ prev : previous child of the parent node , or NULL to get first
*
* Returns a node pointer with refcount incremented , use
* of_node_put ( ) on it when done .
*/
struct device_node * of_get_next_child ( const struct device_node * node ,
struct device_node * prev )
{
struct device_node * next ;
read_lock ( & devtree_lock ) ;
next = prev ? prev - > sibling : node - > child ;
for ( ; next ; next = next - > sibling )
if ( of_node_get ( next ) )
break ;
of_node_put ( prev ) ;
read_unlock ( & devtree_lock ) ;
return next ;
}
EXPORT_SYMBOL ( of_get_next_child ) ;
2007-04-24 11:57:33 +04:00
/**
* of_find_node_by_path - Find a node matching a full OF path
* @ path : The full path to match
*
* Returns a node pointer with refcount incremented , use
* of_node_put ( ) on it when done .
*/
struct device_node * of_find_node_by_path ( const char * path )
{
struct device_node * np = allnodes ;
read_lock ( & devtree_lock ) ;
for ( ; np ; np = np - > allnext ) {
if ( np - > full_name & & ( of_node_cmp ( np - > full_name , path ) = = 0 )
& & of_node_get ( np ) )
break ;
}
read_unlock ( & devtree_lock ) ;
return np ;
}
EXPORT_SYMBOL ( of_find_node_by_path ) ;
/**
* of_find_node_by_name - Find a node by its " name " property
* @ from : The node to start searching from or NULL , the node
* you pass will not be searched , only the next one
* will ; typically , you pass what the previous call
* returned . of_node_put ( ) will be called on it
* @ name : The name string to match against
*
* Returns a node pointer with refcount incremented , use
* of_node_put ( ) on it when done .
*/
struct device_node * of_find_node_by_name ( struct device_node * from ,
const char * name )
{
struct device_node * np ;
read_lock ( & devtree_lock ) ;
np = from ? from - > allnext : allnodes ;
for ( ; np ; np = np - > allnext )
if ( np - > name & & ( of_node_cmp ( np - > name , name ) = = 0 )
& & of_node_get ( np ) )
break ;
of_node_put ( from ) ;
read_unlock ( & devtree_lock ) ;
return np ;
}
EXPORT_SYMBOL ( of_find_node_by_name ) ;
/**
* of_find_node_by_type - Find a node by its " device_type " property
* @ from : The node to start searching from , or NULL to start searching
* the entire device tree . The node you pass will not be
* searched , only the next one will ; typically , you pass
* what the previous call returned . of_node_put ( ) will be
* called on from for you .
* @ type : The type string to match against
*
* Returns a node pointer with refcount incremented , use
* of_node_put ( ) on it when done .
*/
struct device_node * of_find_node_by_type ( struct device_node * from ,
const char * type )
{
struct device_node * np ;
read_lock ( & devtree_lock ) ;
np = from ? from - > allnext : allnodes ;
for ( ; np ; np = np - > allnext )
if ( np - > type & & ( of_node_cmp ( np - > type , type ) = = 0 )
& & of_node_get ( np ) )
break ;
of_node_put ( from ) ;
read_unlock ( & devtree_lock ) ;
return np ;
}
EXPORT_SYMBOL ( of_find_node_by_type ) ;
/**
* of_find_compatible_node - Find a node based on type and one of the
* tokens in its " compatible " property
* @ from : The node to start searching from or NULL , the node
* you pass will not be searched , only the next one
* will ; typically , you pass what the previous call
* returned . of_node_put ( ) will be called on it
* @ type : The type string to match " device_type " or NULL to ignore
* @ compatible : The string to match to one of the tokens in the device
* " compatible " list .
*
* Returns a node pointer with refcount incremented , use
* of_node_put ( ) on it when done .
*/
struct device_node * of_find_compatible_node ( struct device_node * from ,
const char * type , const char * compatible )
{
struct device_node * np ;
read_lock ( & devtree_lock ) ;
np = from ? from - > allnext : allnodes ;
for ( ; np ; np = np - > allnext ) {
if ( type
& & ! ( np - > type & & ( of_node_cmp ( np - > type , type ) = = 0 ) ) )
continue ;
if ( of_device_is_compatible ( np , compatible ) & & of_node_get ( np ) )
break ;
}
of_node_put ( from ) ;
read_unlock ( & devtree_lock ) ;
return np ;
}
EXPORT_SYMBOL ( of_find_compatible_node ) ;
2008-01-08 22:20:40 +03:00
2008-11-12 21:54:42 +03:00
/**
* of_find_node_with_property - Find a node which has a property with
* the given name .
* @ from : The node to start searching from or NULL , the node
* you pass will not be searched , only the next one
* will ; typically , you pass what the previous call
* returned . of_node_put ( ) will be called on it
* @ prop_name : The name of the property to look for .
*
* Returns a node pointer with refcount incremented , use
* of_node_put ( ) on it when done .
*/
struct device_node * of_find_node_with_property ( struct device_node * from ,
const char * prop_name )
{
struct device_node * np ;
struct property * pp ;
read_lock ( & devtree_lock ) ;
np = from ? from - > allnext : allnodes ;
for ( ; np ; np = np - > allnext ) {
for ( pp = np - > properties ; pp ! = 0 ; pp = pp - > next ) {
if ( of_prop_cmp ( pp - > name , prop_name ) = = 0 ) {
of_node_get ( np ) ;
goto out ;
}
}
}
out :
of_node_put ( from ) ;
read_unlock ( & devtree_lock ) ;
return np ;
}
EXPORT_SYMBOL ( of_find_node_with_property ) ;
2008-01-08 22:20:40 +03:00
/**
* of_match_node - Tell if an device_node has a matching of_match structure
* @ matches : array of of device match structures to search in
* @ node : the of device structure to match against
*
* Low level utility function used by device matching .
*/
const struct of_device_id * of_match_node ( const struct of_device_id * matches ,
const struct device_node * node )
{
while ( matches - > name [ 0 ] | | matches - > type [ 0 ] | | matches - > compatible [ 0 ] ) {
int match = 1 ;
if ( matches - > name [ 0 ] )
match & = node - > name
& & ! strcmp ( matches - > name , node - > name ) ;
if ( matches - > type [ 0 ] )
match & = node - > type
& & ! strcmp ( matches - > type , node - > type ) ;
if ( matches - > compatible [ 0 ] )
match & = of_device_is_compatible ( node ,
matches - > compatible ) ;
if ( match )
return matches ;
matches + + ;
}
return NULL ;
}
EXPORT_SYMBOL ( of_match_node ) ;
/**
* of_find_matching_node - Find a node based on an of_device_id match
* table .
* @ from : The node to start searching from or NULL , the node
* you pass will not be searched , only the next one
* will ; typically , you pass what the previous call
* returned . of_node_put ( ) will be called on it
* @ matches : array of of device match structures to search in
*
* Returns a node pointer with refcount incremented , use
* of_node_put ( ) on it when done .
*/
struct device_node * of_find_matching_node ( struct device_node * from ,
const struct of_device_id * matches )
{
struct device_node * np ;
read_lock ( & devtree_lock ) ;
np = from ? from - > allnext : allnodes ;
for ( ; np ; np = np - > allnext ) {
if ( of_match_node ( matches , np ) & & of_node_get ( np ) )
break ;
}
of_node_put ( from ) ;
read_unlock ( & devtree_lock ) ;
return np ;
}
EXPORT_SYMBOL ( of_find_matching_node ) ;
2008-07-26 06:25:13 +04:00
/**
* of_modalias_table : Table of explicit compatible = = > modalias mappings
*
* This table allows particulare compatible property values to be mapped
* to modalias strings . This is useful for busses which do not directly
* understand the OF device tree but are populated based on data contained
* within the device tree . SPI and I2C are the two current users of this
* table .
*
* In most cases , devices do not need to be listed in this table because
* the modalias value can be derived directly from the compatible table .
* However , if for any reason a value cannot be derived , then this table
* provides a method to override the implicit derivation .
*
* At the moment , a single table is used for all bus types because it is
* assumed that the data size is small and that the compatible values
* should already be distinct enough to differentiate between SPI , I2C
* and other devices .
*/
struct of_modalias_table {
char * of_device ;
char * modalias ;
} ;
static struct of_modalias_table of_modalias_table [ ] = {
2008-09-23 14:12:19 +04:00
{ " fsl,mcu-mpc8349emitx " , " mcu-mpc8349emitx " } ,
2009-04-01 02:24:38 +04:00
{ " mmc-spi-slot " , " mmc_spi " } ,
2008-07-26 06:25:13 +04:00
} ;
/**
* of_modalias_node - Lookup appropriate modalias for a device node
* @ node : pointer to a device tree node
* @ modalias : Pointer to buffer that modalias value will be copied into
* @ len : Length of modalias value
*
* Based on the value of the compatible property , this routine will determine
2008-10-08 09:05:29 +04:00
* an appropriate modalias value for a particular device tree node . Two
* separate methods are attempted to derive a modalias value .
2008-07-26 06:25:13 +04:00
*
* First method is to lookup the compatible value in of_modalias_table .
2008-10-08 09:05:29 +04:00
* Second is to strip off the manufacturer prefix from the first
* compatible entry and use the remainder as modalias
2008-07-26 06:25:13 +04:00
*
* This routine returns 0 on success
*/
int of_modalias_node ( struct device_node * node , char * modalias , int len )
{
int i , cplen ;
const char * compatible ;
const char * p ;
/* 1. search for exception list entry */
for ( i = 0 ; i < ARRAY_SIZE ( of_modalias_table ) ; i + + ) {
compatible = of_modalias_table [ i ] . of_device ;
if ( ! of_device_is_compatible ( node , compatible ) )
continue ;
strlcpy ( modalias , of_modalias_table [ i ] . modalias , len ) ;
return 0 ;
}
compatible = of_get_property ( node , " compatible " , & cplen ) ;
if ( ! compatible )
return - ENODEV ;
2008-10-08 09:05:29 +04:00
/* 2. take first compatible entry and strip manufacturer */
2008-07-26 06:25:13 +04:00
p = strchr ( compatible , ' , ' ) ;
if ( ! p )
return - ENODEV ;
p + + ;
strlcpy ( modalias , p , len ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( of_modalias_node ) ;
2010-02-02 07:34:11 +03:00
/**
* of_find_node_by_phandle - Find a node given a phandle
* @ handle : phandle of the node to find
*
* Returns a node pointer with refcount incremented , use
* of_node_put ( ) on it when done .
*/
struct device_node * of_find_node_by_phandle ( phandle handle )
{
struct device_node * np ;
read_lock ( & devtree_lock ) ;
for ( np = allnodes ; np ; np = np - > allnext )
if ( np - > phandle = = handle )
break ;
of_node_get ( np ) ;
read_unlock ( & devtree_lock ) ;
return np ;
}
EXPORT_SYMBOL ( of_find_node_by_phandle ) ;
2009-04-25 16:52:40 +04:00
/**
* of_parse_phandle - Resolve a phandle property to a device_node pointer
* @ np : Pointer to device node holding phandle property
* @ phandle_name : Name of property holding a phandle value
* @ index : For properties holding a table of phandles , this is the index into
* the table
*
* Returns the device_node pointer with refcount incremented . Use
* of_node_put ( ) on it when done .
*/
struct device_node *
of_parse_phandle ( struct device_node * np , const char * phandle_name , int index )
{
const phandle * phandle ;
int size ;
phandle = of_get_property ( np , phandle_name , & size ) ;
if ( ( ! phandle ) | | ( size < sizeof ( * phandle ) * ( index + 1 ) ) )
return NULL ;
return of_find_node_by_phandle ( phandle [ index ] ) ;
}
EXPORT_SYMBOL ( of_parse_phandle ) ;
2008-10-10 08:43:17 +04:00
/**
* of_parse_phandles_with_args - Find a node pointed by phandle in a list
* @ np : pointer to a device tree node containing a list
* @ list_name : property name that contains a list
* @ cells_name : property name that specifies phandles ' arguments count
* @ index : index of a phandle to parse out
2008-12-05 11:15:46 +03:00
* @ out_node : optional pointer to device_node struct pointer ( will be filled )
* @ out_args : optional pointer to arguments pointer ( will be filled )
2008-10-10 08:43:17 +04:00
*
* This function is useful to parse lists of phandles and their arguments .
* Returns 0 on success and fills out_node and out_args , on error returns
* appropriate errno value .
*
* Example :
*
* phandle1 : node1 {
* # list - cells = < 2 > ;
* }
*
* phandle2 : node2 {
* # list - cells = < 1 > ;
* }
*
* node3 {
* list = < & phandle1 1 2 & phandle2 3 > ;
* }
*
* To get a device_node of the ` node2 ' node you may call this :
* of_parse_phandles_with_args ( node3 , " list " , " #list-cells " , 2 , & node2 , & args ) ;
*/
int of_parse_phandles_with_args ( struct device_node * np , const char * list_name ,
const char * cells_name , int index ,
struct device_node * * out_node ,
const void * * out_args )
{
int ret = - EINVAL ;
const u32 * list ;
const u32 * list_end ;
int size ;
int cur_index = 0 ;
struct device_node * node = NULL ;
2008-12-05 11:15:46 +03:00
const void * args = NULL ;
2008-10-10 08:43:17 +04:00
list = of_get_property ( np , list_name , & size ) ;
if ( ! list ) {
ret = - ENOENT ;
goto err0 ;
}
list_end = list + size / sizeof ( * list ) ;
while ( list < list_end ) {
const u32 * cells ;
const phandle * phandle ;
2008-12-05 11:15:39 +03:00
phandle = list + + ;
args = list ;
2008-10-10 08:43:17 +04:00
/* one cell hole in the list = <>; */
2008-12-05 11:15:39 +03:00
if ( ! * phandle )
2008-10-10 08:43:17 +04:00
goto next ;
node = of_find_node_by_phandle ( * phandle ) ;
if ( ! node ) {
pr_debug ( " %s: could not find phandle \n " ,
np - > full_name ) ;
goto err0 ;
}
cells = of_get_property ( node , cells_name , & size ) ;
if ( ! cells | | size ! = sizeof ( * cells ) ) {
pr_debug ( " %s: could not get %s for %s \n " ,
np - > full_name , cells_name , node - > full_name ) ;
goto err1 ;
}
2008-12-05 11:15:39 +03:00
list + = * cells ;
2008-10-10 08:43:17 +04:00
if ( list > list_end ) {
pr_debug ( " %s: insufficient arguments length \n " ,
np - > full_name ) ;
goto err1 ;
}
next :
if ( cur_index = = index )
break ;
of_node_put ( node ) ;
node = NULL ;
2008-12-05 11:15:46 +03:00
args = NULL ;
2008-10-10 08:43:17 +04:00
cur_index + + ;
}
if ( ! node ) {
2008-12-05 11:15:46 +03:00
/*
* args w / o node indicates that the loop above has stopped at
* the ' hole ' cell . Report this differently .
*/
if ( args )
ret = - EEXIST ;
else
ret = - ENOENT ;
2008-10-10 08:43:17 +04:00
goto err0 ;
}
2008-12-05 11:15:46 +03:00
if ( out_node )
* out_node = node ;
if ( out_args )
* out_args = args ;
2008-10-10 08:43:17 +04:00
return 0 ;
err1 :
of_node_put ( node ) ;
err0 :
pr_debug ( " %s failed with status %d \n " , __func__ , ret ) ;
return ret ;
}
EXPORT_SYMBOL ( of_parse_phandles_with_args ) ;
2009-11-24 06:16:45 +03:00
/**
* prom_add_property - Add a property to a node
*/
int prom_add_property ( struct device_node * np , struct property * prop )
{
struct property * * next ;
unsigned long flags ;
prop - > next = NULL ;
write_lock_irqsave ( & devtree_lock , flags ) ;
next = & np - > properties ;
while ( * next ) {
if ( strcmp ( prop - > name , ( * next ) - > name ) = = 0 ) {
/* duplicate ! don't insert it */
write_unlock_irqrestore ( & devtree_lock , flags ) ;
return - 1 ;
}
next = & ( * next ) - > next ;
}
* next = prop ;
write_unlock_irqrestore ( & devtree_lock , flags ) ;
# ifdef CONFIG_PROC_DEVICETREE
/* try to add to proc as well if it was initialized */
if ( np - > pde )
proc_device_tree_add_prop ( np - > pde , prop ) ;
# endif /* CONFIG_PROC_DEVICETREE */
return 0 ;
}
/**
* prom_remove_property - Remove a property from a node .
*
* Note that we don ' t actually remove it , since we have given out
* who - knows - how - many pointers to the data using get - property .
* Instead we just move the property to the " dead properties "
* list , so it won ' t be found any more .
*/
int prom_remove_property ( struct device_node * np , struct property * prop )
{
struct property * * next ;
unsigned long flags ;
int found = 0 ;
write_lock_irqsave ( & devtree_lock , flags ) ;
next = & np - > properties ;
while ( * next ) {
if ( * next = = prop ) {
/* found the node */
* next = prop - > next ;
prop - > next = np - > deadprops ;
np - > deadprops = prop ;
found = 1 ;
break ;
}
next = & ( * next ) - > next ;
}
write_unlock_irqrestore ( & devtree_lock , flags ) ;
if ( ! found )
return - ENODEV ;
# ifdef CONFIG_PROC_DEVICETREE
/* try to remove the proc node as well */
if ( np - > pde )
proc_device_tree_remove_prop ( np - > pde , prop ) ;
# endif /* CONFIG_PROC_DEVICETREE */
return 0 ;
}
/*
* prom_update_property - Update a property in a node .
*
* Note that we don ' t actually remove it , since we have given out
* who - knows - how - many pointers to the data using get - property .
* Instead we just move the property to the " dead properties " list ,
* and add the new property to the property list
*/
int prom_update_property ( struct device_node * np ,
struct property * newprop ,
struct property * oldprop )
{
struct property * * next ;
unsigned long flags ;
int found = 0 ;
write_lock_irqsave ( & devtree_lock , flags ) ;
next = & np - > properties ;
while ( * next ) {
if ( * next = = oldprop ) {
/* found the node */
newprop - > next = oldprop - > next ;
* next = newprop ;
oldprop - > next = np - > deadprops ;
np - > deadprops = oldprop ;
found = 1 ;
break ;
}
next = & ( * next ) - > next ;
}
write_unlock_irqrestore ( & devtree_lock , flags ) ;
if ( ! found )
return - ENODEV ;
# ifdef CONFIG_PROC_DEVICETREE
/* try to add to proc as well if it was initialized */
if ( np - > pde )
proc_device_tree_update_prop ( np - > pde , newprop , oldprop ) ;
# endif /* CONFIG_PROC_DEVICETREE */
return 0 ;
}
2010-01-29 15:04:33 +03:00
# if defined(CONFIG_OF_DYNAMIC)
/*
* Support for dynamic device trees .
*
* On some platforms , the device tree can be manipulated at runtime .
* The routines in this section support adding , removing and changing
* device tree nodes .
*/
/**
* of_attach_node - Plug a device node into the tree and global list .
*/
void of_attach_node ( struct device_node * np )
{
unsigned long flags ;
write_lock_irqsave ( & devtree_lock , flags ) ;
np - > sibling = np - > parent - > child ;
np - > allnext = allnodes ;
np - > parent - > child = np ;
allnodes = np ;
write_unlock_irqrestore ( & devtree_lock , flags ) ;
}
/**
* of_detach_node - " Unplug " a node from the device tree .
*
* The caller must hold a reference to the node . The memory associated with
* the node is not freed until its refcount goes to zero .
*/
void of_detach_node ( struct device_node * np )
{
struct device_node * parent ;
unsigned long flags ;
write_lock_irqsave ( & devtree_lock , flags ) ;
parent = np - > parent ;
if ( ! parent )
goto out_unlock ;
if ( allnodes = = np )
allnodes = np - > allnext ;
else {
struct device_node * prev ;
for ( prev = allnodes ;
prev - > allnext ! = np ;
prev = prev - > allnext )
;
prev - > allnext = np - > allnext ;
}
if ( parent - > child = = np )
parent - > child = np - > sibling ;
else {
struct device_node * prevsib ;
for ( prevsib = np - > parent - > child ;
prevsib - > sibling ! = np ;
prevsib = prevsib - > sibling )
;
prevsib - > sibling = np - > sibling ;
}
of_node_set_flag ( np , OF_DETACHED ) ;
out_unlock :
write_unlock_irqrestore ( & devtree_lock , flags ) ;
}
# endif /* defined(CONFIG_OF_DYNAMIC) */