aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm
diff options
context:
space:
mode:
authorTomasz Figa <t.figa@samsung.com>2014-08-05 08:43:10 -0400
committerOlof Johansson <olof@lixom.net>2014-08-09 11:24:36 -0400
commit01601b349582caa617618b5fa7d9b08bd328626a (patch)
tree6d14f91842c5e9a49f257b38be94d5dbb647f7b5 /arch/arm
parentbb7aedff3f98c5b842f787f70d99370da197f76b (diff)
ARM: EXYNOS: Fix suspend/resume sequences
Due to recent consolidation of Exynos suspend and cpuidle code, some parts of suspend and resume sequences are executed two times, once from exynos_pm_syscore_ops and then from exynos_cpu_pm_notifier() and thus it breaks suspend, at least on Exynos4-based boards. In addition, simple core power down from a cpuidle driver could, in case of CPU 0 could result in calling functions that are specific to suspend and deeper idle states. This patch fixes the issue by moving those operations outside the CPU PM notifier into suspend and AFTR code paths. This leads to a bit of code duplication, but allows additional code simplification, so in the end more code is removed than added. Fixes: 85f9f90808b4 ("ARM: EXYNOS: Use the cpu_pm notifier for pm") Cc: Kukjin Kim <kgene.kim@samsung.com> Cc: Arnd Bergmann <arnd@arndb.de> Cc: Olof Johansson <olof@lixom.net> Cc: arm@kernel.org Signed-off-by: Tomasz Figa <t.figa@samsung.com> [b.zolnierkie: ported patch over current changes] [b.zolnierkie: fixed exynos_aftr_finisher() return value] Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> Signed-off-by: Olof Johansson <olof@lixom.net>
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/mach-exynos/pm.c163
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 */
118static void exynos_set_wakeupmask(long mask)
119{
120 pmu_raw_writel(mask, S5P_WAKEUP_MASK);
121}
122
123static 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
129void 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 */
138static unsigned int save_arm_register[2]; 118static 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
156static 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
166static 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 */
190static void exynos_set_wakeupmask(long mask)
191{
192 pmu_raw_writel(mask, S5P_WAKEUP_MASK);
193}
194
195static 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
201static 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
212void 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
176static int exynos_cpu_suspend(unsigned long arg) 232static 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
220static 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
230static int exynos_pm_suspend(void) 276static 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
247static 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
270static void exynos_pm_resume(void) 293static 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
372static 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
400static struct notifier_block exynos_cpu_pm_notifier_block = {
401 .notifier_call = exynos_cpu_pm_notifier,
402};
403
404void __init exynos_pm_init(void) 395void __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