diff options
author | Tomasz Figa <t.figa@samsung.com> | 2014-09-23 12:24:39 -0400 |
---|---|---|
committer | Kukjin Kim <kgene.kim@samsung.com> | 2014-10-20 11:06:35 -0400 |
commit | 2b9d9c321b5900c7ce82110a81cf3827ca9b33c6 (patch) | |
tree | 6809b6da0bdbef8b2ddabf52c6dabc5ad6afdd9a /arch/arm/mach-exynos | |
parent | 9c261f89a30010a33c15e6b7cfc7c79ae6bea653 (diff) |
ARM: EXYNOS: Add support for firmware-assisted suspend/resume
On a numer of Exynos-based boards Linux kernel is running in non-secure
mode under a secure firmware. This means that certain operations need to
be handled in special way, with firmware assistance. System-wide
suspend/resume is an example of such operations.
This patch adds support for firmware-assisted suspend/resume by
leveraging recently introduced suspend and resume firmware operations
and modifying existing suspend/resume paths to account for presence of
secure firmware.
Signed-off-by: Tomasz Figa <t.figa@samsung.com>
[kgene.kim@samsung.com: rebased]
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
Diffstat (limited to 'arch/arm/mach-exynos')
-rw-r--r-- | arch/arm/mach-exynos/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/mach-exynos/common.h | 4 | ||||
-rw-r--r-- | arch/arm/mach-exynos/firmware.c | 45 | ||||
-rw-r--r-- | arch/arm/mach-exynos/pm.c | 16 | ||||
-rw-r--r-- | arch/arm/mach-exynos/sleep.S | 28 | ||||
-rw-r--r-- | arch/arm/mach-exynos/smc.h | 4 |
6 files changed, 93 insertions, 5 deletions
diff --git a/arch/arm/mach-exynos/Makefile b/arch/arm/mach-exynos/Makefile index 27ae6144679c..45bef21a3b66 100644 --- a/arch/arm/mach-exynos/Makefile +++ b/arch/arm/mach-exynos/Makefile | |||
@@ -21,6 +21,7 @@ CFLAGS_hotplug.o += -march=armv7-a | |||
21 | 21 | ||
22 | plus_sec := $(call as-instr,.arch_extension sec,+sec) | 22 | plus_sec := $(call as-instr,.arch_extension sec,+sec) |
23 | AFLAGS_exynos-smc.o :=-Wa,-march=armv7-a$(plus_sec) | 23 | AFLAGS_exynos-smc.o :=-Wa,-march=armv7-a$(plus_sec) |
24 | AFLAGS_sleep.o :=-Wa,-march=armv7-a$(plus_sec) | ||
24 | 25 | ||
25 | obj-$(CONFIG_EXYNOS5420_MCPM) += mcpm-exynos.o | 26 | obj-$(CONFIG_EXYNOS5420_MCPM) += mcpm-exynos.o |
26 | CFLAGS_mcpm-exynos.o += -march=armv7-a | 27 | CFLAGS_mcpm-exynos.o += -march=armv7-a |
diff --git a/arch/arm/mach-exynos/common.h b/arch/arm/mach-exynos/common.h index 47b904b3b973..c218200f8544 100644 --- a/arch/arm/mach-exynos/common.h +++ b/arch/arm/mach-exynos/common.h | |||
@@ -111,6 +111,9 @@ IS_SAMSUNG_CPU(exynos5800, EXYNOS5800_SOC_ID, EXYNOS5_SOC_MASK) | |||
111 | #define soc_is_exynos5() (soc_is_exynos5250() || soc_is_exynos5410() || \ | 111 | #define soc_is_exynos5() (soc_is_exynos5250() || soc_is_exynos5410() || \ |
112 | soc_is_exynos5420() || soc_is_exynos5800()) | 112 | soc_is_exynos5420() || soc_is_exynos5800()) |
113 | 113 | ||
114 | extern u32 cp15_save_diag; | ||
115 | extern u32 cp15_save_power; | ||
116 | |||
114 | extern void __iomem *sysram_ns_base_addr; | 117 | extern void __iomem *sysram_ns_base_addr; |
115 | extern void __iomem *sysram_base_addr; | 118 | extern void __iomem *sysram_base_addr; |
116 | extern void __iomem *pmu_base_addr; | 119 | extern void __iomem *pmu_base_addr; |
@@ -127,6 +130,7 @@ static inline void exynos_pm_init(void) {} | |||
127 | #endif | 130 | #endif |
128 | 131 | ||
129 | extern void exynos_cpu_resume(void); | 132 | extern void exynos_cpu_resume(void); |
133 | extern void exynos_cpu_resume_ns(void); | ||
130 | 134 | ||
131 | extern struct smp_operations exynos_smp_ops; | 135 | extern struct smp_operations exynos_smp_ops; |
132 | 136 | ||
diff --git a/arch/arm/mach-exynos/firmware.c b/arch/arm/mach-exynos/firmware.c index e8797bb78871..f5e626d55951 100644 --- a/arch/arm/mach-exynos/firmware.c +++ b/arch/arm/mach-exynos/firmware.c | |||
@@ -14,13 +14,20 @@ | |||
14 | #include <linux/of.h> | 14 | #include <linux/of.h> |
15 | #include <linux/of_address.h> | 15 | #include <linux/of_address.h> |
16 | 16 | ||
17 | #include <asm/cacheflush.h> | ||
18 | #include <asm/cputype.h> | ||
17 | #include <asm/firmware.h> | 19 | #include <asm/firmware.h> |
20 | #include <asm/suspend.h> | ||
18 | 21 | ||
19 | #include <mach/map.h> | 22 | #include <mach/map.h> |
20 | 23 | ||
21 | #include "common.h" | 24 | #include "common.h" |
22 | #include "smc.h" | 25 | #include "smc.h" |
23 | 26 | ||
27 | #define EXYNOS_SLEEP_MAGIC 0x00000bad | ||
28 | #define EXYNOS_BOOT_ADDR 0x8 | ||
29 | #define EXYNOS_BOOT_FLAG 0xc | ||
30 | |||
24 | static int exynos_do_idle(void) | 31 | static int exynos_do_idle(void) |
25 | { | 32 | { |
26 | exynos_smc(SMC_CMD_SLEEP, 0, 0, 0); | 33 | exynos_smc(SMC_CMD_SLEEP, 0, 0, 0); |
@@ -69,10 +76,48 @@ static int exynos_set_cpu_boot_addr(int cpu, unsigned long boot_addr) | |||
69 | return 0; | 76 | return 0; |
70 | } | 77 | } |
71 | 78 | ||
79 | static int exynos_cpu_suspend(unsigned long arg) | ||
80 | { | ||
81 | flush_cache_all(); | ||
82 | outer_flush_all(); | ||
83 | |||
84 | exynos_smc(SMC_CMD_SLEEP, 0, 0, 0); | ||
85 | |||
86 | pr_info("Failed to suspend the system\n"); | ||
87 | writel(0, sysram_ns_base_addr + EXYNOS_BOOT_FLAG); | ||
88 | return 1; | ||
89 | } | ||
90 | |||
91 | static int exynos_suspend(void) | ||
92 | { | ||
93 | if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) { | ||
94 | /* Save Power control and Diagnostic registers */ | ||
95 | asm ("mrc p15, 0, %0, c15, c0, 0\n" | ||
96 | "mrc p15, 0, %1, c15, c0, 1\n" | ||
97 | : "=r" (cp15_save_power), "=r" (cp15_save_diag) | ||
98 | : : "cc"); | ||
99 | } | ||
100 | |||
101 | writel(EXYNOS_SLEEP_MAGIC, sysram_ns_base_addr + EXYNOS_BOOT_FLAG); | ||
102 | writel(virt_to_phys(exynos_cpu_resume_ns), | ||
103 | sysram_ns_base_addr + EXYNOS_BOOT_ADDR); | ||
104 | |||
105 | return cpu_suspend(0, exynos_cpu_suspend); | ||
106 | } | ||
107 | |||
108 | static int exynos_resume(void) | ||
109 | { | ||
110 | writel(0, sysram_ns_base_addr + EXYNOS_BOOT_FLAG); | ||
111 | |||
112 | return 0; | ||
113 | } | ||
114 | |||
72 | static const struct firmware_ops exynos_firmware_ops = { | 115 | static const struct firmware_ops exynos_firmware_ops = { |
73 | .do_idle = exynos_do_idle, | 116 | .do_idle = exynos_do_idle, |
74 | .set_cpu_boot_addr = exynos_set_cpu_boot_addr, | 117 | .set_cpu_boot_addr = exynos_set_cpu_boot_addr, |
75 | .cpu_boot = exynos_cpu_boot, | 118 | .cpu_boot = exynos_cpu_boot, |
119 | .suspend = exynos_suspend, | ||
120 | .resume = exynos_resume, | ||
76 | }; | 121 | }; |
77 | 122 | ||
78 | void __init exynos_firmware_init(void) | 123 | void __init exynos_firmware_init(void) |
diff --git a/arch/arm/mach-exynos/pm.c b/arch/arm/mach-exynos/pm.c index 16b23d156eec..047ac302835d 100644 --- a/arch/arm/mach-exynos/pm.c +++ b/arch/arm/mach-exynos/pm.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <linux/clk.h> | 23 | #include <linux/clk.h> |
24 | 24 | ||
25 | #include <asm/cacheflush.h> | 25 | #include <asm/cacheflush.h> |
26 | #include <asm/firmware.h> | ||
26 | #include <asm/hardware/cache-l2x0.h> | 27 | #include <asm/hardware/cache-l2x0.h> |
27 | #include <asm/smp_scu.h> | 28 | #include <asm/smp_scu.h> |
28 | #include <asm/suspend.h> | 29 | #include <asm/suspend.h> |
@@ -331,12 +332,11 @@ static void exynos_pm_release_retention(void) | |||
331 | 332 | ||
332 | static void exynos_pm_resume(void) | 333 | static void exynos_pm_resume(void) |
333 | { | 334 | { |
335 | u32 cpuid = read_cpuid_part(); | ||
336 | |||
334 | if (exynos_pm_central_resume()) | 337 | if (exynos_pm_central_resume()) |
335 | goto early_wakeup; | 338 | goto early_wakeup; |
336 | 339 | ||
337 | if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) | ||
338 | exynos_cpu_restore_register(); | ||
339 | |||
340 | /* For release retention */ | 340 | /* For release retention */ |
341 | exynos_pm_release_retention(); | 341 | exynos_pm_release_retention(); |
342 | 342 | ||
@@ -346,9 +346,13 @@ static void exynos_pm_resume(void) | |||
346 | 346 | ||
347 | s3c_pm_do_restore_core(exynos_core_save, ARRAY_SIZE(exynos_core_save)); | 347 | s3c_pm_do_restore_core(exynos_core_save, ARRAY_SIZE(exynos_core_save)); |
348 | 348 | ||
349 | if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) | 349 | if (cpuid == ARM_CPU_PART_CORTEX_A9) |
350 | scu_enable(S5P_VA_SCU); | 350 | scu_enable(S5P_VA_SCU); |
351 | 351 | ||
352 | if (call_firmware_op(resume) == -ENOSYS | ||
353 | && cpuid == ARM_CPU_PART_CORTEX_A9) | ||
354 | exynos_cpu_restore_register(); | ||
355 | |||
352 | early_wakeup: | 356 | early_wakeup: |
353 | 357 | ||
354 | /* Clear SLEEP mode set in INFORM1 */ | 358 | /* Clear SLEEP mode set in INFORM1 */ |
@@ -383,7 +387,9 @@ static int exynos_suspend_enter(suspend_state_t state) | |||
383 | flush_cache_all(); | 387 | flush_cache_all(); |
384 | s3c_pm_check_store(); | 388 | s3c_pm_check_store(); |
385 | 389 | ||
386 | ret = cpu_suspend(0, pm_data->cpu_suspend); | 390 | ret = call_firmware_op(suspend); |
391 | if (ret == -ENOSYS) | ||
392 | ret = cpu_suspend(0, pm_data->cpu_suspend); | ||
387 | if (ret) | 393 | if (ret) |
388 | return ret; | 394 | return ret; |
389 | 395 | ||
diff --git a/arch/arm/mach-exynos/sleep.S b/arch/arm/mach-exynos/sleep.S index 108a45f4bb62..e3c373082bbe 100644 --- a/arch/arm/mach-exynos/sleep.S +++ b/arch/arm/mach-exynos/sleep.S | |||
@@ -16,6 +16,7 @@ | |||
16 | */ | 16 | */ |
17 | 17 | ||
18 | #include <linux/linkage.h> | 18 | #include <linux/linkage.h> |
19 | #include "smc.h" | ||
19 | 20 | ||
20 | #define CPU_MASK 0xff0ffff0 | 21 | #define CPU_MASK 0xff0ffff0 |
21 | #define CPU_CORTEX_A9 0x410fc090 | 22 | #define CPU_CORTEX_A9 0x410fc090 |
@@ -55,3 +56,30 @@ ENTRY(exynos_cpu_resume) | |||
55 | #endif | 56 | #endif |
56 | b cpu_resume | 57 | b cpu_resume |
57 | ENDPROC(exynos_cpu_resume) | 58 | ENDPROC(exynos_cpu_resume) |
59 | |||
60 | .align | ||
61 | |||
62 | ENTRY(exynos_cpu_resume_ns) | ||
63 | mrc p15, 0, r0, c0, c0, 0 | ||
64 | ldr r1, =CPU_MASK | ||
65 | and r0, r0, r1 | ||
66 | ldr r1, =CPU_CORTEX_A9 | ||
67 | cmp r0, r1 | ||
68 | bne skip_cp15 | ||
69 | |||
70 | adr r0, cp15_save_power | ||
71 | ldr r1, [r0] | ||
72 | adr r0, cp15_save_diag | ||
73 | ldr r2, [r0] | ||
74 | mov r0, #SMC_CMD_C15RESUME | ||
75 | dsb | ||
76 | smc #0 | ||
77 | skip_cp15: | ||
78 | b cpu_resume | ||
79 | ENDPROC(exynos_cpu_resume_ns) | ||
80 | .globl cp15_save_diag | ||
81 | cp15_save_diag: | ||
82 | .long 0 @ cp15 diagnostic | ||
83 | .globl cp15_save_power | ||
84 | cp15_save_power: | ||
85 | .long 0 @ cp15 power control | ||
diff --git a/arch/arm/mach-exynos/smc.h b/arch/arm/mach-exynos/smc.h index 13a1dc8ecbf2..f7b82f9c1e21 100644 --- a/arch/arm/mach-exynos/smc.h +++ b/arch/arm/mach-exynos/smc.h | |||
@@ -26,6 +26,10 @@ | |||
26 | #define SMC_CMD_L2X0INVALL (-24) | 26 | #define SMC_CMD_L2X0INVALL (-24) |
27 | #define SMC_CMD_L2X0DEBUG (-25) | 27 | #define SMC_CMD_L2X0DEBUG (-25) |
28 | 28 | ||
29 | #ifndef __ASSEMBLY__ | ||
30 | |||
29 | extern void exynos_smc(u32 cmd, u32 arg1, u32 arg2, u32 arg3); | 31 | extern void exynos_smc(u32 cmd, u32 arg1, u32 arg2, u32 arg3); |
30 | 32 | ||
33 | #endif /* __ASSEMBLY__ */ | ||
34 | |||
31 | #endif | 35 | #endif |