1 /* 2 * Copyright (c) 2011-2014 Samsung Electronics Co., Ltd. 3 * http://www.samsung.com 4 * 5 * EXYNOS - Power Management support 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> 18 #include <linux/cpu_pm.h> 19 #include <linux/io.h> 20 #include <linux/err.h> 21 22 #include <asm/firmware.h> 23 #include <asm/smp_scu.h> 24 #include <asm/suspend.h> 25 26 #include <mach/map.h> 27 28 #include <plat/pm-common.h> 29 30 #include "common.h" 31 #include "exynos-pmu.h" 32 #include "regs-pmu.h" 33 34 static inline void __iomem *exynos_boot_vector_addr(void) 35 { 36 if (samsung_rev() == EXYNOS4210_REV_1_1) 37 return pmu_base_addr + S5P_INFORM7; 38 else if (samsung_rev() == EXYNOS4210_REV_1_0) 39 return sysram_base_addr + 0x24; 40 return pmu_base_addr + S5P_INFORM0; 41 } 42 43 static inline void __iomem *exynos_boot_vector_flag(void) 44 { 45 if (samsung_rev() == EXYNOS4210_REV_1_1) 46 return pmu_base_addr + S5P_INFORM6; 47 else if (samsung_rev() == EXYNOS4210_REV_1_0) 48 return sysram_base_addr + 0x20; 49 return pmu_base_addr + S5P_INFORM1; 50 } 51 52 #define S5P_CHECK_AFTR 0xFCBA0D10 53 54 /* For Cortex-A9 Diagnostic and Power control register */ 55 static unsigned int save_arm_register[2]; 56 57 void exynos_cpu_save_register(void) 58 { 59 unsigned long tmp; 60 61 /* Save Power control register */ 62 asm ("mrc p15, 0, %0, c15, c0, 0" 63 : "=r" (tmp) : : "cc"); 64 65 save_arm_register[0] = tmp; 66 67 /* Save Diagnostic register */ 68 asm ("mrc p15, 0, %0, c15, c0, 1" 69 : "=r" (tmp) : : "cc"); 70 71 save_arm_register[1] = tmp; 72 } 73 74 void exynos_cpu_restore_register(void) 75 { 76 unsigned long tmp; 77 78 /* Restore Power control register */ 79 tmp = save_arm_register[0]; 80 81 asm volatile ("mcr p15, 0, %0, c15, c0, 0" 82 : : "r" (tmp) 83 : "cc"); 84 85 /* Restore Diagnostic register */ 86 tmp = save_arm_register[1]; 87 88 asm volatile ("mcr p15, 0, %0, c15, c0, 1" 89 : : "r" (tmp) 90 : "cc"); 91 } 92 93 void exynos_pm_central_suspend(void) 94 { 95 unsigned long tmp; 96 97 /* Setting Central Sequence Register for power down mode */ 98 tmp = pmu_raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION); 99 tmp &= ~S5P_CENTRAL_LOWPWR_CFG; 100 pmu_raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION); 101 } 102 103 int exynos_pm_central_resume(void) 104 { 105 unsigned long tmp; 106 107 /* 108 * If PMU failed while entering sleep mode, WFI will be 109 * ignored by PMU and then exiting cpu_do_idle(). 110 * S5P_CENTRAL_LOWPWR_CFG bit will not be set automatically 111 * in this situation. 112 */ 113 tmp = pmu_raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION); 114 if (!(tmp & S5P_CENTRAL_LOWPWR_CFG)) { 115 tmp |= S5P_CENTRAL_LOWPWR_CFG; 116 pmu_raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION); 117 /* clear the wakeup state register */ 118 pmu_raw_writel(0x0, S5P_WAKEUP_STAT); 119 /* No need to perform below restore code */ 120 return -1; 121 } 122 123 return 0; 124 } 125 126 /* Ext-GIC nIRQ/nFIQ is the only wakeup source in AFTR */ 127 static void exynos_set_wakeupmask(long mask) 128 { 129 pmu_raw_writel(mask, S5P_WAKEUP_MASK); 130 if (soc_is_exynos3250()) 131 pmu_raw_writel(0x0, S5P_WAKEUP_MASK2); 132 } 133 134 static void exynos_cpu_set_boot_vector(long flags) 135 { 136 __raw_writel(virt_to_phys(exynos_cpu_resume), 137 exynos_boot_vector_addr()); 138 __raw_writel(flags, exynos_boot_vector_flag()); 139 } 140 141 static int exynos_aftr_finisher(unsigned long flags) 142 { 143 int ret; 144 145 exynos_set_wakeupmask(soc_is_exynos3250() ? 0x40003ffe : 0x0000ff3e); 146 /* Set value of power down register for aftr mode */ 147 exynos_sys_powerdown_conf(SYS_AFTR); 148 149 ret = call_firmware_op(do_idle, FW_DO_IDLE_AFTR); 150 if (ret == -ENOSYS) { 151 if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) 152 exynos_cpu_save_register(); 153 exynos_cpu_set_boot_vector(S5P_CHECK_AFTR); 154 cpu_do_idle(); 155 } 156 157 return 1; 158 } 159 160 void exynos_enter_aftr(void) 161 { 162 unsigned int cpuid = smp_processor_id(); 163 164 cpu_pm_enter(); 165 166 if (soc_is_exynos3250()) 167 exynos_set_boot_flag(cpuid, C2_STATE); 168 169 exynos_pm_central_suspend(); 170 171 if (of_machine_is_compatible("samsung,exynos4212") || 172 of_machine_is_compatible("samsung,exynos4412")) { 173 /* Setting SEQ_OPTION register */ 174 pmu_raw_writel(S5P_USE_STANDBY_WFI0 | S5P_USE_STANDBY_WFE0, 175 S5P_CENTRAL_SEQ_OPTION); 176 } 177 178 cpu_suspend(0, exynos_aftr_finisher); 179 180 if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) { 181 scu_enable(S5P_VA_SCU); 182 if (call_firmware_op(resume) == -ENOSYS) 183 exynos_cpu_restore_register(); 184 } 185 186 exynos_pm_central_resume(); 187 188 if (soc_is_exynos3250()) 189 exynos_clear_boot_flag(cpuid, C2_STATE); 190 191 cpu_pm_exit(); 192 } 193 194 #if defined(CONFIG_SMP) && defined(CONFIG_ARM_EXYNOS_CPUIDLE) 195 static atomic_t cpu1_wakeup = ATOMIC_INIT(0); 196 197 static int exynos_cpu0_enter_aftr(void) 198 { 199 int ret = -1; 200 201 /* 202 * If the other cpu is powered on, we have to power it off, because 203 * the AFTR state won't work otherwise 204 */ 205 if (cpu_online(1)) { 206 /* 207 * We reach a sync point with the coupled idle state, we know 208 * the other cpu will power down itself or will abort the 209 * sequence, let's wait for one of these to happen 210 */ 211 while (exynos_cpu_power_state(1)) { 212 /* 213 * The other cpu may skip idle and boot back 214 * up again 215 */ 216 if (atomic_read(&cpu1_wakeup)) 217 goto abort; 218 219 /* 220 * The other cpu may bounce through idle and 221 * boot back up again, getting stuck in the 222 * boot rom code 223 */ 224 if (__raw_readl(cpu_boot_reg_base()) == 0) 225 goto abort; 226 227 cpu_relax(); 228 } 229 } 230 231 exynos_enter_aftr(); 232 ret = 0; 233 234 abort: 235 if (cpu_online(1)) { 236 /* 237 * Set the boot vector to something non-zero 238 */ 239 __raw_writel(virt_to_phys(exynos_cpu_resume), 240 cpu_boot_reg_base()); 241 dsb(); 242 243 /* 244 * Turn on cpu1 and wait for it to be on 245 */ 246 exynos_cpu_power_up(1); 247 while (exynos_cpu_power_state(1) != S5P_CORE_LOCAL_PWR_EN) 248 cpu_relax(); 249 250 while (!atomic_read(&cpu1_wakeup)) { 251 /* 252 * Poke cpu1 out of the boot rom 253 */ 254 __raw_writel(virt_to_phys(exynos_cpu_resume), 255 cpu_boot_reg_base()); 256 257 arch_send_wakeup_ipi_mask(cpumask_of(1)); 258 } 259 } 260 261 return ret; 262 } 263 264 static int exynos_wfi_finisher(unsigned long flags) 265 { 266 cpu_do_idle(); 267 268 return -1; 269 } 270 271 static int exynos_cpu1_powerdown(void) 272 { 273 int ret = -1; 274 275 /* 276 * Idle sequence for cpu1 277 */ 278 if (cpu_pm_enter()) 279 goto cpu1_aborted; 280 281 /* 282 * Turn off cpu 1 283 */ 284 exynos_cpu_power_down(1); 285 286 ret = cpu_suspend(0, exynos_wfi_finisher); 287 288 cpu_pm_exit(); 289 290 cpu1_aborted: 291 dsb(); 292 /* 293 * Notify cpu 0 that cpu 1 is awake 294 */ 295 atomic_set(&cpu1_wakeup, 1); 296 297 return ret; 298 } 299 300 static void exynos_pre_enter_aftr(void) 301 { 302 __raw_writel(virt_to_phys(exynos_cpu_resume), cpu_boot_reg_base()); 303 } 304 305 static void exynos_post_enter_aftr(void) 306 { 307 atomic_set(&cpu1_wakeup, 0); 308 } 309 310 struct cpuidle_exynos_data cpuidle_coupled_exynos_data = { 311 .cpu0_enter_aftr = exynos_cpu0_enter_aftr, 312 .cpu1_powerdown = exynos_cpu1_powerdown, 313 .pre_enter_aftr = exynos_pre_enter_aftr, 314 .post_enter_aftr = exynos_post_enter_aftr, 315 }; 316 #endif /* CONFIG_SMP && CONFIG_ARM_EXYNOS_CPUIDLE */ 317