2019-06-04 10:11:33 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2011-04-28 14:27:20 -06:00
/*
* linux / arch / arm / kernel / devtree . c
*
* Copyright ( C ) 2009 Canonical Ltd . < jeremy . kerr @ canonical . com >
*/
# 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/memblock.h>
# include <linux/of.h>
# include <linux/of_fdt.h>
# include <linux/of_irq.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>
2016-06-17 18:21:07 +01:00
# include <asm/prom.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
2020-10-21 19:36:07 -07:00
__used __section ( " __cpu_method_of_table_end " ) ;
2014-03-24 16:11:54 -05:00
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 ;
2018-08-27 10:53:34 -05:00
for_each_of_cpu_node ( cpu ) {
2021-10-06 11:43:22 -05:00
u32 hwid = of_get_cpu_hwid ( cpu , 0 ) ;
2011-11-17 17:31:51 +00:00
2017-07-21 14:28:32 -05:00
pr_debug ( " * %pOF... \n " , cpu ) ;
2011-11-17 17:31:51 +00:00
/*
2016-09-26 16:50:55 +01:00
* Bits n : 24 must be set to 0 in the DT since the reg property
2011-11-17 17:31:51 +00:00
* defines the MPIDR [ 23 : 0 ] .
*/
2021-10-06 11:43:22 -05:00
if ( hwid & ~ MPIDR_HWID_BITMASK ) {
2015-10-10 23:41:42 +02:00
of_node_put ( cpu ) ;
2011-11-17 17:31:51 +00:00
return ;
2015-10-10 23:41:42 +02:00
}
2011-11-17 17:31:51 +00:00
/*
* 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 + + )
2015-10-10 23:41:42 +02:00
if ( WARN ( tmp_map [ j ] = = hwid ,
" Duplicate /cpu reg properties in the DT \n " ) ) {
of_node_put ( cpu ) ;
2011-11-17 17:31:51 +00:00
return ;
2015-10-10 23:41:42 +02:00
}
2011-11-17 17:31:51 +00:00
/*
* 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 ;
2015-10-10 23:41:42 +02:00
of_node_put ( cpu ) ;
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
2020-10-11 10:20:16 +01:00
* @ dt_virt : virtual address of dt blob
2011-04-28 14:27:21 -06:00
*
* If a dtb was passed to the kernel in r2 , then use it to choose the
* correct machine_desc and to setup the system .
*/
2020-10-11 10:20:16 +01:00
const struct machine_desc * __init setup_machine_fdt ( void * dt_virt )
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
DT_MACHINE_START ( GENERIC_DT , " Generic DT based system " )
2016-04-04 09:22:28 +01:00
. l2c_aux_val = 0x0 ,
. l2c_aux_mask = ~ 0x0 ,
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
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
2020-10-11 10:20:16 +01:00
if ( ! dt_virt | | ! early_init_dt_verify ( dt_virt ) )
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 */
}
2014-07-15 10:03:36 -07:00
/* We really don't want to do this, but sometimes firmware provides buggy data */
if ( mdesc - > dt_fixup )
mdesc - > dt_fixup ( ) ;
early_init_dt_scan_nodes ( ) ;
2011-04-28 14:27:21 -06:00
/* 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
}