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 .
*/
2016-10-17 22:21:23 +03:00
# define pr_fmt(fmt) "OF: fdt: " fmt
2016-06-15 16:32:18 +03:00
2014-11-14 20:05:35 +03:00
# include <linux/crc32.h>
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>
2015-12-06 03:13:53 +03:00
# include <linux/mutex.h>
2009-11-24 00:53:09 +03:00
# include <linux/of.h>
# include <linux/of_fdt.h>
2014-02-28 17:42:48 +04:00
# include <linux/of_reserved_mem.h>
2014-02-28 17:42:47 +04:00
# include <linux/sizes.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>
2014-04-03 00:10:14 +04:00
# include <linux/libfdt.h>
2014-04-03 01:56:48 +04:00
# include <linux/debugfs.h>
2014-03-27 17:07:01 +04:00
# include <linux/serial_core.h>
2014-11-14 20:05:35 +03:00
# include <linux/sysfs.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 */
2010-02-14 17:13:47 +03:00
# include <asm/page.h>
2017-04-26 03:09:54 +03:00
# include "of_private.h"
2014-07-15 21:03:35 +04:00
/*
* of_fdt_limit_memory - limit the number of regions in the / memory node
* @ limit : maximum entries
*
* Adjust the flattened device tree to have at most ' limit ' number of
* memory entries in the / memory node . This function may be called
* any time after initial_boot_param is set .
*/
void of_fdt_limit_memory ( int limit )
{
int memory ;
int len ;
const void * val ;
int nr_address_cells = OF_ROOT_NODE_ADDR_CELLS_DEFAULT ;
int nr_size_cells = OF_ROOT_NODE_SIZE_CELLS_DEFAULT ;
2017-05-04 20:56:12 +03:00
const __be32 * addr_prop ;
const __be32 * size_prop ;
2014-07-15 21:03:35 +04:00
int root_offset ;
int cell_size ;
root_offset = fdt_path_offset ( initial_boot_params , " / " ) ;
if ( root_offset < 0 )
return ;
addr_prop = fdt_getprop ( initial_boot_params , root_offset ,
" #address-cells " , NULL ) ;
if ( addr_prop )
nr_address_cells = fdt32_to_cpu ( * addr_prop ) ;
size_prop = fdt_getprop ( initial_boot_params , root_offset ,
" #size-cells " , NULL ) ;
if ( size_prop )
nr_size_cells = fdt32_to_cpu ( * size_prop ) ;
cell_size = sizeof ( uint32_t ) * ( nr_address_cells + nr_size_cells ) ;
memory = fdt_path_offset ( initial_boot_params , " /memory " ) ;
if ( memory > 0 ) {
val = fdt_getprop ( initial_boot_params , memory , " reg " , & len ) ;
if ( len > limit * cell_size ) {
len = limit * cell_size ;
pr_debug ( " Limiting number of entries to %d \n " , limit ) ;
fdt_setprop ( initial_boot_params , memory , " reg " , val ,
len ) ;
}
}
}
2010-11-19 02:54:59 +03:00
/**
* 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
*/
2014-04-02 07:48:01 +04:00
int of_fdt_is_compatible ( const void * blob ,
2010-11-19 02:54:59 +03:00
unsigned long node , const char * compat )
{
const char * cp ;
2014-04-02 08:49:03 +04:00
int cplen ;
unsigned long l , score = 0 ;
2010-11-19 02:54:59 +03:00
2014-04-03 00:10:14 +04:00
cp = fdt_getprop ( blob , node , " compatible " , & cplen ) ;
2010-11-19 02:54:59 +03:00
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 ;
}
2015-04-09 23:05:15 +03:00
/**
* of_fdt_is_big_endian - Return true if given node needs BE MMIO accesses
* @ blob : A device tree blob
* @ node : node to test
*
* Returns true if the node has a " big-endian " property , or if the kernel
* was compiled for BE * and * the node has a " native-endian " property .
* Returns false otherwise .
*/
bool of_fdt_is_big_endian ( const void * blob , unsigned long node )
{
if ( fdt_getprop ( blob , node , " big-endian " , NULL ) )
return true ;
if ( IS_ENABLED ( CONFIG_CPU_BIG_ENDIAN ) & &
fdt_getprop ( blob , node , " native-endian " , NULL ) )
return true ;
return false ;
}
2010-10-30 19:49:09 +04:00
/**
* of_fdt_match - Return true if node matches a list of compatible values
*/
2014-04-02 07:48:01 +04:00
int of_fdt_match ( const void * 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 ;
}
2016-05-03 16:22:47 +03:00
static void populate_properties ( const void * blob ,
int offset ,
void * * mem ,
struct device_node * np ,
const char * nodename ,
2014-10-03 19:28:27 +04:00
bool dryrun )
2009-11-24 06:07:00 +03:00
{
2016-05-03 16:22:47 +03:00
struct property * pp , * * pprev = NULL ;
int cur ;
bool has_name = false ;
pprev = & np - > properties ;
for ( cur = fdt_first_property_offset ( blob , offset ) ;
cur > = 0 ;
cur = fdt_next_property_offset ( blob , cur ) ) {
const __be32 * val ;
const char * pname ;
u32 sz ;
val = fdt_getprop_by_offset ( blob , cur , & pname , & sz ) ;
if ( ! val ) {
2016-06-15 16:32:18 +03:00
pr_warn ( " Cannot locate property at 0x%x \n " , cur ) ;
2016-05-03 16:22:47 +03:00
continue ;
}
if ( ! pname ) {
2016-06-15 16:32:18 +03:00
pr_warn ( " Cannot find property name at 0x%x \n " , cur ) ;
2016-05-03 16:22:47 +03:00
continue ;
}
if ( ! strcmp ( pname , " name " ) )
has_name = true ;
pp = unflatten_dt_alloc ( mem , sizeof ( struct property ) ,
__alignof__ ( struct property ) ) ;
if ( dryrun )
continue ;
/* 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 " ) | |
! strcmp ( pname , " linux,phandle " ) ) {
if ( ! np - > phandle )
np - > phandle = be32_to_cpup ( val ) ;
}
/* And we process the "ibm,phandle" property
* used in pSeries dynamic device tree
* stuff
*/
if ( ! strcmp ( pname , " ibm,phandle " ) )
np - > phandle = be32_to_cpup ( val ) ;
pp - > name = ( char * ) pname ;
pp - > length = sz ;
pp - > value = ( __be32 * ) val ;
* pprev = pp ;
pprev = & pp - > next ;
}
/* With version 0x10 we may not have the name property,
* recreate it here from the unit name if absent
*/
if ( ! has_name ) {
const char * p = nodename , * ps = p , * pa = NULL ;
int len ;
while ( * p ) {
if ( ( * p ) = = ' @ ' )
pa = p ;
else if ( ( * p ) = = ' / ' )
ps = p + 1 ;
p + + ;
}
if ( pa < ps )
pa = p ;
len = ( pa - ps ) + 1 ;
pp = unflatten_dt_alloc ( mem , sizeof ( struct property ) + len ,
__alignof__ ( struct property ) ) ;
if ( ! dryrun ) {
pp - > name = " name " ;
pp - > length = len ;
pp - > value = pp + 1 ;
* pprev = pp ;
pprev = & pp - > next ;
memcpy ( pp - > value , ps , len - 1 ) ;
( ( char * ) pp - > value ) [ len - 1 ] = 0 ;
pr_debug ( " fixed up name for %s -> %s \n " ,
nodename , ( char * ) pp - > value ) ;
}
}
if ( ! dryrun )
* pprev = NULL ;
}
2016-05-13 14:31:39 +03:00
static unsigned int populate_node ( const void * blob ,
int offset ,
void * * mem ,
struct device_node * dad ,
unsigned int fpsize ,
struct device_node * * pnp ,
bool dryrun )
2016-05-03 16:22:47 +03:00
{
2009-11-24 06:07:00 +03:00
struct device_node * np ;
2014-04-03 00:10:14 +04:00
const char * pathp ;
2009-11-24 06:07:00 +03:00
unsigned int l , allocl ;
int new_format = 0 ;
2016-05-03 16:22:47 +03:00
pathp = fdt_get_name ( blob , offset , & l ) ;
if ( ! pathp ) {
* pnp = NULL ;
return 0 ;
}
2014-04-03 00:10:14 +04:00
2015-04-14 07:36:05 +03:00
allocl = + + l ;
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 ;
2014-04-03 00:10:14 +04:00
pathp = " " ;
2009-11-24 06:07:00 +03:00
} else {
/* account for '/' and path size minus terminal 0
* already in ' l '
*/
fpsize + = l ;
allocl = fpsize ;
}
}
2016-05-03 16:22:47 +03:00
np = unflatten_dt_alloc ( mem , sizeof ( struct device_node ) + allocl ,
2009-11-24 06:07:00 +03:00
__alignof__ ( struct device_node ) ) ;
2014-10-03 19:28:27 +04:00
if ( ! dryrun ) {
2012-11-15 02:37:12 +04:00
char * fn ;
2013-12-13 22:08:59 +04:00
of_node_init ( np ) ;
2012-11-15 02:37:12 +04:00
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
if ( dad ! = NULL ) {
np - > parent = dad ;
2014-11-28 19:03:33 +03:00
np - > sibling = dad - > child ;
dad - > child = np ;
2009-11-24 06:07:00 +03:00
}
}
2014-04-03 00:10:14 +04:00
2016-05-03 16:22:47 +03:00
populate_properties ( blob , offset , mem , np , pathp , dryrun ) ;
2014-10-03 19:28:27 +04:00
if ( ! dryrun ) {
2009-11-24 06:07:00 +03:00
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> " ;
}
2014-04-03 00:10:14 +04:00
2016-05-03 16:22:47 +03:00
* pnp = np ;
return fpsize ;
}
2016-05-03 16:22:48 +03:00
static void reverse_nodes ( struct device_node * parent )
{
struct device_node * child , * next ;
/* In-depth first */
child = parent - > child ;
while ( child ) {
reverse_nodes ( child ) ;
child = child - > sibling ;
}
/* Reverse the nodes in the child list */
child = parent - > child ;
parent - > child = NULL ;
while ( child ) {
next = child - > sibling ;
child - > sibling = parent - > child ;
parent - > child = child ;
child = next ;
}
}
2016-05-03 16:22:47 +03:00
/**
2016-05-03 16:22:49 +03:00
* unflatten_dt_nodes - Alloc and populate a device_node from the flat tree
2016-05-03 16:22:47 +03:00
* @ blob : The parent device tree blob
* @ mem : Memory chunk to use for allocating device nodes and properties
* @ dad : Parent struct device_node
* @ nodepp : The device_node tree created by the call
2016-05-03 16:22:48 +03:00
*
* It returns the size of unflattened device tree or error code
2016-05-03 16:22:47 +03:00
*/
2016-05-03 16:22:49 +03:00
static int unflatten_dt_nodes ( const void * blob ,
void * mem ,
struct device_node * dad ,
struct device_node * * nodepp )
2016-05-03 16:22:47 +03:00
{
2016-05-03 16:22:48 +03:00
struct device_node * root ;
2016-06-09 08:50:49 +03:00
int offset = 0 , depth = 0 , initial_depth = 0 ;
2016-05-03 16:22:48 +03:00
# define FDT_MAX_DEPTH 64
2016-05-13 14:31:39 +03:00
unsigned int fpsizes [ FDT_MAX_DEPTH ] ;
2016-05-03 16:22:48 +03:00
struct device_node * nps [ FDT_MAX_DEPTH ] ;
void * base = mem ;
bool dryrun = ! base ;
2016-05-03 16:22:47 +03:00
2016-05-03 16:22:48 +03:00
if ( nodepp )
* nodepp = NULL ;
2016-06-09 08:50:49 +03:00
/*
* We ' re unflattening device sub - tree if @ dad is valid . There are
* possibly multiple nodes in the first level of depth . We need
* set @ depth to 1 to make fdt_next_node ( ) happy as it bails
* immediately when negative @ depth is found . Otherwise , the device
* nodes except the first one won ' t be unflattened successfully .
*/
if ( dad )
depth = initial_depth = 1 ;
2016-05-03 16:22:48 +03:00
root = dad ;
fpsizes [ depth ] = dad ? strlen ( of_node_full_name ( dad ) ) : 0 ;
2016-05-11 20:36:57 +03:00
nps [ depth ] = dad ;
2016-06-09 08:50:49 +03:00
2016-05-03 16:22:48 +03:00
for ( offset = 0 ;
2016-06-09 08:50:49 +03:00
offset > = 0 & & depth > = initial_depth ;
2016-05-03 16:22:48 +03:00
offset = fdt_next_node ( blob , offset , & depth ) ) {
if ( WARN_ON_ONCE ( depth > = FDT_MAX_DEPTH ) )
continue ;
2016-05-03 16:22:47 +03:00
2016-05-11 20:36:57 +03:00
fpsizes [ depth + 1 ] = populate_node ( blob , offset , & mem ,
nps [ depth ] ,
fpsizes [ depth ] ,
& nps [ depth + 1 ] , dryrun ) ;
if ( ! fpsizes [ depth + 1 ] )
2016-05-03 16:22:48 +03:00
return mem - base ;
if ( ! dryrun & & nodepp & & ! * nodepp )
2016-05-11 20:36:57 +03:00
* nodepp = nps [ depth + 1 ] ;
2016-05-03 16:22:48 +03:00
if ( ! dryrun & & ! root )
2016-05-11 20:36:57 +03:00
root = nps [ depth + 1 ] ;
2016-05-03 16:22:48 +03:00
}
2014-04-03 00:10:14 +04:00
2016-05-03 16:22:48 +03:00
if ( offset < 0 & & offset ! = - FDT_ERR_NOTFOUND ) {
2016-06-15 16:32:18 +03:00
pr_err ( " Error %d processing FDT \n " , offset ) ;
2016-05-03 16:22:48 +03:00
return - EINVAL ;
}
2014-04-03 00:10:14 +04:00
2014-11-28 19:03:33 +03:00
/*
* Reverse the child list . Some drivers assumes node order matches . dts
* node order
*/
2016-05-03 16:22:48 +03:00
if ( ! dryrun )
reverse_nodes ( root ) ;
2014-04-03 00:10:14 +04:00
2016-05-03 16:22:48 +03:00
return mem - base ;
2009-11-24 06:07:00 +03:00
}
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
2016-05-03 16:22:50 +03:00
* @ dad : Parent device node
2010-11-19 02:55:02 +03:00
* @ mynodes : The device_node tree created by the call
* @ dt_alloc : An allocator that provides a virtual address to memory
* for the resulting tree
2016-05-03 16:22:51 +03:00
*
* Returns NULL on failure or the memory chunk containing the unflattened
* device tree on success .
2010-11-19 02:55:02 +03:00
*/
2017-04-26 03:09:54 +03:00
void * __unflatten_device_tree ( const void * blob ,
struct device_node * dad ,
struct device_node * * mynodes ,
void * ( * dt_alloc ) ( u64 size , u64 align ) ,
bool detached )
2010-11-19 02:55:02 +03:00
{
2016-05-03 16:22:48 +03:00
int size ;
2014-04-03 00:10:14 +04:00
void * mem ;
2010-11-19 02:55:02 +03:00
pr_debug ( " -> unflatten_device_tree() \n " ) ;
if ( ! blob ) {
pr_debug ( " No device tree pointer \n " ) ;
2016-05-03 16:22:51 +03:00
return NULL ;
2010-11-19 02:55:02 +03:00
}
pr_debug ( " Unflattening device tree: \n " ) ;
2014-04-02 07:48:01 +04:00
pr_debug ( " magic: %08x \n " , fdt_magic ( blob ) ) ;
pr_debug ( " size: %08x \n " , fdt_totalsize ( blob ) ) ;
pr_debug ( " version: %08x \n " , fdt_version ( blob ) ) ;
2010-11-19 02:55:02 +03:00
2014-04-02 07:48:01 +04:00
if ( fdt_check_header ( blob ) ) {
2010-11-19 02:55:02 +03:00
pr_err ( " Invalid device tree blob header \n " ) ;
2016-05-03 16:22:51 +03:00
return NULL ;
2010-11-19 02:55:02 +03:00
}
/* First pass, scan for size */
2016-05-03 16:22:50 +03:00
size = unflatten_dt_nodes ( blob , NULL , dad , NULL ) ;
2016-05-03 16:22:48 +03:00
if ( size < 0 )
2016-05-03 16:22:51 +03:00
return NULL ;
2010-11-19 02:55:02 +03:00
2016-05-03 16:22:48 +03:00
size = ALIGN ( size , 4 ) ;
pr_debug ( " size is %d, allocating... \n " , size ) ;
2010-11-19 02:55:02 +03:00
/* 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 */
2016-05-03 16:22:50 +03:00
unflatten_dt_nodes ( blob , mem , dad , mynodes ) ;
2013-08-29 16:30:35 +04:00
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
2016-08-01 10:17:53 +03:00
if ( detached & & mynodes ) {
2016-07-19 01:01:12 +03:00
of_node_set_flag ( * mynodes , OF_DETACHED ) ;
pr_debug ( " unflattened tree is detached \n " ) ;
}
2010-11-19 02:55:02 +03:00
pr_debug ( " <- unflatten_device_tree() \n " ) ;
2016-05-03 16:22:51 +03:00
return mem ;
2010-11-19 02:55:02 +03:00
}
static void * kernel_tree_alloc ( u64 size , u64 align )
{
return kzalloc ( size , GFP_KERNEL ) ;
}
2015-12-06 03:13:53 +03:00
static DEFINE_MUTEX ( of_fdt_unflatten_mutex ) ;
2010-11-19 02:55:02 +03:00
/**
* of_fdt_unflatten_tree - create tree of device_nodes from flat blob
2016-05-03 16:22:50 +03:00
* @ blob : Flat device tree blob
* @ dad : Parent device node
* @ mynodes : The device tree created by the call
2010-11-19 02:55:02 +03: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 .
2016-05-03 16:22:51 +03:00
*
* Returns NULL on failure or the memory chunk containing the unflattened
* device tree on success .
2010-11-19 02:55:02 +03:00
*/
2016-05-03 16:22:51 +03:00
void * of_fdt_unflatten_tree ( const unsigned long * blob ,
struct device_node * dad ,
struct device_node * * mynodes )
2010-11-19 02:55:02 +03:00
{
2016-05-03 16:22:51 +03:00
void * mem ;
2015-12-06 03:13:53 +03:00
mutex_lock ( & of_fdt_unflatten_mutex ) ;
2016-07-19 01:01:12 +03:00
mem = __unflatten_device_tree ( blob , dad , mynodes , & kernel_tree_alloc ,
true ) ;
2015-12-06 03:13:53 +03:00
mutex_unlock ( & of_fdt_unflatten_mutex ) ;
2016-05-03 16:22:51 +03:00
return mem ;
2010-11-19 02:55:02 +03:00
}
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 ;
2014-04-01 00:25:04 +04:00
void * initial_boot_params ;
2010-11-19 02:55:01 +03:00
# ifdef CONFIG_OF_EARLY_FLATTREE
2014-11-14 20:05:35 +03:00
static u32 of_fdt_crc32 ;
2014-02-28 17:42:47 +04:00
/**
* res_mem_reserve_reg ( ) - reserve all memory described in ' reg ' property
*/
static int __init __reserved_mem_reserve_reg ( unsigned long node ,
const char * uname )
{
int t_len = ( dt_root_addr_cells + dt_root_size_cells ) * sizeof ( __be32 ) ;
phys_addr_t base , size ;
2014-04-02 08:49:03 +04:00
int len ;
const __be32 * prop ;
2014-02-28 17:42:48 +04:00
int nomap , first = 1 ;
2014-02-28 17:42:47 +04:00
prop = of_get_flat_dt_prop ( node , " reg " , & len ) ;
if ( ! prop )
return - ENOENT ;
if ( len & & len % t_len ! = 0 ) {
pr_err ( " Reserved memory: invalid reg property in '%s', skipping node. \n " ,
uname ) ;
return - EINVAL ;
}
nomap = of_get_flat_dt_prop ( node , " no-map " , NULL ) ! = NULL ;
while ( len > = t_len ) {
base = dt_mem_next_cell ( dt_root_addr_cells , & prop ) ;
size = dt_mem_next_cell ( dt_root_size_cells , & prop ) ;
2014-08-07 00:30:04 +04:00
if ( size & &
2014-02-28 17:42:47 +04:00
early_init_dt_reserve_memory_arch ( base , size , nomap ) = = 0 )
pr_debug ( " Reserved memory: reserved region for node '%s': base %pa, size %ld MiB \n " ,
uname , & base , ( unsigned long ) size / SZ_1M ) ;
else
pr_info ( " Reserved memory: failed to reserve memory for node '%s': base %pa, size %ld MiB \n " ,
uname , & base , ( unsigned long ) size / SZ_1M ) ;
len - = t_len ;
2014-02-28 17:42:48 +04:00
if ( first ) {
fdt_reserved_mem_save_node ( node , uname , base , size ) ;
first = 0 ;
}
2014-02-28 17:42:47 +04:00
}
return 0 ;
}
/**
* __reserved_mem_check_root ( ) - check if # size - cells , # address - cells provided
* in / reserved - memory matches the values supported by the current implementation ,
* also check if ranges property has been provided
*/
2014-04-08 09:48:07 +04:00
static int __init __reserved_mem_check_root ( unsigned long node )
2014-02-28 17:42:47 +04:00
{
2014-04-02 08:49:03 +04:00
const __be32 * prop ;
2014-02-28 17:42:47 +04:00
prop = of_get_flat_dt_prop ( node , " #size-cells " , NULL ) ;
if ( ! prop | | be32_to_cpup ( prop ) ! = dt_root_size_cells )
return - EINVAL ;
prop = of_get_flat_dt_prop ( node , " #address-cells " , NULL ) ;
if ( ! prop | | be32_to_cpup ( prop ) ! = dt_root_addr_cells )
return - EINVAL ;
prop = of_get_flat_dt_prop ( node , " ranges " , NULL ) ;
if ( ! prop )
return - EINVAL ;
return 0 ;
}
/**
* fdt_scan_reserved_mem ( ) - scan a single FDT node for reserved memory
*/
static int __init __fdt_scan_reserved_mem ( unsigned long node , const char * uname ,
int depth , void * data )
{
static int found ;
const char * status ;
2014-02-28 17:42:48 +04:00
int err ;
2014-02-28 17:42:47 +04:00
if ( ! found & & depth = = 1 & & strcmp ( uname , " reserved-memory " ) = = 0 ) {
if ( __reserved_mem_check_root ( node ) ! = 0 ) {
pr_err ( " Reserved memory: unsupported node format, ignoring \n " ) ;
/* break scan */
return 1 ;
}
found = 1 ;
/* scan next node */
return 0 ;
} else if ( ! found ) {
/* scan next node */
return 0 ;
} else if ( found & & depth < 2 ) {
/* scanning of /reserved-memory has been finished */
return 1 ;
}
status = of_get_flat_dt_prop ( node , " status " , NULL ) ;
if ( status & & strcmp ( status , " okay " ) ! = 0 & & strcmp ( status , " ok " ) ! = 0 )
return 0 ;
2014-02-28 17:42:48 +04:00
err = __reserved_mem_reserve_reg ( node , uname ) ;
if ( err = = - ENOENT & & of_get_flat_dt_prop ( node , " size " , NULL ) )
fdt_reserved_mem_save_node ( node , uname , 0 , 0 ) ;
2014-02-28 17:42:47 +04:00
/* scan next node */
return 0 ;
}
/**
* early_init_fdt_scan_reserved_mem ( ) - create reserved memory regions
*
* This function grabs memory from early allocator for device exclusive use
* defined in device tree structures . It should be called by arch specific code
* once the early allocator ( i . e . memblock ) has been fully activated .
*/
void __init early_init_fdt_scan_reserved_mem ( void )
{
2014-04-02 07:46:48 +04:00
int n ;
u64 base , size ;
2014-03-14 01:36:36 +04:00
if ( ! initial_boot_params )
return ;
2014-04-02 07:46:48 +04:00
/* Process header /memreserve/ fields */
for ( n = 0 ; ; n + + ) {
fdt_get_mem_rsv ( initial_boot_params , n , & base , & size ) ;
if ( ! size )
break ;
early_init_dt_reserve_memory_arch ( base , size , 0 ) ;
}
2014-02-28 17:42:47 +04:00
of_scan_flat_dt ( __fdt_scan_reserved_mem , NULL ) ;
2014-02-28 17:42:48 +04:00
fdt_init_reserved_mem ( ) ;
2014-02-28 17:42:47 +04:00
}
2015-06-01 14:40:31 +03:00
/**
* early_init_fdt_reserve_self ( ) - reserve the memory used by the FDT blob
*/
void __init early_init_fdt_reserve_self ( void )
{
if ( ! initial_boot_params )
return ;
/* Reserve the dtb region */
early_init_dt_reserve_memory_arch ( __pa ( initial_boot_params ) ,
fdt_totalsize ( initial_boot_params ) ,
0 ) ;
}
2010-11-19 02:55:01 +03:00
/**
* 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 )
{
2014-04-03 00:10:14 +04:00
const void * blob = initial_boot_params ;
const char * pathp ;
int offset , rc = 0 , depth = - 1 ;
2016-11-23 12:40:07 +03:00
if ( ! blob )
return 0 ;
for ( offset = fdt_next_node ( blob , - 1 , & depth ) ;
offset > = 0 & & depth > = 0 & & ! rc ;
offset = fdt_next_node ( blob , offset , & depth ) ) {
2014-04-03 00:10:14 +04:00
pathp = fdt_get_name ( blob , offset , NULL ) ;
2012-12-18 04:01:28 +04:00
if ( * pathp = = ' / ' )
pathp = kbasename ( pathp ) ;
2014-04-03 00:10:14 +04:00
rc = it ( offset , pathp , depth , data ) ;
}
2010-11-19 02:55:01 +03:00
return rc ;
}
2016-04-07 15:03:33 +03:00
/**
* of_get_flat_dt_subnode_by_name - get the subnode by given name
*
* @ node : the parent node
* @ uname : the name of subnode
* @ return offset of the subnode , or - FDT_ERR_NOTFOUND if there is none
*/
int of_get_flat_dt_subnode_by_name ( unsigned long node , const char * uname )
{
return fdt_subnode_offset ( initial_boot_params , node , uname ) ;
}
2010-11-19 02:55:01 +03:00
/**
* of_get_flat_dt_root - find the root node in the flat blob
*/
unsigned long __init of_get_flat_dt_root ( void )
{
2014-04-03 00:10:14 +04:00
return 0 ;
2010-11-19 02:55:01 +03:00
}
2014-04-22 21:55:10 +04:00
/**
* of_get_flat_dt_size - Return the total size of the FDT
*/
int __init of_get_flat_dt_size ( void )
{
return fdt_totalsize ( initial_boot_params ) ;
}
2010-11-19 02:55:01 +03:00
/**
* 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
*/
2014-04-02 08:49:03 +04:00
const void * __init of_get_flat_dt_prop ( unsigned long node , const char * name ,
int * size )
2010-11-19 02:55:01 +03:00
{
2014-04-03 00:10:14 +04:00
return fdt_getprop ( initial_boot_params , node , name , size ) ;
2010-11-19 02:55:01 +03:00
}
/**
* 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 ;
} ;
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 ;
2014-04-02 08:49:03 +04:00
int size ;
2013-08-28 06:41:56 +04:00
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
2016-02-16 15:52:33 +03:00
# ifndef __early_init_dt_declare_initrd
static void __early_init_dt_declare_initrd ( unsigned long start ,
unsigned long end )
{
initrd_start = ( unsigned long ) __va ( start ) ;
initrd_end = ( unsigned long ) __va ( end ) ;
initrd_below_start_ok = 1 ;
}
# endif
2009-11-24 13:26:58 +03:00
/**
* 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 ;
2014-04-02 08:49:03 +04:00
int len ;
const __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
2016-02-16 15:52:33 +03:00
__early_init_dt_declare_initrd ( start , end ) ;
2013-08-31 02:06:53 +04:00
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 */
2014-03-27 17:07:01 +04:00
# ifdef CONFIG_SERIAL_EARLYCON
2016-09-27 23:54:12 +03:00
int __init early_init_dt_scan_chosen_stdout ( void )
2014-03-27 17:07:01 +04:00
{
int offset ;
2016-01-17 02:23:42 +03:00
const char * p , * q , * options = NULL ;
2014-03-27 17:07:01 +04:00
int l ;
2016-01-17 02:23:39 +03:00
const struct earlycon_id * match ;
2014-03-27 17:07:01 +04:00
const void * fdt = initial_boot_params ;
offset = fdt_path_offset ( fdt , " /chosen " ) ;
if ( offset < 0 )
offset = fdt_path_offset ( fdt , " /chosen@0 " ) ;
if ( offset < 0 )
return - ENOENT ;
p = fdt_getprop ( fdt , offset , " stdout-path " , & l ) ;
if ( ! p )
p = fdt_getprop ( fdt , offset , " linux,stdout-path " , & l ) ;
if ( ! p | | ! l )
return - ENOENT ;
2016-01-17 02:23:42 +03:00
q = strchrnul ( p , ' : ' ) ;
if ( * q ! = ' \0 ' )
options = q + 1 ;
2016-01-17 02:23:48 +03:00
l = q - p ;
2015-10-10 11:29:30 +03:00
2014-03-27 17:07:01 +04:00
/* Get the node specified by stdout-path */
2016-01-17 02:23:48 +03:00
offset = fdt_path_offset_namelen ( fdt , p , l ) ;
if ( offset < 0 ) {
pr_warn ( " earlycon: stdout-path %.*s not found \n " , l , p ) ;
return 0 ;
}
2014-03-27 17:07:01 +04:00
2016-01-17 02:23:39 +03:00
for ( match = __earlycon_table ; match < __earlycon_table_end ; match + + ) {
if ( ! match - > compatible [ 0 ] )
continue ;
if ( fdt_node_check_compatible ( fdt , offset , match - > compatible ) )
2014-03-27 17:07:01 +04:00
continue ;
2016-01-17 02:23:44 +03:00
of_setup_earlycon ( match , offset , options ) ;
2014-03-27 17:07:01 +04:00
return 0 ;
}
return - ENODEV ;
}
# endif
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 )
{
2014-04-02 08:49:03 +04:00
const __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 ;
}
2014-04-02 08:49:03 +04:00
u64 __init dt_mem_next_cell ( int s , const __be32 * * cellp )
2009-11-24 13:37:56 +03:00
{
2014-04-02 08:49:03 +04:00
const __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 )
{
2014-04-02 08:49:03 +04:00
const char * type = of_get_flat_dt_prop ( node , " device_type " , NULL ) ;
const __be32 * reg , * endp ;
int l ;
2016-12-13 03:43:02 +03:00
bool hotpluggable ;
2010-02-02 07:34:14 +03:00
/* 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.
*/
2014-04-17 21:42:01 +04:00
if ( ! IS_ENABLED ( CONFIG_PPC32 ) | | depth ! = 1 | | strcmp ( uname , " memory@0 " ) ! = 0 )
2010-02-02 07:34:14 +03:00
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 ) ) ;
2016-12-13 03:43:02 +03:00
hotpluggable = of_get_flat_dt_prop ( node , " hotpluggable " , NULL ) ;
2010-02-02 07:34:14 +03:00
2015-04-12 23:16:26 +03:00
pr_debug ( " memory scan node %s, reg size %d, \n " , uname , l ) ;
2010-02-02 07:34:14 +03:00
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 ) ;
2016-12-13 03:43:02 +03:00
if ( ! hotpluggable )
continue ;
if ( early_init_dt_mark_hotplug_memory_arch ( base , size ) )
pr_warn ( " failed to mark hotplug range 0x%llx - 0x%llx \n " ,
base , base + size ) ;
2010-02-02 07:34:14 +03:00
}
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 )
{
2014-04-02 08:49:03 +04:00
int l ;
const char * p ;
2009-12-11 09:42:21 +03:00
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
2016-04-13 12:52:16 +03:00
# if defined(CONFIG_CMDLINE_EXTEND)
strlcat ( data , " " , COMMAND_LINE_SIZE ) ;
strlcat ( data , CONFIG_CMDLINE , COMMAND_LINE_SIZE ) ;
# elif defined(CONFIG_CMDLINE_FORCE)
strlcpy ( data , CONFIG_CMDLINE , COMMAND_LINE_SIZE ) ;
# else
/* No arguments from boot loader, use kernel's cmdl*/
2011-09-19 22:50:15 +04:00
if ( ! ( ( char * ) data ) [ 0 ] )
2011-04-29 10:18:16 +04:00
strlcpy ( data , CONFIG_CMDLINE , COMMAND_LINE_SIZE ) ;
2016-04-13 12:52:16 +03:00
# endif
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
2016-02-16 15:52:32 +03:00
# ifndef MIN_MEMBLOCK_ADDR
# define MIN_MEMBLOCK_ADDR __pa(PAGE_OFFSET)
# endif
2015-08-18 12:34:41 +03:00
# ifndef MAX_MEMBLOCK_ADDR
# define MAX_MEMBLOCK_ADDR ((phys_addr_t)~0)
# endif
2014-07-08 04:45:43 +04:00
2013-09-25 07:20:01 +04:00
void __init __weak early_init_dt_add_memory_arch ( u64 base , u64 size )
{
2016-02-16 15:52:32 +03:00
const u64 phys_offset = MIN_MEMBLOCK_ADDR ;
2014-08-20 19:10:31 +04:00
if ( ! PAGE_ALIGNED ( base ) ) {
2014-10-29 19:09:32 +03:00
if ( size < PAGE_SIZE - ( base & ~ PAGE_MASK ) ) {
pr_warn ( " Ignoring memory block 0x%llx - 0x%llx \n " ,
base , base + size ) ;
return ;
}
2014-08-20 19:10:31 +04:00
size - = PAGE_SIZE - ( base & ~ PAGE_MASK ) ;
base = PAGE_ALIGN ( base ) ;
}
2013-09-25 07:20:01 +04:00
size & = PAGE_MASK ;
2014-06-20 07:13:38 +04:00
2015-08-18 12:34:41 +03:00
if ( base > MAX_MEMBLOCK_ADDR ) {
2014-07-08 04:45:43 +04:00
pr_warning ( " Ignoring memory block 0x%llx - 0x%llx \n " ,
base , base + size ) ;
return ;
}
2014-06-20 07:13:38 +04:00
2015-08-18 12:34:41 +03:00
if ( base + size - 1 > MAX_MEMBLOCK_ADDR ) {
2014-09-23 13:59:09 +04:00
pr_warning ( " Ignoring memory range 0x%llx - 0x%llx \n " ,
2015-08-18 12:34:41 +03:00
( ( u64 ) MAX_MEMBLOCK_ADDR ) + 1 , base + size ) ;
size = MAX_MEMBLOCK_ADDR - base + 1 ;
2014-06-20 07:13:38 +04:00
}
2013-09-25 07:20:01 +04:00
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 ) ;
}
2016-12-13 03:43:02 +03:00
int __init __weak early_init_dt_mark_hotplug_memory_arch ( u64 base , u64 size )
{
return memblock_mark_hotplug ( base , size ) ;
}
2014-02-28 17:42:47 +04:00
int __init __weak early_init_dt_reserve_memory_arch ( phys_addr_t base ,
phys_addr_t size , bool nomap )
{
if ( nomap )
return memblock_remove ( base , size ) ;
return memblock_reserve ( 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 ) ) ;
}
2014-02-28 17:42:47 +04:00
# else
2015-06-19 04:35:46 +03:00
void __init __weak early_init_dt_add_memory_arch ( u64 base , u64 size )
{
WARN_ON ( 1 ) ;
}
2016-12-13 03:43:02 +03:00
int __init __weak early_init_dt_mark_hotplug_memory_arch ( u64 base , u64 size )
{
return - ENOSYS ;
}
2014-02-28 17:42:47 +04:00
int __init __weak early_init_dt_reserve_memory_arch ( phys_addr_t base ,
phys_addr_t size , bool nomap )
{
2015-11-30 23:45:48 +03:00
pr_err ( " Reserved memory not supported, ignoring range %pa - %pa%s \n " ,
2014-04-22 21:50:24 +04:00
& base , & size , nomap ? " (nomap) " : " " ) ;
2014-02-28 17:42:47 +04:00
return - ENOSYS ;
}
2015-06-19 04:35:46 +03:00
void * __init __weak early_init_dt_alloc_memory_arch ( u64 size , u64 align )
{
WARN_ON ( 1 ) ;
return NULL ;
}
2013-08-29 00:18:32 +04:00
# endif
2014-07-15 21:03:34 +04:00
bool __init early_init_dt_verify ( void * params )
2013-08-26 18:47:40 +04:00
{
if ( ! params )
return false ;
/* check device tree validity */
2014-10-29 21:15:00 +03:00
if ( fdt_check_header ( params ) )
2013-08-26 18:47:40 +04:00
return false ;
2014-10-29 21:15:00 +03:00
/* Setup flat device-tree pointer */
initial_boot_params = params ;
2014-11-14 20:05:35 +03:00
of_fdt_crc32 = crc32_be ( ~ 0 , initial_boot_params ,
fdt_totalsize ( initial_boot_params ) ) ;
2014-07-15 21:03:34 +04:00
return true ;
}
void __init early_init_dt_scan_nodes ( void )
{
2013-08-26 18:47:40 +04:00
/* 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 ) ;
2014-07-15 21:03:34 +04:00
}
bool __init early_init_dt_scan ( void * params )
{
bool status ;
status = early_init_dt_verify ( params ) ;
if ( ! status )
return false ;
2013-08-26 18:47:40 +04:00
2014-07-15 21:03:34 +04:00
early_init_dt_scan_nodes ( ) ;
2013-08-26 18:47:40 +04:00
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 )
{
2016-05-03 16:22:50 +03:00
__unflatten_device_tree ( initial_boot_params , NULL , & of_root ,
2016-07-19 01:01:12 +03:00
early_init_dt_alloc_memory_arch , false ) ;
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 ) ;
2017-04-26 03:09:54 +03:00
unittest_unflatten_overlay_base ( ) ;
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 )
{
2013-11-21 17:44:14 +04:00
int size ;
void * dt ;
if ( ! initial_boot_params ) {
pr_warn ( " No valid device tree found, continuing without \n " ) ;
return ;
}
2014-04-02 07:48:01 +04:00
size = fdt_totalsize ( initial_boot_params ) ;
2013-11-21 17:44:14 +04:00
dt = early_init_dt_alloc_memory_arch ( size ,
2014-04-02 07:48:01 +04:00
roundup_pow_of_two ( FDT_V17_SIZE ) ) ;
2013-08-26 20:22:45 +04:00
if ( dt ) {
memcpy ( dt , initial_boot_params , size ) ;
initial_boot_params = dt ;
}
unflatten_device_tree ( ) ;
}
2014-11-14 20:05:35 +03:00
# ifdef CONFIG_SYSFS
static ssize_t of_fdt_raw_read ( struct file * filp , struct kobject * kobj ,
struct bin_attribute * bin_attr ,
char * buf , loff_t off , size_t count )
2014-04-03 01:56:48 +04:00
{
2014-11-14 20:05:35 +03:00
memcpy ( buf , initial_boot_params + off , count ) ;
return count ;
}
2014-04-03 01:56:48 +04:00
2014-11-14 20:05:35 +03:00
static int __init of_fdt_raw_init ( void )
{
static struct bin_attribute of_fdt_raw_attr =
__BIN_ATTR ( fdt , S_IRUSR , of_fdt_raw_read , NULL , 0 ) ;
2014-04-03 01:56:48 +04:00
2014-11-14 20:05:35 +03:00
if ( ! initial_boot_params )
return 0 ;
2014-04-03 01:56:48 +04:00
2014-11-14 20:05:35 +03:00
if ( of_fdt_crc32 ! = crc32_be ( ~ 0 , initial_boot_params ,
fdt_totalsize ( initial_boot_params ) ) ) {
2016-06-15 16:32:18 +03:00
pr_warn ( " not creating '/sys/firmware/fdt': CRC check failed \n " ) ;
2014-11-14 20:05:35 +03:00
return 0 ;
}
of_fdt_raw_attr . size = fdt_totalsize ( initial_boot_params ) ;
return sysfs_create_bin_file ( firmware_kobj , & of_fdt_raw_attr ) ;
2014-04-03 01:56:48 +04:00
}
2014-11-14 20:05:35 +03:00
late_initcall ( of_fdt_raw_init ) ;
2014-04-03 01:56:48 +04:00
# endif
2010-11-19 02:54:56 +03:00
# endif /* CONFIG_OF_EARLY_FLATTREE */