]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
ARM: EXYNOS: add coupled cpuidle support for Exynos3250
authorBartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
Wed, 18 Mar 2015 13:09:57 +0000 (14:09 +0100)
committerKukjin Kim <kgene@kernel.org>
Fri, 5 Jun 2015 17:17:57 +0000 (02:17 +0900)
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>
arch/arm/mach-exynos/common.h
arch/arm/mach-exynos/exynos.c
arch/arm/mach-exynos/firmware.c
arch/arm/mach-exynos/platsmp.c
arch/arm/mach-exynos/pm.c

index 5f5cd562c593ef5f6a9224254c146e7d3d0136a3..e3a9256ed55fecc49e65aea229e74f82e933a91f 100644 (file)
@@ -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)
 {
index 5917a30eee33f286c80708dfb84cdd610bd2dd9d..4bd8b76538175aa2ba4ab3f58a127b94bda08939 100644 (file)
@@ -234,7 +234,8 @@ static void __init exynos_dt_machine_init(void)
                exynos_sysram_init();
 
 #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;
 #endif
        if (of_machine_is_compatible("samsung,exynos4210") ||
index b30562daf6961575d7f017e0cd586999e5b32156..245f6dec1ded11a126b9386203eed4e9572a0157 100644 (file)
@@ -49,6 +49,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,
index 449edd1121e4d85a15b5e5e8ff40804407b480f5..58e05a2eae5737c1a558c3658e692184b064da34 100644 (file)
@@ -169,7 +169,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;
@@ -195,7 +195,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;
 
@@ -247,7 +247,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;
 
@@ -272,7 +272,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;
 
index cc75ab448be3b4ce9b3c0fc2e626da4ed3d6e6ca..9c1506b499bca6f4599a20ece67494078a95d4d0 100644 (file)
@@ -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>
 
@@ -209,6 +210,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
@@ -221,7 +224,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();
@@ -233,11 +240,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();
 
                /*
@@ -247,22 +257,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;
@@ -283,6 +313,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();
@@ -299,7 +332,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)