2012-11-15 01:51:08 +04:00
/*
* Symmetric Multi Processing ( SMP ) support for Armada XP
*
* Copyright ( C ) 2012 Marvell
*
* Lior Amsalem < alior @ marvell . com >
* Yehuda Yitschak < yehuday @ marvell . com >
* Gregory CLEMENT < gregory . clement @ free - electrons . com >
* Thomas Petazzoni < thomas . petazzoni @ free - electrons . com >
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed " as is " without any
* warranty of any kind , whether express or implied .
*
* The Armada XP SoC has 4 ARMv7 PJ4B CPUs running in full HW coherency
* This file implements the routines for preparing the SMP infrastructure
* and waking up the secondary CPUs
*/
# include <linux/init.h>
# include <linux/smp.h>
# include <linux/clk.h>
# include <linux/of.h>
2013-07-26 17:17:54 +04:00
# include <linux/of_address.h>
2013-03-21 20:59:15 +04:00
# include <linux/mbus.h>
2012-11-15 01:51:08 +04:00
# include <asm/cacheflush.h>
# include <asm/smp_plat.h>
# include "common.h"
# include "armada-370-xp.h"
# include "pmsu.h"
# include "coherency.h"
2014-10-30 14:39:41 +03:00
# define ARMADA_XP_MAX_CPUS 4
2013-07-26 17:17:54 +04:00
# define AXP_BOOTROM_BASE 0xfff00000
# define AXP_BOOTROM_SIZE 0x100000
2018-06-18 18:32:30 +03:00
static struct clk * boot_cpu_clk ;
ARM: mvebu: synchronize secondary CPU clocks on resume
The Armada XP has multiple cores clocked by independent clocks. The
SMP startup code contains a function called set_secondary_cpus_clock()
called in armada_xp_smp_prepare_cpus() to ensure the clocks of the
secondary CPUs match the clock of the boot CPU.
With the introduction of suspend/resume, this operation is no longer
needed when booting the system, but also when existing the suspend to
RAM state. Therefore this commit reworks a bit the logic: instead of
configuring the clock of all secondary CPUs in
armada_xp_smp_prepare_cpus(), we do it on a per-secondary CPU basis in
armada_xp_boot_secondary(), as this function gets called when existing
suspend to RAM for each secondary CPU.
Since the function now only takes care of one CPU, we rename it from
set_secondary_cpus_clock() to set_secondary_cpu_clock(), and it looses
its __init marker, as it is now used beyond the system initialization.
Note that we can't use smp_processor_id() directly, because when
exiting from suspend to RAM, the code is apparently executed with
preemption enabled, so smp_processor_id() is not happy (prints a
warning). We therefore switch to using get_cpu()/put_cpu(), even
though we pretty much have the guarantee that the code starting the
secondary CPUs is going to run on the boot CPU and will not be
migrated.
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Acked-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
Link: https://lkml.kernel.org/r/1416585613-2113-14-git-send-email-thomas.petazzoni@free-electrons.com
Signed-off-by: Jason Cooper <jason@lakedaemon.net>
2014-11-21 19:00:10 +03:00
static struct clk * get_cpu_clk ( int cpu )
2013-07-03 19:01:42 +04:00
{
struct clk * cpu_clk ;
struct device_node * np = of_get_cpu_node ( cpu , NULL ) ;
if ( WARN ( ! np , " missing cpu node \n " ) )
return NULL ;
cpu_clk = of_clk_get ( np , 0 ) ;
if ( WARN_ON ( IS_ERR ( cpu_clk ) ) )
return NULL ;
return cpu_clk ;
}
2013-06-17 23:43:14 +04:00
static int armada_xp_boot_secondary ( unsigned int cpu , struct task_struct * idle )
2012-11-15 01:51:08 +04:00
{
2014-04-14 17:53:58 +04:00
int ret , hw_cpu ;
2012-11-15 01:51:08 +04:00
pr_info ( " Booting CPU %d \n " , cpu ) ;
2014-04-14 17:53:58 +04:00
hw_cpu = cpu_logical_map ( cpu ) ;
mvebu_pmsu_set_cpu_boot_addr ( hw_cpu , armada_xp_secondary_startup ) ;
2014-05-31 00:18:17 +04:00
/*
* This is needed to wake up CPUs in the offline state after
* using CPU hotplug .
*/
arch_send_wakeup_ipi_mask ( cpumask_of ( cpu ) ) ;
/*
* This is needed to take secondary CPUs out of reset on the
* initial boot .
*/
2014-04-14 17:53:58 +04:00
ret = mvebu_cpu_reset_deassert ( hw_cpu ) ;
if ( ret ) {
pr_warn ( " unable to boot CPU: %d \n " , ret ) ;
return ret ;
}
2012-11-15 01:51:08 +04:00
return 0 ;
}
2014-05-31 00:18:17 +04:00
/*
* When a CPU is brought back online , either through CPU hotplug , or
* because of the boot of a kexec ' ed kernel , the PMSU configuration
* for this CPU might be in the deep idle state , preventing this CPU
* from receiving interrupts . Here , we therefore take out the current
* CPU from this state , which was entered by armada_xp_cpu_die ( )
* below .
*/
static void armada_xp_secondary_init ( unsigned int cpu )
{
2014-07-23 17:00:42 +04:00
mvebu_v7_pmsu_idle_exit ( ) ;
2014-05-31 00:18:17 +04:00
}
2012-11-15 01:51:08 +04:00
static void __init armada_xp_smp_init_cpus ( void )
{
2013-07-23 15:32:42 +04:00
unsigned int ncores = num_possible_cpus ( ) ;
arm: mvebu: remove dependency of SMP init on static I/O mapping
The ->smp_init_cpus() function is called very early during boot, at a
point where dynamic I/O mappings are not yet possible. However, in the
Armada 370/XP implementation of this function, we have to get the
number of CPUs. We used to do that by accessing a hardware register,
which requires relying on a static I/O mapping set up by
->map_io(). Not only this requires hardcoding a virtual address, but
it also prevents us from removing the static I/O mapping.
So this commit changes the way used to get the number of CPUs: we now
use the Device Tree, which is a representation of the hardware, and
provides us the number of available CPUs. This is also more accurate,
because it potentially allows to boot the Linux kernel on only a
number of CPUs given by the Device Tree, instead of unconditionally on
all CPUs.
As a consequence, the coherency_get_cpu_count() function becomes no
longer used, so we remove it.
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Jason Cooper <jason@lakedaemon.net>
2013-06-05 11:04:54 +04:00
if ( ncores = = 0 | | ncores > ARMADA_XP_MAX_CPUS )
panic ( " Invalid number of CPUs in DT \n " ) ;
2012-11-15 01:51:08 +04:00
}
2018-06-18 18:32:30 +03:00
static int armada_xp_sync_secondary_clk ( unsigned int cpu )
{
struct clk * cpu_clk = get_cpu_clk ( cpu ) ;
if ( ! cpu_clk | | ! boot_cpu_clk )
return 0 ;
clk_prepare_enable ( cpu_clk ) ;
clk_set_rate ( cpu_clk , clk_get_rate ( boot_cpu_clk ) ) ;
return 0 ;
}
2013-11-07 13:02:38 +04:00
static void __init armada_xp_smp_prepare_cpus ( unsigned int max_cpus )
2012-11-15 01:51:08 +04:00
{
2013-07-26 17:17:54 +04:00
struct device_node * node ;
struct resource res ;
int err ;
2012-11-15 01:51:08 +04:00
flush_cache_all ( ) ;
2014-04-14 19:10:07 +04:00
set_cpu_coherent ( ) ;
2013-07-26 17:17:54 +04:00
2018-06-18 18:32:30 +03:00
boot_cpu_clk = get_cpu_clk ( smp_processor_id ( ) ) ;
if ( boot_cpu_clk ) {
clk_prepare_enable ( boot_cpu_clk ) ;
cpuhp_setup_state_nocalls ( CPUHP_AP_ARM_MVEBU_SYNC_CLOCKS ,
" arm/mvebu/sync_clocks:online " ,
armada_xp_sync_secondary_clk , NULL ) ;
}
2013-07-26 17:17:54 +04:00
/*
* In order to boot the secondary CPUs we need to ensure
* the bootROM is mapped at the correct address .
*/
node = of_find_compatible_node ( NULL , NULL , " marvell,bootrom " ) ;
if ( ! node )
panic ( " Cannot find 'marvell,bootrom' compatible node " ) ;
err = of_address_to_resource ( node , 0 , & res ) ;
2016-02-08 09:17:12 +03:00
of_node_put ( node ) ;
2013-07-26 17:17:54 +04:00
if ( err < 0 )
panic ( " Cannot get 'bootrom' node address " ) ;
if ( res . start ! = AXP_BOOTROM_BASE | |
resource_size ( & res ) ! = AXP_BOOTROM_SIZE )
panic ( " The address for the BootROM is incorrect " ) ;
2012-11-15 01:51:08 +04:00
}
2014-05-31 00:18:17 +04:00
# ifdef CONFIG_HOTPLUG_CPU
static void armada_xp_cpu_die ( unsigned int cpu )
{
/*
* CPU hotplug is implemented by putting offline CPUs into the
* deep idle sleep state .
*/
armada_370_xp_pmsu_idle_enter ( true ) ;
}
/*
* We need a dummy function , so that platform_can_cpu_hotplug ( ) knows
* we support CPU hotplug . However , the function does not need to do
* anything , because CPUs going offline can enter the deep idle state
* by themselves , without any help from a still alive CPU .
*/
static int armada_xp_cpu_kill ( unsigned int cpu )
{
return 1 ;
}
# endif
2015-11-15 04:39:53 +03:00
const struct smp_operations armada_xp_smp_ops __initconst = {
2012-11-15 01:51:08 +04:00
. smp_init_cpus = armada_xp_smp_init_cpus ,
. smp_prepare_cpus = armada_xp_smp_prepare_cpus ,
. smp_boot_secondary = armada_xp_boot_secondary ,
2014-05-31 00:18:17 +04:00
. smp_secondary_init = armada_xp_secondary_init ,
2012-11-15 01:51:08 +04:00
# ifdef CONFIG_HOTPLUG_CPU
. cpu_die = armada_xp_cpu_die ,
2014-05-31 00:18:17 +04:00
. cpu_kill = armada_xp_cpu_kill ,
2012-11-15 01:51:08 +04:00
# endif
} ;
2014-04-14 17:53:59 +04:00
CPU_METHOD_OF_DECLARE ( armada_xp_smp , " marvell,armada-xp-smp " ,
& armada_xp_smp_ops ) ;
2017-01-30 02:20:32 +03:00
# define MV98DX3236_CPU_RESUME_CTRL_REG 0x08
# define MV98DX3236_CPU_RESUME_ADDR_REG 0x04
static const struct of_device_id of_mv98dx3236_resume_table [ ] = {
{
. compatible = " marvell,98dx3336-resume-ctrl " ,
} ,
{ /* end of list */ } ,
} ;
static int mv98dx3236_resume_set_cpu_boot_addr ( int hw_cpu , void * boot_addr )
{
struct device_node * np ;
void __iomem * base ;
WARN_ON ( hw_cpu ! = 1 ) ;
np = of_find_matching_node ( NULL , of_mv98dx3236_resume_table ) ;
if ( ! np )
return - ENODEV ;
base = of_io_request_and_map ( np , 0 , of_node_full_name ( np ) ) ;
of_node_put ( np ) ;
if ( IS_ERR ( base ) )
return PTR_ERR ( base ) ;
writel ( 0 , base + MV98DX3236_CPU_RESUME_CTRL_REG ) ;
2017-07-07 10:59:28 +03:00
writel ( __pa_symbol ( boot_addr ) , base + MV98DX3236_CPU_RESUME_ADDR_REG ) ;
2017-01-30 02:20:32 +03:00
iounmap ( base ) ;
return 0 ;
}
static int mv98dx3236_boot_secondary ( unsigned int cpu , struct task_struct * idle )
{
int ret , hw_cpu ;
hw_cpu = cpu_logical_map ( cpu ) ;
mv98dx3236_resume_set_cpu_boot_addr ( hw_cpu ,
armada_xp_secondary_startup ) ;
/*
* This is needed to wake up CPUs in the offline state after
* using CPU hotplug .
*/
arch_send_wakeup_ipi_mask ( cpumask_of ( cpu ) ) ;
/*
* This is needed to take secondary CPUs out of reset on the
* initial boot .
*/
ret = mvebu_cpu_reset_deassert ( hw_cpu ) ;
if ( ret ) {
pr_warn ( " unable to boot CPU: %d \n " , ret ) ;
return ret ;
}
return 0 ;
}
static const struct smp_operations mv98dx3236_smp_ops __initconst = {
. smp_init_cpus = armada_xp_smp_init_cpus ,
. smp_prepare_cpus = armada_xp_smp_prepare_cpus ,
. smp_boot_secondary = mv98dx3236_boot_secondary ,
. smp_secondary_init = armada_xp_secondary_init ,
# ifdef CONFIG_HOTPLUG_CPU
. cpu_die = armada_xp_cpu_die ,
. cpu_kill = armada_xp_cpu_kill ,
# endif
} ;
CPU_METHOD_OF_DECLARE ( mv98dx3236_smp , " marvell,98dx3236-smp " ,
& mv98dx3236_smp_ops ) ;