diff options
Diffstat (limited to 'arch/arm/mach-exynos/pm.c')
-rw-r--r-- | arch/arm/mach-exynos/pm.c | 134 |
1 files changed, 103 insertions, 31 deletions
diff --git a/arch/arm/mach-exynos/pm.c b/arch/arm/mach-exynos/pm.c index e5421faff59..362b8c45317 100644 --- a/arch/arm/mach-exynos/pm.c +++ b/arch/arm/mach-exynos/pm.c @@ -19,11 +19,14 @@ #include <linux/io.h> #include <linux/err.h> #include <linux/clk.h> +#include <linux/suspend.h> +#include <linux/delay.h> #include <asm/cacheflush.h> #include <asm/firmware.h> #include <asm/hardware/cache-l2x0.h> #include <asm/smp_scu.h> +#include <asm/cpu.h> #include <plat/cpu.h> #include <plat/pm.h> @@ -39,6 +42,9 @@ #include "common.h" #include "smc.h" +#define POWER_UP_DELAY_MS (5) +#define msecs_to_loops(ms) (loops_per_jiffy / 1000 * HZ * ms) + static struct sleep_save exynos4_set_clksrc[] = { { .reg = EXYNOS4_CLKSRC_MASK_TOP , .val = 0x00000001, }, { .reg = EXYNOS4_CLKSRC_MASK_CAM , .val = 0x11111111, }, @@ -89,6 +95,45 @@ static int exynos_cpu_suspend(unsigned long arg) outer_flush_all(); #endif + if (soc_is_exynos3250()) { + unsigned int tmp, loops, cpu; + + /* Set clock source for PWI */ + tmp = __raw_readl(EXYNOS3_CLKSRC_ACP); + tmp &= ~(0xF << 16); + tmp |= (0x1 << 16); + __raw_writel(tmp, EXYNOS3_CLKSRC_ACP); + + + /* FIXME: W/A code for prevent A7hotplug in fail */ + call_firmware_op(writesfr, 0x02020004, 0); + call_firmware_op(writesfr, 0x02020008, 0); + call_firmware_op(writesfr, 0x0202000C, 0); + + /* + * FIXME: W/A code to prevent suspend fail by writing + * CORE_LOCAL_PWER_EN/CORE_AUTOWAKEUP_EN again to + * ARM_COREx_CONFIGURATION register. + */ + for_each_possible_cpu(cpu) { + tmp = __raw_readl(S5P_ARM_CORE_CONFIGURATION(cpu)); + tmp |= S5P_CORE_LOCAL_PWR_EN; + tmp |= S5P_CORE_AUTOWAKEUP_EN; + __raw_writel(tmp, S5P_ARM_CORE_CONFIGURATION(cpu)); + } + + /* FIXME: W/A code, Wait until changing core status for 5ms */ + loops = msecs_to_loops(5); + for_each_possible_cpu(cpu) { + do { + if (--loops == 0) + BUG(); + tmp = __raw_readl(S5P_ARM_CORE_CONFIGURATION(cpu)); + } while ((tmp & S5P_CORE_LOCAL_PWR_EN) + != S5P_CORE_LOCAL_PWR_EN); + } + } + /* issue the standby signal into the pm unit. */ if (call_firmware_op(suspend, virt_to_phys(s3c_cpu_resume)) == -ENOSYS) cpu_do_idle(); @@ -101,31 +146,41 @@ static void exynos_pm_prepare(void) { unsigned int tmp; - s3c_pm_do_save(exynos_core_save, ARRAY_SIZE(exynos_core_save)); - if (!soc_is_exynos5250()) { - s3c_pm_do_save(exynos4_epll_save, ARRAY_SIZE(exynos4_epll_save)); - s3c_pm_do_save(exynos4_vpll_save, ARRAY_SIZE(exynos4_vpll_save)); + if (soc_is_exynos3250()) { + /* Decides whether to use retention capability */ + tmp = __raw_readl(EXYNOS3_ARM_L2_OPTION); + tmp &= ~EXYNOS3_OPTION_USE_RETENTION; + __raw_writel(tmp, EXYNOS3_ARM_L2_OPTION); } else { - s3c_pm_do_save(exynos5_sys_save, ARRAY_SIZE(exynos5_sys_save)); - /* Disable USE_RETENTION of JPEG_MEM_OPTION */ - tmp = __raw_readl(EXYNOS5_JPEG_MEM_OPTION); - tmp &= ~EXYNOS5_OPTION_USE_RETENTION; - __raw_writel(tmp, EXYNOS5_JPEG_MEM_OPTION); + s3c_pm_do_save(exynos_core_save, ARRAY_SIZE(exynos_core_save)); + + if (!soc_is_exynos5250()) { + s3c_pm_do_save(exynos4_epll_save, ARRAY_SIZE(exynos4_epll_save)); + s3c_pm_do_save(exynos4_vpll_save, ARRAY_SIZE(exynos4_vpll_save)); + } else { + s3c_pm_do_save(exynos5_sys_save, ARRAY_SIZE(exynos5_sys_save)); + /* Disable USE_RETENTION of JPEG_MEM_OPTION */ + tmp = __raw_readl(EXYNOS5_JPEG_MEM_OPTION); + tmp &= ~EXYNOS5_OPTION_USE_RETENTION; + __raw_writel(tmp, EXYNOS5_JPEG_MEM_OPTION); + } } /* Set value of power down register for sleep mode */ exynos_sys_powerdown_conf(SYS_SLEEP); - __raw_writel(S5P_CHECK_SLEEP, S5P_INFORM1); - /* ensure at least INFORM0 has the resume address */ + if (!soc_is_exynos3250()) { + __raw_writel(S5P_CHECK_SLEEP, S5P_INFORM1); - __raw_writel(virt_to_phys(s3c_cpu_resume), S5P_INFORM0); + /* ensure at least INFORM0 has the resume address */ + __raw_writel(virt_to_phys(s3c_cpu_resume), S5P_INFORM0); + } /* Before enter central sequence mode, clock src register have to set */ - if (!soc_is_exynos5250()) + if (!soc_is_exynos3250() && !soc_is_exynos5250()) s3c_pm_do_restore_core(exynos4_set_clksrc, ARRAY_SIZE(exynos4_set_clksrc)); if (soc_is_exynos4210()) @@ -222,7 +277,6 @@ static __init int exynos_pm_drvinit(void) s3c_pm_init(); /* All wakeup disable */ - tmp = __raw_readl(S5P_WAKEUP_MASK); tmp |= ((0xFF << 8) | (0x1F << 1)); __raw_writel(tmp, S5P_WAKEUP_MASK); @@ -252,19 +306,26 @@ static int exynos_pm_suspend(void) /* Setting SEQ_OPTION register */ - tmp = (S5P_USE_STANDBY_WFI0 | S5P_USE_STANDBY_WFE0); - __raw_writel(tmp, S5P_CENTRAL_SEQ_OPTION); - - if (!soc_is_exynos5250()) { - /* Save Power control register */ - asm ("mrc p15, 0, %0, c15, c0, 0" - : "=r" (tmp) : : "cc"); - save_arm_register[0] = tmp; - - /* Save Diagnostic register */ - asm ("mrc p15, 0, %0, c15, c0, 1" - : "=r" (tmp) : : "cc"); - save_arm_register[1] = tmp; + if (soc_is_exynos3250()) { + /* FIXME: PMU Config before power down*/ + tmp = __raw_readl(EXYNOS3_CENTRAL_SEQ_CONFIGURATION_COREBLK); + tmp &= ~S5P_CENTRAL_LOWPWR_CFG; + __raw_writel(tmp, EXYNOS3_CENTRAL_SEQ_CONFIGURATION_COREBLK); + } else { + tmp = (S5P_USE_STANDBY_WFI0 | S5P_USE_STANDBY_WFE0); + __raw_writel(tmp, S5P_CENTRAL_SEQ_OPTION); + + if (!soc_is_exynos5250()) { + /* Save Power control register */ + asm ("mrc p15, 0, %0, c15, c0, 0" + : "=r" (tmp) : : "cc"); + save_arm_register[0] = tmp; + + /* Save Diagnostic register */ + asm ("mrc p15, 0, %0, c15, c0, 1" + : "=r" (tmp) : : "cc"); + save_arm_register[1] = tmp; + } } return 0; @@ -274,6 +335,9 @@ static void exynos_pm_resume(void) { unsigned long tmp; + if (soc_is_exynos3250()) + __raw_writel(S5P_USE_STANDBY_WFI_ALL, S5P_CENTRAL_SEQ_OPTION); + /* * If PMU failed while entering sleep mode, WFI will be * ignored by PMU and then exiting cpu_do_idle(). @@ -290,7 +354,7 @@ static void exynos_pm_resume(void) goto early_wakeup; } - if (!soc_is_exynos5250() + if (!soc_is_exynos3250() && !soc_is_exynos5250() && call_firmware_op(c15resume, save_arm_register) == -ENOSYS) { /* Restore Power control register */ @@ -316,16 +380,23 @@ static void exynos_pm_resume(void) __raw_writel((1 << 28), S5P_PAD_RET_EBIA_OPTION); __raw_writel((1 << 28), S5P_PAD_RET_EBIB_OPTION); + if (soc_is_exynos3250()) { + __raw_writel((1 << 28), S5P_PAD_RET_MMC2_OPTION); + __raw_writel((1 << 28), S5P_PAD_RET_SPI_OPTION); + } + if (soc_is_exynos5250()) s3c_pm_do_restore(exynos5_sys_save, ARRAY_SIZE(exynos5_sys_save)); - s3c_pm_do_restore_core(exynos_core_save, ARRAY_SIZE(exynos_core_save)); + if (!soc_is_exynos3250()) + s3c_pm_do_restore_core(exynos_core_save, ARRAY_SIZE(exynos_core_save)); - if (!soc_is_exynos5250()) { + if (!soc_is_exynos3250() && !soc_is_exynos5250()) { exynos4_restore_pll(); #ifdef CONFIG_SMP + if (!soc_is_exynos3250()) scu_enable(S5P_VA_SCU); #endif @@ -339,7 +410,8 @@ static void exynos_pm_resume(void) early_wakeup: /* Clear SLEEP mode set in INFORM1 */ - __raw_writel(0x0, S5P_INFORM1); + if (!soc_is_exynos3250()) + __raw_writel(0x0, S5P_INFORM1); call_firmware_op(resume); return; |