diff options
author | Abhilash Kesavan <a.kesavan@samsung.com> | 2014-11-06 19:20:16 -0500 |
---|---|---|
committer | Kukjin Kim <kgene.kim@samsung.com> | 2014-11-21 08:49:46 -0500 |
commit | adc548d77c22daa371d5217b382a139b593dec47 (patch) | |
tree | e8bf2f7eb460e4126053db2f4469aba631bc8ad6 | |
parent | 0fdf088fd84d87d6fc5d3961cc86feb8bd295398 (diff) |
ARM: EXYNOS: Use MCPM call-backs to support S2R on exynos5420
Use the MCPM layer to handle core suspend/resume on Exynos5420.
Also, restore the entry address setup code post-resume.
Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com>
Acked-by: Nicolas Pitre <nico@linaro.org>
Reviewed-by: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
Tested-by: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
-rw-r--r-- | arch/arm/mach-exynos/mcpm-exynos.c | 32 | ||||
-rw-r--r-- | arch/arm/mach-exynos/platsmp.c | 12 | ||||
-rw-r--r-- | arch/arm/mach-exynos/suspend.c | 49 |
3 files changed, 78 insertions, 15 deletions
diff --git a/arch/arm/mach-exynos/mcpm-exynos.c b/arch/arm/mach-exynos/mcpm-exynos.c index dc9a764a7c37..b0d3c2e876fb 100644 --- a/arch/arm/mach-exynos/mcpm-exynos.c +++ b/arch/arm/mach-exynos/mcpm-exynos.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #include <linux/delay.h> | 15 | #include <linux/delay.h> |
16 | #include <linux/io.h> | 16 | #include <linux/io.h> |
17 | #include <linux/of_address.h> | 17 | #include <linux/of_address.h> |
18 | #include <linux/syscore_ops.h> | ||
18 | 19 | ||
19 | #include <asm/cputype.h> | 20 | #include <asm/cputype.h> |
20 | #include <asm/cp15.h> | 21 | #include <asm/cp15.h> |
@@ -30,6 +31,8 @@ | |||
30 | #define EXYNOS5420_USE_ARM_CORE_DOWN_STATE BIT(29) | 31 | #define EXYNOS5420_USE_ARM_CORE_DOWN_STATE BIT(29) |
31 | #define EXYNOS5420_USE_L2_COMMON_UP_STATE BIT(30) | 32 | #define EXYNOS5420_USE_L2_COMMON_UP_STATE BIT(30) |
32 | 33 | ||
34 | static void __iomem *ns_sram_base_addr; | ||
35 | |||
33 | /* | 36 | /* |
34 | * The common v7_exit_coherency_flush API could not be used because of the | 37 | * The common v7_exit_coherency_flush API could not be used because of the |
35 | * Erratum 799270 workaround. This macro is the same as the common one (in | 38 | * Erratum 799270 workaround. This macro is the same as the common one (in |
@@ -318,10 +321,26 @@ static const struct of_device_id exynos_dt_mcpm_match[] = { | |||
318 | {}, | 321 | {}, |
319 | }; | 322 | }; |
320 | 323 | ||
324 | static void exynos_mcpm_setup_entry_point(void) | ||
325 | { | ||
326 | /* | ||
327 | * U-Boot SPL is hardcoded to jump to the start of ns_sram_base_addr | ||
328 | * as part of secondary_cpu_start(). Let's redirect it to the | ||
329 | * mcpm_entry_point(). This is done during both secondary boot-up as | ||
330 | * well as system resume. | ||
331 | */ | ||
332 | __raw_writel(0xe59f0000, ns_sram_base_addr); /* ldr r0, [pc, #0] */ | ||
333 | __raw_writel(0xe12fff10, ns_sram_base_addr + 4); /* bx r0 */ | ||
334 | __raw_writel(virt_to_phys(mcpm_entry_point), ns_sram_base_addr + 8); | ||
335 | } | ||
336 | |||
337 | static struct syscore_ops exynos_mcpm_syscore_ops = { | ||
338 | .resume = exynos_mcpm_setup_entry_point, | ||
339 | }; | ||
340 | |||
321 | static int __init exynos_mcpm_init(void) | 341 | static int __init exynos_mcpm_init(void) |
322 | { | 342 | { |
323 | struct device_node *node; | 343 | struct device_node *node; |
324 | void __iomem *ns_sram_base_addr; | ||
325 | unsigned int value, i; | 344 | unsigned int value, i; |
326 | int ret; | 345 | int ret; |
327 | 346 | ||
@@ -387,16 +406,9 @@ static int __init exynos_mcpm_init(void) | |||
387 | pmu_raw_writel(value, EXYNOS_COMMON_OPTION(i)); | 406 | pmu_raw_writel(value, EXYNOS_COMMON_OPTION(i)); |
388 | } | 407 | } |
389 | 408 | ||
390 | /* | 409 | exynos_mcpm_setup_entry_point(); |
391 | * U-Boot SPL is hardcoded to jump to the start of ns_sram_base_addr | ||
392 | * as part of secondary_cpu_start(). Let's redirect it to the | ||
393 | * mcpm_entry_point(). | ||
394 | */ | ||
395 | __raw_writel(0xe59f0000, ns_sram_base_addr); /* ldr r0, [pc, #0] */ | ||
396 | __raw_writel(0xe12fff10, ns_sram_base_addr + 4); /* bx r0 */ | ||
397 | __raw_writel(virt_to_phys(mcpm_entry_point), ns_sram_base_addr + 8); | ||
398 | 410 | ||
399 | iounmap(ns_sram_base_addr); | 411 | register_syscore_ops(&exynos_mcpm_syscore_ops); |
400 | 412 | ||
401 | return ret; | 413 | return ret; |
402 | } | 414 | } |
diff --git a/arch/arm/mach-exynos/platsmp.c b/arch/arm/mach-exynos/platsmp.c index adb36a8e8785..7a1ebfeeeeb8 100644 --- a/arch/arm/mach-exynos/platsmp.c +++ b/arch/arm/mach-exynos/platsmp.c | |||
@@ -126,6 +126,18 @@ static inline void platform_do_lowpower(unsigned int cpu, int *spurious) | |||
126 | */ | 126 | */ |
127 | void exynos_cpu_power_down(int cpu) | 127 | void exynos_cpu_power_down(int cpu) |
128 | { | 128 | { |
129 | if (cpu == 0 && (of_machine_is_compatible("samsung,exynos5420") || | ||
130 | of_machine_is_compatible("samsung,exynos5800"))) { | ||
131 | /* | ||
132 | * Bypass power down for CPU0 during suspend. Check for | ||
133 | * the SYS_PWR_REG value to decide if we are suspending | ||
134 | * the system. | ||
135 | */ | ||
136 | int val = pmu_raw_readl(EXYNOS5_ARM_CORE0_SYS_PWR_REG); | ||
137 | |||
138 | if (!(val & S5P_CORE_LOCAL_PWR_EN)) | ||
139 | return; | ||
140 | } | ||
129 | pmu_raw_writel(0, EXYNOS_ARM_CORE_CONFIGURATION(cpu)); | 141 | pmu_raw_writel(0, EXYNOS_ARM_CORE_CONFIGURATION(cpu)); |
130 | } | 142 | } |
131 | 143 | ||
diff --git a/arch/arm/mach-exynos/suspend.c b/arch/arm/mach-exynos/suspend.c index 8cef6141c408..cc8d2374f1b1 100644 --- a/arch/arm/mach-exynos/suspend.c +++ b/arch/arm/mach-exynos/suspend.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <asm/cacheflush.h> | 24 | #include <asm/cacheflush.h> |
25 | #include <asm/hardware/cache-l2x0.h> | 25 | #include <asm/hardware/cache-l2x0.h> |
26 | #include <asm/firmware.h> | 26 | #include <asm/firmware.h> |
27 | #include <asm/mcpm.h> | ||
27 | #include <asm/smp_scu.h> | 28 | #include <asm/smp_scu.h> |
28 | #include <asm/suspend.h> | 29 | #include <asm/suspend.h> |
29 | 30 | ||
@@ -72,6 +73,7 @@ struct exynos_pm_data { | |||
72 | unsigned int *release_ret_regs; | 73 | unsigned int *release_ret_regs; |
73 | 74 | ||
74 | void (*pm_prepare)(void); | 75 | void (*pm_prepare)(void); |
76 | void (*pm_resume_prepare)(void); | ||
75 | void (*pm_resume)(void); | 77 | void (*pm_resume)(void); |
76 | int (*pm_suspend)(void); | 78 | int (*pm_suspend)(void); |
77 | int (*cpu_suspend)(unsigned long); | 79 | int (*cpu_suspend)(unsigned long); |
@@ -172,9 +174,28 @@ static int exynos_cpu_suspend(unsigned long arg) | |||
172 | 174 | ||
173 | static int exynos5420_cpu_suspend(unsigned long arg) | 175 | static int exynos5420_cpu_suspend(unsigned long arg) |
174 | { | 176 | { |
175 | exynos_flush_cache_all(); | 177 | /* MCPM works with HW CPU identifiers */ |
178 | unsigned int mpidr = read_cpuid_mpidr(); | ||
179 | unsigned int cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); | ||
180 | unsigned int cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); | ||
181 | |||
176 | __raw_writel(0x0, sysram_base_addr + EXYNOS5420_CPU_STATE); | 182 | __raw_writel(0x0, sysram_base_addr + EXYNOS5420_CPU_STATE); |
177 | return exynos_cpu_do_idle(); | 183 | |
184 | if (IS_ENABLED(CONFIG_EXYNOS5420_MCPM)) { | ||
185 | mcpm_set_entry_vector(cpu, cluster, exynos_cpu_resume); | ||
186 | |||
187 | /* | ||
188 | * Residency value passed to mcpm_cpu_suspend back-end | ||
189 | * has to be given clear semantics. Set to 0 as a | ||
190 | * temporary value. | ||
191 | */ | ||
192 | mcpm_cpu_suspend(0); | ||
193 | } | ||
194 | |||
195 | pr_info("Failed to suspend the system\n"); | ||
196 | |||
197 | /* return value != 0 means failure */ | ||
198 | return 1; | ||
178 | } | 199 | } |
179 | 200 | ||
180 | static void exynos_pm_set_wakeup_mask(void) | 201 | static void exynos_pm_set_wakeup_mask(void) |
@@ -189,9 +210,6 @@ static void exynos_pm_enter_sleep_mode(void) | |||
189 | /* Set value of power down register for sleep mode */ | 210 | /* Set value of power down register for sleep mode */ |
190 | exynos_sys_powerdown_conf(SYS_SLEEP); | 211 | exynos_sys_powerdown_conf(SYS_SLEEP); |
191 | pmu_raw_writel(S5P_CHECK_SLEEP, S5P_INFORM1); | 212 | pmu_raw_writel(S5P_CHECK_SLEEP, S5P_INFORM1); |
192 | |||
193 | /* ensure at least INFORM0 has the resume address */ | ||
194 | pmu_raw_writel(virt_to_phys(exynos_cpu_resume), S5P_INFORM0); | ||
195 | } | 213 | } |
196 | 214 | ||
197 | static void exynos_pm_prepare(void) | 215 | static void exynos_pm_prepare(void) |
@@ -206,6 +224,9 @@ static void exynos_pm_prepare(void) | |||
206 | pm_data->num_extra_save); | 224 | pm_data->num_extra_save); |
207 | 225 | ||
208 | exynos_pm_enter_sleep_mode(); | 226 | exynos_pm_enter_sleep_mode(); |
227 | |||
228 | /* ensure at least INFORM0 has the resume address */ | ||
229 | pmu_raw_writel(virt_to_phys(exynos_cpu_resume), S5P_INFORM0); | ||
209 | } | 230 | } |
210 | 231 | ||
211 | static void exynos5420_pm_prepare(void) | 232 | static void exynos5420_pm_prepare(void) |
@@ -230,6 +251,10 @@ static void exynos5420_pm_prepare(void) | |||
230 | 251 | ||
231 | exynos_pm_enter_sleep_mode(); | 252 | exynos_pm_enter_sleep_mode(); |
232 | 253 | ||
254 | /* ensure at least INFORM0 has the resume address */ | ||
255 | if (IS_ENABLED(CONFIG_EXYNOS5420_MCPM)) | ||
256 | pmu_raw_writel(virt_to_phys(mcpm_entry_point), S5P_INFORM0); | ||
257 | |||
233 | tmp = pmu_raw_readl(EXYNOS5_ARM_L2_OPTION); | 258 | tmp = pmu_raw_readl(EXYNOS5_ARM_L2_OPTION); |
234 | tmp &= ~EXYNOS5_USE_RETENTION; | 259 | tmp &= ~EXYNOS5_USE_RETENTION; |
235 | pmu_raw_writel(tmp, EXYNOS5_ARM_L2_OPTION); | 260 | pmu_raw_writel(tmp, EXYNOS5_ARM_L2_OPTION); |
@@ -318,10 +343,21 @@ early_wakeup: | |||
318 | pmu_raw_writel(0x0, S5P_INFORM1); | 343 | pmu_raw_writel(0x0, S5P_INFORM1); |
319 | } | 344 | } |
320 | 345 | ||
346 | static void exynos5420_prepare_pm_resume(void) | ||
347 | { | ||
348 | if (IS_ENABLED(CONFIG_EXYNOS5420_MCPM)) | ||
349 | WARN_ON(mcpm_cpu_powered_up()); | ||
350 | } | ||
351 | |||
321 | static void exynos5420_pm_resume(void) | 352 | static void exynos5420_pm_resume(void) |
322 | { | 353 | { |
323 | unsigned long tmp; | 354 | unsigned long tmp; |
324 | 355 | ||
356 | /* Restore the CPU0 low power state register */ | ||
357 | tmp = pmu_raw_readl(EXYNOS5_ARM_CORE0_SYS_PWR_REG); | ||
358 | pmu_raw_writel(tmp | S5P_CORE_LOCAL_PWR_EN, | ||
359 | EXYNOS5_ARM_CORE0_SYS_PWR_REG); | ||
360 | |||
325 | /* Restore the sysram cpu state register */ | 361 | /* Restore the sysram cpu state register */ |
326 | __raw_writel(exynos5420_cpu_state, | 362 | __raw_writel(exynos5420_cpu_state, |
327 | sysram_base_addr + EXYNOS5420_CPU_STATE); | 363 | sysram_base_addr + EXYNOS5420_CPU_STATE); |
@@ -391,6 +427,8 @@ static int exynos_suspend_enter(suspend_state_t state) | |||
391 | if (ret) | 427 | if (ret) |
392 | return ret; | 428 | return ret; |
393 | 429 | ||
430 | if (pm_data->pm_resume_prepare) | ||
431 | pm_data->pm_resume_prepare(); | ||
394 | s3c_pm_restore_uarts(); | 432 | s3c_pm_restore_uarts(); |
395 | 433 | ||
396 | S3C_PMDBG("%s: wakeup stat: %08x\n", __func__, | 434 | S3C_PMDBG("%s: wakeup stat: %08x\n", __func__, |
@@ -448,6 +486,7 @@ static struct exynos_pm_data exynos5420_pm_data = { | |||
448 | .wkup_irq = exynos5250_wkup_irq, | 486 | .wkup_irq = exynos5250_wkup_irq, |
449 | .wake_disable_mask = (0x7F << 7) | (0x1F << 1), | 487 | .wake_disable_mask = (0x7F << 7) | (0x1F << 1), |
450 | .release_ret_regs = exynos5420_release_ret_regs, | 488 | .release_ret_regs = exynos5420_release_ret_regs, |
489 | .pm_resume_prepare = exynos5420_prepare_pm_resume, | ||
451 | .pm_resume = exynos5420_pm_resume, | 490 | .pm_resume = exynos5420_pm_resume, |
452 | .pm_suspend = exynos5420_pm_suspend, | 491 | .pm_suspend = exynos5420_pm_suspend, |
453 | .pm_prepare = exynos5420_pm_prepare, | 492 | .pm_prepare = exynos5420_pm_prepare, |