forked from Minki/linux
20aa11358d
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>
149 lines
3.7 KiB
C
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
|
|
};
|