Commit af997114 authored by Bartlomiej Zolnierkiewicz's avatar Bartlomiej Zolnierkiewicz Committed by Kukjin Kim

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: default avatarBartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
Cc: Daniel Lezcano <daniel.lezcano@linaro.org>
Signed-off-by: default avatarKrzysztof Kozlowski <k.kozlowski@samsung.com>
Signed-off-by: default avatarKukjin Kim <kgene@kernel.org>
parent 1225ad72
...@@ -163,7 +163,9 @@ extern void exynos_set_delayed_reset_assertion(bool enable); ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 @@ static int exynos_set_boot_addr(u32 core_id, unsigned long boot_addr) ...@@ -272,7 +272,7 @@ static int exynos_set_boot_addr(u32 core_id, unsigned long boot_addr)
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 @@ ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 @@ static int exynos_cpu0_enter_aftr(void) ...@@ -247,22 +257,42 @@ static int exynos_cpu0_enter_aftr(void)
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) ...@@ -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 @@ static int exynos_cpu1_powerdown(void) ...@@ -299,7 +332,9 @@ static int exynos_cpu1_powerdown(void)
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)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment