1 /* linux/arch/arm/mach-exynos4/pm.c 2 * 3 * Copyright (c) 2011 Samsung Electronics Co., Ltd. 4 * http://www.samsung.com 5 * 6 * EXYNOS4210 - 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 * This program is free software; you can redistribute it and/or modify 13 * it under the terms of the GNU General Public License version 2 as 14 * published by the Free Software Foundation. 15 */ 16 17 #include <linux/init.h> 18 #include <linux/suspend.h> 19 #include <linux/syscore_ops.h> 20 #include <linux/io.h> 21 #include <linux/err.h> 22 #include <linux/clk.h> 23 24 #include <asm/cacheflush.h> 25 #include <asm/hardware/cache-l2x0.h> 26 27 #include <plat/cpu.h> 28 #include <plat/pm.h> 29 #include <plat/pll.h> 30 #include <plat/regs-srom.h> 31 32 #include <mach/regs-irq.h> 33 #include <mach/regs-gpio.h> 34 #include <mach/regs-clock.h> 35 #include <mach/regs-pmu.h> 36 #include <mach/pm-core.h> 37 #include <mach/pmu.h> 38 39 static struct sleep_save exynos4_set_clksrc[] = { 40 { .reg = S5P_CLKSRC_MASK_TOP , .val = 0x00000001, }, 41 { .reg = S5P_CLKSRC_MASK_CAM , .val = 0x11111111, }, 42 { .reg = S5P_CLKSRC_MASK_TV , .val = 0x00000111, }, 43 { .reg = S5P_CLKSRC_MASK_LCD0 , .val = 0x00001111, }, 44 { .reg = S5P_CLKSRC_MASK_MAUDIO , .val = 0x00000001, }, 45 { .reg = S5P_CLKSRC_MASK_FSYS , .val = 0x01011111, }, 46 { .reg = S5P_CLKSRC_MASK_PERIL0 , .val = 0x01111111, }, 47 { .reg = S5P_CLKSRC_MASK_PERIL1 , .val = 0x01110111, }, 48 { .reg = S5P_CLKSRC_MASK_DMC , .val = 0x00010000, }, 49 }; 50 51 static struct sleep_save exynos4210_set_clksrc[] = { 52 { .reg = S5P_CLKSRC_MASK_LCD1 , .val = 0x00001111, }, 53 }; 54 55 static struct sleep_save exynos4_epll_save[] = { 56 SAVE_ITEM(S5P_EPLL_CON0), 57 SAVE_ITEM(S5P_EPLL_CON1), 58 }; 59 60 static struct sleep_save exynos4_vpll_save[] = { 61 SAVE_ITEM(S5P_VPLL_CON0), 62 SAVE_ITEM(S5P_VPLL_CON1), 63 }; 64 65 static struct sleep_save exynos4_core_save[] = { 66 /* GIC side */ 67 SAVE_ITEM(S5P_VA_GIC_CPU + 0x000), 68 SAVE_ITEM(S5P_VA_GIC_CPU + 0x004), 69 SAVE_ITEM(S5P_VA_GIC_CPU + 0x008), 70 SAVE_ITEM(S5P_VA_GIC_CPU + 0x00C), 71 SAVE_ITEM(S5P_VA_GIC_CPU + 0x014), 72 SAVE_ITEM(S5P_VA_GIC_CPU + 0x018), 73 SAVE_ITEM(S5P_VA_GIC_DIST + 0x000), 74 SAVE_ITEM(S5P_VA_GIC_DIST + 0x004), 75 SAVE_ITEM(S5P_VA_GIC_DIST + 0x100), 76 SAVE_ITEM(S5P_VA_GIC_DIST + 0x104), 77 SAVE_ITEM(S5P_VA_GIC_DIST + 0x108), 78 SAVE_ITEM(S5P_VA_GIC_DIST + 0x300), 79 SAVE_ITEM(S5P_VA_GIC_DIST + 0x304), 80 SAVE_ITEM(S5P_VA_GIC_DIST + 0x308), 81 SAVE_ITEM(S5P_VA_GIC_DIST + 0x400), 82 SAVE_ITEM(S5P_VA_GIC_DIST + 0x404), 83 SAVE_ITEM(S5P_VA_GIC_DIST + 0x408), 84 SAVE_ITEM(S5P_VA_GIC_DIST + 0x40C), 85 SAVE_ITEM(S5P_VA_GIC_DIST + 0x410), 86 SAVE_ITEM(S5P_VA_GIC_DIST + 0x414), 87 SAVE_ITEM(S5P_VA_GIC_DIST + 0x418), 88 SAVE_ITEM(S5P_VA_GIC_DIST + 0x41C), 89 SAVE_ITEM(S5P_VA_GIC_DIST + 0x420), 90 SAVE_ITEM(S5P_VA_GIC_DIST + 0x424), 91 SAVE_ITEM(S5P_VA_GIC_DIST + 0x428), 92 SAVE_ITEM(S5P_VA_GIC_DIST + 0x42C), 93 SAVE_ITEM(S5P_VA_GIC_DIST + 0x430), 94 SAVE_ITEM(S5P_VA_GIC_DIST + 0x434), 95 SAVE_ITEM(S5P_VA_GIC_DIST + 0x438), 96 SAVE_ITEM(S5P_VA_GIC_DIST + 0x43C), 97 SAVE_ITEM(S5P_VA_GIC_DIST + 0x440), 98 SAVE_ITEM(S5P_VA_GIC_DIST + 0x444), 99 SAVE_ITEM(S5P_VA_GIC_DIST + 0x448), 100 SAVE_ITEM(S5P_VA_GIC_DIST + 0x44C), 101 SAVE_ITEM(S5P_VA_GIC_DIST + 0x450), 102 SAVE_ITEM(S5P_VA_GIC_DIST + 0x454), 103 SAVE_ITEM(S5P_VA_GIC_DIST + 0x458), 104 SAVE_ITEM(S5P_VA_GIC_DIST + 0x45C), 105 106 SAVE_ITEM(S5P_VA_GIC_DIST + 0x800), 107 SAVE_ITEM(S5P_VA_GIC_DIST + 0x804), 108 SAVE_ITEM(S5P_VA_GIC_DIST + 0x808), 109 SAVE_ITEM(S5P_VA_GIC_DIST + 0x80C), 110 SAVE_ITEM(S5P_VA_GIC_DIST + 0x810), 111 SAVE_ITEM(S5P_VA_GIC_DIST + 0x814), 112 SAVE_ITEM(S5P_VA_GIC_DIST + 0x818), 113 SAVE_ITEM(S5P_VA_GIC_DIST + 0x81C), 114 SAVE_ITEM(S5P_VA_GIC_DIST + 0x820), 115 SAVE_ITEM(S5P_VA_GIC_DIST + 0x824), 116 SAVE_ITEM(S5P_VA_GIC_DIST + 0x828), 117 SAVE_ITEM(S5P_VA_GIC_DIST + 0x82C), 118 SAVE_ITEM(S5P_VA_GIC_DIST + 0x830), 119 SAVE_ITEM(S5P_VA_GIC_DIST + 0x834), 120 SAVE_ITEM(S5P_VA_GIC_DIST + 0x838), 121 SAVE_ITEM(S5P_VA_GIC_DIST + 0x83C), 122 SAVE_ITEM(S5P_VA_GIC_DIST + 0x840), 123 SAVE_ITEM(S5P_VA_GIC_DIST + 0x844), 124 SAVE_ITEM(S5P_VA_GIC_DIST + 0x848), 125 SAVE_ITEM(S5P_VA_GIC_DIST + 0x84C), 126 SAVE_ITEM(S5P_VA_GIC_DIST + 0x850), 127 SAVE_ITEM(S5P_VA_GIC_DIST + 0x854), 128 SAVE_ITEM(S5P_VA_GIC_DIST + 0x858), 129 SAVE_ITEM(S5P_VA_GIC_DIST + 0x85C), 130 131 SAVE_ITEM(S5P_VA_GIC_DIST + 0xC00), 132 SAVE_ITEM(S5P_VA_GIC_DIST + 0xC04), 133 SAVE_ITEM(S5P_VA_GIC_DIST + 0xC08), 134 SAVE_ITEM(S5P_VA_GIC_DIST + 0xC0C), 135 SAVE_ITEM(S5P_VA_GIC_DIST + 0xC10), 136 SAVE_ITEM(S5P_VA_GIC_DIST + 0xC14), 137 138 SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x000), 139 SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x010), 140 SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x020), 141 SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x030), 142 SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x040), 143 SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x050), 144 SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x060), 145 SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x070), 146 SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x080), 147 SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x090), 148 149 /* SROM side */ 150 SAVE_ITEM(S5P_SROM_BW), 151 SAVE_ITEM(S5P_SROM_BC0), 152 SAVE_ITEM(S5P_SROM_BC1), 153 SAVE_ITEM(S5P_SROM_BC2), 154 SAVE_ITEM(S5P_SROM_BC3), 155 }; 156 157 static struct sleep_save exynos4_l2cc_save[] = { 158 SAVE_ITEM(S5P_VA_L2CC + L2X0_TAG_LATENCY_CTRL), 159 SAVE_ITEM(S5P_VA_L2CC + L2X0_DATA_LATENCY_CTRL), 160 SAVE_ITEM(S5P_VA_L2CC + L2X0_PREFETCH_CTRL), 161 SAVE_ITEM(S5P_VA_L2CC + L2X0_POWER_CTRL), 162 SAVE_ITEM(S5P_VA_L2CC + L2X0_AUX_CTRL), 163 }; 164 165 /* For Cortex-A9 Diagnostic and Power control register */ 166 static unsigned int save_arm_register[2]; 167 168 static int exynos4_cpu_suspend(unsigned long arg) 169 { 170 outer_flush_all(); 171 172 /* issue the standby signal into the pm unit. */ 173 cpu_do_idle(); 174 175 /* we should never get past here */ 176 panic("sleep resumed to originator?"); 177 } 178 179 static void exynos4_pm_prepare(void) 180 { 181 u32 tmp; 182 183 s3c_pm_do_save(exynos4_core_save, ARRAY_SIZE(exynos4_core_save)); 184 s3c_pm_do_save(exynos4_l2cc_save, ARRAY_SIZE(exynos4_l2cc_save)); 185 s3c_pm_do_save(exynos4_epll_save, ARRAY_SIZE(exynos4_epll_save)); 186 s3c_pm_do_save(exynos4_vpll_save, ARRAY_SIZE(exynos4_vpll_save)); 187 188 tmp = __raw_readl(S5P_INFORM1); 189 190 /* Set value of power down register for sleep mode */ 191 192 exynos4_sys_powerdown_conf(SYS_SLEEP); 193 __raw_writel(S5P_CHECK_SLEEP, S5P_INFORM1); 194 195 /* ensure at least INFORM0 has the resume address */ 196 197 __raw_writel(virt_to_phys(s3c_cpu_resume), S5P_INFORM0); 198 199 /* Before enter central sequence mode, clock src register have to set */ 200 201 s3c_pm_do_restore_core(exynos4_set_clksrc, ARRAY_SIZE(exynos4_set_clksrc)); 202 203 if (soc_is_exynos4210()) 204 s3c_pm_do_restore_core(exynos4210_set_clksrc, ARRAY_SIZE(exynos4210_set_clksrc)); 205 206 } 207 208 static int exynos4_pm_add(struct sys_device *sysdev) 209 { 210 pm_cpu_prep = exynos4_pm_prepare; 211 pm_cpu_sleep = exynos4_cpu_suspend; 212 213 return 0; 214 } 215 216 /* This function copy from linux/arch/arm/kernel/smp_scu.c */ 217 218 void exynos4_scu_enable(void __iomem *scu_base) 219 { 220 u32 scu_ctrl; 221 222 scu_ctrl = __raw_readl(scu_base); 223 /* already enabled? */ 224 if (scu_ctrl & 1) 225 return; 226 227 scu_ctrl |= 1; 228 __raw_writel(scu_ctrl, scu_base); 229 230 /* 231 * Ensure that the data accessed by CPU0 before the SCU was 232 * initialised is visible to the other CPUs. 233 */ 234 flush_cache_all(); 235 } 236 237 static unsigned long pll_base_rate; 238 239 static void exynos4_restore_pll(void) 240 { 241 unsigned long pll_con, locktime, lockcnt; 242 unsigned long pll_in_rate; 243 unsigned int p_div, epll_wait = 0, vpll_wait = 0; 244 245 if (pll_base_rate == 0) 246 return; 247 248 pll_in_rate = pll_base_rate; 249 250 /* EPLL */ 251 pll_con = exynos4_epll_save[0].val; 252 253 if (pll_con & (1 << 31)) { 254 pll_con &= (PLL46XX_PDIV_MASK << PLL46XX_PDIV_SHIFT); 255 p_div = (pll_con >> PLL46XX_PDIV_SHIFT); 256 257 pll_in_rate /= 1000000; 258 259 locktime = (3000 / pll_in_rate) * p_div; 260 lockcnt = locktime * 10000 / (10000 / pll_in_rate); 261 262 __raw_writel(lockcnt, S5P_EPLL_LOCK); 263 264 s3c_pm_do_restore_core(exynos4_epll_save, 265 ARRAY_SIZE(exynos4_epll_save)); 266 epll_wait = 1; 267 } 268 269 pll_in_rate = pll_base_rate; 270 271 /* VPLL */ 272 pll_con = exynos4_vpll_save[0].val; 273 274 if (pll_con & (1 << 31)) { 275 pll_in_rate /= 1000000; 276 /* 750us */ 277 locktime = 750; 278 lockcnt = locktime * 10000 / (10000 / pll_in_rate); 279 280 __raw_writel(lockcnt, S5P_VPLL_LOCK); 281 282 s3c_pm_do_restore_core(exynos4_vpll_save, 283 ARRAY_SIZE(exynos4_vpll_save)); 284 vpll_wait = 1; 285 } 286 287 /* Wait PLL locking */ 288 289 do { 290 if (epll_wait) { 291 pll_con = __raw_readl(S5P_EPLL_CON0); 292 if (pll_con & (1 << S5P_EPLLCON0_LOCKED_SHIFT)) 293 epll_wait = 0; 294 } 295 296 if (vpll_wait) { 297 pll_con = __raw_readl(S5P_VPLL_CON0); 298 if (pll_con & (1 << S5P_VPLLCON0_LOCKED_SHIFT)) 299 vpll_wait = 0; 300 } 301 } while (epll_wait || vpll_wait); 302 } 303 304 static struct sysdev_driver exynos4_pm_driver = { 305 .add = exynos4_pm_add, 306 }; 307 308 static __init int exynos4_pm_drvinit(void) 309 { 310 struct clk *pll_base; 311 unsigned int tmp; 312 313 s3c_pm_init(); 314 315 /* All wakeup disable */ 316 317 tmp = __raw_readl(S5P_WAKEUP_MASK); 318 tmp |= ((0xFF << 8) | (0x1F << 1)); 319 __raw_writel(tmp, S5P_WAKEUP_MASK); 320 321 pll_base = clk_get(NULL, "xtal"); 322 323 if (!IS_ERR(pll_base)) { 324 pll_base_rate = clk_get_rate(pll_base); 325 clk_put(pll_base); 326 } 327 328 return sysdev_driver_register(&exynos4_sysclass, &exynos4_pm_driver); 329 } 330 arch_initcall(exynos4_pm_drvinit); 331 332 static int exynos4_pm_suspend(void) 333 { 334 unsigned long tmp; 335 336 /* Setting Central Sequence Register for power down mode */ 337 338 tmp = __raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION); 339 tmp &= ~S5P_CENTRAL_LOWPWR_CFG; 340 __raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION); 341 342 if (soc_is_exynos4212()) { 343 tmp = __raw_readl(S5P_CENTRAL_SEQ_OPTION); 344 tmp &= ~(S5P_USE_STANDBYWFI_ISP_ARM | 345 S5P_USE_STANDBYWFE_ISP_ARM); 346 __raw_writel(tmp, S5P_CENTRAL_SEQ_OPTION); 347 } 348 349 /* Save Power control register */ 350 asm ("mrc p15, 0, %0, c15, c0, 0" 351 : "=r" (tmp) : : "cc"); 352 save_arm_register[0] = tmp; 353 354 /* Save Diagnostic register */ 355 asm ("mrc p15, 0, %0, c15, c0, 1" 356 : "=r" (tmp) : : "cc"); 357 save_arm_register[1] = tmp; 358 359 return 0; 360 } 361 362 static void exynos4_pm_resume(void) 363 { 364 unsigned long tmp; 365 366 /* 367 * If PMU failed while entering sleep mode, WFI will be 368 * ignored by PMU and then exiting cpu_do_idle(). 369 * S5P_CENTRAL_LOWPWR_CFG bit will not be set automatically 370 * in this situation. 371 */ 372 tmp = __raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION); 373 if (!(tmp & S5P_CENTRAL_LOWPWR_CFG)) { 374 tmp |= S5P_CENTRAL_LOWPWR_CFG; 375 __raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION); 376 /* No need to perform below restore code */ 377 goto early_wakeup; 378 } 379 /* Restore Power control register */ 380 tmp = save_arm_register[0]; 381 asm volatile ("mcr p15, 0, %0, c15, c0, 0" 382 : : "r" (tmp) 383 : "cc"); 384 385 /* Restore Diagnostic register */ 386 tmp = save_arm_register[1]; 387 asm volatile ("mcr p15, 0, %0, c15, c0, 1" 388 : : "r" (tmp) 389 : "cc"); 390 391 /* For release retention */ 392 393 __raw_writel((1 << 28), S5P_PAD_RET_MAUDIO_OPTION); 394 __raw_writel((1 << 28), S5P_PAD_RET_GPIO_OPTION); 395 __raw_writel((1 << 28), S5P_PAD_RET_UART_OPTION); 396 __raw_writel((1 << 28), S5P_PAD_RET_MMCA_OPTION); 397 __raw_writel((1 << 28), S5P_PAD_RET_MMCB_OPTION); 398 __raw_writel((1 << 28), S5P_PAD_RET_EBIA_OPTION); 399 __raw_writel((1 << 28), S5P_PAD_RET_EBIB_OPTION); 400 401 s3c_pm_do_restore_core(exynos4_core_save, ARRAY_SIZE(exynos4_core_save)); 402 403 exynos4_restore_pll(); 404 405 exynos4_scu_enable(S5P_VA_SCU); 406 407 #ifdef CONFIG_CACHE_L2X0 408 s3c_pm_do_restore_core(exynos4_l2cc_save, ARRAY_SIZE(exynos4_l2cc_save)); 409 outer_inv_all(); 410 /* enable L2X0*/ 411 writel_relaxed(1, S5P_VA_L2CC + L2X0_CTRL); 412 #endif 413 414 early_wakeup: 415 return; 416 } 417 418 static struct syscore_ops exynos4_pm_syscore_ops = { 419 .suspend = exynos4_pm_suspend, 420 .resume = exynos4_pm_resume, 421 }; 422 423 static __init int exynos4_pm_syscore_init(void) 424 { 425 register_syscore_ops(&exynos4_pm_syscore_ops); 426 return 0; 427 } 428 arch_initcall(exynos4_pm_syscore_init); 429