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"
2013-07-26 17:17:54 +04:00
# define AXP_BOOTROM_BASE 0xfff00000
# define AXP_BOOTROM_SIZE 0x100000
2013-07-03 19:01:42 +04:00
static struct clk * __init get_cpu_clk ( int cpu )
{
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-11-07 13:02:38 +04:00
static void __init set_secondary_cpus_clock ( void )
2012-11-15 01:51:08 +04:00
{
2013-07-03 19:01:42 +04:00
int thiscpu , cpu ;
2012-11-15 01:51:08 +04:00
unsigned long rate ;
2013-07-03 19:01:42 +04:00
struct clk * cpu_clk ;
2012-11-15 01:51:08 +04:00
thiscpu = smp_processor_id ( ) ;
2013-07-03 19:01:42 +04:00
cpu_clk = get_cpu_clk ( thiscpu ) ;
if ( ! cpu_clk )
2012-11-15 01:51:08 +04:00
return ;
clk_prepare_enable ( cpu_clk ) ;
rate = clk_get_rate ( cpu_clk ) ;
/* set all the other CPU clk to the same rate than the boot CPU */
2013-07-03 19:01:42 +04:00
for_each_possible_cpu ( cpu ) {
if ( cpu = = thiscpu )
continue ;
cpu_clk = get_cpu_clk ( cpu ) ;
if ( ! cpu_clk )
2012-11-15 01:51:08 +04:00
return ;
2013-07-03 19:01:42 +04:00
clk_set_rate ( cpu_clk , rate ) ;
2012-11-15 01:51:08 +04:00
}
}
2013-06-17 23:43:14 +04:00
static void armada_xp_secondary_init ( unsigned int cpu )
2012-11-15 01:51:08 +04:00
{
armada_xp_mpic_smp_cpu_init ( ) ;
}
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
{
pr_info ( " Booting CPU %d \n " , cpu ) ;
armada_xp_boot_cpu ( cpu , armada_xp_secondary_startup ) ;
return 0 ;
}
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
set_smp_cross_call ( armada_mpic_send_doorbell ) ;
}
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
set_secondary_cpus_clock ( ) ;
flush_cache_all ( ) ;
set_cpu_coherent ( cpu_logical_map ( smp_processor_id ( ) ) , 0 ) ;
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 ) ;
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
}
struct smp_operations armada_xp_smp_ops __initdata = {
. smp_init_cpus = armada_xp_smp_init_cpus ,
. smp_prepare_cpus = armada_xp_smp_prepare_cpus ,
. smp_secondary_init = armada_xp_secondary_init ,
. smp_boot_secondary = armada_xp_boot_secondary ,
# ifdef CONFIG_HOTPLUG_CPU
. cpu_die = armada_xp_cpu_die ,
# endif
} ;