]>
Commit | Line | Data |
---|---|---|
c9347101 | 1 | /* |
0d713cf1 | 2 | * Copyright (c) 2011-2014 Samsung Electronics Co., Ltd. |
1663895c JL |
3 | * http://www.samsung.com |
4 | * | |
c9347101 | 5 | * EXYNOS - Power Management support |
1663895c JL |
6 | * |
7 | * Based on arch/arm/mach-s3c2410/pm.c | |
8 | * Copyright (c) 2006 Simtec Electronics | |
9 | * Ben Dooks <ben@simtec.co.uk> | |
10 | * | |
11 | * This program is free software; you can redistribute it and/or modify | |
12 | * it under the terms of the GNU General Public License version 2 as | |
13 | * published by the Free Software Foundation. | |
14 | */ | |
15 | ||
16 | #include <linux/init.h> | |
17 | #include <linux/suspend.h> | |
85f9f908 | 18 | #include <linux/cpu_pm.h> |
1663895c | 19 | #include <linux/io.h> |
56c03d91 | 20 | #include <linux/err.h> |
1663895c | 21 | |
2b9d9c32 | 22 | #include <asm/firmware.h> |
63b870f1 | 23 | #include <asm/smp_scu.h> |
d710aa31 | 24 | #include <asm/suspend.h> |
1663895c | 25 | |
d710aa31 | 26 | #include <plat/pm-common.h> |
ccd458c1 KK |
27 | |
28 | #include "common.h" | |
6b7bfd82 | 29 | #include "exynos-pmu.h" |
65c9a853 | 30 | #include "regs-pmu.h" |
318fd20b | 31 | #include "regs-sys.h" |
1663895c | 32 | |
134abc29 BZ |
33 | static inline void __iomem *exynos_boot_vector_addr(void) |
34 | { | |
35 | if (samsung_rev() == EXYNOS4210_REV_1_1) | |
36 | return pmu_base_addr + S5P_INFORM7; | |
37 | else if (samsung_rev() == EXYNOS4210_REV_1_0) | |
38 | return sysram_base_addr + 0x24; | |
39 | return pmu_base_addr + S5P_INFORM0; | |
40 | } | |
41 | ||
42 | static inline void __iomem *exynos_boot_vector_flag(void) | |
43 | { | |
44 | if (samsung_rev() == EXYNOS4210_REV_1_1) | |
45 | return pmu_base_addr + S5P_INFORM6; | |
46 | else if (samsung_rev() == EXYNOS4210_REV_1_0) | |
47 | return sysram_base_addr + 0x20; | |
48 | return pmu_base_addr + S5P_INFORM1; | |
49 | } | |
3681bafe | 50 | |
e30b154b | 51 | #define S5P_CHECK_AFTR 0xFCBA0D10 |
3681bafe | 52 | |
f4ba4b01 JL |
53 | /* For Cortex-A9 Diagnostic and Power control register */ |
54 | static unsigned int save_arm_register[2]; | |
55 | ||
0d713cf1 | 56 | void exynos_cpu_save_register(void) |
309e08c4 DL |
57 | { |
58 | unsigned long tmp; | |
59 | ||
60 | /* Save Power control register */ | |
61 | asm ("mrc p15, 0, %0, c15, c0, 0" | |
62 | : "=r" (tmp) : : "cc"); | |
63 | ||
64 | save_arm_register[0] = tmp; | |
65 | ||
66 | /* Save Diagnostic register */ | |
67 | asm ("mrc p15, 0, %0, c15, c0, 1" | |
68 | : "=r" (tmp) : : "cc"); | |
69 | ||
70 | save_arm_register[1] = tmp; | |
71 | } | |
72 | ||
0d713cf1 | 73 | void exynos_cpu_restore_register(void) |
309e08c4 DL |
74 | { |
75 | unsigned long tmp; | |
76 | ||
77 | /* Restore Power control register */ | |
78 | tmp = save_arm_register[0]; | |
79 | ||
80 | asm volatile ("mcr p15, 0, %0, c15, c0, 0" | |
81 | : : "r" (tmp) | |
82 | : "cc"); | |
83 | ||
84 | /* Restore Diagnostic register */ | |
85 | tmp = save_arm_register[1]; | |
86 | ||
87 | asm volatile ("mcr p15, 0, %0, c15, c0, 1" | |
88 | : : "r" (tmp) | |
89 | : "cc"); | |
90 | } | |
91 | ||
0d713cf1 | 92 | void exynos_pm_central_suspend(void) |
01601b34 TF |
93 | { |
94 | unsigned long tmp; | |
95 | ||
96 | /* Setting Central Sequence Register for power down mode */ | |
97 | tmp = pmu_raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION); | |
98 | tmp &= ~S5P_CENTRAL_LOWPWR_CFG; | |
99 | pmu_raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION); | |
c2dd114d BZ |
100 | |
101 | /* Setting SEQ_OPTION register */ | |
102 | pmu_raw_writel(S5P_USE_STANDBY_WFI0 | S5P_USE_STANDBY_WFE0, | |
103 | S5P_CENTRAL_SEQ_OPTION); | |
01601b34 TF |
104 | } |
105 | ||
0d713cf1 | 106 | int exynos_pm_central_resume(void) |
01601b34 TF |
107 | { |
108 | unsigned long tmp; | |
109 | ||
110 | /* | |
111 | * If PMU failed while entering sleep mode, WFI will be | |
112 | * ignored by PMU and then exiting cpu_do_idle(). | |
113 | * S5P_CENTRAL_LOWPWR_CFG bit will not be set automatically | |
114 | * in this situation. | |
115 | */ | |
116 | tmp = pmu_raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION); | |
117 | if (!(tmp & S5P_CENTRAL_LOWPWR_CFG)) { | |
118 | tmp |= S5P_CENTRAL_LOWPWR_CFG; | |
119 | pmu_raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION); | |
120 | /* clear the wakeup state register */ | |
121 | pmu_raw_writel(0x0, S5P_WAKEUP_STAT); | |
122 | /* No need to perform below restore code */ | |
123 | return -1; | |
124 | } | |
125 | ||
126 | return 0; | |
127 | } | |
128 | ||
129 | /* Ext-GIC nIRQ/nFIQ is the only wakeup source in AFTR */ | |
130 | static void exynos_set_wakeupmask(long mask) | |
131 | { | |
132 | pmu_raw_writel(mask, S5P_WAKEUP_MASK); | |
133 | } | |
134 | ||
135 | static void exynos_cpu_set_boot_vector(long flags) | |
136 | { | |
134abc29 BZ |
137 | __raw_writel(virt_to_phys(exynos_cpu_resume), |
138 | exynos_boot_vector_addr()); | |
139 | __raw_writel(flags, exynos_boot_vector_flag()); | |
01601b34 TF |
140 | } |
141 | ||
142 | static int exynos_aftr_finisher(unsigned long flags) | |
143 | { | |
a135e201 BZ |
144 | int ret; |
145 | ||
01601b34 | 146 | exynos_set_wakeupmask(0x0000ff3e); |
01601b34 TF |
147 | /* Set value of power down register for aftr mode */ |
148 | exynos_sys_powerdown_conf(SYS_AFTR); | |
a135e201 BZ |
149 | |
150 | ret = call_firmware_op(do_idle, FW_DO_IDLE_AFTR); | |
151 | if (ret == -ENOSYS) { | |
152 | if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) | |
153 | exynos_cpu_save_register(); | |
154 | exynos_cpu_set_boot_vector(S5P_CHECK_AFTR); | |
155 | cpu_do_idle(); | |
156 | } | |
01601b34 TF |
157 | |
158 | return 1; | |
159 | } | |
160 | ||
161 | void exynos_enter_aftr(void) | |
162 | { | |
163 | cpu_pm_enter(); | |
164 | ||
165 | exynos_pm_central_suspend(); | |
01601b34 TF |
166 | |
167 | cpu_suspend(0, exynos_aftr_finisher); | |
168 | ||
169 | if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) { | |
170 | scu_enable(S5P_VA_SCU); | |
a135e201 BZ |
171 | if (call_firmware_op(resume) == -ENOSYS) |
172 | exynos_cpu_restore_register(); | |
01601b34 TF |
173 | } |
174 | ||
175 | exynos_pm_central_resume(); | |
176 | ||
177 | cpu_pm_exit(); | |
178 | } |