linux/arch/arm/mach-shmobile/smp-sh73a0.c
Bastian Hecht 20aa11358d ARM: SH-Mobile: sh73a0: Add CPU Hotplug
Add the capability to add and remove CPUs on the fly.
The Cortex-A9 offers the possibility to take single cores out of the
MP Core. We add this capabilty taking care that caches are kept
coherent. For verifying the shutdown we rely on the internal SH73A0
Power Status Register PSTR.

Signed-off-by: Bastian Hecht <hechtb+renesas@gmail.com>
Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
2013-01-25 12:43:48 +09:00

149 lines
3.7 KiB
C

/*
* SMP support for R-Mobile / SH-Mobile - sh73a0 portion
*
* Copyright (C) 2010 Magnus Damm
* Copyright (C) 2010 Takashi Yoshii
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/smp.h>
#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/irqchip/arm-gic.h>
#include <mach/common.h>
#include <asm/cacheflush.h>
#include <asm/smp_plat.h>
#include <mach/sh73a0.h>
#include <asm/smp_scu.h>
#include <asm/smp_twd.h>
#define WUPCR IOMEM(0xe6151010)
#define SRESCR IOMEM(0xe6151018)
#define PSTR IOMEM(0xe6151040)
#define SBAR IOMEM(0xe6180020)
#define APARMBAREA IOMEM(0xe6f10020)
#define PSTR_SHUTDOWN_MODE 3
static void __iomem *scu_base_addr(void)
{
return (void __iomem *)0xf0000000;
}
#ifdef CONFIG_HAVE_ARM_TWD
static DEFINE_TWD_LOCAL_TIMER(twd_local_timer, 0xf0000600, 29);
void __init sh73a0_register_twd(void)
{
twd_local_timer_register(&twd_local_timer);
}
#endif
static unsigned int __init sh73a0_get_core_count(void)
{
void __iomem *scu_base = scu_base_addr();
return scu_get_core_count(scu_base);
}
static void __cpuinit sh73a0_secondary_init(unsigned int cpu)
{
gic_secondary_init(0);
}
static int __cpuinit sh73a0_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
cpu = cpu_logical_map(cpu);
if (((__raw_readl(PSTR) >> (4 * cpu)) & 3) == 3)
__raw_writel(1 << cpu, WUPCR); /* wake up */
else
__raw_writel(1 << cpu, SRESCR); /* reset */
return 0;
}
static void __init sh73a0_smp_prepare_cpus(unsigned int max_cpus)
{
scu_enable(scu_base_addr());
/* Map the reset vector (in headsmp-sh73a0.S) */
__raw_writel(0, APARMBAREA); /* 4k */
__raw_writel(__pa(sh73a0_secondary_vector), SBAR);
/* enable cache coherency on booting CPU */
scu_power_mode(scu_base_addr(), SCU_PM_NORMAL);
}
static void __init sh73a0_smp_init_cpus(void)
{
unsigned int ncores = sh73a0_get_core_count();
shmobile_smp_init_cpus(ncores);
}
#ifdef CONFIG_HOTPLUG_CPU
static int sh73a0_cpu_kill(unsigned int cpu)
{
int k;
u32 pstr;
/*
* wait until the power status register confirms the shutdown of the
* offline target
*/
for (k = 0; k < 1000; k++) {
pstr = (__raw_readl(PSTR) >> (4 * cpu)) & 3;
if (pstr == PSTR_SHUTDOWN_MODE)
return 1;
mdelay(1);
}
return 0;
}
static void sh73a0_cpu_die(unsigned int cpu)
{
/*
* The ARM MPcore does not issue a cache coherency request for the L1
* cache when powering off single CPUs. We must take care of this and
* further caches.
*/
dsb();
flush_cache_all();
/* Set power off mode. This takes the CPU out of the MP cluster */
scu_power_mode(scu_base_addr(), SCU_PM_POWEROFF);
/* Enter shutdown mode */
cpu_do_idle();
}
#endif /* CONFIG_HOTPLUG_CPU */
struct smp_operations sh73a0_smp_ops __initdata = {
.smp_init_cpus = sh73a0_smp_init_cpus,
.smp_prepare_cpus = sh73a0_smp_prepare_cpus,
.smp_secondary_init = sh73a0_secondary_init,
.smp_boot_secondary = sh73a0_boot_secondary,
#ifdef CONFIG_HOTPLUG_CPU
.cpu_kill = sh73a0_cpu_kill,
.cpu_die = sh73a0_cpu_die,
.cpu_disable = shmobile_cpu_disable,
#endif
};