aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-exynos/pm.c163
-rw-r--r--drivers/cpuidle/cpuidle-exynos.c25
2 files changed, 80 insertions, 108 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
diff --git a/drivers/cpuidle/cpuidle-exynos.c b/drivers/cpuidle/cpuidle-exynos.c
index 7c0151263828..ba9b34b579f3 100644
--- a/drivers/cpuidle/cpuidle-exynos.c
+++ b/drivers/cpuidle/cpuidle-exynos.c
@@ -20,25 +20,6 @@
20 20
21static void (*exynos_enter_aftr)(void); 21static void (*exynos_enter_aftr)(void);
22 22
23static int idle_finisher(unsigned long flags)
24{
25 exynos_enter_aftr();
26 cpu_do_idle();
27
28 return 1;
29}
30
31static int exynos_enter_core0_aftr(struct cpuidle_device *dev,
32 struct cpuidle_driver *drv,
33 int index)
34{
35 cpu_pm_enter();
36 cpu_suspend(0, idle_finisher);
37 cpu_pm_exit();
38
39 return index;
40}
41
42static int exynos_enter_lowpower(struct cpuidle_device *dev, 23static int exynos_enter_lowpower(struct cpuidle_device *dev,
43 struct cpuidle_driver *drv, 24 struct cpuidle_driver *drv,
44 int index) 25 int index)
@@ -51,8 +32,10 @@ static int exynos_enter_lowpower(struct cpuidle_device *dev,
51 32
52 if (new_index == 0) 33 if (new_index == 0)
53 return arm_cpuidle_simple_enter(dev, drv, new_index); 34 return arm_cpuidle_simple_enter(dev, drv, new_index);
54 else 35
55 return exynos_enter_core0_aftr(dev, drv, new_index); 36 exynos_enter_aftr();
37
38 return new_index;
56} 39}
57 40
58static struct cpuidle_driver exynos_idle_driver = { 41static struct cpuidle_driver exynos_idle_driver = {