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