[PATCH 5/5] cpuidle: exynos: add coupled cpuidle support for Exynos3250

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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()

Cc: Daniel Lezcano <daniel.lezcano@xxxxxxxxxx>
Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@xxxxxxxxxxx>
---
 arch/arm/mach-exynos/common.h    |  4 +++-
 arch/arm/mach-exynos/exynos.c    |  3 ++-
 arch/arm/mach-exynos/firmware.c  |  1 +
 arch/arm/mach-exynos/platsmp.c   |  8 +++----
 arch/arm/mach-exynos/pm.c        | 51 +++++++++++++++++++++++++++++++++-------
 drivers/cpuidle/cpuidle-exynos.c |  3 ++-
 6 files changed, 55 insertions(+), 15 deletions(-)

diff --git a/arch/arm/mach-exynos/common.h b/arch/arm/mach-exynos/common.h
index 826367f..bc515f0 100644
--- a/arch/arm/mach-exynos/common.h
+++ b/arch/arm/mach-exynos/common.h
@@ -163,7 +163,9 @@ extern void exynos_set_delayed_reset_assertion(bool enable);
 
 extern void s5p_init_cpu(void __iomem *cpuid_addr);
 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)
 {
diff --git a/arch/arm/mach-exynos/exynos.c b/arch/arm/mach-exynos/exynos.c
index c8996b3..c605826 100644
--- a/arch/arm/mach-exynos/exynos.c
+++ b/arch/arm/mach-exynos/exynos.c
@@ -264,7 +264,8 @@ static void __init exynos_dt_machine_init(void)
 		exynos_sysram_init();
 
 #ifdef 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;
 #endif
 	if (of_machine_is_compatible("samsung,exynos4210") ||
diff --git a/arch/arm/mach-exynos/firmware.c b/arch/arm/mach-exynos/firmware.c
index 0f37d0b..d19b05c 100644
--- a/arch/arm/mach-exynos/firmware.c
+++ b/arch/arm/mach-exynos/firmware.c
@@ -48,6 +48,7 @@ static int exynos_do_idle(unsigned long mode)
 			     sysram_ns_base_addr + 0x24);
 		__raw_writel(EXYNOS_AFTR_MAGIC, sysram_ns_base_addr + 0x20);
 		if (soc_is_exynos3250()) {
+			flush_cache_all();
 			exynos_smc(SMC_CMD_SAVE, OP_TYPE_CORE,
 				   SMC_POWERSTATE_IDLE, 0);
 			exynos_smc(SMC_CMD_SHUTDOWN, OP_TYPE_CLUSTER,
diff --git a/arch/arm/mach-exynos/platsmp.c b/arch/arm/mach-exynos/platsmp.c
index c5c6700..a461be7 100644
--- a/arch/arm/mach-exynos/platsmp.c
+++ b/arch/arm/mach-exynos/platsmp.c
@@ -170,7 +170,7 @@ int exynos_cluster_power_state(int cluster)
 		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)
 		return pmu_base_addr + S5P_INFORM5;
@@ -196,7 +196,7 @@ static inline void __iomem *cpu_boot_reg(int cpu)
  *
  * 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;
 
@@ -248,7 +248,7 @@ static void exynos_secondary_init(unsigned int cpu)
 	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;
 
@@ -273,7 +273,7 @@ fail:
 	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;
 
diff --git a/arch/arm/mach-exynos/pm.c b/arch/arm/mach-exynos/pm.c
index 0a7e3af..1b00146 100644
--- a/arch/arm/mach-exynos/pm.c
+++ b/arch/arm/mach-exynos/pm.c
@@ -22,6 +22,7 @@
 #include <asm/firmware.h>
 #include <asm/smp_scu.h>
 #include <asm/suspend.h>
+#include <asm/cacheflush.h>
 
 #include <mach/map.h>
 
@@ -208,6 +209,8 @@ static int exynos_cpu0_enter_aftr(void)
 		 * sequence, let's wait for one of these to happen
 		 */
 		while (exynos_cpu_power_state(1)) {
+			unsigned long boot_addr;
+
 			/*
 			 * The other cpu may skip idle and boot back
 			 * up again
@@ -220,7 +223,11 @@ static int exynos_cpu0_enter_aftr(void)
 			 * boot back up again, getting stuck in the
 			 * 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;
 
 			cpu_relax();
@@ -232,11 +239,14 @@ static int exynos_cpu0_enter_aftr(void)
 
 abort:
 	if (cpu_online(1)) {
+		unsigned long boot_addr = virt_to_phys(exynos_cpu_resume);
+
 		/*
 		 * Set the boot vector to something non-zero
 		 */
-		__raw_writel(virt_to_phys(exynos_cpu_resume),
-			     cpu_boot_reg_base());
+		ret = exynos_set_boot_addr(1, boot_addr);
+		if (ret)
+			goto fail;
 		dsb();
 
 		/*
@@ -246,22 +256,42 @@ abort:
 		while (exynos_cpu_power_state(1) != S5P_CORE_LOCAL_PWR_EN)
 			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)) {
+			smp_rmb();
+
 			/*
 			 * 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;
 }
 
 static int exynos_wfi_finisher(unsigned long flags)
 {
+	if (soc_is_exynos3250())
+		flush_cache_all();
 	cpu_do_idle();
 
 	return -1;
@@ -282,6 +312,9 @@ static int exynos_cpu1_powerdown(void)
 	 */
 	exynos_cpu_power_down(1);
 
+	if (soc_is_exynos3250())
+		pmu_raw_writel(0, S5P_PMU_SPARE2);
+
 	ret = cpu_suspend(0, exynos_wfi_finisher);
 
 	cpu_pm_exit();
@@ -298,7 +331,9 @@ cpu1_aborted:
 
 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)
diff --git a/drivers/cpuidle/cpuidle-exynos.c b/drivers/cpuidle/cpuidle-exynos.c
index 26f5f29..bbd7682 100644
--- a/drivers/cpuidle/cpuidle-exynos.c
+++ b/drivers/cpuidle/cpuidle-exynos.c
@@ -117,7 +117,8 @@ static int exynos_cpuidle_probe(struct platform_device *pdev)
 {
 	int ret;
 
-	if (of_machine_is_compatible("samsung,exynos4210")) {
+	if (of_machine_is_compatible("samsung,exynos4210") ||
+	    of_machine_is_compatible("samsung,exynos3250")) {
 		exynos_cpuidle_pdata = pdev->dev.platform_data;
 
 		ret = cpuidle_register(&exynos_coupled_idle_driver,
-- 
1.8.2.3

--
To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux SoC Development]     [Linux Rockchip Development]     [Linux USB Development]     [Video for Linux]     [Linux Audio Users]     [Linux SCSI]     [Yosemite News]

  Powered by Linux