2005-10-06 06:06:20 +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
*
* 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 .
*/
# undef DEBUG
# include <stdarg.h>
# include <linux/kernel.h>
# include <linux/string.h>
# include <linux/init.h>
# include <linux/threads.h>
# include <linux/spinlock.h>
# include <linux/types.h>
# include <linux/pci.h>
# include <linux/stringify.h>
# include <linux/delay.h>
# include <linux/initrd.h>
# include <linux/bitops.h>
# include <linux/module.h>
2005-12-04 10:39:48 +03:00
# include <linux/kexec.h>
2006-06-23 12:16:03 +04:00
# include <linux/debugfs.h>
2006-07-03 15:36:01 +04:00
# include <linux/irq.h>
2005-10-06 06:06:20 +04:00
# include <asm/prom.h>
# include <asm/rtas.h>
# include <asm/lmb.h>
# include <asm/page.h>
# include <asm/processor.h>
# include <asm/irq.h>
# include <asm/io.h>
2005-12-04 10:39:37 +03:00
# include <asm/kdump.h>
2005-10-06 06:06:20 +04:00
# include <asm/smp.h>
# include <asm/system.h>
# include <asm/mmu.h>
# include <asm/pgtable.h>
# include <asm/pci.h>
# include <asm/iommu.h>
# include <asm/btext.h>
# include <asm/sections.h>
# include <asm/machdep.h>
# include <asm/pSeries_reconfig.h>
2005-10-10 16:50:37 +04:00
# include <asm/pci-bridge.h>
2006-05-17 12:00:46 +04:00
# include <asm/kexec.h>
2005-10-06 06:06:20 +04:00
# ifdef DEBUG
# define DBG(fmt...) printk(KERN_ERR fmt)
# else
# define DBG(fmt...)
# endif
static int __initdata dt_root_addr_cells ;
static int __initdata dt_root_size_cells ;
# ifdef CONFIG_PPC64
2006-04-13 06:52:33 +04:00
int __initdata iommu_is_off ;
2005-10-06 06:06:20 +04:00
int __initdata iommu_force_on ;
2005-10-31 05:07:02 +03:00
unsigned long tce_alloc_start , tce_alloc_end ;
2005-10-06 06:06:20 +04:00
# endif
typedef u32 cell_t ;
#if 0
static struct boot_param_header * initial_boot_params __initdata ;
# else
struct boot_param_header * initial_boot_params ;
# endif
static struct device_node * allnodes = NULL ;
/* use when traversing tree through the allnext, child, sibling,
* or parent members of struct device_node .
*/
static DEFINE_RWLOCK ( devtree_lock ) ;
/* export that to outside world */
struct device_node * of_chosen ;
static inline char * find_flat_dt_string ( u32 offset )
{
return ( ( char * ) initial_boot_params ) +
initial_boot_params - > off_dt_strings + offset ;
}
/**
* This function is used to scan the flattened device - tree , it is
* used to extract the memory informations at boot before we can
* unflatten the tree
*/
2005-11-07 03:06:55 +03:00
int __init of_scan_flat_dt ( int ( * it ) ( unsigned long node ,
const char * uname , int depth ,
void * data ) ,
void * data )
2005-10-06 06:06:20 +04:00
{
unsigned long p = ( ( unsigned long ) initial_boot_params ) +
initial_boot_params - > off_dt_struct ;
int rc = 0 ;
int depth = - 1 ;
do {
u32 tag = * ( ( u32 * ) p ) ;
char * pathp ;
p + = 4 ;
if ( tag = = OF_DT_END_NODE ) {
depth - - ;
continue ;
}
if ( tag = = OF_DT_NOP )
continue ;
if ( tag = = OF_DT_END )
break ;
if ( tag = = OF_DT_PROP ) {
u32 sz = * ( ( u32 * ) p ) ;
p + = 8 ;
if ( initial_boot_params - > version < 0x10 )
p = _ALIGN ( p , sz > = 8 ? 8 : 4 ) ;
p + = sz ;
p = _ALIGN ( p , 4 ) ;
continue ;
}
if ( tag ! = OF_DT_BEGIN_NODE ) {
printk ( KERN_WARNING " Invalid tag %x scanning flattened "
" device tree ! \n " , tag ) ;
return - EINVAL ;
}
depth + + ;
pathp = ( char * ) p ;
p = _ALIGN ( p + strlen ( pathp ) + 1 , 4 ) ;
if ( ( * pathp ) = = ' / ' ) {
char * lp , * np ;
for ( lp = NULL , np = pathp ; * np ; np + + )
if ( ( * np ) = = ' / ' )
lp = np + 1 ;
if ( lp ! = NULL )
pathp = lp ;
}
rc = it ( p , pathp , depth , data ) ;
if ( rc ! = 0 )
break ;
} while ( 1 ) ;
return rc ;
}
2006-03-28 16:15:54 +04:00
unsigned long __init of_get_flat_dt_root ( void )
{
unsigned long p = ( ( unsigned long ) initial_boot_params ) +
initial_boot_params - > off_dt_struct ;
while ( * ( ( u32 * ) p ) = = OF_DT_NOP )
p + = 4 ;
BUG_ON ( * ( ( u32 * ) p ) ! = OF_DT_BEGIN_NODE ) ;
p + = 4 ;
return _ALIGN ( p + strlen ( ( char * ) p ) + 1 , 4 ) ;
}
2005-10-06 06:06:20 +04:00
/**
* This function can be used within scan_flattened_dt callback to get
* access to properties
*/
2005-11-07 03:06:55 +03:00
void * __init of_get_flat_dt_prop ( unsigned long node , const char * name ,
unsigned long * size )
2005-10-06 06:06:20 +04:00
{
unsigned long p = node ;
do {
u32 tag = * ( ( u32 * ) p ) ;
u32 sz , noff ;
const char * nstr ;
p + = 4 ;
if ( tag = = OF_DT_NOP )
continue ;
if ( tag ! = OF_DT_PROP )
return NULL ;
sz = * ( ( u32 * ) p ) ;
noff = * ( ( u32 * ) ( p + 4 ) ) ;
p + = 8 ;
if ( initial_boot_params - > version < 0x10 )
p = _ALIGN ( p , sz > = 8 ? 8 : 4 ) ;
nstr = find_flat_dt_string ( noff ) ;
if ( nstr = = NULL ) {
printk ( KERN_WARNING " Can't find property index "
" name ! \n " ) ;
return NULL ;
}
if ( strcmp ( name , nstr ) = = 0 ) {
if ( size )
* size = sz ;
return ( void * ) p ;
}
p + = sz ;
p = _ALIGN ( p , 4 ) ;
} while ( 1 ) ;
}
2006-03-28 16:15:54 +04:00
int __init of_flat_dt_is_compatible ( unsigned long node , const char * compat )
{
const char * cp ;
unsigned long cplen , l ;
cp = of_get_flat_dt_prop ( node , " compatible " , & cplen ) ;
if ( cp = = NULL )
return 0 ;
while ( cplen > 0 ) {
if ( strncasecmp ( cp , compat , strlen ( compat ) ) = = 0 )
return 1 ;
l = strlen ( cp ) + 1 ;
cp + = l ;
cplen - = l ;
}
return 0 ;
}
2005-10-06 06:06:20 +04:00
static void * __init unflatten_dt_alloc ( unsigned long * mem , unsigned long size ,
unsigned long align )
{
void * res ;
* mem = _ALIGN ( * mem , align ) ;
res = ( void * ) * mem ;
* mem + = size ;
return res ;
}
static unsigned long __init unflatten_dt_node ( unsigned long mem ,
unsigned long * p ,
struct device_node * dad ,
struct device_node * * * allnextpp ,
unsigned long fpsize )
{
struct device_node * np ;
struct property * pp , * * prev_pp = NULL ;
char * pathp ;
u32 tag ;
unsigned int l , allocl ;
int has_name = 0 ;
int new_format = 0 ;
tag = * ( ( u32 * ) ( * p ) ) ;
if ( tag ! = OF_DT_BEGIN_NODE ) {
printk ( " Weird tag at start of node: %x \n " , tag ) ;
return mem ;
}
* p + = 4 ;
pathp = ( char * ) * p ;
l = allocl = strlen ( pathp ) + 1 ;
* p = _ALIGN ( * p + l , 4 ) ;
/* version 0x10 has a more compact unit name here instead of the full
* path . we accumulate the full path size using " fpsize " , we ' ll rebuild
* it later . We detect this because the first character of the name is
* not ' / ' .
*/
if ( ( * pathp ) ! = ' / ' ) {
new_format = 1 ;
if ( fpsize = = 0 ) {
/* root node: special case. fpsize accounts for path
* plus terminating zero . root node only has ' / ' , so
* fpsize should be 2 , but we want to avoid the first
* level nodes to have two ' / ' so we use fpsize 1 here
*/
fpsize = 1 ;
allocl = 2 ;
} else {
/* account for '/' and path size minus terminal 0
* already in ' l '
*/
fpsize + = l ;
allocl = fpsize ;
}
}
np = unflatten_dt_alloc ( & mem , sizeof ( struct device_node ) + allocl ,
__alignof__ ( struct device_node ) ) ;
if ( allnextpp ) {
memset ( np , 0 , sizeof ( * np ) ) ;
np - > full_name = ( ( char * ) np ) + sizeof ( struct device_node ) ;
if ( new_format ) {
char * p = np - > full_name ;
/* rebuild full path for new format */
if ( dad & & dad - > parent ) {
strcpy ( p , dad - > full_name ) ;
# ifdef DEBUG
if ( ( strlen ( p ) + l + 1 ) ! = allocl ) {
DBG ( " %s: p: %d, l: %d, a: %d \n " ,
2006-03-28 16:15:54 +04:00
pathp , ( int ) strlen ( p ) , l , allocl ) ;
2005-10-06 06:06:20 +04:00
}
# endif
p + = strlen ( p ) ;
}
* ( p + + ) = ' / ' ;
memcpy ( p , pathp , l ) ;
} else
memcpy ( np - > full_name , pathp , l ) ;
prev_pp = & np - > properties ;
* * allnextpp = np ;
* allnextpp = & np - > allnext ;
if ( dad ! = NULL ) {
np - > parent = dad ;
/* we temporarily use the next field as `last_child'*/
if ( dad - > next = = 0 )
dad - > child = np ;
else
dad - > next - > sibling = np ;
dad - > next = np ;
}
kref_init ( & np - > kref ) ;
}
while ( 1 ) {
u32 sz , noff ;
char * pname ;
tag = * ( ( u32 * ) ( * p ) ) ;
if ( tag = = OF_DT_NOP ) {
* p + = 4 ;
continue ;
}
if ( tag ! = OF_DT_PROP )
break ;
* p + = 4 ;
sz = * ( ( u32 * ) ( * p ) ) ;
noff = * ( ( u32 * ) ( ( * p ) + 4 ) ) ;
* p + = 8 ;
if ( initial_boot_params - > version < 0x10 )
* p = _ALIGN ( * p , sz > = 8 ? 8 : 4 ) ;
pname = find_flat_dt_string ( noff ) ;
if ( pname = = NULL ) {
printk ( " Can't find property name in list ! \n " ) ;
break ;
}
if ( strcmp ( pname , " name " ) = = 0 )
has_name = 1 ;
l = strlen ( pname ) + 1 ;
pp = unflatten_dt_alloc ( & mem , sizeof ( struct property ) ,
__alignof__ ( struct property ) ) ;
if ( allnextpp ) {
if ( strcmp ( pname , " linux,phandle " ) = = 0 ) {
np - > node = * ( ( u32 * ) * p ) ;
if ( np - > linux_phandle = = 0 )
np - > linux_phandle = np - > node ;
}
if ( strcmp ( pname , " ibm,phandle " ) = = 0 )
np - > linux_phandle = * ( ( u32 * ) * p ) ;
pp - > name = pname ;
pp - > length = sz ;
pp - > value = ( void * ) * p ;
* prev_pp = pp ;
prev_pp = & pp - > next ;
}
* p = _ALIGN ( ( * p ) + sz , 4 ) ;
}
/* with version 0x10 we may not have the name property, recreate
* it here from the unit name if absent
*/
if ( ! has_name ) {
char * p = pathp , * ps = pathp , * pa = NULL ;
int sz ;
while ( * p ) {
if ( ( * p ) = = ' @ ' )
pa = p ;
if ( ( * p ) = = ' / ' )
ps = p + 1 ;
p + + ;
}
if ( pa < ps )
pa = p ;
sz = ( pa - ps ) + 1 ;
pp = unflatten_dt_alloc ( & mem , sizeof ( struct property ) + sz ,
__alignof__ ( struct property ) ) ;
if ( allnextpp ) {
pp - > name = " name " ;
pp - > length = sz ;
pp - > value = ( unsigned char * ) ( pp + 1 ) ;
* prev_pp = pp ;
prev_pp = & pp - > next ;
memcpy ( pp - > value , ps , sz - 1 ) ;
( ( char * ) pp - > value ) [ sz - 1 ] = 0 ;
DBG ( " fixed up name for %s -> %s \n " , pathp , pp - > value ) ;
}
}
if ( allnextpp ) {
* prev_pp = NULL ;
np - > name = get_property ( np , " name " , NULL ) ;
np - > type = get_property ( np , " device_type " , NULL ) ;
if ( ! np - > name )
np - > name = " <NULL> " ;
if ( ! np - > type )
np - > type = " <NULL> " ;
}
while ( tag = = OF_DT_BEGIN_NODE ) {
mem = unflatten_dt_node ( mem , p , np , allnextpp , fpsize ) ;
tag = * ( ( u32 * ) ( * p ) ) ;
}
if ( tag ! = OF_DT_END_NODE ) {
printk ( " Weird tag at end of node: %x \n " , tag ) ;
return mem ;
}
* p + = 4 ;
return mem ;
}
2006-05-17 12:00:46 +04:00
static int __init early_parse_mem ( char * p )
{
if ( ! p )
return 1 ;
memory_limit = PAGE_ALIGN ( memparse ( p , & p ) ) ;
DBG ( " memory limit = 0x%lx \n " , memory_limit ) ;
return 0 ;
}
early_param ( " mem " , early_parse_mem ) ;
/*
* The device tree may be allocated below our memory limit , or inside the
* crash kernel region for kdump . If so , move it out now .
*/
static void move_device_tree ( void )
{
unsigned long start , size ;
void * p ;
DBG ( " -> move_device_tree \n " ) ;
start = __pa ( initial_boot_params ) ;
size = initial_boot_params - > totalsize ;
if ( ( memory_limit & & ( start + size ) > memory_limit ) | |
overlaps_crashkernel ( start , size ) ) {
p = __va ( lmb_alloc_base ( size , PAGE_SIZE , lmb . rmo_size ) ) ;
memcpy ( p , initial_boot_params , size ) ;
initial_boot_params = ( struct boot_param_header * ) p ;
DBG ( " Moved device tree to 0x%p \n " , p ) ;
}
DBG ( " <- move_device_tree \n " ) ;
}
2005-10-06 06:06:20 +04:00
/**
* unflattens the device - tree passed by the firmware , creating the
* tree of struct device_node . It also fills the " name " and " type "
* pointers of the nodes so the normal device - tree walking functions
* can be used ( this used to be done by finish_device_tree )
*/
void __init unflatten_device_tree ( void )
{
unsigned long start , mem , size ;
struct device_node * * allnextp = & allnodes ;
DBG ( " -> unflatten_device_tree() \n " ) ;
/* First pass, scan for size */
start = ( ( unsigned long ) initial_boot_params ) +
initial_boot_params - > off_dt_struct ;
size = unflatten_dt_node ( 0 , & start , NULL , NULL , 0 ) ;
size = ( size | 3 ) + 1 ;
DBG ( " size is %lx, allocating... \n " , size ) ;
/* Allocate memory for the expanded device tree */
mem = lmb_alloc ( size + 4 , __alignof__ ( struct device_node ) ) ;
mem = ( unsigned long ) __va ( mem ) ;
( ( u32 * ) mem ) [ size / 4 ] = 0xdeadbeef ;
DBG ( " unflattening %lx... \n " , mem ) ;
/* Second pass, do actual unflattening */
start = ( ( unsigned long ) initial_boot_params ) +
initial_boot_params - > off_dt_struct ;
unflatten_dt_node ( mem , & start , NULL , & allnextp , 0 ) ;
if ( * ( ( u32 * ) start ) ! = OF_DT_END )
printk ( KERN_WARNING " Weird tag at end of tree: %08x \n " , * ( ( u32 * ) start ) ) ;
if ( ( ( u32 * ) mem ) [ size / 4 ] ! = 0xdeadbeef )
printk ( KERN_WARNING " End of tree marker overwritten: %08x \n " ,
( ( u32 * ) mem ) [ size / 4 ] ) ;
* allnextp = NULL ;
/* Get pointer to OF "/chosen" node for use everywhere */
of_chosen = of_find_node_by_path ( " /chosen " ) ;
2005-10-23 11:23:21 +04:00
if ( of_chosen = = NULL )
of_chosen = of_find_node_by_path ( " /chosen@0 " ) ;
2005-10-06 06:06:20 +04:00
DBG ( " <- unflatten_device_tree() \n " ) ;
}
2006-05-03 17:04:37 +04:00
/*
* ibm , pa - features is a per - cpu property that contains a string of
* attribute descriptors , each of which has a 2 byte header plus up
* to 254 bytes worth of processor attribute bits . First header
* byte specifies the number of bytes following the header .
* Second header byte is an " attribute-specifier " type , of which
* zero is the only currently - defined value .
* Implementation : Pass in the byte and bit offset for the feature
* that we are interested in . The function will return - 1 if the
* pa - features property is missing , or a 1 / 0 to indicate if the feature
* is supported / not supported . Note that the bit numbers are
* big - endian to match the definition in PAPR .
*/
static struct ibm_pa_feature {
unsigned long cpu_features ; /* CPU_FTR_xxx bit */
unsigned int cpu_user_ftrs ; /* PPC_FEATURE_xxx bit */
unsigned char pabyte ; /* byte number in ibm,pa-features */
unsigned char pabit ; /* bit number (big-endian) */
unsigned char invert ; /* if 1, pa bit set => clear feature */
} ibm_pa_features [ ] __initdata = {
{ 0 , PPC_FEATURE_HAS_MMU , 0 , 0 , 0 } ,
{ 0 , PPC_FEATURE_HAS_FPU , 0 , 1 , 0 } ,
{ CPU_FTR_SLB , 0 , 0 , 2 , 0 } ,
{ CPU_FTR_CTRL , 0 , 0 , 3 , 0 } ,
{ CPU_FTR_NOEXECUTE , 0 , 0 , 6 , 0 } ,
{ CPU_FTR_NODSISRALIGN , 0 , 1 , 1 , 1 } ,
2006-06-15 04:45:18 +04:00
#if 0
/* put this back once we know how to test if firmware does 64k IO */
2006-05-03 17:04:37 +04:00
{ CPU_FTR_CI_LARGE_PAGE , 0 , 1 , 2 , 0 } ,
2006-06-15 04:45:18 +04:00
# endif
2006-06-29 11:12:30 +04:00
{ CPU_FTR_REAL_LE , PPC_FEATURE_TRUE_LE , 5 , 0 , 0 } ,
2006-05-03 17:04:37 +04:00
} ;
static void __init check_cpu_pa_features ( unsigned long node )
{
unsigned char * pa_ftrs ;
unsigned long len , tablelen , i , bit ;
pa_ftrs = of_get_flat_dt_prop ( node , " ibm,pa-features " , & tablelen ) ;
if ( pa_ftrs = = NULL )
return ;
/* find descriptor with type == 0 */
for ( ; ; ) {
if ( tablelen < 3 )
return ;
len = 2 + pa_ftrs [ 0 ] ;
if ( tablelen < len )
return ; /* descriptor 0 not found */
if ( pa_ftrs [ 1 ] = = 0 )
break ;
tablelen - = len ;
pa_ftrs + = len ;
}
/* loop over bits we know about */
for ( i = 0 ; i < ARRAY_SIZE ( ibm_pa_features ) ; + + i ) {
struct ibm_pa_feature * fp = & ibm_pa_features [ i ] ;
if ( fp - > pabyte > = pa_ftrs [ 0 ] )
continue ;
bit = ( pa_ftrs [ 2 + fp - > pabyte ] > > ( 7 - fp - > pabit ) ) & 1 ;
if ( bit ^ fp - > invert ) {
cur_cpu_spec - > cpu_features | = fp - > cpu_features ;
cur_cpu_spec - > cpu_user_features | = fp - > cpu_user_ftrs ;
} else {
cur_cpu_spec - > cpu_features & = ~ fp - > cpu_features ;
cur_cpu_spec - > cpu_user_features & = ~ fp - > cpu_user_ftrs ;
}
}
}
2005-10-06 06:06:20 +04:00
static int __init early_init_dt_scan_cpus ( unsigned long node ,
2006-03-25 09:25:17 +03:00
const char * uname , int depth ,
void * data )
2005-10-06 06:06:20 +04:00
{
2006-03-25 09:25:17 +03:00
static int logical_cpuid = 0 ;
char * type = of_get_flat_dt_prop ( node , " device_type " , NULL ) ;
2006-03-28 10:14:44 +04:00
# ifdef CONFIG_ALTIVEC
u32 * prop ;
# endif
u32 * intserv ;
2006-03-25 09:25:17 +03:00
int i , nthreads ;
unsigned long len ;
int found = 0 ;
2005-10-06 06:06:20 +04:00
/* We are scanning "cpu" nodes only */
if ( type = = NULL | | strcmp ( type , " cpu " ) ! = 0 )
return 0 ;
2006-03-25 09:25:17 +03:00
/* Get physical cpuid */
intserv = of_get_flat_dt_prop ( node , " ibm,ppc-interrupt-server#s " , & len ) ;
if ( intserv ) {
nthreads = len / sizeof ( int ) ;
2005-10-06 06:06:20 +04:00
} else {
2006-03-25 09:25:17 +03:00
intserv = of_get_flat_dt_prop ( node , " reg " , NULL ) ;
nthreads = 1 ;
}
/*
* Now see if any of these threads match our boot cpu .
* NOTE : This must match the parsing done in smp_setup_cpu_maps .
*/
for ( i = 0 ; i < nthreads ; i + + ) {
/*
* version 2 of the kexec param format adds the phys cpuid of
* booted proc .
*/
if ( initial_boot_params & & initial_boot_params - > version > = 2 ) {
if ( intserv [ i ] = =
initial_boot_params - > boot_cpuid_phys ) {
found = 1 ;
break ;
}
} else {
/*
* Check if it ' s the boot - cpu , set it ' s hw index now ,
* unfortunately this format did not support booting
* off secondary threads .
*/
if ( of_get_flat_dt_prop ( node ,
2005-11-07 03:06:55 +03:00
" linux,boot-cpu " , NULL ) ! = NULL ) {
2006-03-25 09:25:17 +03:00
found = 1 ;
break ;
}
2005-10-06 06:06:20 +04:00
}
2006-03-25 09:25:17 +03:00
# ifdef CONFIG_SMP
/* logical cpu id is always 0 on UP kernels */
logical_cpuid + + ;
# endif
}
if ( found ) {
DBG ( " boot cpu: logical %d physical %d \n " , logical_cpuid ,
intserv [ i ] ) ;
boot_cpuid = logical_cpuid ;
set_hard_smp_processor_id ( boot_cpuid , intserv [ i ] ) ;
2005-10-06 06:06:20 +04:00
}
# ifdef CONFIG_ALTIVEC
/* Check if we have a VMX and eventually update CPU features */
2005-11-10 06:16:21 +03:00
prop = ( u32 * ) of_get_flat_dt_prop ( node , " ibm,vmx " , NULL ) ;
2005-10-06 06:06:20 +04:00
if ( prop & & ( * prop ) > 0 ) {
cur_cpu_spec - > cpu_features | = CPU_FTR_ALTIVEC ;
cur_cpu_spec - > cpu_user_features | = PPC_FEATURE_HAS_ALTIVEC ;
}
/* Same goes for Apple's "altivec" property */
2005-11-07 03:06:55 +03:00
prop = ( u32 * ) of_get_flat_dt_prop ( node , " altivec " , NULL ) ;
2005-10-06 06:06:20 +04:00
if ( prop ) {
cur_cpu_spec - > cpu_features | = CPU_FTR_ALTIVEC ;
cur_cpu_spec - > cpu_user_features | = PPC_FEATURE_HAS_ALTIVEC ;
}
# endif /* CONFIG_ALTIVEC */
2006-05-03 17:04:37 +04:00
check_cpu_pa_features ( node ) ;
2005-10-06 06:06:20 +04:00
# ifdef CONFIG_PPC_PSERIES
2006-03-25 09:25:17 +03:00
if ( nthreads > 1 )
2005-10-06 06:06:20 +04:00
cur_cpu_spec - > cpu_features | = CPU_FTR_SMT ;
2006-03-25 09:25:17 +03:00
else
cur_cpu_spec - > cpu_features & = ~ CPU_FTR_SMT ;
2005-10-06 06:06:20 +04:00
# endif
return 0 ;
}
static int __init early_init_dt_scan_chosen ( unsigned long node ,
const char * uname , int depth , void * data )
{
unsigned long * lprop ;
2006-02-24 19:54:52 +03:00
unsigned long l ;
char * p ;
2005-10-06 06:06:20 +04:00
DBG ( " search \" chosen \" , depth: %d, uname: %s \n " , depth , uname ) ;
2005-10-23 11:23:21 +04:00
if ( depth ! = 1 | |
( strcmp ( uname , " chosen " ) ! = 0 & & strcmp ( uname , " chosen@0 " ) ! = 0 ) )
2005-10-06 06:06:20 +04:00
return 0 ;
# ifdef CONFIG_PPC64
/* check if iommu is forced on or off */
2005-11-07 03:06:55 +03:00
if ( of_get_flat_dt_prop ( node , " linux,iommu-off " , NULL ) ! = NULL )
2005-10-06 06:06:20 +04:00
iommu_is_off = 1 ;
2005-11-07 03:06:55 +03:00
if ( of_get_flat_dt_prop ( node , " linux,iommu-force-on " , NULL ) ! = NULL )
2005-10-06 06:06:20 +04:00
iommu_force_on = 1 ;
# endif
2006-05-17 12:00:46 +04:00
/* mem=x on the command line is the preferred mechanism */
2005-11-07 03:06:55 +03:00
lprop = of_get_flat_dt_prop ( node , " linux,memory-limit " , NULL ) ;
2005-10-06 06:06:20 +04:00
if ( lprop )
memory_limit = * lprop ;
# ifdef CONFIG_PPC64
2005-11-07 03:06:55 +03:00
lprop = of_get_flat_dt_prop ( node , " linux,tce-alloc-start " , NULL ) ;
2005-10-06 06:06:20 +04:00
if ( lprop )
tce_alloc_start = * lprop ;
2005-11-07 03:06:55 +03:00
lprop = of_get_flat_dt_prop ( node , " linux,tce-alloc-end " , NULL ) ;
2005-10-06 06:06:20 +04:00
if ( lprop )
tce_alloc_end = * lprop ;
# endif
2005-12-04 10:39:48 +03:00
# ifdef CONFIG_KEXEC
lprop = ( u64 * ) of_get_flat_dt_prop ( node , " linux,crashkernel-base " , NULL ) ;
if ( lprop )
crashk_res . start = * lprop ;
lprop = ( u64 * ) of_get_flat_dt_prop ( node , " linux,crashkernel-size " , NULL ) ;
if ( lprop )
crashk_res . end = crashk_res . start + * lprop - 1 ;
# endif
2006-02-24 19:54:52 +03:00
/* Retreive command line */
p = of_get_flat_dt_prop ( node , " bootargs " , & l ) ;
if ( p ! = NULL & & l > 0 )
strlcpy ( cmd_line , p , min ( ( int ) l , COMMAND_LINE_SIZE ) ) ;
# ifdef CONFIG_CMDLINE
2006-10-05 22:35:16 +04:00
if ( p = = NULL | | l = = 0 | | ( l = = 1 & & ( * p ) = = 0 ) )
2006-02-24 19:54:52 +03:00
strlcpy ( cmd_line , CONFIG_CMDLINE , COMMAND_LINE_SIZE ) ;
# endif /* CONFIG_CMDLINE */
DBG ( " Command line is: %s \n " , cmd_line ) ;
2005-10-06 06:06:20 +04:00
/* break now */
return 1 ;
}
static int __init early_init_dt_scan_root ( unsigned long node ,
const char * uname , int depth , void * data )
{
u32 * prop ;
if ( depth ! = 0 )
return 0 ;
2005-11-07 03:06:55 +03:00
prop = of_get_flat_dt_prop ( node , " #size-cells " , NULL ) ;
2005-10-06 06:06:20 +04:00
dt_root_size_cells = ( prop = = NULL ) ? 1 : * prop ;
DBG ( " dt_root_size_cells = %x \n " , dt_root_size_cells ) ;
2005-11-07 03:06:55 +03:00
prop = of_get_flat_dt_prop ( node , " #address-cells " , NULL ) ;
2005-10-06 06:06:20 +04:00
dt_root_addr_cells = ( prop = = NULL ) ? 2 : * prop ;
DBG ( " dt_root_addr_cells = %x \n " , dt_root_addr_cells ) ;
/* break now */
return 1 ;
}
static unsigned long __init dt_mem_next_cell ( int s , cell_t * * cellp )
{
cell_t * p = * cellp ;
2006-09-19 08:06:27 +04:00
* cellp = p + s ;
return of_read_ulong ( p , s ) ;
2005-10-06 06:06:20 +04:00
}
static int __init early_init_dt_scan_memory ( unsigned long node ,
const char * uname , int depth , void * data )
{
2005-11-07 03:06:55 +03:00
char * type = of_get_flat_dt_prop ( node , " device_type " , NULL ) ;
2005-10-06 06:06:20 +04:00
cell_t * reg , * endp ;
unsigned long l ;
/* We are scanning "memory" nodes only */
2005-11-10 04:00:55 +03:00
if ( type = = NULL ) {
/*
* The longtrail doesn ' t have a device_type on the
* / memory node , so look for the node called / memory @ 0.
*/
if ( depth ! = 1 | | strcmp ( uname , " memory@0 " ) ! = 0 )
return 0 ;
} else if ( strcmp ( type , " memory " ) ! = 0 )
2005-10-06 06:06:20 +04:00
return 0 ;
2005-12-04 10:39:55 +03:00
reg = ( cell_t * ) of_get_flat_dt_prop ( node , " linux,usable-memory " , & l ) ;
if ( reg = = NULL )
reg = ( cell_t * ) of_get_flat_dt_prop ( node , " reg " , & l ) ;
2005-10-06 06:06:20 +04:00
if ( reg = = NULL )
return 0 ;
endp = reg + ( l / sizeof ( cell_t ) ) ;
2005-11-03 07:39:09 +03:00
DBG ( " memory scan node %s, reg size %ld, data: %x %x %x %x, \n " ,
2005-10-06 06:06:20 +04:00
uname , l , reg [ 0 ] , reg [ 1 ] , reg [ 2 ] , reg [ 3 ] ) ;
while ( ( endp - reg ) > = ( dt_root_addr_cells + dt_root_size_cells ) ) {
unsigned long base , size ;
base = dt_mem_next_cell ( dt_root_addr_cells , & reg ) ;
size = dt_mem_next_cell ( dt_root_size_cells , & reg ) ;
if ( size = = 0 )
continue ;
DBG ( " - %lx , %lx \n " , base , size ) ;
# ifdef CONFIG_PPC64
if ( iommu_is_off ) {
if ( base > = 0x80000000ul )
continue ;
if ( ( base + size ) > 0x80000000ul )
size = 0x80000000ul - base ;
}
# endif
lmb_add ( base , size ) ;
}
return 0 ;
}
static void __init early_reserve_mem ( void )
{
2006-01-12 02:57:13 +03:00
u64 base , size ;
u64 * reserve_map ;
2006-06-18 02:51:09 +04:00
unsigned long self_base ;
unsigned long self_size ;
2005-10-06 06:06:20 +04:00
2006-01-12 02:57:13 +03:00
reserve_map = ( u64 * ) ( ( ( unsigned long ) initial_boot_params ) +
2005-10-06 06:06:20 +04:00
initial_boot_params - > off_mem_rsvmap ) ;
2006-05-19 02:03:05 +04:00
/* before we do anything, lets reserve the dt blob */
2006-06-18 02:51:09 +04:00
self_base = __pa ( ( unsigned long ) initial_boot_params ) ;
self_size = initial_boot_params - > totalsize ;
lmb_reserve ( self_base , self_size ) ;
2006-05-19 02:03:05 +04:00
2006-01-12 02:57:13 +03:00
# ifdef CONFIG_PPC32
/*
* Handle the case where we might be booting from an old kexec
* image that setup the mem_rsvmap as pairs of 32 - bit values
*/
if ( * reserve_map > 0xffffffffull ) {
u32 base_32 , size_32 ;
u32 * reserve_map_32 = ( u32 * ) reserve_map ;
while ( 1 ) {
base_32 = * ( reserve_map_32 + + ) ;
size_32 = * ( reserve_map_32 + + ) ;
if ( size_32 = = 0 )
break ;
2006-06-18 02:51:09 +04:00
/* skip if the reservation is for the blob */
if ( base_32 = = self_base & & size_32 = = self_size )
continue ;
2006-02-24 19:54:52 +03:00
DBG ( " reserving: %x -> %x \n " , base_32 , size_32 ) ;
2006-01-12 02:57:13 +03:00
lmb_reserve ( base_32 , size_32 ) ;
}
return ;
}
# endif
2005-10-06 06:06:20 +04:00
while ( 1 ) {
base = * ( reserve_map + + ) ;
size = * ( reserve_map + + ) ;
if ( size = = 0 )
break ;
2006-06-18 02:51:09 +04:00
/* skip if the reservation is for the blob */
if ( base = = self_base & & size = = self_size )
continue ;
2006-01-12 02:57:13 +03:00
DBG ( " reserving: %llx -> %llx \n " , base , size ) ;
2005-10-06 06:06:20 +04:00
lmb_reserve ( base , size ) ;
}
#if 0
DBG ( " memory reserved, lmbs : \n " ) ;
lmb_dump_all ( ) ;
# endif
}
void __init early_init_devtree ( void * params )
{
DBG ( " -> early_init_devtree() \n " ) ;
/* Setup flat device-tree pointer */
initial_boot_params = params ;
2006-06-23 12:20:13 +04:00
# ifdef CONFIG_PPC_RTAS
/* Some machines might need RTAS info for debugging, grab it now. */
of_scan_flat_dt ( early_init_dt_scan_rtas , NULL ) ;
# endif
2005-10-06 06:06:20 +04:00
/* Retrieve various informations from the /chosen node of the
* device - tree , including the platform type , initrd location and
* size , TCE reserve , and more . . .
*/
2005-11-07 03:06:55 +03:00
of_scan_flat_dt ( early_init_dt_scan_chosen , NULL ) ;
2005-10-06 06:06:20 +04:00
/* Scan memory nodes and rebuild LMBs */
lmb_init ( ) ;
2005-11-07 03:06:55 +03:00
of_scan_flat_dt ( early_init_dt_scan_root , NULL ) ;
of_scan_flat_dt ( early_init_dt_scan_memory , NULL ) ;
2006-05-17 12:00:45 +04:00
/* Save command line for /proc/cmdline and then parse parameters */
strlcpy ( saved_command_line , cmd_line , COMMAND_LINE_SIZE ) ;
parse_early_param ( ) ;
2005-10-06 06:06:20 +04:00
/* Reserve LMB regions used by kernel, initrd, dt, etc... */
2005-12-04 10:39:37 +03:00
lmb_reserve ( PHYSICAL_START , __pa ( klimit ) - PHYSICAL_START ) ;
2006-05-17 12:00:49 +04:00
reserve_kdump_trampoline ( ) ;
2006-05-18 05:16:11 +04:00
reserve_crashkernel ( ) ;
2005-10-06 06:06:20 +04:00
early_reserve_mem ( ) ;
2006-05-17 12:00:46 +04:00
lmb_enforce_memory_limit ( memory_limit ) ;
lmb_analyze ( ) ;
DBG ( " Phys. mem: %lx \n " , lmb_phys_mem_size ( ) ) ;
/* We may need to relocate the flat tree, do it now.
* FIXME . . and the initrd too ? */
move_device_tree ( ) ;
2005-10-06 06:06:20 +04:00
DBG ( " Scanning CPUs ... \n " ) ;
2005-11-07 03:06:55 +03:00
/* Retreive CPU related informations from the flat tree
* ( altivec support , boot CPU ID , . . . )
2005-10-06 06:06:20 +04:00
*/
2005-11-07 03:06:55 +03:00
of_scan_flat_dt ( early_init_dt_scan_cpus , NULL ) ;
2005-10-06 06:06:20 +04:00
DBG ( " <- early_init_devtree() \n " ) ;
}
# undef printk
int
prom_n_addr_cells ( struct device_node * np )
{
2006-07-12 09:35:54 +04:00
const int * ip ;
2005-10-06 06:06:20 +04:00
do {
if ( np - > parent )
np = np - > parent ;
2006-07-12 09:35:54 +04:00
ip = get_property ( np , " #address-cells " , NULL ) ;
2005-10-06 06:06:20 +04:00
if ( ip ! = NULL )
return * ip ;
} while ( np - > parent ) ;
/* No #address-cells property for the root node, default to 1 */
return 1 ;
}
2005-11-14 09:30:40 +03:00
EXPORT_SYMBOL ( prom_n_addr_cells ) ;
2005-10-06 06:06:20 +04:00
int
prom_n_size_cells ( struct device_node * np )
{
2006-07-12 09:35:54 +04:00
const int * ip ;
2005-10-06 06:06:20 +04:00
do {
if ( np - > parent )
np = np - > parent ;
2006-07-12 09:35:54 +04:00
ip = get_property ( np , " #size-cells " , NULL ) ;
2005-10-06 06:06:20 +04:00
if ( ip ! = NULL )
return * ip ;
} while ( np - > parent ) ;
/* No #size-cells property for the root node, default to 1 */
return 1 ;
}
2005-11-14 09:30:40 +03:00
EXPORT_SYMBOL ( prom_n_size_cells ) ;
2005-10-06 06:06:20 +04:00
/**
* Construct and return a list of the device_nodes with a given name .
*/
struct device_node * find_devices ( const char * name )
{
struct device_node * head , * * prevp , * np ;
prevp = & head ;
for ( np = allnodes ; np ! = 0 ; np = np - > allnext ) {
if ( np - > name ! = 0 & & strcasecmp ( np - > name , name ) = = 0 ) {
* prevp = np ;
prevp = & np - > next ;
}
}
* prevp = NULL ;
return head ;
}
EXPORT_SYMBOL ( find_devices ) ;
/**
* Construct and return a list of the device_nodes with a given type .
*/
struct device_node * find_type_devices ( const char * type )
{
struct device_node * head , * * prevp , * np ;
prevp = & head ;
for ( np = allnodes ; np ! = 0 ; np = np - > allnext ) {
if ( np - > type ! = 0 & & strcasecmp ( np - > type , type ) = = 0 ) {
* prevp = np ;
prevp = & np - > next ;
}
}
* prevp = NULL ;
return head ;
}
EXPORT_SYMBOL ( find_type_devices ) ;
/**
* Returns all nodes linked together
*/
struct device_node * find_all_nodes ( void )
{
struct device_node * head , * * prevp , * np ;
prevp = & head ;
for ( np = allnodes ; np ! = 0 ; np = np - > allnext ) {
* prevp = np ;
prevp = & np - > next ;
}
* prevp = NULL ;
return head ;
}
EXPORT_SYMBOL ( find_all_nodes ) ;
/** Checks if the given "compat" string matches one of the strings in
* the device ' s " compatible " property
*/
2006-10-20 05:49:54 +04:00
int device_is_compatible ( const struct device_node * device , const char * compat )
2005-10-06 06:06:20 +04:00
{
const char * cp ;
int cplen , l ;
2006-07-12 09:35:54 +04:00
cp = get_property ( device , " compatible " , & cplen ) ;
2005-10-06 06:06:20 +04:00
if ( cp = = NULL )
return 0 ;
while ( cplen > 0 ) {
if ( strncasecmp ( cp , compat , strlen ( compat ) ) = = 0 )
return 1 ;
l = strlen ( cp ) + 1 ;
cp + = l ;
cplen - = l ;
}
return 0 ;
}
EXPORT_SYMBOL ( device_is_compatible ) ;
/**
* Indicates whether the root node has a 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 = device_is_compatible ( root , compat ) ;
of_node_put ( root ) ;
}
return rc ;
}
EXPORT_SYMBOL ( machine_is_compatible ) ;
/**
* Construct and return a list of the device_nodes with a given type
* and compatible property .
*/
struct device_node * find_compatible_devices ( const char * type ,
const char * compat )
{
struct device_node * head , * * prevp , * np ;
prevp = & head ;
for ( np = allnodes ; np ! = 0 ; np = np - > allnext ) {
if ( type ! = NULL
& & ! ( np - > type ! = 0 & & strcasecmp ( np - > type , type ) = = 0 ) )
continue ;
if ( device_is_compatible ( np , compat ) ) {
* prevp = np ;
prevp = & np - > next ;
}
}
* prevp = NULL ;
return head ;
}
EXPORT_SYMBOL ( find_compatible_devices ) ;
/**
* Find the device_node with a given full_name .
*/
struct device_node * find_path_device ( const char * path )
{
struct device_node * np ;
for ( np = allnodes ; np ! = 0 ; np = np - > allnext )
if ( np - > full_name ! = 0 & & strcasecmp ( np - > full_name , path ) = = 0 )
return np ;
return NULL ;
}
EXPORT_SYMBOL ( find_path_device ) ;
/*******
*
* New implementation of the OF " find " APIs , return a refcounted
* object , call of_node_put ( ) when done . The device tree and list
* are protected by a rw_lock .
*
* Note that property management will need some locking as well ,
* this isn ' t dealt with yet .
*
* * * * * * */
/**
* 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 ;
2006-02-04 14:44:56 +03:00
for ( ; np ! = NULL ; np = np - > allnext )
if ( np - > name ! = NULL & & strcasecmp ( np - > name , name ) = = 0
2005-10-06 06:06:20 +04:00
& & of_node_get ( np ) )
break ;
if ( from )
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 , 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 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 ! = 0 ; np = np - > allnext )
if ( np - > type ! = 0 & & strcasecmp ( np - > type , type ) = = 0
& & of_node_get ( np ) )
break ;
if ( from )
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 ! = 0 ; np = np - > allnext ) {
if ( type ! = NULL
& & ! ( np - > type ! = 0 & & strcasecmp ( np - > type , type ) = = 0 ) )
continue ;
if ( device_is_compatible ( np , compatible ) & & of_node_get ( np ) )
break ;
}
if ( from )
of_node_put ( from ) ;
read_unlock ( & devtree_lock ) ;
return np ;
}
EXPORT_SYMBOL ( of_find_compatible_node ) ;
/**
* 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 ! = 0 ; np = np - > allnext ) {
if ( np - > full_name ! = 0 & & strcasecmp ( 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_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 ! = 0 ; np = np - > allnext )
if ( np - > linux_phandle = = handle )
break ;
if ( np )
of_node_get ( np ) ;
read_unlock ( & devtree_lock ) ;
return np ;
}
EXPORT_SYMBOL ( of_find_node_by_phandle ) ;
/**
* 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 ! = 0 ; np = np - > allnext )
if ( of_node_get ( np ) )
break ;
if ( prev )
of_node_put ( prev ) ;
read_unlock ( & devtree_lock ) ;
return np ;
}
EXPORT_SYMBOL ( of_find_all_nodes ) ;
/**
* 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 ) ;
/**
* 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 ! = 0 ; next = next - > sibling )
if ( of_node_get ( next ) )
break ;
if ( prev )
of_node_put ( prev ) ;
read_unlock ( & devtree_lock ) ;
return next ;
}
EXPORT_SYMBOL ( of_get_next_child ) ;
/**
* 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 ;
if ( ! OF_IS_DYNAMIC ( node ) )
return ;
while ( prop ) {
struct property * next = prop - > next ;
kfree ( prop - > name ) ;
kfree ( prop - > value ) ;
kfree ( prop ) ;
prop = next ;
2006-01-13 01:08:27 +03:00
if ( ! prop ) {
prop = node - > deadprops ;
node - > deadprops = NULL ;
}
2005-10-06 06:06:20 +04:00
}
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 ) ;
/*
* Plug a device node into the tree and global list .
*/
void of_attach_node ( struct device_node * np )
{
write_lock ( & devtree_lock ) ;
np - > sibling = np - > parent - > child ;
np - > allnext = allnodes ;
np - > parent - > child = np ;
allnodes = np ;
write_unlock ( & devtree_lock ) ;
}
/*
* " 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 ( const struct device_node * np )
{
struct device_node * parent ;
write_lock ( & devtree_lock ) ;
parent = np - > parent ;
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 ;
}
write_unlock ( & devtree_lock ) ;
}
# ifdef CONFIG_PPC_PSERIES
/*
* Fix up the uninitialized fields in a new device node :
2006-07-03 15:36:01 +04:00
* name , type and pci - specific fields
2005-10-06 06:06:20 +04:00
*/
2005-12-13 10:01:21 +03:00
static int of_finish_dynamic_node ( struct device_node * node )
2005-10-06 06:06:20 +04:00
{
struct device_node * parent = of_get_parent ( node ) ;
int err = 0 ;
2006-07-12 09:35:54 +04:00
const phandle * ibm_phandle ;
2005-10-06 06:06:20 +04:00
node - > name = get_property ( node , " name " , NULL ) ;
node - > type = get_property ( node , " device_type " , NULL ) ;
if ( ! parent ) {
err = - ENODEV ;
goto out ;
}
/* We don't support that function on PowerMac, at least
* not yet
*/
2006-03-28 16:15:54 +04:00
if ( machine_is ( powermac ) )
2005-10-06 06:06:20 +04:00
return - ENODEV ;
/* fix up new node's linux_phandle field */
2006-07-12 09:35:54 +04:00
if ( ( ibm_phandle = get_property ( node , " ibm,phandle " , NULL ) ) )
2005-10-06 06:06:20 +04:00
node - > linux_phandle = * ibm_phandle ;
out :
of_node_put ( parent ) ;
return err ;
}
static int prom_reconfig_notifier ( struct notifier_block * nb ,
unsigned long action , void * node )
{
int err ;
switch ( action ) {
case PSERIES_RECONFIG_ADD :
2005-12-13 10:01:21 +03:00
err = of_finish_dynamic_node ( node ) ;
2005-10-06 06:06:20 +04:00
if ( err < 0 ) {
printk ( KERN_ERR " finish_node returned %d \n " , err ) ;
err = NOTIFY_BAD ;
}
break ;
default :
err = NOTIFY_DONE ;
break ;
}
return err ;
}
static struct notifier_block prom_reconfig_nb = {
. notifier_call = prom_reconfig_notifier ,
. priority = 10 , /* This one needs to run first */
} ;
static int __init prom_reconfig_setup ( void )
{
return pSeries_reconfig_notifier_register ( & prom_reconfig_nb ) ;
}
__initcall ( prom_reconfig_setup ) ;
# endif
2006-10-20 05:49:54 +04:00
struct property * of_find_property ( const struct device_node * np ,
const char * name ,
2006-01-13 01:09:29 +03:00
int * lenp )
2005-10-06 06:06:20 +04:00
{
struct property * pp ;
2006-01-13 01:08:27 +03:00
read_lock ( & devtree_lock ) ;
2005-10-06 06:06:20 +04:00
for ( pp = np - > properties ; pp ! = 0 ; pp = pp - > next )
if ( strcmp ( pp - > name , name ) = = 0 ) {
if ( lenp ! = 0 )
* lenp = pp - > length ;
2006-01-13 01:08:27 +03:00
break ;
2005-10-06 06:06:20 +04:00
}
2006-01-13 01:08:27 +03:00
read_unlock ( & devtree_lock ) ;
2006-01-13 01:09:29 +03:00
return pp ;
}
/*
* Find a property with a given name for a given node
* and return the value .
*/
2006-10-20 05:49:54 +04:00
const void * get_property ( const struct device_node * np , const char * name ,
int * lenp )
2006-01-13 01:09:29 +03:00
{
struct property * pp = of_find_property ( np , name , lenp ) ;
2006-01-13 01:08:27 +03:00
return pp ? pp - > value : NULL ;
2005-10-06 06:06:20 +04:00
}
EXPORT_SYMBOL ( get_property ) ;
/*
* Add a property to a node
*/
2005-11-07 06:29:02 +03:00
int prom_add_property ( struct device_node * np , struct property * prop )
2005-10-06 06:06:20 +04:00
{
2005-11-07 06:29:02 +03:00
struct property * * next ;
2005-10-06 06:06:20 +04:00
prop - > next = NULL ;
2005-11-07 06:29:02 +03:00
write_lock ( & devtree_lock ) ;
next = & np - > properties ;
while ( * next ) {
if ( strcmp ( prop - > name , ( * next ) - > name ) = = 0 ) {
/* duplicate ! don't insert it */
write_unlock ( & devtree_lock ) ;
return - 1 ;
}
2005-10-06 06:06:20 +04:00
next = & ( * next ) - > next ;
2005-11-07 06:29:02 +03:00
}
2005-10-06 06:06:20 +04:00
* next = prop ;
2005-11-07 06:29:02 +03:00
write_unlock ( & devtree_lock ) ;
2005-11-10 05:37:51 +03:00
# ifdef CONFIG_PROC_DEVICETREE
2005-11-07 06:29:02 +03:00
/* try to add to proc as well if it was initialized */
if ( np - > pde )
proc_device_tree_add_prop ( np - > pde , prop ) ;
2005-11-10 05:37:51 +03:00
# endif /* CONFIG_PROC_DEVICETREE */
2005-11-07 06:29:02 +03:00
return 0 ;
2005-10-06 06:06:20 +04:00
}
2006-01-13 01:08:27 +03:00
/*
* 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 ;
int found = 0 ;
write_lock ( & devtree_lock ) ;
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 ( & devtree_lock ) ;
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 ;
}
/*
* 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 ;
int found = 0 ;
write_lock ( & devtree_lock ) ;
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 ( & devtree_lock ) ;
if ( ! found )
return - ENODEV ;
2005-10-06 06:06:20 +04:00
2006-01-13 01:08:27 +03:00
# 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 ;
}
2006-02-03 11:05:47 +03:00
2006-06-19 22:33:16 +04:00
/* Find the device node for a given logical cpu number, also returns the cpu
* local thread number ( index in ibm , interrupt - server # s ) if relevant and
* asked for ( non NULL )
*/
struct device_node * of_get_cpu_node ( int cpu , unsigned int * thread )
{
int hardid ;
struct device_node * np ;
hardid = get_hard_smp_processor_id ( cpu ) ;
for_each_node_by_type ( np , " cpu " ) {
2006-07-12 09:35:54 +04:00
const u32 * intserv ;
2006-06-19 22:33:16 +04:00
unsigned int plen , t ;
/* Check for ibm,ppc-interrupt-server#s. If it doesn't exist
* fallback to " reg " property and assume no threads
*/
2006-07-12 09:35:54 +04:00
intserv = get_property ( np , " ibm,ppc-interrupt-server#s " ,
& plen ) ;
2006-06-19 22:33:16 +04:00
if ( intserv = = NULL ) {
2006-07-12 09:35:54 +04:00
const u32 * reg = get_property ( np , " reg " , NULL ) ;
2006-06-19 22:33:16 +04:00
if ( reg = = NULL )
continue ;
if ( * reg = = hardid ) {
if ( thread )
* thread = 0 ;
return np ;
}
} else {
plen / = sizeof ( u32 ) ;
for ( t = 0 ; t < plen ; t + + ) {
if ( hardid = = intserv [ t ] ) {
if ( thread )
* thread = t ;
return np ;
}
}
}
}
return NULL ;
}
2006-10-24 20:39:45 +04:00
EXPORT_SYMBOL ( of_get_cpu_node ) ;
2006-06-23 12:16:03 +04:00
# ifdef DEBUG
static struct debugfs_blob_wrapper flat_dt_blob ;
static int __init export_flat_device_tree ( void )
{
struct dentry * d ;
d = debugfs_create_dir ( " powerpc " , NULL ) ;
if ( ! d )
return 1 ;
flat_dt_blob . data = initial_boot_params ;
flat_dt_blob . size = initial_boot_params - > totalsize ;
d = debugfs_create_blob ( " flat-device-tree " , S_IFREG | S_IRUSR ,
d , & flat_dt_blob ) ;
if ( ! d )
return 1 ;
return 0 ;
}
__initcall ( export_flat_device_tree ) ;
# endif