diff options
Diffstat (limited to 'arch/arm')
-rw-r--r-- | arch/arm/mach-exynos/pm.c | 163 |
1 files changed, 76 insertions, 87 deletions
diff --git a/arch/arm/mach-exynos/pm.c b/arch/arm/mach-exynos/pm.c index 18646b7e226b..abefacb45976 100644 --- a/arch/arm/mach-exynos/pm.c +++ b/arch/arm/mach-exynos/pm.c | |||
@@ -114,26 +114,6 @@ static int exynos_irq_set_wake(struct irq_data *data, unsigned int state) | |||
114 | #define S5P_CHECK_AFTR 0xFCBA0D10 | 114 | #define S5P_CHECK_AFTR 0xFCBA0D10 |
115 | #define S5P_CHECK_SLEEP 0x00000BAD | 115 | #define S5P_CHECK_SLEEP 0x00000BAD |
116 | 116 | ||
117 | /* Ext-GIC nIRQ/nFIQ is the only wakeup source in AFTR */ | ||
118 | static void exynos_set_wakeupmask(long mask) | ||
119 | { | ||
120 | pmu_raw_writel(mask, S5P_WAKEUP_MASK); | ||
121 | } | ||
122 | |||
123 | static void exynos_cpu_set_boot_vector(long flags) | ||
124 | { | ||
125 | __raw_writel(virt_to_phys(exynos_cpu_resume), EXYNOS_BOOT_VECTOR_ADDR); | ||
126 | __raw_writel(flags, EXYNOS_BOOT_VECTOR_FLAG); | ||
127 | } | ||
128 | |||
129 | void exynos_enter_aftr(void) | ||
130 | { | ||
131 | exynos_set_wakeupmask(0x0000ff3e); | ||
132 | exynos_cpu_set_boot_vector(S5P_CHECK_AFTR); | ||
133 | /* Set value of power down register for aftr mode */ | ||
134 | exynos_sys_powerdown_conf(SYS_AFTR); | ||
135 | } | ||
136 | |||
137 | /* For Cortex-A9 Diagnostic and Power control register */ | 117 | /* For Cortex-A9 Diagnostic and Power control register */ |
138 | static unsigned int save_arm_register[2]; | 118 | static unsigned int save_arm_register[2]; |
139 | 119 | ||
@@ -173,6 +153,82 @@ static void exynos_cpu_restore_register(void) | |||
173 | : "cc"); | 153 | : "cc"); |
174 | } | 154 | } |
175 | 155 | ||
156 | static void exynos_pm_central_suspend(void) | ||
157 | { | ||
158 | unsigned long tmp; | ||
159 | |||
160 | /* Setting Central Sequence Register for power down mode */ | ||
161 | tmp = pmu_raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION); | ||
162 | tmp &= ~S5P_CENTRAL_LOWPWR_CFG; | ||
163 | pmu_raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION); | ||
164 | } | ||
165 | |||
166 | static int exynos_pm_central_resume(void) | ||
167 | { | ||
168 | unsigned long tmp; | ||
169 | |||
170 | /* | ||
171 | * If PMU failed while entering sleep mode, WFI will be | ||
172 | * ignored by PMU and then exiting cpu_do_idle(). | ||
173 | * S5P_CENTRAL_LOWPWR_CFG bit will not be set automatically | ||
174 | * in this situation. | ||
175 | */ | ||
176 | tmp = pmu_raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION); | ||
177 | if (!(tmp & S5P_CENTRAL_LOWPWR_CFG)) { | ||
178 | tmp |= S5P_CENTRAL_LOWPWR_CFG; | ||
179 | pmu_raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION); | ||
180 | /* clear the wakeup state register */ | ||
181 | pmu_raw_writel(0x0, S5P_WAKEUP_STAT); | ||
182 | /* No need to perform below restore code */ | ||
183 | return -1; | ||
184 | } | ||
185 | |||
186 | return 0; | ||
187 | } | ||
188 | |||
189 | /* Ext-GIC nIRQ/nFIQ is the only wakeup source in AFTR */ | ||
190 | static void exynos_set_wakeupmask(long mask) | ||
191 | { | ||
192 | pmu_raw_writel(mask, S5P_WAKEUP_MASK); | ||
193 | } | ||
194 | |||
195 | static void exynos_cpu_set_boot_vector(long flags) | ||
196 | { | ||
197 | __raw_writel(virt_to_phys(exynos_cpu_resume), EXYNOS_BOOT_VECTOR_ADDR); | ||
198 | __raw_writel(flags, EXYNOS_BOOT_VECTOR_FLAG); | ||
199 | } | ||
200 | |||
201 | static int exynos_aftr_finisher(unsigned long flags) | ||
202 | { | ||
203 | exynos_set_wakeupmask(0x0000ff3e); | ||
204 | exynos_cpu_set_boot_vector(S5P_CHECK_AFTR); | ||
205 | /* Set value of power down register for aftr mode */ | ||
206 | exynos_sys_powerdown_conf(SYS_AFTR); | ||
207 | cpu_do_idle(); | ||
208 | |||
209 | return 1; | ||
210 | } | ||
211 | |||
212 | void exynos_enter_aftr(void) | ||
213 | { | ||
214 | cpu_pm_enter(); | ||
215 | |||
216 | exynos_pm_central_suspend(); | ||
217 | if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) | ||
218 | exynos_cpu_save_register(); | ||
219 | |||
220 | cpu_suspend(0, exynos_aftr_finisher); | ||
221 | |||
222 | if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) { | ||
223 | scu_enable(S5P_VA_SCU); | ||
224 | exynos_cpu_restore_register(); | ||
225 | } | ||
226 | |||
227 | exynos_pm_central_resume(); | ||
228 | |||
229 | cpu_pm_exit(); | ||
230 | } | ||
231 | |||
176 | static int exynos_cpu_suspend(unsigned long arg) | 232 | static int exynos_cpu_suspend(unsigned long arg) |
177 | { | 233 | { |
178 | #ifdef CONFIG_CACHE_L2X0 | 234 | #ifdef CONFIG_CACHE_L2X0 |
@@ -217,16 +273,6 @@ static void exynos_pm_prepare(void) | |||
217 | pmu_raw_writel(virt_to_phys(exynos_cpu_resume), S5P_INFORM0); | 273 | pmu_raw_writel(virt_to_phys(exynos_cpu_resume), S5P_INFORM0); |
218 | } | 274 | } |
219 | 275 | ||
220 | static void exynos_pm_central_suspend(void) | ||
221 | { | ||
222 | unsigned long tmp; | ||
223 | |||
224 | /* Setting Central Sequence Register for power down mode */ | ||
225 | tmp = pmu_raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION); | ||
226 | tmp &= ~S5P_CENTRAL_LOWPWR_CFG; | ||
227 | pmu_raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION); | ||
228 | } | ||
229 | |||
230 | static int exynos_pm_suspend(void) | 276 | static int exynos_pm_suspend(void) |
231 | { | 277 | { |
232 | unsigned long tmp; | 278 | unsigned long tmp; |
@@ -244,29 +290,6 @@ static int exynos_pm_suspend(void) | |||
244 | return 0; | 290 | return 0; |
245 | } | 291 | } |
246 | 292 | ||
247 | static int exynos_pm_central_resume(void) | ||
248 | { | ||
249 | unsigned long tmp; | ||
250 | |||
251 | /* | ||
252 | * If PMU failed while entering sleep mode, WFI will be | ||
253 | * ignored by PMU and then exiting cpu_do_idle(). | ||
254 | * S5P_CENTRAL_LOWPWR_CFG bit will not be set automatically | ||
255 | * in this situation. | ||
256 | */ | ||
257 | tmp = pmu_raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION); | ||
258 | if (!(tmp & S5P_CENTRAL_LOWPWR_CFG)) { | ||
259 | tmp |= S5P_CENTRAL_LOWPWR_CFG; | ||
260 | pmu_raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION); | ||
261 | /* clear the wakeup state register */ | ||
262 | pmu_raw_writel(0x0, S5P_WAKEUP_STAT); | ||
263 | /* No need to perform below restore code */ | ||
264 | return -1; | ||
265 | } | ||
266 | |||
267 | return 0; | ||
268 | } | ||
269 | |||
270 | static void exynos_pm_resume(void) | 293 | static void exynos_pm_resume(void) |
271 | { | 294 | { |
272 | if (exynos_pm_central_resume()) | 295 | if (exynos_pm_central_resume()) |
@@ -369,44 +392,10 @@ static const struct platform_suspend_ops exynos_suspend_ops = { | |||
369 | .valid = suspend_valid_only_mem, | 392 | .valid = suspend_valid_only_mem, |
370 | }; | 393 | }; |
371 | 394 | ||
372 | static int exynos_cpu_pm_notifier(struct notifier_block *self, | ||
373 | unsigned long cmd, void *v) | ||
374 | { | ||
375 | int cpu = smp_processor_id(); | ||
376 | |||
377 | switch (cmd) { | ||
378 | case CPU_PM_ENTER: | ||
379 | if (cpu == 0) { | ||
380 | exynos_pm_central_suspend(); | ||
381 | if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) | ||
382 | exynos_cpu_save_register(); | ||
383 | } | ||
384 | break; | ||
385 | |||
386 | case CPU_PM_EXIT: | ||
387 | if (cpu == 0) { | ||
388 | if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) { | ||
389 | scu_enable(S5P_VA_SCU); | ||
390 | exynos_cpu_restore_register(); | ||
391 | } | ||
392 | exynos_pm_central_resume(); | ||
393 | } | ||
394 | break; | ||
395 | } | ||
396 | |||
397 | return NOTIFY_OK; | ||
398 | } | ||
399 | |||
400 | static struct notifier_block exynos_cpu_pm_notifier_block = { | ||
401 | .notifier_call = exynos_cpu_pm_notifier, | ||
402 | }; | ||
403 | |||
404 | void __init exynos_pm_init(void) | 395 | void __init exynos_pm_init(void) |
405 | { | 396 | { |
406 | u32 tmp; | 397 | u32 tmp; |
407 | 398 | ||
408 | cpu_pm_register_notifier(&exynos_cpu_pm_notifier_block); | ||
409 | |||
410 | /* Platform-specific GIC callback */ | 399 | /* Platform-specific GIC callback */ |
411 | gic_arch_extn.irq_set_wake = exynos_irq_set_wake; | 400 | gic_arch_extn.irq_set_wake = exynos_irq_set_wake; |
412 | 401 | ||