2011-04-28 14:27:20 -06: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 10:58:34 -04:00
# include <linux/export.h>
2011-04-28 14:27:20 -06: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>
2013-10-30 18:21:09 -07:00
# include <linux/smp.h>
2011-04-28 14:27:20 -06:00
2011-11-17 17:31:51 +00:00
# include <asm/cputype.h>
2011-04-28 14:27:20 -06:00
# include <asm/setup.h>
# include <asm/page.h>
2011-11-17 17:31:51 +00:00
# include <asm/smp_plat.h>
2011-04-28 14:27:21 -06:00
# include <asm/mach/arch.h>
# include <asm/mach-types.h>
2011-04-28 14:27:20 -06:00
2013-10-30 18:21:09 -07:00
# ifdef CONFIG_SMP
2014-03-24 16:11:54 -05:00
extern struct of_cpu_method __cpu_method_of_table [ ] ;
static const struct of_cpu_method __cpu_method_of_table_sentinel
__used __section ( __cpu_method_of_table_end ) ;
2013-10-30 18:21:09 -07:00
static int __init set_smp_ops_by_method ( struct device_node * node )
{
const char * method ;
2014-03-24 16:11:54 -05:00
struct of_cpu_method * m = __cpu_method_of_table ;
2013-10-30 18:21:09 -07:00
if ( of_property_read_string ( node , " enable-method " , & method ) )
return 0 ;
2014-03-24 16:11:54 -05:00
for ( ; m - > method ; m + + )
2013-10-30 18:21:09 -07:00
if ( ! strcmp ( m - > method , method ) ) {
smp_set_ops ( m - > ops ) ;
return 1 ;
}
return 0 ;
}
# else
static inline int set_smp_ops_by_method ( struct device_node * node )
{
return 1 ;
}
# endif
2011-11-17 17:31:51 +00: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 ;
2013-10-30 18:21:09 -07:00
int found_method = 0 ;
2011-11-17 17:31:51 +00:00
u32 i , j , cpuidx = 1 ;
u32 mpidr = is_smp ( ) ? read_cpuid_mpidr ( ) & MPIDR_HWID_BITMASK : 0 ;
2013-06-19 10:40:48 +01:00
u32 tmp_map [ NR_CPUS ] = { [ 0 . . . NR_CPUS - 1 ] = MPIDR_INVALID } ;
2011-11-17 17:31:51 +00: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 10:36:26 +01:00
if ( of_node_cmp ( cpu - > type , " cpu " ) )
continue ;
2011-11-17 17:31:51 +00: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 18:02:54 +01: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 17:31:51 +00:00
break ;
2012-11-22 18:02:54 +01:00
}
tmp_map [ i ] = hwid ;
2013-10-30 18:21:09 -07:00
if ( ! found_method )
found_method = set_smp_ops_by_method ( cpu ) ;
2011-11-17 17:31:51 +00:00
}
2013-10-30 18:21:09 -07:00
/*
* Fallback to an enable - method in the cpus node if nothing found in
* a cpu node .
*/
if ( ! found_method )
set_smp_ops_by_method ( cpus ) ;
2013-06-29 16:25:14 -07:00
if ( ! bootcpu_valid ) {
pr_warn ( " DT missing boot CPU MPIDR[23:0], fall back to default cpu_logical_map \n " ) ;
2011-11-17 17:31:51 +00:00
return ;
2013-06-29 16:25:14 -07:00
}
2011-11-17 17:31:51 +00: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 ) ) ;
}
}
2013-06-17 13:11:29 +01:00
bool arch_match_cpu_phys_id ( int cpu , u64 phys_id )
{
2014-01-08 18:24:21 +01:00
return phys_id = = cpu_logical_map ( cpu ) ;
2013-06-17 13:11:29 +01:00
}
2013-08-27 21:43:49 -05:00
static const void * __init arch_get_next_mach ( const char * const * * match )
{
static const struct machine_desc * mdesc = __arch_info_begin ;
const struct machine_desc * m = mdesc ;
if ( m > = __arch_info_end )
return NULL ;
mdesc + + ;
* match = m - > dt_compat ;
return m ;
}
2011-04-28 14:27:21 -06: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 .
*/
2013-07-26 14:55:59 +01:00
const struct machine_desc * __init setup_machine_fdt ( unsigned int dt_phys )
2011-04-28 14:27:21 -06:00
{
2013-07-26 14:55:59 +01:00
const struct machine_desc * mdesc , * mdesc_best = NULL ;
2011-04-28 14:27:21 -06:00
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 17:51:18 +00:00
# ifdef CONFIG_ARCH_MULTIPLATFORM
DT_MACHINE_START ( GENERIC_DT , " Generic DT based system " )
MACHINE_END
2013-07-26 14:55:59 +01:00
mdesc_best = & __mach_desc_GENERIC_DT ;
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 17:51:18 +00:00
# endif
2013-08-26 10:13:01 -05:00
if ( ! dt_phys | | ! early_init_dt_scan ( phys_to_virt ( dt_phys ) ) )
2011-06-09 04:58:36 +01:00
return NULL ;
2013-08-27 21:43:49 -05:00
mdesc = of_flat_dt_match_machine ( mdesc_best , arch_get_next_mach ) ;
2011-04-28 14:27:21 -06:00
2013-08-27 21:43:49 -05:00
if ( ! mdesc ) {
2011-04-28 14:27:21 -06:00
const char * prop ;
2014-04-01 23:49:03 -05:00
int size ;
2013-08-27 21:43:49 -05:00
unsigned long dt_root ;
2011-04-28 14:27:21 -06:00
early_print ( " \n Error: unrecognized/unsupported "
" device tree compatible list: \n [ " ) ;
2013-08-27 21:43:49 -05:00
dt_root = of_get_flat_dt_root ( ) ;
2011-04-28 14:27:21 -06:00
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 */
}
/* Change machine number to match the mdesc we're using */
2013-08-27 21:43:49 -05:00
__machine_arch_type = mdesc - > nr ;
2011-04-28 14:27:21 -06:00
2013-08-27 21:43:49 -05:00
return mdesc ;
2011-04-28 14:27:21 -06:00
}