ARM: EXYNOS: add coupled cpuidle support for Exynos3250
The following patch adds coupled cpuidle support for Exynos3250 to an existing cpuidle-exynos driver. As a result it enables AFTR mode to be used by default on Exynos3250 without the need to hot unplug CPU1 first. The detailed changelog: - use exynos_[get,set]_boot_addr() in cpuidle-exynos.c and then make cpu_boot_reg_base() static - use exynos_core_restart() in exynos_cpu0_enter_aftr() - add missing smp_rmb() to exynos_cpu0_enter_aftr() (to make the code in-sync with the platform SMP code) - add call_firmware_op(cpu_boot, 1) to exynos_cpu0_enter_aftr() - use dsb_sev() instead of IPI wakeup for Exynos3250 in exynos_cpu0_enter_aftr() - add CPU0 vs CPU1 synchronization based on S5P_PMU_SPARE2 register for Exynos3250 to cpuidle-exynos.c - add flush_cache_all() for CPU1/0 before powerdown/AFTR for Exynos3250 to exynos_wfi_finisher()/exynos_do_idle() Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> Cc: Daniel Lezcano <daniel.lezcano@linaro.org> Signed-off-by: Krzysztof Kozlowski <k.kozlowski@samsung.com> Signed-off-by: Kukjin Kim <kgene@kernel.org>
This commit is contained in:
parent
1225ad7286
commit
af9971144d
@ -163,7 +163,9 @@ extern void exynos_set_delayed_reset_assertion(bool enable);
|
|||||||
|
|
||||||
extern void s5p_init_cpu(void __iomem *cpuid_addr);
|
extern void s5p_init_cpu(void __iomem *cpuid_addr);
|
||||||
extern unsigned int samsung_rev(void);
|
extern unsigned int samsung_rev(void);
|
||||||
extern void __iomem *cpu_boot_reg_base(void);
|
extern void exynos_core_restart(u32 core_id);
|
||||||
|
extern int exynos_set_boot_addr(u32 core_id, unsigned long boot_addr);
|
||||||
|
extern int exynos_get_boot_addr(u32 core_id, unsigned long *boot_addr);
|
||||||
|
|
||||||
static inline void pmu_raw_writel(u32 val, u32 offset)
|
static inline void pmu_raw_writel(u32 val, u32 offset)
|
||||||
{
|
{
|
||||||
|
@ -234,7 +234,8 @@ static void __init exynos_dt_machine_init(void)
|
|||||||
exynos_sysram_init();
|
exynos_sysram_init();
|
||||||
|
|
||||||
#if defined(CONFIG_SMP) && defined(CONFIG_ARM_EXYNOS_CPUIDLE)
|
#if defined(CONFIG_SMP) && defined(CONFIG_ARM_EXYNOS_CPUIDLE)
|
||||||
if (of_machine_is_compatible("samsung,exynos4210"))
|
if (of_machine_is_compatible("samsung,exynos4210") ||
|
||||||
|
of_machine_is_compatible("samsung,exynos3250"))
|
||||||
exynos_cpuidle.dev.platform_data = &cpuidle_coupled_exynos_data;
|
exynos_cpuidle.dev.platform_data = &cpuidle_coupled_exynos_data;
|
||||||
#endif
|
#endif
|
||||||
if (of_machine_is_compatible("samsung,exynos4210") ||
|
if (of_machine_is_compatible("samsung,exynos4210") ||
|
||||||
|
@ -49,6 +49,7 @@ static int exynos_do_idle(unsigned long mode)
|
|||||||
sysram_ns_base_addr + 0x24);
|
sysram_ns_base_addr + 0x24);
|
||||||
__raw_writel(EXYNOS_AFTR_MAGIC, sysram_ns_base_addr + 0x20);
|
__raw_writel(EXYNOS_AFTR_MAGIC, sysram_ns_base_addr + 0x20);
|
||||||
if (soc_is_exynos3250()) {
|
if (soc_is_exynos3250()) {
|
||||||
|
flush_cache_all();
|
||||||
exynos_smc(SMC_CMD_SAVE, OP_TYPE_CORE,
|
exynos_smc(SMC_CMD_SAVE, OP_TYPE_CORE,
|
||||||
SMC_POWERSTATE_IDLE, 0);
|
SMC_POWERSTATE_IDLE, 0);
|
||||||
exynos_smc(SMC_CMD_SHUTDOWN, OP_TYPE_CLUSTER,
|
exynos_smc(SMC_CMD_SHUTDOWN, OP_TYPE_CLUSTER,
|
||||||
|
@ -169,7 +169,7 @@ int exynos_cluster_power_state(int cluster)
|
|||||||
S5P_CORE_LOCAL_PWR_EN);
|
S5P_CORE_LOCAL_PWR_EN);
|
||||||
}
|
}
|
||||||
|
|
||||||
void __iomem *cpu_boot_reg_base(void)
|
static void __iomem *cpu_boot_reg_base(void)
|
||||||
{
|
{
|
||||||
if (soc_is_exynos4210() && samsung_rev() == EXYNOS4210_REV_1_1)
|
if (soc_is_exynos4210() && samsung_rev() == EXYNOS4210_REV_1_1)
|
||||||
return pmu_base_addr + S5P_INFORM5;
|
return pmu_base_addr + S5P_INFORM5;
|
||||||
@ -195,7 +195,7 @@ static inline void __iomem *cpu_boot_reg(int cpu)
|
|||||||
*
|
*
|
||||||
* Currently this is needed only when booting secondary CPU on Exynos3250.
|
* Currently this is needed only when booting secondary CPU on Exynos3250.
|
||||||
*/
|
*/
|
||||||
static void exynos_core_restart(u32 core_id)
|
void exynos_core_restart(u32 core_id)
|
||||||
{
|
{
|
||||||
u32 val;
|
u32 val;
|
||||||
|
|
||||||
@ -247,7 +247,7 @@ static void exynos_secondary_init(unsigned int cpu)
|
|||||||
spin_unlock(&boot_lock);
|
spin_unlock(&boot_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int exynos_set_boot_addr(u32 core_id, unsigned long boot_addr)
|
int exynos_set_boot_addr(u32 core_id, unsigned long boot_addr)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@ -272,7 +272,7 @@ fail:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int exynos_get_boot_addr(u32 core_id, unsigned long *boot_addr)
|
int exynos_get_boot_addr(u32 core_id, unsigned long *boot_addr)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include <asm/firmware.h>
|
#include <asm/firmware.h>
|
||||||
#include <asm/smp_scu.h>
|
#include <asm/smp_scu.h>
|
||||||
#include <asm/suspend.h>
|
#include <asm/suspend.h>
|
||||||
|
#include <asm/cacheflush.h>
|
||||||
|
|
||||||
#include <mach/map.h>
|
#include <mach/map.h>
|
||||||
|
|
||||||
@ -209,6 +210,8 @@ static int exynos_cpu0_enter_aftr(void)
|
|||||||
* sequence, let's wait for one of these to happen
|
* sequence, let's wait for one of these to happen
|
||||||
*/
|
*/
|
||||||
while (exynos_cpu_power_state(1)) {
|
while (exynos_cpu_power_state(1)) {
|
||||||
|
unsigned long boot_addr;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The other cpu may skip idle and boot back
|
* The other cpu may skip idle and boot back
|
||||||
* up again
|
* up again
|
||||||
@ -221,7 +224,11 @@ static int exynos_cpu0_enter_aftr(void)
|
|||||||
* boot back up again, getting stuck in the
|
* boot back up again, getting stuck in the
|
||||||
* boot rom code
|
* boot rom code
|
||||||
*/
|
*/
|
||||||
if (__raw_readl(cpu_boot_reg_base()) == 0)
|
ret = exynos_get_boot_addr(1, &boot_addr);
|
||||||
|
if (ret)
|
||||||
|
goto fail;
|
||||||
|
ret = -1;
|
||||||
|
if (boot_addr == 0)
|
||||||
goto abort;
|
goto abort;
|
||||||
|
|
||||||
cpu_relax();
|
cpu_relax();
|
||||||
@ -233,11 +240,14 @@ static int exynos_cpu0_enter_aftr(void)
|
|||||||
|
|
||||||
abort:
|
abort:
|
||||||
if (cpu_online(1)) {
|
if (cpu_online(1)) {
|
||||||
|
unsigned long boot_addr = virt_to_phys(exynos_cpu_resume);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set the boot vector to something non-zero
|
* Set the boot vector to something non-zero
|
||||||
*/
|
*/
|
||||||
__raw_writel(virt_to_phys(exynos_cpu_resume),
|
ret = exynos_set_boot_addr(1, boot_addr);
|
||||||
cpu_boot_reg_base());
|
if (ret)
|
||||||
|
goto fail;
|
||||||
dsb();
|
dsb();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -247,22 +257,42 @@ abort:
|
|||||||
while (exynos_cpu_power_state(1) != S5P_CORE_LOCAL_PWR_EN)
|
while (exynos_cpu_power_state(1) != S5P_CORE_LOCAL_PWR_EN)
|
||||||
cpu_relax();
|
cpu_relax();
|
||||||
|
|
||||||
|
if (soc_is_exynos3250()) {
|
||||||
|
while (!pmu_raw_readl(S5P_PMU_SPARE2) &&
|
||||||
|
!atomic_read(&cpu1_wakeup))
|
||||||
|
cpu_relax();
|
||||||
|
|
||||||
|
if (!atomic_read(&cpu1_wakeup))
|
||||||
|
exynos_core_restart(1);
|
||||||
|
}
|
||||||
|
|
||||||
while (!atomic_read(&cpu1_wakeup)) {
|
while (!atomic_read(&cpu1_wakeup)) {
|
||||||
|
smp_rmb();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Poke cpu1 out of the boot rom
|
* Poke cpu1 out of the boot rom
|
||||||
*/
|
*/
|
||||||
__raw_writel(virt_to_phys(exynos_cpu_resume),
|
|
||||||
cpu_boot_reg_base());
|
|
||||||
|
|
||||||
arch_send_wakeup_ipi_mask(cpumask_of(1));
|
ret = exynos_set_boot_addr(1, boot_addr);
|
||||||
|
if (ret)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
call_firmware_op(cpu_boot, 1);
|
||||||
|
|
||||||
|
if (soc_is_exynos3250())
|
||||||
|
dsb_sev();
|
||||||
|
else
|
||||||
|
arch_send_wakeup_ipi_mask(cpumask_of(1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fail:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int exynos_wfi_finisher(unsigned long flags)
|
static int exynos_wfi_finisher(unsigned long flags)
|
||||||
{
|
{
|
||||||
|
if (soc_is_exynos3250())
|
||||||
|
flush_cache_all();
|
||||||
cpu_do_idle();
|
cpu_do_idle();
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
@ -283,6 +313,9 @@ static int exynos_cpu1_powerdown(void)
|
|||||||
*/
|
*/
|
||||||
exynos_cpu_power_down(1);
|
exynos_cpu_power_down(1);
|
||||||
|
|
||||||
|
if (soc_is_exynos3250())
|
||||||
|
pmu_raw_writel(0, S5P_PMU_SPARE2);
|
||||||
|
|
||||||
ret = cpu_suspend(0, exynos_wfi_finisher);
|
ret = cpu_suspend(0, exynos_wfi_finisher);
|
||||||
|
|
||||||
cpu_pm_exit();
|
cpu_pm_exit();
|
||||||
@ -299,7 +332,9 @@ cpu1_aborted:
|
|||||||
|
|
||||||
static void exynos_pre_enter_aftr(void)
|
static void exynos_pre_enter_aftr(void)
|
||||||
{
|
{
|
||||||
__raw_writel(virt_to_phys(exynos_cpu_resume), cpu_boot_reg_base());
|
unsigned long boot_addr = virt_to_phys(exynos_cpu_resume);
|
||||||
|
|
||||||
|
(void)exynos_set_boot_addr(1, boot_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void exynos_post_enter_aftr(void)
|
static void exynos_post_enter_aftr(void)
|
||||||
|
Loading…
Reference in New Issue
Block a user