2011-04-29 00:27:20 +04:00
/*
* linux / arch / arm / kernel / devtree . c
*
* Copyright ( C ) 2009 Canonical Ltd . < jeremy . kerr @ canonical . com >
*
* 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 .
*/
# include <linux/init.h>
2011-07-22 18:58:34 +04:00
# include <linux/export.h>
2011-04-29 00:27:20 +04:00
# include <linux/errno.h>
# include <linux/types.h>
# include <linux/bootmem.h>
# include <linux/memblock.h>
# include <linux/of.h>
# include <linux/of_fdt.h>
# include <linux/of_irq.h>
# include <linux/of_platform.h>
2011-11-17 21:31:51 +04:00
# include <asm/cputype.h>
2011-04-29 00:27:20 +04:00
# include <asm/setup.h>
# include <asm/page.h>
2011-11-17 21:31:51 +04:00
# include <asm/smp_plat.h>
2011-04-29 00:27:21 +04:00
# include <asm/mach/arch.h>
# include <asm/mach-types.h>
2011-04-29 00:27:20 +04:00
void __init early_init_dt_add_memory_arch ( u64 base , u64 size )
{
arm_add_memory ( base , size ) ;
}
void * __init early_init_dt_alloc_memory_arch ( u64 size , u64 align )
{
return alloc_bootmem_align ( size , align ) ;
}
2011-04-29 00:27:21 +04:00
void __init arm_dt_memblock_reserve ( void )
{
u64 * reserve_map , base , size ;
if ( ! initial_boot_params )
return ;
/* Reserve the dtb region */
memblock_reserve ( virt_to_phys ( initial_boot_params ) ,
be32_to_cpu ( initial_boot_params - > totalsize ) ) ;
/*
* Process the reserve map . This will probably overlap the initrd
* and dtb locations which are already reserved , but overlaping
* doesn ' t hurt anything
*/
reserve_map = ( ( void * ) initial_boot_params ) +
be32_to_cpu ( initial_boot_params - > off_mem_rsvmap ) ;
while ( 1 ) {
base = be64_to_cpup ( reserve_map + + ) ;
size = be64_to_cpup ( reserve_map + + ) ;
if ( ! size )
break ;
memblock_reserve ( base , size ) ;
}
}
2011-11-17 21:31:51 +04:00
/*
* arm_dt_init_cpu_maps - Function retrieves cpu nodes from the device tree
* and builds the cpu logical map array containing MPIDR values related to
* logical cpus
*
* Updates the cpu possible mask with the number of parsed cpu nodes
*/
void __init arm_dt_init_cpu_maps ( void )
{
/*
* Temp logical map is initialized with UINT_MAX values that are
* considered invalid logical map entries since the logical map must
* contain a list of MPIDR [ 23 : 0 ] values where MPIDR [ 31 : 24 ] must
* read as 0.
*/
struct device_node * cpu , * cpus ;
u32 i , j , cpuidx = 1 ;
u32 mpidr = is_smp ( ) ? read_cpuid_mpidr ( ) & MPIDR_HWID_BITMASK : 0 ;
2013-06-19 13:40:48 +04:00
u32 tmp_map [ NR_CPUS ] = { [ 0 . . . NR_CPUS - 1 ] = MPIDR_INVALID } ;
2011-11-17 21:31:51 +04:00
bool bootcpu_valid = false ;
cpus = of_find_node_by_path ( " /cpus " ) ;
if ( ! cpus )
return ;
for_each_child_of_node ( cpus , cpu ) {
u32 hwid ;
2013-06-19 13:36:26 +04:00
if ( of_node_cmp ( cpu - > type , " cpu " ) )
continue ;
2011-11-17 21:31:51 +04:00
pr_debug ( " * %s... \n " , cpu - > full_name ) ;
/*
* A device tree containing CPU nodes with missing " reg "
* properties is considered invalid to build the
* cpu_logical_map .
*/
if ( of_property_read_u32 ( cpu , " reg " , & hwid ) ) {
pr_debug ( " * %s missing reg property \n " ,
cpu - > full_name ) ;
return ;
}
/*
* 8 MSBs must be set to 0 in the DT since the reg property
* defines the MPIDR [ 23 : 0 ] .
*/
if ( hwid & ~ MPIDR_HWID_BITMASK )
return ;
/*
* Duplicate MPIDRs are a recipe for disaster .
* Scan all initialized entries and check for
* duplicates . If any is found just bail out .
* temp values were initialized to UINT_MAX
* to avoid matching valid MPIDR [ 23 : 0 ] values .
*/
for ( j = 0 ; j < cpuidx ; j + + )
if ( WARN ( tmp_map [ j ] = = hwid , " Duplicate /cpu reg "
" properties in the DT \n " ) )
return ;
/*
* Build a stashed array of MPIDR values . Numbering scheme
* requires that if detected the boot CPU must be assigned
* logical id 0. Other CPUs get sequential indexes starting
* from 1. If a CPU node with a reg property matching the
* boot CPU MPIDR is detected , this is recorded so that the
* logical map built from DT is validated and can be used
* to override the map created in smp_setup_processor_id ( ) .
*/
if ( hwid = = mpidr ) {
i = 0 ;
bootcpu_valid = true ;
} else {
i = cpuidx + + ;
}
2012-11-22 21:02:54 +04:00
if ( WARN ( cpuidx > nr_cpu_ids , " DT /cpu %u nodes greater than "
" max cores %u, capping them \n " ,
cpuidx , nr_cpu_ids ) ) {
cpuidx = nr_cpu_ids ;
2011-11-17 21:31:51 +04:00
break ;
2012-11-22 21:02:54 +04:00
}
tmp_map [ i ] = hwid ;
2011-11-17 21:31:51 +04:00
}
2013-06-30 03:25:14 +04:00
if ( ! bootcpu_valid ) {
pr_warn ( " DT missing boot CPU MPIDR[23:0], fall back to default cpu_logical_map \n " ) ;
2011-11-17 21:31:51 +04:00
return ;
2013-06-30 03:25:14 +04:00
}
2011-11-17 21:31:51 +04:00
/*
* Since the boot CPU node contains proper data , and all nodes have
* a reg property , the DT CPU list can be considered valid and the
* logical map created in smp_setup_processor_id ( ) can be overridden
*/
for ( i = 0 ; i < cpuidx ; i + + ) {
set_cpu_possible ( i , true ) ;
cpu_logical_map ( i ) = tmp_map [ i ] ;
pr_debug ( " cpu logical map 0x%x \n " , cpu_logical_map ( i ) ) ;
}
}
2011-04-29 00:27:21 +04:00
/**
* setup_machine_fdt - Machine setup when an dtb was passed to the kernel
* @ dt_phys : physical address of dt blob
*
* If a dtb was passed to the kernel in r2 , then use it to choose the
* correct machine_desc and to setup the system .
*/
struct machine_desc * __init setup_machine_fdt ( unsigned int dt_phys )
{
struct boot_param_header * devtree ;
struct machine_desc * mdesc , * mdesc_best = NULL ;
unsigned int score , mdesc_score = ~ 1 ;
unsigned long dt_root ;
const char * model ;
ARM: default machine descriptor for multiplatform
Since we now have default implementations for init_time and init_irq,
the init_machine callback is the only one that is not yet optional,
but since simple DT based platforms all have the same
of_platform_populate function call in there, we can consolidate them
as well, and then actually boot with a completely empty machine_desc.
Unofortunately we cannot just default to an empty init_machine: We
cannot call of_platform_populate before init_machine because that
does not work in case of auxdata, and we cannot call it after
init_machine either because the machine might need to run code
after adding the devices.
To take the final step, this adds support for booting without defining
any machine_desc whatsoever.
For the case that CONFIG_MULTIPLATFORM is enabled, it adds a
global machine descriptor that never matches any machine but is
used as a fallback if nothing else matches. We assume that without
CONFIG_MULTIPLATFORM, we only want to boot on the systems that the kernel
is built for, so we still retain the build-time warning for missing
machine descriptors and the run-time warning when the platform does not
match in that case.
In the case that we run on a multiplatform kernel and the machine
provides a fully populated device tree, we attempt to keep booting,
hoping that no machine specific callbacks are necessary.
Finally, this also removes the misguided "select ARCH_VEXPRESS" that
was only added to avoid a build error for allnoconfig kernels.
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Acked-by: Nicolas Pitre <nico@linaro.org>
Acked-by: Olof Johansson <olof@lixom.net>
Cc: "Russell King - ARM Linux" <linux@arm.linux.org.uk>
Cc: Rob Herring <robherring2@gmail.com>
2013-01-31 21:51:18 +04:00
# ifdef CONFIG_ARCH_MULTIPLATFORM
DT_MACHINE_START ( GENERIC_DT , " Generic DT based system " )
MACHINE_END
mdesc_best = ( struct machine_desc * ) & __mach_desc_GENERIC_DT ;
# endif
2011-06-09 07:58:36 +04:00
if ( ! dt_phys )
return NULL ;
2011-04-29 00:27:21 +04:00
devtree = phys_to_virt ( dt_phys ) ;
/* check device tree validity */
if ( be32_to_cpu ( devtree - > magic ) ! = OF_DT_HEADER )
return NULL ;
/* Search the mdescs for the 'best' compatible value match */
initial_boot_params = devtree ;
dt_root = of_get_flat_dt_root ( ) ;
for_each_machine_desc ( mdesc ) {
score = of_flat_dt_match ( dt_root , mdesc - > dt_compat ) ;
if ( score > 0 & & score < mdesc_score ) {
mdesc_best = mdesc ;
mdesc_score = score ;
}
}
if ( ! mdesc_best ) {
const char * prop ;
long size ;
early_print ( " \n Error: unrecognized/unsupported "
" device tree compatible list: \n [ " ) ;
prop = of_get_flat_dt_prop ( dt_root , " compatible " , & size ) ;
while ( size > 0 ) {
early_print ( " '%s' " , prop ) ;
size - = strlen ( prop ) + 1 ;
prop + = strlen ( prop ) + 1 ;
}
early_print ( " ] \n \n " ) ;
dump_machine_table ( ) ; /* does not return */
}
model = of_get_flat_dt_prop ( dt_root , " model " , NULL ) ;
if ( ! model )
model = of_get_flat_dt_prop ( dt_root , " compatible " , NULL ) ;
if ( ! model )
model = " <unknown> " ;
pr_info ( " Machine: %s, model: %s \n " , mdesc_best - > name , model ) ;
/* 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 ) ;
/* Change machine number to match the mdesc we're using */
__machine_arch_type = mdesc_best - > nr ;
return mdesc_best ;
}