aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-exynos
diff options
context:
space:
mode:
authorTomasz Figa <t.figa@samsung.com>2014-09-23 12:24:39 -0400
committerKukjin Kim <kgene.kim@samsung.com>2014-10-20 11:06:35 -0400
commit2b9d9c321b5900c7ce82110a81cf3827ca9b33c6 (patch)
tree6809b6da0bdbef8b2ddabf52c6dabc5ad6afdd9a /arch/arm/mach-exynos
parent9c261f89a30010a33c15e6b7cfc7c79ae6bea653 (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/Makefile1
-rw-r--r--arch/arm/mach-exynos/common.h4
-rw-r--r--arch/arm/mach-exynos/firmware.c45
-rw-r--r--arch/arm/mach-exynos/pm.c16
-rw-r--r--arch/arm/mach-exynos/sleep.S28
-rw-r--r--arch/arm/mach-exynos/smc.h4
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
22plus_sec := $(call as-instr,.arch_extension sec,+sec) 22plus_sec := $(call as-instr,.arch_extension sec,+sec)
23AFLAGS_exynos-smc.o :=-Wa,-march=armv7-a$(plus_sec) 23AFLAGS_exynos-smc.o :=-Wa,-march=armv7-a$(plus_sec)
24AFLAGS_sleep.o :=-Wa,-march=armv7-a$(plus_sec)
24 25
25obj-$(CONFIG_EXYNOS5420_MCPM) += mcpm-exynos.o 26obj-$(CONFIG_EXYNOS5420_MCPM) += mcpm-exynos.o
26CFLAGS_mcpm-exynos.o += -march=armv7-a 27CFLAGS_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
114extern u32 cp15_save_diag;
115extern u32 cp15_save_power;
116
114extern void __iomem *sysram_ns_base_addr; 117extern void __iomem *sysram_ns_base_addr;
115extern void __iomem *sysram_base_addr; 118extern void __iomem *sysram_base_addr;
116extern void __iomem *pmu_base_addr; 119extern void __iomem *pmu_base_addr;
@@ -127,6 +130,7 @@ static inline void exynos_pm_init(void) {}
127#endif 130#endif
128 131
129extern void exynos_cpu_resume(void); 132extern void exynos_cpu_resume(void);
133extern void exynos_cpu_resume_ns(void);
130 134
131extern struct smp_operations exynos_smp_ops; 135extern 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
24static int exynos_do_idle(void) 31static 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
79static 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
91static 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
108static int exynos_resume(void)
109{
110 writel(0, sysram_ns_base_addr + EXYNOS_BOOT_FLAG);
111
112 return 0;
113}
114
72static const struct firmware_ops exynos_firmware_ops = { 115static 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
78void __init exynos_firmware_init(void) 123void __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
332static void exynos_pm_resume(void) 333static 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
352early_wakeup: 356early_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
57ENDPROC(exynos_cpu_resume) 58ENDPROC(exynos_cpu_resume)
59
60 .align
61
62ENTRY(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
77skip_cp15:
78 b cpu_resume
79ENDPROC(exynos_cpu_resume_ns)
80 .globl cp15_save_diag
81cp15_save_diag:
82 .long 0 @ cp15 diagnostic
83 .globl cp15_save_power
84cp15_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
29extern void exynos_smc(u32 cmd, u32 arg1, u32 arg2, u32 arg3); 31extern void exynos_smc(u32 cmd, u32 arg1, u32 arg2, u32 arg3);
30 32
33#endif /* __ASSEMBLY__ */
34
31#endif 35#endif