183014579SKukjin Kim /* linux/arch/arm/mach-exynos4/pm.c 283014579SKukjin Kim * 383014579SKukjin Kim * Copyright (c) 2011 Samsung Electronics Co., Ltd. 483014579SKukjin Kim * http://www.samsung.com 583014579SKukjin Kim * 683014579SKukjin Kim * EXYNOS4210 - Power Management support 783014579SKukjin Kim * 883014579SKukjin Kim * Based on arch/arm/mach-s3c2410/pm.c 983014579SKukjin Kim * Copyright (c) 2006 Simtec Electronics 1083014579SKukjin Kim * Ben Dooks <ben@simtec.co.uk> 1183014579SKukjin Kim * 1283014579SKukjin Kim * This program is free software; you can redistribute it and/or modify 1383014579SKukjin Kim * it under the terms of the GNU General Public License version 2 as 1483014579SKukjin Kim * published by the Free Software Foundation. 1583014579SKukjin Kim */ 1683014579SKukjin Kim 1783014579SKukjin Kim #include <linux/init.h> 1883014579SKukjin Kim #include <linux/suspend.h> 1983014579SKukjin Kim #include <linux/syscore_ops.h> 2083014579SKukjin Kim #include <linux/io.h> 2183014579SKukjin Kim #include <linux/err.h> 2283014579SKukjin Kim #include <linux/clk.h> 2383014579SKukjin Kim 2483014579SKukjin Kim #include <asm/cacheflush.h> 2583014579SKukjin Kim #include <asm/hardware/cache-l2x0.h> 2683014579SKukjin Kim 2783014579SKukjin Kim #include <plat/cpu.h> 2883014579SKukjin Kim #include <plat/pm.h> 2983014579SKukjin Kim #include <plat/pll.h> 3083014579SKukjin Kim #include <plat/regs-srom.h> 3183014579SKukjin Kim 3283014579SKukjin Kim #include <mach/regs-irq.h> 3383014579SKukjin Kim #include <mach/regs-gpio.h> 3483014579SKukjin Kim #include <mach/regs-clock.h> 3583014579SKukjin Kim #include <mach/regs-pmu.h> 3683014579SKukjin Kim #include <mach/pm-core.h> 3783014579SKukjin Kim #include <mach/pmu.h> 3883014579SKukjin Kim 3983014579SKukjin Kim static struct sleep_save exynos4_set_clksrc[] = { 4083014579SKukjin Kim { .reg = S5P_CLKSRC_MASK_TOP , .val = 0x00000001, }, 4183014579SKukjin Kim { .reg = S5P_CLKSRC_MASK_CAM , .val = 0x11111111, }, 4283014579SKukjin Kim { .reg = S5P_CLKSRC_MASK_TV , .val = 0x00000111, }, 4383014579SKukjin Kim { .reg = S5P_CLKSRC_MASK_LCD0 , .val = 0x00001111, }, 4483014579SKukjin Kim { .reg = S5P_CLKSRC_MASK_MAUDIO , .val = 0x00000001, }, 4583014579SKukjin Kim { .reg = S5P_CLKSRC_MASK_FSYS , .val = 0x01011111, }, 4683014579SKukjin Kim { .reg = S5P_CLKSRC_MASK_PERIL0 , .val = 0x01111111, }, 4783014579SKukjin Kim { .reg = S5P_CLKSRC_MASK_PERIL1 , .val = 0x01110111, }, 4883014579SKukjin Kim { .reg = S5P_CLKSRC_MASK_DMC , .val = 0x00010000, }, 4983014579SKukjin Kim }; 5083014579SKukjin Kim 5183014579SKukjin Kim static struct sleep_save exynos4210_set_clksrc[] = { 5283014579SKukjin Kim { .reg = S5P_CLKSRC_MASK_LCD1 , .val = 0x00001111, }, 5383014579SKukjin Kim }; 5483014579SKukjin Kim 5583014579SKukjin Kim static struct sleep_save exynos4_epll_save[] = { 5683014579SKukjin Kim SAVE_ITEM(S5P_EPLL_CON0), 5783014579SKukjin Kim SAVE_ITEM(S5P_EPLL_CON1), 5883014579SKukjin Kim }; 5983014579SKukjin Kim 6083014579SKukjin Kim static struct sleep_save exynos4_vpll_save[] = { 6183014579SKukjin Kim SAVE_ITEM(S5P_VPLL_CON0), 6283014579SKukjin Kim SAVE_ITEM(S5P_VPLL_CON1), 6383014579SKukjin Kim }; 6483014579SKukjin Kim 6583014579SKukjin Kim static struct sleep_save exynos4_core_save[] = { 6683014579SKukjin Kim /* GIC side */ 6783014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_CPU + 0x000), 6883014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_CPU + 0x004), 6983014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_CPU + 0x008), 7083014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_CPU + 0x00C), 7183014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_CPU + 0x014), 7283014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_CPU + 0x018), 7383014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x000), 7483014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x004), 7583014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x100), 7683014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x104), 7783014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x108), 7883014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x300), 7983014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x304), 8083014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x308), 8183014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x400), 8283014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x404), 8383014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x408), 8483014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x40C), 8583014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x410), 8683014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x414), 8783014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x418), 8883014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x41C), 8983014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x420), 9083014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x424), 9183014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x428), 9283014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x42C), 9383014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x430), 9483014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x434), 9583014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x438), 9683014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x43C), 9783014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x440), 9883014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x444), 9983014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x448), 10083014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x44C), 10183014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x450), 10283014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x454), 10383014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x458), 10483014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x45C), 10583014579SKukjin Kim 10683014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x800), 10783014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x804), 10883014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x808), 10983014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x80C), 11083014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x810), 11183014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x814), 11283014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x818), 11383014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x81C), 11483014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x820), 11583014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x824), 11683014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x828), 11783014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x82C), 11883014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x830), 11983014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x834), 12083014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x838), 12183014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x83C), 12283014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x840), 12383014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x844), 12483014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x848), 12583014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x84C), 12683014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x850), 12783014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x854), 12883014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x858), 12983014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0x85C), 13083014579SKukjin Kim 13183014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0xC00), 13283014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0xC04), 13383014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0xC08), 13483014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0xC0C), 13583014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0xC10), 13683014579SKukjin Kim SAVE_ITEM(S5P_VA_GIC_DIST + 0xC14), 13783014579SKukjin Kim 13883014579SKukjin Kim SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x000), 13983014579SKukjin Kim SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x010), 14083014579SKukjin Kim SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x020), 14183014579SKukjin Kim SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x030), 14283014579SKukjin Kim SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x040), 14383014579SKukjin Kim SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x050), 14483014579SKukjin Kim SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x060), 14583014579SKukjin Kim SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x070), 14683014579SKukjin Kim SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x080), 14783014579SKukjin Kim SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x090), 14883014579SKukjin Kim 14983014579SKukjin Kim /* SROM side */ 15083014579SKukjin Kim SAVE_ITEM(S5P_SROM_BW), 15183014579SKukjin Kim SAVE_ITEM(S5P_SROM_BC0), 15283014579SKukjin Kim SAVE_ITEM(S5P_SROM_BC1), 15383014579SKukjin Kim SAVE_ITEM(S5P_SROM_BC2), 15483014579SKukjin Kim SAVE_ITEM(S5P_SROM_BC3), 15583014579SKukjin Kim }; 15683014579SKukjin Kim 15783014579SKukjin Kim static struct sleep_save exynos4_l2cc_save[] = { 15883014579SKukjin Kim SAVE_ITEM(S5P_VA_L2CC + L2X0_TAG_LATENCY_CTRL), 15983014579SKukjin Kim SAVE_ITEM(S5P_VA_L2CC + L2X0_DATA_LATENCY_CTRL), 16083014579SKukjin Kim SAVE_ITEM(S5P_VA_L2CC + L2X0_PREFETCH_CTRL), 16183014579SKukjin Kim SAVE_ITEM(S5P_VA_L2CC + L2X0_POWER_CTRL), 16283014579SKukjin Kim SAVE_ITEM(S5P_VA_L2CC + L2X0_AUX_CTRL), 16383014579SKukjin Kim }; 16483014579SKukjin Kim 16583014579SKukjin Kim /* For Cortex-A9 Diagnostic and Power control register */ 16683014579SKukjin Kim static unsigned int save_arm_register[2]; 16783014579SKukjin Kim 16883014579SKukjin Kim static int exynos4_cpu_suspend(unsigned long arg) 16983014579SKukjin Kim { 17083014579SKukjin Kim outer_flush_all(); 17183014579SKukjin Kim 17283014579SKukjin Kim /* issue the standby signal into the pm unit. */ 17383014579SKukjin Kim cpu_do_idle(); 17483014579SKukjin Kim 17583014579SKukjin Kim /* we should never get past here */ 17683014579SKukjin Kim panic("sleep resumed to originator?"); 17783014579SKukjin Kim } 17883014579SKukjin Kim 17983014579SKukjin Kim static void exynos4_pm_prepare(void) 18083014579SKukjin Kim { 18183014579SKukjin Kim u32 tmp; 18283014579SKukjin Kim 18383014579SKukjin Kim s3c_pm_do_save(exynos4_core_save, ARRAY_SIZE(exynos4_core_save)); 18483014579SKukjin Kim s3c_pm_do_save(exynos4_l2cc_save, ARRAY_SIZE(exynos4_l2cc_save)); 18583014579SKukjin Kim s3c_pm_do_save(exynos4_epll_save, ARRAY_SIZE(exynos4_epll_save)); 18683014579SKukjin Kim s3c_pm_do_save(exynos4_vpll_save, ARRAY_SIZE(exynos4_vpll_save)); 18783014579SKukjin Kim 18883014579SKukjin Kim tmp = __raw_readl(S5P_INFORM1); 18983014579SKukjin Kim 19083014579SKukjin Kim /* Set value of power down register for sleep mode */ 19183014579SKukjin Kim 19283014579SKukjin Kim exynos4_sys_powerdown_conf(SYS_SLEEP); 19383014579SKukjin Kim __raw_writel(S5P_CHECK_SLEEP, S5P_INFORM1); 19483014579SKukjin Kim 19583014579SKukjin Kim /* ensure at least INFORM0 has the resume address */ 19683014579SKukjin Kim 19783014579SKukjin Kim __raw_writel(virt_to_phys(s3c_cpu_resume), S5P_INFORM0); 19883014579SKukjin Kim 19983014579SKukjin Kim /* Before enter central sequence mode, clock src register have to set */ 20083014579SKukjin Kim 20183014579SKukjin Kim s3c_pm_do_restore_core(exynos4_set_clksrc, ARRAY_SIZE(exynos4_set_clksrc)); 20283014579SKukjin Kim 20383014579SKukjin Kim if (soc_is_exynos4210()) 20483014579SKukjin Kim s3c_pm_do_restore_core(exynos4210_set_clksrc, ARRAY_SIZE(exynos4210_set_clksrc)); 20583014579SKukjin Kim 20683014579SKukjin Kim } 20783014579SKukjin Kim 2084a858cfcSKay Sievers static int exynos4_pm_add(struct device *dev) 20983014579SKukjin Kim { 21083014579SKukjin Kim pm_cpu_prep = exynos4_pm_prepare; 21183014579SKukjin Kim pm_cpu_sleep = exynos4_cpu_suspend; 21283014579SKukjin Kim 21383014579SKukjin Kim return 0; 21483014579SKukjin Kim } 21583014579SKukjin Kim 21683014579SKukjin Kim /* This function copy from linux/arch/arm/kernel/smp_scu.c */ 21783014579SKukjin Kim 21883014579SKukjin Kim void exynos4_scu_enable(void __iomem *scu_base) 21983014579SKukjin Kim { 22083014579SKukjin Kim u32 scu_ctrl; 22183014579SKukjin Kim 22283014579SKukjin Kim scu_ctrl = __raw_readl(scu_base); 22383014579SKukjin Kim /* already enabled? */ 22483014579SKukjin Kim if (scu_ctrl & 1) 22583014579SKukjin Kim return; 22683014579SKukjin Kim 22783014579SKukjin Kim scu_ctrl |= 1; 22883014579SKukjin Kim __raw_writel(scu_ctrl, scu_base); 22983014579SKukjin Kim 23083014579SKukjin Kim /* 23183014579SKukjin Kim * Ensure that the data accessed by CPU0 before the SCU was 23283014579SKukjin Kim * initialised is visible to the other CPUs. 23383014579SKukjin Kim */ 23483014579SKukjin Kim flush_cache_all(); 23583014579SKukjin Kim } 23683014579SKukjin Kim 23783014579SKukjin Kim static unsigned long pll_base_rate; 23883014579SKukjin Kim 23983014579SKukjin Kim static void exynos4_restore_pll(void) 24083014579SKukjin Kim { 24183014579SKukjin Kim unsigned long pll_con, locktime, lockcnt; 24283014579SKukjin Kim unsigned long pll_in_rate; 24383014579SKukjin Kim unsigned int p_div, epll_wait = 0, vpll_wait = 0; 24483014579SKukjin Kim 24583014579SKukjin Kim if (pll_base_rate == 0) 24683014579SKukjin Kim return; 24783014579SKukjin Kim 24883014579SKukjin Kim pll_in_rate = pll_base_rate; 24983014579SKukjin Kim 25083014579SKukjin Kim /* EPLL */ 25183014579SKukjin Kim pll_con = exynos4_epll_save[0].val; 25283014579SKukjin Kim 25383014579SKukjin Kim if (pll_con & (1 << 31)) { 25483014579SKukjin Kim pll_con &= (PLL46XX_PDIV_MASK << PLL46XX_PDIV_SHIFT); 25583014579SKukjin Kim p_div = (pll_con >> PLL46XX_PDIV_SHIFT); 25683014579SKukjin Kim 25783014579SKukjin Kim pll_in_rate /= 1000000; 25883014579SKukjin Kim 25983014579SKukjin Kim locktime = (3000 / pll_in_rate) * p_div; 26083014579SKukjin Kim lockcnt = locktime * 10000 / (10000 / pll_in_rate); 26183014579SKukjin Kim 26283014579SKukjin Kim __raw_writel(lockcnt, S5P_EPLL_LOCK); 26383014579SKukjin Kim 26483014579SKukjin Kim s3c_pm_do_restore_core(exynos4_epll_save, 26583014579SKukjin Kim ARRAY_SIZE(exynos4_epll_save)); 26683014579SKukjin Kim epll_wait = 1; 26783014579SKukjin Kim } 26883014579SKukjin Kim 26983014579SKukjin Kim pll_in_rate = pll_base_rate; 27083014579SKukjin Kim 27183014579SKukjin Kim /* VPLL */ 27283014579SKukjin Kim pll_con = exynos4_vpll_save[0].val; 27383014579SKukjin Kim 27483014579SKukjin Kim if (pll_con & (1 << 31)) { 27583014579SKukjin Kim pll_in_rate /= 1000000; 27683014579SKukjin Kim /* 750us */ 27783014579SKukjin Kim locktime = 750; 27883014579SKukjin Kim lockcnt = locktime * 10000 / (10000 / pll_in_rate); 27983014579SKukjin Kim 28083014579SKukjin Kim __raw_writel(lockcnt, S5P_VPLL_LOCK); 28183014579SKukjin Kim 28283014579SKukjin Kim s3c_pm_do_restore_core(exynos4_vpll_save, 28383014579SKukjin Kim ARRAY_SIZE(exynos4_vpll_save)); 28483014579SKukjin Kim vpll_wait = 1; 28583014579SKukjin Kim } 28683014579SKukjin Kim 28783014579SKukjin Kim /* Wait PLL locking */ 28883014579SKukjin Kim 28983014579SKukjin Kim do { 29083014579SKukjin Kim if (epll_wait) { 29183014579SKukjin Kim pll_con = __raw_readl(S5P_EPLL_CON0); 29283014579SKukjin Kim if (pll_con & (1 << S5P_EPLLCON0_LOCKED_SHIFT)) 29383014579SKukjin Kim epll_wait = 0; 29483014579SKukjin Kim } 29583014579SKukjin Kim 29683014579SKukjin Kim if (vpll_wait) { 29783014579SKukjin Kim pll_con = __raw_readl(S5P_VPLL_CON0); 29883014579SKukjin Kim if (pll_con & (1 << S5P_VPLLCON0_LOCKED_SHIFT)) 29983014579SKukjin Kim vpll_wait = 0; 30083014579SKukjin Kim } 30183014579SKukjin Kim } while (epll_wait || vpll_wait); 30283014579SKukjin Kim } 30383014579SKukjin Kim 3044a858cfcSKay Sievers static struct subsys_interface exynos4_pm_interface = { 3054a858cfcSKay Sievers .name = "exynos4_pm", 3064a858cfcSKay Sievers .subsys = &exynos4_subsys, 3074a858cfcSKay Sievers .add_dev = exynos4_pm_add, 30883014579SKukjin Kim }; 30983014579SKukjin Kim 31083014579SKukjin Kim static __init int exynos4_pm_drvinit(void) 31183014579SKukjin Kim { 31283014579SKukjin Kim struct clk *pll_base; 31383014579SKukjin Kim unsigned int tmp; 31483014579SKukjin Kim 31583014579SKukjin Kim s3c_pm_init(); 31683014579SKukjin Kim 31783014579SKukjin Kim /* All wakeup disable */ 31883014579SKukjin Kim 31983014579SKukjin Kim tmp = __raw_readl(S5P_WAKEUP_MASK); 32083014579SKukjin Kim tmp |= ((0xFF << 8) | (0x1F << 1)); 32183014579SKukjin Kim __raw_writel(tmp, S5P_WAKEUP_MASK); 32283014579SKukjin Kim 32383014579SKukjin Kim pll_base = clk_get(NULL, "xtal"); 32483014579SKukjin Kim 32583014579SKukjin Kim if (!IS_ERR(pll_base)) { 32683014579SKukjin Kim pll_base_rate = clk_get_rate(pll_base); 32783014579SKukjin Kim clk_put(pll_base); 32883014579SKukjin Kim } 32983014579SKukjin Kim 3304a858cfcSKay Sievers return subsys_interface_register(&exynos4_pm_interface); 33183014579SKukjin Kim } 33283014579SKukjin Kim arch_initcall(exynos4_pm_drvinit); 33383014579SKukjin Kim 33483014579SKukjin Kim static int exynos4_pm_suspend(void) 33583014579SKukjin Kim { 33683014579SKukjin Kim unsigned long tmp; 33783014579SKukjin Kim 33883014579SKukjin Kim /* Setting Central Sequence Register for power down mode */ 33983014579SKukjin Kim 34083014579SKukjin Kim tmp = __raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION); 34183014579SKukjin Kim tmp &= ~S5P_CENTRAL_LOWPWR_CFG; 34283014579SKukjin Kim __raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION); 34383014579SKukjin Kim 34483014579SKukjin Kim if (soc_is_exynos4212()) { 34583014579SKukjin Kim tmp = __raw_readl(S5P_CENTRAL_SEQ_OPTION); 34683014579SKukjin Kim tmp &= ~(S5P_USE_STANDBYWFI_ISP_ARM | 34783014579SKukjin Kim S5P_USE_STANDBYWFE_ISP_ARM); 34883014579SKukjin Kim __raw_writel(tmp, S5P_CENTRAL_SEQ_OPTION); 34983014579SKukjin Kim } 35083014579SKukjin Kim 35183014579SKukjin Kim /* Save Power control register */ 35283014579SKukjin Kim asm ("mrc p15, 0, %0, c15, c0, 0" 35383014579SKukjin Kim : "=r" (tmp) : : "cc"); 35483014579SKukjin Kim save_arm_register[0] = tmp; 35583014579SKukjin Kim 35683014579SKukjin Kim /* Save Diagnostic register */ 35783014579SKukjin Kim asm ("mrc p15, 0, %0, c15, c0, 1" 35883014579SKukjin Kim : "=r" (tmp) : : "cc"); 35983014579SKukjin Kim save_arm_register[1] = tmp; 36083014579SKukjin Kim 36183014579SKukjin Kim return 0; 36283014579SKukjin Kim } 36383014579SKukjin Kim 36483014579SKukjin Kim static void exynos4_pm_resume(void) 36583014579SKukjin Kim { 36683014579SKukjin Kim unsigned long tmp; 36783014579SKukjin Kim 36883014579SKukjin Kim /* 36983014579SKukjin Kim * If PMU failed while entering sleep mode, WFI will be 37083014579SKukjin Kim * ignored by PMU and then exiting cpu_do_idle(). 37183014579SKukjin Kim * S5P_CENTRAL_LOWPWR_CFG bit will not be set automatically 37283014579SKukjin Kim * in this situation. 37383014579SKukjin Kim */ 37483014579SKukjin Kim tmp = __raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION); 37583014579SKukjin Kim if (!(tmp & S5P_CENTRAL_LOWPWR_CFG)) { 37683014579SKukjin Kim tmp |= S5P_CENTRAL_LOWPWR_CFG; 37783014579SKukjin Kim __raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION); 37883014579SKukjin Kim /* No need to perform below restore code */ 37983014579SKukjin Kim goto early_wakeup; 38083014579SKukjin Kim } 38183014579SKukjin Kim /* Restore Power control register */ 38283014579SKukjin Kim tmp = save_arm_register[0]; 38383014579SKukjin Kim asm volatile ("mcr p15, 0, %0, c15, c0, 0" 38483014579SKukjin Kim : : "r" (tmp) 38583014579SKukjin Kim : "cc"); 38683014579SKukjin Kim 38783014579SKukjin Kim /* Restore Diagnostic register */ 38883014579SKukjin Kim tmp = save_arm_register[1]; 38983014579SKukjin Kim asm volatile ("mcr p15, 0, %0, c15, c0, 1" 39083014579SKukjin Kim : : "r" (tmp) 39183014579SKukjin Kim : "cc"); 39283014579SKukjin Kim 39383014579SKukjin Kim /* For release retention */ 39483014579SKukjin Kim 39583014579SKukjin Kim __raw_writel((1 << 28), S5P_PAD_RET_MAUDIO_OPTION); 39683014579SKukjin Kim __raw_writel((1 << 28), S5P_PAD_RET_GPIO_OPTION); 39783014579SKukjin Kim __raw_writel((1 << 28), S5P_PAD_RET_UART_OPTION); 39883014579SKukjin Kim __raw_writel((1 << 28), S5P_PAD_RET_MMCA_OPTION); 39983014579SKukjin Kim __raw_writel((1 << 28), S5P_PAD_RET_MMCB_OPTION); 40083014579SKukjin Kim __raw_writel((1 << 28), S5P_PAD_RET_EBIA_OPTION); 40183014579SKukjin Kim __raw_writel((1 << 28), S5P_PAD_RET_EBIB_OPTION); 40283014579SKukjin Kim 40383014579SKukjin Kim s3c_pm_do_restore_core(exynos4_core_save, ARRAY_SIZE(exynos4_core_save)); 40483014579SKukjin Kim 40583014579SKukjin Kim exynos4_restore_pll(); 40683014579SKukjin Kim 40783014579SKukjin Kim exynos4_scu_enable(S5P_VA_SCU); 40883014579SKukjin Kim 40983014579SKukjin Kim #ifdef CONFIG_CACHE_L2X0 41083014579SKukjin Kim s3c_pm_do_restore_core(exynos4_l2cc_save, ARRAY_SIZE(exynos4_l2cc_save)); 41183014579SKukjin Kim outer_inv_all(); 41283014579SKukjin Kim /* enable L2X0*/ 41383014579SKukjin Kim writel_relaxed(1, S5P_VA_L2CC + L2X0_CTRL); 41483014579SKukjin Kim #endif 41583014579SKukjin Kim 41683014579SKukjin Kim early_wakeup: 41783014579SKukjin Kim return; 41883014579SKukjin Kim } 41983014579SKukjin Kim 42083014579SKukjin Kim static struct syscore_ops exynos4_pm_syscore_ops = { 42183014579SKukjin Kim .suspend = exynos4_pm_suspend, 42283014579SKukjin Kim .resume = exynos4_pm_resume, 42383014579SKukjin Kim }; 42483014579SKukjin Kim 42583014579SKukjin Kim static __init int exynos4_pm_syscore_init(void) 42683014579SKukjin Kim { 42783014579SKukjin Kim register_syscore_ops(&exynos4_pm_syscore_ops); 42883014579SKukjin Kim return 0; 42983014579SKukjin Kim } 43083014579SKukjin Kim arch_initcall(exynos4_pm_syscore_init); 431