diff options
| -rw-r--r-- | arch/arm/mach-exynos/pm.c | 163 | ||||
| -rw-r--r-- | drivers/cpuidle/cpuidle-exynos.c | 25 |
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 */ | ||
| 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 | ||
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 | ||
| 21 | static void (*exynos_enter_aftr)(void); | 21 | static void (*exynos_enter_aftr)(void); |
| 22 | 22 | ||
| 23 | static int idle_finisher(unsigned long flags) | ||
| 24 | { | ||
| 25 | exynos_enter_aftr(); | ||
| 26 | cpu_do_idle(); | ||
| 27 | |||
| 28 | return 1; | ||
| 29 | } | ||
| 30 | |||
| 31 | static 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 | |||
| 42 | static int exynos_enter_lowpower(struct cpuidle_device *dev, | 23 | static 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 | ||
| 58 | static struct cpuidle_driver exynos_idle_driver = { | 41 | static struct cpuidle_driver exynos_idle_driver = { |
