2009-11-24 00:53:09 +03:00
/*
* Functions for working with the Flattened Device Tree data format
*
* Copyright 2009 Benjamin Herrenschmidt , IBM Corp
* benh @ kernel . crashing . org
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation .
*/
2009-11-24 06:07:01 +03:00
# include <linux/kernel.h>
2009-11-24 13:26:58 +03:00
# include <linux/initrd.h>
2013-08-29 00:18:32 +04:00
# include <linux/memblock.h>
2010-11-19 02:55:02 +03:00
# include <linux/module.h>
2009-11-24 00:53:09 +03:00
# include <linux/of.h>
# include <linux/of_fdt.h>
2010-02-14 17:13:47 +03:00
# include <linux/string.h>
# include <linux/errno.h>
2010-11-19 02:55:02 +03:00
# include <linux/slab.h>
2013-07-29 07:11:50 +04:00
# include <linux/random.h>
2010-02-02 07:34:14 +03:00
ARM: prom.h: Fix build error by removing unneeded header file
Fix the following build error:
CC [M] fs/udf/balloc.o
In file included from /home/fabio/next/linux-next/arch/arm/include/asm/prom.h:16,
from include/linux/of.h:140,
from include/asm-generic/gpio.h:7,
from arch/arm/plat-mxc/include/mach/irqs.h:14,
from /home/fabio/next/linux-next/arch/arm/include/asm/irq.h:4,
from /home/fabio/next/linux-next/arch/arm/include/asm/hardirq.h:6,
from include/linux/hardirq.h:7,
from include/linux/highmem.h:8,
from include/linux/pagemap.h:10,
from include/linux/buffer_head.h:13,
from fs/udf/udfdecl.h:11,
from fs/udf/balloc.c:22:
/home/fabio/next/linux-next/arch/arm/include/asm/setup.h:146: error: redefinition of 'struct tag'
Signed-off-by: Fabio Estevam <fabio.estevam@freescale.com>
[grant.likely: fix build failure on drivers/of/fdt.c]
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
2012-01-02 20:19:03 +04:00
# include <asm/setup.h> /* for COMMAND_LINE_SIZE */
2009-12-11 09:42:21 +03:00
# ifdef CONFIG_PPC
# include <asm/machdep.h>
# endif /* CONFIG_PPC */
2010-02-14 17:13:47 +03:00
# include <asm/page.h>
2010-11-19 02:54:59 +03:00
char * of_fdt_get_string ( struct boot_param_header * blob , u32 offset )
{
return ( ( char * ) blob ) +
be32_to_cpu ( blob - > off_dt_strings ) + offset ;
}
/**
* of_fdt_get_property - Given a node in the given flat blob , return
* the property ptr
*/
void * of_fdt_get_property ( struct boot_param_header * blob ,
unsigned long node , const char * name ,
unsigned long * size )
{
unsigned long p = node ;
do {
u32 tag = be32_to_cpup ( ( __be32 * ) p ) ;
u32 sz , noff ;
const char * nstr ;
p + = 4 ;
if ( tag = = OF_DT_NOP )
continue ;
if ( tag ! = OF_DT_PROP )
return NULL ;
sz = be32_to_cpup ( ( __be32 * ) p ) ;
noff = be32_to_cpup ( ( __be32 * ) ( p + 4 ) ) ;
p + = 8 ;
if ( be32_to_cpu ( blob - > version ) < 0x10 )
p = ALIGN ( p , sz > = 8 ? 8 : 4 ) ;
nstr = of_fdt_get_string ( blob , noff ) ;
if ( nstr = = NULL ) {
pr_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 ) ;
}
/**
* of_fdt_is_compatible - Return true if given node from the given blob has
* compat in its compatible list
* @ blob : A device tree blob
* @ node : node to test
* @ compat : compatible string to compare with compatible list .
2010-10-30 19:49:09 +04:00
*
* On match , returns a non - zero value with smaller values returned for more
* specific compatible values .
2010-11-19 02:54:59 +03:00
*/
int of_fdt_is_compatible ( struct boot_param_header * blob ,
unsigned long node , const char * compat )
{
const char * cp ;
2010-10-30 19:49:09 +04:00
unsigned long cplen , l , score = 0 ;
2010-11-19 02:54:59 +03:00
cp = of_fdt_get_property ( blob , node , " compatible " , & cplen ) ;
if ( cp = = NULL )
return 0 ;
while ( cplen > 0 ) {
2010-10-30 19:49:09 +04:00
score + + ;
2010-11-19 02:54:59 +03:00
if ( of_compat_cmp ( cp , compat , strlen ( compat ) ) = = 0 )
2010-10-30 19:49:09 +04:00
return score ;
2010-11-19 02:54:59 +03:00
l = strlen ( cp ) + 1 ;
cp + = l ;
cplen - = l ;
}
return 0 ;
}
2010-10-30 19:49:09 +04:00
/**
* of_fdt_match - Return true if node matches a list of compatible values
*/
int of_fdt_match ( struct boot_param_header * blob , unsigned long node ,
2011-12-21 01:56:45 +04:00
const char * const * compat )
2010-10-30 19:49:09 +04:00
{
unsigned int tmp , score = 0 ;
if ( ! compat )
return 0 ;
while ( * compat ) {
tmp = of_fdt_is_compatible ( blob , node , * compat ) ;
if ( tmp & & ( score = = 0 | | ( tmp < score ) ) )
score = tmp ;
compat + + ;
}
return score ;
}
2013-08-29 16:30:35 +04:00
static void * unflatten_dt_alloc ( void * * mem , unsigned long size ,
2009-11-24 06:07:00 +03:00
unsigned long align )
{
void * res ;
2013-08-29 16:30:35 +04:00
* mem = PTR_ALIGN ( * mem , align ) ;
res = * mem ;
2009-11-24 06:07:00 +03:00
* mem + = size ;
return res ;
}
/**
* unflatten_dt_node - Alloc and populate a device_node from the flat tree
2010-11-19 02:55:00 +03:00
* @ blob : The parent device tree blob
2011-03-18 03:32:35 +03:00
* @ mem : Memory chunk to use for allocating device nodes and properties
2009-11-24 06:07:00 +03:00
* @ p : pointer to node in flat tree
* @ dad : Parent struct device_node
* @ allnextpp : pointer to - > allnext from last allocated device_node
* @ fpsize : Size of the node path up at the current depth .
*/
2013-08-29 16:30:35 +04:00
static void * unflatten_dt_node ( struct boot_param_header * blob ,
void * mem ,
void * * p ,
2010-11-19 02:55:00 +03:00
struct device_node * dad ,
struct device_node * * * allnextpp ,
unsigned long fpsize )
2009-11-24 06:07:00 +03:00
{
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 ;
2013-08-29 16:30:35 +04:00
tag = be32_to_cpup ( * p ) ;
2009-11-24 06:07:00 +03:00
if ( tag ! = OF_DT_BEGIN_NODE ) {
pr_err ( " Weird tag at start of node: %x \n " , tag ) ;
return mem ;
}
* p + = 4 ;
2013-08-29 16:30:35 +04:00
pathp = * p ;
2009-11-24 06:07:00 +03:00
l = allocl = strlen ( pathp ) + 1 ;
2013-08-29 16:30:35 +04:00
* p = PTR_ALIGN ( * p + l , 4 ) ;
2009-11-24 06:07:00 +03:00
/* 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 ;
2012-11-16 19:14:38 +04:00
l = 1 ;
* pathp = ' \0 ' ;
2009-11-24 06:07:00 +03:00
} 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 ) {
2012-11-15 02:37:12 +04:00
char * fn ;
np - > full_name = fn = ( ( char * ) np ) + sizeof ( * np ) ;
2009-11-24 06:07:00 +03:00
if ( new_format ) {
/* rebuild full path for new format */
if ( dad & & dad - > parent ) {
strcpy ( fn , dad - > full_name ) ;
# ifdef DEBUG
if ( ( strlen ( fn ) + l + 1 ) ! = allocl ) {
pr_debug ( " %s: p: %d, l: %d, a: %d \n " ,
pathp , ( int ) strlen ( fn ) ,
l , allocl ) ;
}
# endif
fn + = strlen ( fn ) ;
}
* ( fn + + ) = ' / ' ;
2012-11-15 02:37:12 +04:00
}
memcpy ( fn , pathp , l ) ;
2009-11-24 06:07:00 +03:00
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 = = NULL )
dad - > child = np ;
else
dad - > next - > sibling = np ;
dad - > next = np ;
}
kref_init ( & np - > kref ) ;
}
2011-03-18 03:32:35 +03:00
/* process properties */
2009-11-24 06:07:00 +03:00
while ( 1 ) {
u32 sz , noff ;
char * pname ;
2013-08-29 16:30:35 +04:00
tag = be32_to_cpup ( * p ) ;
2009-11-24 06:07:00 +03:00
if ( tag = = OF_DT_NOP ) {
* p + = 4 ;
continue ;
}
if ( tag ! = OF_DT_PROP )
break ;
* p + = 4 ;
2013-08-29 16:30:35 +04:00
sz = be32_to_cpup ( * p ) ;
noff = be32_to_cpup ( * p + 4 ) ;
2009-11-24 06:07:00 +03:00
* p + = 8 ;
2010-11-19 02:55:00 +03:00
if ( be32_to_cpu ( blob - > version ) < 0x10 )
2013-08-29 16:30:35 +04:00
* p = PTR_ALIGN ( * p , sz > = 8 ? 8 : 4 ) ;
2009-11-24 06:07:00 +03:00
2010-11-19 02:55:00 +03:00
pname = of_fdt_get_string ( blob , noff ) ;
2009-11-24 06:07:00 +03:00
if ( pname = = NULL ) {
pr_info ( " 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 ) {
2010-02-02 07:34:15 +03:00
/* We accept flattened tree phandles either in
* ePAPR - style " phandle " properties , or the
* legacy " linux,phandle " properties . If both
* appear and have different values , things
* will get weird . Don ' t do that . */
if ( ( strcmp ( pname , " phandle " ) = = 0 ) | |
( strcmp ( pname , " linux,phandle " ) = = 0 ) ) {
2010-01-29 00:06:53 +03:00
if ( np - > phandle = = 0 )
2010-07-23 11:48:25 +04:00
np - > phandle = be32_to_cpup ( ( __be32 * ) * p ) ;
2009-11-24 06:07:00 +03:00
}
2010-02-02 07:34:15 +03:00
/* And we process the "ibm,phandle" property
* used in pSeries dynamic device tree
* stuff */
2009-11-24 06:07:00 +03:00
if ( strcmp ( pname , " ibm,phandle " ) = = 0 )
2010-07-23 11:48:25 +04:00
np - > phandle = be32_to_cpup ( ( __be32 * ) * p ) ;
2009-11-24 06:07:00 +03:00
pp - > name = pname ;
pp - > length = sz ;
2013-08-29 16:30:35 +04:00
pp - > value = * p ;
2009-11-24 06:07:00 +03:00
* prev_pp = pp ;
prev_pp = & pp - > next ;
}
2013-08-29 16:30:35 +04:00
* p = PTR_ALIGN ( ( * p ) + sz , 4 ) ;
2009-11-24 06:07:00 +03:00
}
/* with version 0x10 we may not have the name property, recreate
* it here from the unit name if absent
*/
if ( ! has_name ) {
char * p1 = pathp , * ps = pathp , * pa = NULL ;
int sz ;
while ( * p1 ) {
if ( ( * p1 ) = = ' @ ' )
pa = p1 ;
if ( ( * p1 ) = = ' / ' )
ps = p1 + 1 ;
p1 + + ;
}
if ( pa < ps )
pa = p1 ;
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 = pp + 1 ;
* prev_pp = pp ;
prev_pp = & pp - > next ;
memcpy ( pp - > value , ps , sz - 1 ) ;
( ( char * ) pp - > value ) [ sz - 1 ] = 0 ;
pr_debug ( " fixed up name for %s -> %s \n " , pathp ,
( char * ) pp - > value ) ;
}
}
if ( allnextpp ) {
* prev_pp = NULL ;
np - > name = of_get_property ( np , " name " , NULL ) ;
np - > type = of_get_property ( np , " device_type " , NULL ) ;
if ( ! np - > name )
np - > name = " <NULL> " ;
if ( ! np - > type )
np - > type = " <NULL> " ;
}
2010-03-27 07:09:56 +03:00
while ( tag = = OF_DT_BEGIN_NODE | | tag = = OF_DT_NOP ) {
if ( tag = = OF_DT_NOP )
* p + = 4 ;
else
2010-11-19 02:55:00 +03:00
mem = unflatten_dt_node ( blob , mem , p , np , allnextpp ,
fpsize ) ;
2013-08-29 16:30:35 +04:00
tag = be32_to_cpup ( * p ) ;
2009-11-24 06:07:00 +03:00
}
if ( tag ! = OF_DT_END_NODE ) {
pr_err ( " Weird tag at end of node: %x \n " , tag ) ;
return mem ;
}
* p + = 4 ;
return mem ;
}
2009-11-24 06:07:01 +03:00
2010-11-19 02:55:02 +03:00
/**
* __unflatten_device_tree - create tree of device_nodes from flat blob
*
* unflattens a device - tree , 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 .
* @ blob : The blob to expand
* @ mynodes : The device_node tree created by the call
* @ dt_alloc : An allocator that provides a virtual address to memory
* for the resulting tree
*/
2011-03-18 03:32:35 +03:00
static void __unflatten_device_tree ( struct boot_param_header * blob ,
2010-11-19 02:55:02 +03:00
struct device_node * * mynodes ,
void * ( * dt_alloc ) ( u64 size , u64 align ) )
{
2013-08-29 16:30:35 +04:00
unsigned long size ;
void * start , * mem ;
2010-11-19 02:55:02 +03:00
struct device_node * * allnextp = mynodes ;
pr_debug ( " -> unflatten_device_tree() \n " ) ;
if ( ! blob ) {
pr_debug ( " No device tree pointer \n " ) ;
return ;
}
pr_debug ( " Unflattening device tree: \n " ) ;
pr_debug ( " magic: %08x \n " , be32_to_cpu ( blob - > magic ) ) ;
pr_debug ( " size: %08x \n " , be32_to_cpu ( blob - > totalsize ) ) ;
pr_debug ( " version: %08x \n " , be32_to_cpu ( blob - > version ) ) ;
if ( be32_to_cpu ( blob - > magic ) ! = OF_DT_HEADER ) {
pr_err ( " Invalid device tree blob header \n " ) ;
return ;
}
/* First pass, scan for size */
2013-08-29 16:30:35 +04:00
start = ( ( void * ) blob ) + be32_to_cpu ( blob - > off_dt_struct ) ;
size = ( unsigned long ) unflatten_dt_node ( blob , 0 , & start , NULL , NULL , 0 ) ;
size = ALIGN ( size , 4 ) ;
2010-11-19 02:55:02 +03:00
pr_debug ( " size is %lx, allocating... \n " , size ) ;
/* Allocate memory for the expanded device tree */
2013-08-29 16:30:35 +04:00
mem = dt_alloc ( size + 4 , __alignof__ ( struct device_node ) ) ;
memset ( mem , 0 , size ) ;
2010-11-19 02:55:02 +03:00
2013-08-29 16:30:35 +04:00
* ( __be32 * ) ( mem + size ) = cpu_to_be32 ( 0xdeadbeef ) ;
2013-08-12 15:06:53 +04:00
2013-08-29 16:30:35 +04:00
pr_debug ( " unflattening %p... \n " , mem ) ;
2010-11-19 02:55:02 +03:00
/* Second pass, do actual unflattening */
2013-08-29 16:30:35 +04:00
start = ( ( void * ) blob ) + be32_to_cpu ( blob - > off_dt_struct ) ;
2010-11-19 02:55:02 +03:00
unflatten_dt_node ( blob , mem , & start , NULL , & allnextp , 0 ) ;
2013-08-29 16:30:35 +04:00
if ( be32_to_cpup ( start ) ! = OF_DT_END )
pr_warning ( " Weird tag at end of tree: %08x \n " , be32_to_cpup ( start ) ) ;
if ( be32_to_cpup ( mem + size ) ! = 0xdeadbeef )
2010-11-19 02:55:02 +03:00
pr_warning ( " End of tree marker overwritten: %08x \n " ,
2013-08-29 16:30:35 +04:00
be32_to_cpup ( mem + size ) ) ;
2010-11-19 02:55:02 +03:00
* allnextp = NULL ;
pr_debug ( " <- unflatten_device_tree() \n " ) ;
}
static void * kernel_tree_alloc ( u64 size , u64 align )
{
return kzalloc ( size , GFP_KERNEL ) ;
}
/**
* of_fdt_unflatten_tree - create tree of device_nodes from flat blob
*
* 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 .
*/
void of_fdt_unflatten_tree ( unsigned long * blob ,
struct device_node * * mynodes )
{
struct boot_param_header * device_tree =
( struct boot_param_header * ) blob ;
__unflatten_device_tree ( device_tree , mynodes , & kernel_tree_alloc ) ;
}
EXPORT_SYMBOL_GPL ( of_fdt_unflatten_tree ) ;
2010-11-19 02:55:01 +03:00
/* Everything below here references initial_boot_params directly. */
int __initdata dt_root_addr_cells ;
int __initdata dt_root_size_cells ;
struct boot_param_header * initial_boot_params ;
# ifdef CONFIG_OF_EARLY_FLATTREE
/**
* of_scan_flat_dt - scan flattened tree blob and call callback on each .
* @ it : callback function
* @ data : context data pointer
*
* This function is used to scan the flattened device - tree , it is
* used to extract the memory information at boot before we can
* unflatten the tree
*/
int __init of_scan_flat_dt ( int ( * it ) ( unsigned long node ,
const char * uname , int depth ,
void * data ) ,
void * data )
{
unsigned long p = ( ( unsigned long ) initial_boot_params ) +
be32_to_cpu ( initial_boot_params - > off_dt_struct ) ;
int rc = 0 ;
int depth = - 1 ;
do {
u32 tag = be32_to_cpup ( ( __be32 * ) p ) ;
2012-11-13 00:30:49 +04:00
const char * pathp ;
2010-11-19 02:55:01 +03:00
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 = be32_to_cpup ( ( __be32 * ) p ) ;
p + = 8 ;
if ( be32_to_cpu ( 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 ) {
pr_err ( " Invalid tag %x in flat device tree! \n " , tag ) ;
return - EINVAL ;
}
depth + + ;
pathp = ( char * ) p ;
p = ALIGN ( p + strlen ( pathp ) + 1 , 4 ) ;
2012-12-18 04:01:28 +04:00
if ( * pathp = = ' / ' )
pathp = kbasename ( pathp ) ;
2010-11-19 02:55:01 +03:00
rc = it ( p , pathp , depth , data ) ;
if ( rc ! = 0 )
break ;
} while ( 1 ) ;
return rc ;
}
/**
* of_get_flat_dt_root - find the root node in the flat blob
*/
unsigned long __init of_get_flat_dt_root ( void )
{
unsigned long p = ( ( unsigned long ) initial_boot_params ) +
be32_to_cpu ( initial_boot_params - > off_dt_struct ) ;
while ( be32_to_cpup ( ( __be32 * ) p ) = = OF_DT_NOP )
p + = 4 ;
BUG_ON ( be32_to_cpup ( ( __be32 * ) p ) ! = OF_DT_BEGIN_NODE ) ;
p + = 4 ;
return ALIGN ( p + strlen ( ( char * ) p ) + 1 , 4 ) ;
}
/**
* of_get_flat_dt_prop - Given a node in the flat blob , return the property ptr
*
* This function can be used within scan_flattened_dt callback to get
* access to properties
*/
void * __init of_get_flat_dt_prop ( unsigned long node , const char * name ,
unsigned long * size )
{
return of_fdt_get_property ( initial_boot_params , node , name , size ) ;
}
/**
* of_flat_dt_is_compatible - Return true if given node has compat in compatible list
* @ node : node to test
* @ compat : compatible string to compare with compatible list .
*/
int __init of_flat_dt_is_compatible ( unsigned long node , const char * compat )
{
return of_fdt_is_compatible ( initial_boot_params , node , compat ) ;
}
2010-10-30 19:49:09 +04:00
/**
* of_flat_dt_match - Return true if node matches a list of compatible values
*/
2011-12-21 01:56:45 +04:00
int __init of_flat_dt_match ( unsigned long node , const char * const * compat )
2010-10-30 19:49:09 +04:00
{
return of_fdt_match ( initial_boot_params , node , compat ) ;
}
2013-08-26 16:41:56 +04:00
struct fdt_scan_status {
const char * name ;
int namelen ;
int depth ;
int found ;
int ( * iterator ) ( unsigned long node , const char * uname , int depth , void * data ) ;
void * data ;
} ;
/**
* fdt_scan_node_by_path - iterator for of_scan_flat_dt_by_path function
*/
static int __init fdt_scan_node_by_path ( unsigned long node , const char * uname ,
int depth , void * data )
{
struct fdt_scan_status * st = data ;
/*
* if scan at the requested fdt node has been completed ,
* return - ENXIO to abort further scanning
*/
if ( depth < = st - > depth )
return - ENXIO ;
/* requested fdt node has been found, so call iterator function */
if ( st - > found )
return st - > iterator ( node , uname , depth , st - > data ) ;
/* check if scanning automata is entering next level of fdt nodes */
if ( depth = = st - > depth + 1 & &
strncmp ( st - > name , uname , st - > namelen ) = = 0 & &
uname [ st - > namelen ] = = 0 ) {
st - > depth + = 1 ;
if ( st - > name [ st - > namelen ] = = 0 ) {
st - > found = 1 ;
} else {
const char * next = st - > name + st - > namelen + 1 ;
st - > name = next ;
st - > namelen = strcspn ( next , " / " ) ;
}
return 0 ;
}
/* scan next fdt node */
return 0 ;
}
/**
* of_scan_flat_dt_by_path - scan flattened tree blob and call callback on each
* child of the given path .
* @ path : path to start searching for children
* @ it : callback function
* @ data : context data pointer
*
* This function is used to scan the flattened device - tree starting from the
* node given by path . It is used to extract information ( like reserved
* memory ) , which is required on ealy boot before we can unflatten the tree .
*/
int __init of_scan_flat_dt_by_path ( const char * path ,
int ( * it ) ( unsigned long node , const char * name , int depth , void * data ) ,
void * data )
{
struct fdt_scan_status st = { path , 0 , - 1 , 0 , it , data } ;
int ret = 0 ;
if ( initial_boot_params )
ret = of_scan_flat_dt ( fdt_scan_node_by_path , & st ) ;
if ( ! st . found )
return - ENOENT ;
else if ( ret = = - ENXIO ) /* scan has been completed */
return 0 ;
else
return ret ;
}
2013-08-28 06:41:56 +04:00
const char * __init of_flat_dt_get_machine_name ( void )
{
const char * name ;
unsigned long dt_root = of_get_flat_dt_root ( ) ;
name = of_get_flat_dt_prop ( dt_root , " model " , NULL ) ;
if ( ! name )
name = of_get_flat_dt_prop ( dt_root , " compatible " , NULL ) ;
return name ;
}
/**
* of_flat_dt_match_machine - Iterate match tables to find matching machine .
*
* @ default_match : A machine specific ptr to return in case of no match .
* @ get_next_compat : callback function to return next compatible match table .
*
* Iterate through machine match tables to find the best match for the machine
* compatible string in the FDT .
*/
const void * __init of_flat_dt_match_machine ( const void * default_match ,
const void * ( * get_next_compat ) ( const char * const * * ) )
{
const void * data = NULL ;
const void * best_data = default_match ;
const char * const * compat ;
unsigned long dt_root ;
unsigned int best_score = ~ 1 , score = 0 ;
dt_root = of_get_flat_dt_root ( ) ;
while ( ( data = get_next_compat ( & compat ) ) ) {
score = of_flat_dt_match ( dt_root , compat ) ;
if ( score > 0 & & score < best_score ) {
best_data = data ;
best_score = score ;
}
}
if ( ! best_data ) {
const char * prop ;
long size ;
pr_err ( " \n unrecognized device tree list: \n [ " ) ;
prop = of_get_flat_dt_prop ( dt_root , " compatible " , & size ) ;
if ( prop ) {
while ( size > 0 ) {
printk ( " '%s' " , prop ) ;
size - = strlen ( prop ) + 1 ;
prop + = strlen ( prop ) + 1 ;
}
}
printk ( " ] \n \n " ) ;
return NULL ;
}
pr_info ( " Machine model: %s \n " , of_flat_dt_get_machine_name ( ) ) ;
return best_data ;
}
2009-11-24 13:26:58 +03:00
# ifdef CONFIG_BLK_DEV_INITRD
/**
* early_init_dt_check_for_initrd - Decode initrd location from flat tree
* @ node : reference to node containing initrd location ( ' chosen ' )
*/
2013-08-31 02:06:53 +04:00
static void __init early_init_dt_check_for_initrd ( unsigned long node )
2009-11-24 13:26:58 +03:00
{
2013-07-01 22:20:35 +04:00
u64 start , end ;
unsigned long len ;
2010-01-30 11:45:26 +03:00
__be32 * prop ;
2009-11-24 13:26:58 +03:00
pr_debug ( " Looking for initrd properties... " ) ;
prop = of_get_flat_dt_prop ( node , " linux,initrd-start " , & len ) ;
2010-01-30 11:31:21 +03:00
if ( ! prop )
return ;
2013-07-01 22:20:35 +04:00
start = of_read_number ( prop , len / 4 ) ;
2010-01-30 11:31:21 +03:00
prop = of_get_flat_dt_prop ( node , " linux,initrd-end " , & len ) ;
if ( ! prop )
return ;
2013-07-01 22:20:35 +04:00
end = of_read_number ( prop , len / 4 ) ;
2009-11-24 13:26:58 +03:00
2013-08-31 02:06:53 +04:00
initrd_start = ( unsigned long ) __va ( start ) ;
initrd_end = ( unsigned long ) __va ( end ) ;
initrd_below_start_ok = 1 ;
2013-07-01 22:20:35 +04:00
pr_debug ( " initrd_start=0x%llx initrd_end=0x%llx \n " ,
( unsigned long long ) start , ( unsigned long long ) end ) ;
2009-11-24 13:26:58 +03:00
}
# else
2013-08-31 02:06:53 +04:00
static inline void early_init_dt_check_for_initrd ( unsigned long node )
2009-11-24 13:26:58 +03:00
{
}
# endif /* CONFIG_BLK_DEV_INITRD */
2009-11-24 13:27:10 +03:00
/**
* early_init_dt_scan_root - fetch the top level address and size cells
*/
int __init early_init_dt_scan_root ( unsigned long node , const char * uname ,
int depth , void * data )
{
2010-01-30 11:45:26 +03:00
__be32 * prop ;
2009-11-24 13:27:10 +03:00
if ( depth ! = 0 )
return 0 ;
2010-01-30 11:45:26 +03:00
dt_root_size_cells = OF_ROOT_NODE_SIZE_CELLS_DEFAULT ;
dt_root_addr_cells = OF_ROOT_NODE_ADDR_CELLS_DEFAULT ;
2009-11-24 13:27:10 +03:00
prop = of_get_flat_dt_prop ( node , " #size-cells " , NULL ) ;
2010-01-30 11:45:26 +03:00
if ( prop )
dt_root_size_cells = be32_to_cpup ( prop ) ;
2009-11-24 13:27:10 +03:00
pr_debug ( " dt_root_size_cells = %x \n " , dt_root_size_cells ) ;
prop = of_get_flat_dt_prop ( node , " #address-cells " , NULL ) ;
2010-01-30 11:45:26 +03:00
if ( prop )
dt_root_addr_cells = be32_to_cpup ( prop ) ;
2009-11-24 13:27:10 +03:00
pr_debug ( " dt_root_addr_cells = %x \n " , dt_root_addr_cells ) ;
/* break now */
return 1 ;
}
2010-01-30 11:41:49 +03:00
u64 __init dt_mem_next_cell ( int s , __be32 * * cellp )
2009-11-24 13:37:56 +03:00
{
2010-01-30 11:41:49 +03:00
__be32 * p = * cellp ;
2009-11-24 13:37:56 +03:00
* cellp = p + s ;
return of_read_number ( p , s ) ;
}
2010-02-02 07:34:14 +03:00
/**
* early_init_dt_scan_memory - Look for an parse memory nodes
*/
int __init early_init_dt_scan_memory ( unsigned long node , const char * uname ,
int depth , void * data )
{
char * type = of_get_flat_dt_prop ( node , " device_type " , NULL ) ;
__be32 * reg , * endp ;
unsigned long l ;
/* We are scanning "memory" nodes only */
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 )
return 0 ;
reg = of_get_flat_dt_prop ( node , " linux,usable-memory " , & l ) ;
if ( reg = = NULL )
reg = of_get_flat_dt_prop ( node , " reg " , & l ) ;
if ( reg = = NULL )
return 0 ;
endp = reg + ( l / sizeof ( __be32 ) ) ;
pr_debug ( " memory scan node %s, reg size %ld, data: %x %x %x %x, \n " ,
uname , l , reg [ 0 ] , reg [ 1 ] , reg [ 2 ] , reg [ 3 ] ) ;
while ( ( endp - reg ) > = ( dt_root_addr_cells + dt_root_size_cells ) ) {
u64 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 ;
pr_debug ( " - %llx , %llx \n " , ( unsigned long long ) base ,
( unsigned long long ) size ) ;
early_init_dt_add_memory_arch ( base , size ) ;
}
return 0 ;
}
2009-12-11 09:42:21 +03:00
int __init early_init_dt_scan_chosen ( unsigned long node , const char * uname ,
int depth , void * data )
{
unsigned long l ;
char * p ;
pr_debug ( " search \" chosen \" , depth: %d, uname: %s \n " , depth , uname ) ;
2011-04-29 10:18:16 +04:00
if ( depth ! = 1 | | ! data | |
2009-12-11 09:42:21 +03:00
( strcmp ( uname , " chosen " ) ! = 0 & & strcmp ( uname , " chosen@0 " ) ! = 0 ) )
return 0 ;
early_init_dt_check_for_initrd ( node ) ;
2011-03-31 05:57:33 +04:00
/* Retrieve command line */
2009-12-11 09:42:21 +03:00
p = of_get_flat_dt_prop ( node , " bootargs " , & l ) ;
if ( p ! = NULL & & l > 0 )
2011-04-29 10:18:16 +04:00
strlcpy ( data , p , min ( ( int ) l , COMMAND_LINE_SIZE ) ) ;
2009-12-11 09:42:21 +03:00
2011-09-19 22:50:15 +04:00
/*
* CONFIG_CMDLINE is meant to be a default in case nothing else
* managed to set the command line , unless CONFIG_CMDLINE_FORCE
* is set in which case we override whatever was found earlier .
*/
2009-12-11 09:42:21 +03:00
# ifdef CONFIG_CMDLINE
# ifndef CONFIG_CMDLINE_FORCE
2011-09-19 22:50:15 +04:00
if ( ! ( ( char * ) data ) [ 0 ] )
2009-12-11 09:42:21 +03:00
# endif
2011-04-29 10:18:16 +04:00
strlcpy ( data , CONFIG_CMDLINE , COMMAND_LINE_SIZE ) ;
2009-12-11 09:42:21 +03:00
# endif /* CONFIG_CMDLINE */
2011-04-29 10:18:16 +04:00
pr_debug ( " Command line is: %s \n " , ( char * ) data ) ;
2009-12-11 09:42:21 +03:00
/* break now */
return 1 ;
}
2013-08-29 00:18:32 +04:00
# ifdef CONFIG_HAVE_MEMBLOCK
2013-09-25 07:20:01 +04:00
void __init __weak early_init_dt_add_memory_arch ( u64 base , u64 size )
{
const u64 phys_offset = __pa ( PAGE_OFFSET ) ;
base & = PAGE_MASK ;
size & = PAGE_MASK ;
if ( base + size < phys_offset ) {
pr_warning ( " Ignoring memory block 0x%llx - 0x%llx \n " ,
base , base + size ) ;
return ;
}
if ( base < phys_offset ) {
pr_warning ( " Ignoring memory range 0x%llx - 0x%llx \n " ,
base , phys_offset ) ;
size - = phys_offset - base ;
base = phys_offset ;
}
memblock_add ( base , size ) ;
}
2013-08-29 00:18:32 +04:00
/*
* called from unflatten_device_tree ( ) to bootstrap devicetree itself
* Architectures can override this definition if memblock isn ' t used
*/
void * __init __weak early_init_dt_alloc_memory_arch ( u64 size , u64 align )
{
return __va ( memblock_alloc ( size , align ) ) ;
}
# endif
2013-08-26 18:47:40 +04:00
bool __init early_init_dt_scan ( void * params )
{
if ( ! params )
return false ;
/* Setup flat device-tree pointer */
initial_boot_params = params ;
/* check device tree validity */
if ( be32_to_cpu ( initial_boot_params - > magic ) ! = OF_DT_HEADER ) {
initial_boot_params = NULL ;
return false ;
}
/* Retrieve various information from the /chosen node */
of_scan_flat_dt ( early_init_dt_scan_chosen , boot_command_line ) ;
/* Initialize {size,address}-cells info */
of_scan_flat_dt ( early_init_dt_scan_root , NULL ) ;
/* Setup memory, calling early_init_dt_add_memory_arch */
of_scan_flat_dt ( early_init_dt_scan_memory , NULL ) ;
return true ;
}
2009-11-24 06:07:01 +03:00
/**
* unflatten_device_tree - create tree of device_nodes from flat blob
*
* 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 .
*/
void __init unflatten_device_tree ( void )
{
2012-11-30 14:01:51 +04:00
__unflatten_device_tree ( initial_boot_params , & of_allnodes ,
2011-01-14 01:36:09 +03:00
early_init_dt_alloc_memory_arch ) ;
2009-11-24 06:07:01 +03:00
2013-05-30 13:38:08 +04:00
/* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */
2011-08-15 11:28:14 +04:00
of_alias_scan ( early_init_dt_alloc_memory_arch ) ;
2009-11-24 06:07:01 +03:00
}
2010-11-19 02:54:56 +03:00
2013-08-26 20:22:45 +04:00
/**
* unflatten_and_copy_device_tree - copy and create tree of device_nodes from flat blob
*
* Copies and 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 should only be used when the FDT memory has not been
* reserved such is the case when the FDT is built - in to the kernel init
* section . If the FDT memory is reserved already then unflatten_device_tree
* should be used instead .
*/
void __init unflatten_and_copy_device_tree ( void )
{
int size = __be32_to_cpu ( initial_boot_params - > totalsize ) ;
void * dt = early_init_dt_alloc_memory_arch ( size ,
__alignof__ ( struct boot_param_header ) ) ;
if ( dt ) {
memcpy ( dt , initial_boot_params , size ) ;
initial_boot_params = dt ;
}
unflatten_device_tree ( ) ;
}
2010-11-19 02:54:56 +03:00
# endif /* CONFIG_OF_EARLY_FLATTREE */
2013-07-29 07:11:50 +04:00
/* Feed entire flattened device tree into the random pool */
static int __init add_fdt_randomness ( void )
{
if ( initial_boot_params )
add_device_randomness ( initial_boot_params ,
be32_to_cpu ( initial_boot_params - > totalsize ) ) ;
return 0 ;
}
core_initcall ( add_fdt_randomness ) ;