diff options
author | Chanwoo Choi <cw00.choi@samsung.com> | 2014-07-10 21:37:13 +0900 |
---|---|---|
committer | Chanho Park <chanho61.park@samsung.com> | 2014-11-18 12:00:12 +0900 |
commit | 9a81acaa551c9dbc47e5129381d924882f158b30 (patch) | |
tree | e050e5697f5a54528128b2dbea61af6c0b087691 /arch/arm | |
parent | 43b8bfbf2fcfcf54431518392d0115a3c98352f6 (diff) | |
download | linux-3.10-9a81acaa551c9dbc47e5129381d924882f158b30.tar.gz linux-3.10-9a81acaa551c9dbc47e5129381d924882f158b30.tar.bz2 linux-3.10-9a81acaa551c9dbc47e5129381d924882f158b30.zip |
ARM: EXYNOS: Add support for suspend-to-ram of Exynos3250
This patch add support for suspend-to-ram of Exynos3250 based on Cortex-A7
dual-core. The Exynos3250 hasn't L2 cache.
Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
Diffstat (limited to 'arch/arm')
-rw-r--r-- | arch/arm/mach-exynos/common.c | 10 | ||||
-rw-r--r-- | arch/arm/mach-exynos/include/mach/regs-clock.h | 3 | ||||
-rw-r--r-- | arch/arm/mach-exynos/include/mach/regs-pmu.h | 6 | ||||
-rw-r--r-- | arch/arm/mach-exynos/pm.c | 134 | ||||
-rw-r--r-- | arch/arm/plat-samsung/include/plat/map-s5p.h | 3 |
5 files changed, 125 insertions, 31 deletions
diff --git a/arch/arm/mach-exynos/common.c b/arch/arm/mach-exynos/common.c index a68da003299..2cb94358e4d 100644 --- a/arch/arm/mach-exynos/common.c +++ b/arch/arm/mach-exynos/common.c @@ -168,6 +168,16 @@ static struct map_desc exynos3_iodesc[] __initdata = { .length = SZ_128K, .type = MT_DEVICE, }, { + .virtual = (unsigned long)S5P_VA_CMU_ACP, + .pfn = __phys_to_pfn(EXYNOS3_PA_CMU_ACP), + .length = SZ_16K, + .type = MT_DEVICE, + }, { + .virtual = (unsigned long)S5P_VA_CMU_DMC, + .pfn = __phys_to_pfn(EXYNOS3_PA_CMU_DMC), + .length = SZ_16K, + .type = MT_DEVICE, + }, { .virtual = (unsigned long)S5P_VA_PMU, .pfn = __phys_to_pfn(EXYNOS3_PA_PMU), .length = SZ_64K, diff --git a/arch/arm/mach-exynos/include/mach/regs-clock.h b/arch/arm/mach-exynos/include/mach/regs-clock.h index 0cf9ad0af65..5e0f6c41006 100644 --- a/arch/arm/mach-exynos/include/mach/regs-clock.h +++ b/arch/arm/mach-exynos/include/mach/regs-clock.h @@ -18,6 +18,9 @@ #define EXYNOS_CLKREG(x) (S5P_VA_CMU + (x)) +#define EXYNOS3_MIF_R_REG(x) (S5P_VA_CMU_ACP + (x)) /* 0x1045_0000 */ +#define EXYNOS3_CLKSRC_ACP EXYNOS3_MIF_R_REG(0x0300) + #define EXYNOS4_CLKDIV_LEFTBUS EXYNOS_CLKREG(0x04500) #define EXYNOS4_CLKDIV_STAT_LEFTBUS EXYNOS_CLKREG(0x04600) #define EXYNOS4_CLKGATE_IP_LEFTBUS EXYNOS_CLKREG(0x04800) diff --git a/arch/arm/mach-exynos/include/mach/regs-pmu.h b/arch/arm/mach-exynos/include/mach/regs-pmu.h index 3fa864e9de1..05d405ab655 100644 --- a/arch/arm/mach-exynos/include/mach/regs-pmu.h +++ b/arch/arm/mach-exynos/include/mach/regs-pmu.h @@ -163,12 +163,14 @@ #define S5P_GPS_ALIVE_OPTION S5P_PMUREG(0x3D08) #define S5P_PAD_RET_MAUDIO_OPTION S5P_PMUREG(0x3028) +#define S5P_PAD_RET_MMC2_OPTION S5P_PMUREG(0x30C8) #define S5P_PAD_RET_GPIO_OPTION S5P_PMUREG(0x3108) #define S5P_PAD_RET_UART_OPTION S5P_PMUREG(0x3128) #define S5P_PAD_RET_MMCA_OPTION S5P_PMUREG(0x3148) #define S5P_PAD_RET_MMCB_OPTION S5P_PMUREG(0x3168) #define S5P_PAD_RET_EBIA_OPTION S5P_PMUREG(0x3188) #define S5P_PAD_RET_EBIB_OPTION S5P_PMUREG(0x31A8) +#define S5P_PAD_RET_SPI_OPTION S5P_PMUREG(0x31C8) #define S5P_PS_HOLD_CONTROL S5P_PMUREG(0x330C) #define S5P_PS_HOLD_EN (1 << 31) @@ -266,6 +268,8 @@ /* For EXYNOS3 */ #define EXYNOS3_COREPORESET(cpu) ((1 << 4) << cpu) +#define EXYNOS3_CENTRAL_SEQ_CONFIGURATION_COREBLK S5P_PMUREG(0x0240) + #define EXYNOS3_ARM_CORE0_SYS_PWR_REG S5P_PMUREG(0x1000) #define EXYNOS3_DIS_IRQ_ARM_CORE0_LOCAL_SYS_PWR_REG S5P_PMUREG(0x1004) #define EXYNOS3_DIS_IRQ_ARM_CORE0_CENTRAL_SYS_PWR_REG S5P_PMUREG(0x1008) @@ -361,6 +365,7 @@ #define EXYNOS3_ARM_CORE_OPTION(_nr) (EXYNOS3_ARM_CORE0_OPTION + ((_nr) * 0x80)) #define EXYNOS3_ARM_COMMON_OPTION S5P_PMUREG(0x2408) +#define EXYNOS3_ARM_L2_OPTION S5P_PMUREG(0x2608) #define EXYNOS3_TOP_PWR_OPTION S5P_PMUREG(0x2C48) #define EXYNOS3_CORE_TOP_PWR_OPTION S5P_PMUREG(0x2CA8) #define EXYNOS3_CORE_TOP_PWR_DURATION S5P_PMUREG(0x2CB0) @@ -373,6 +378,7 @@ #define EXYNOS3_OPTION_USE_SC_COUNTER (1 << 0) #define EXYNOS3_OPTION_USE_SC_FEEDBACK (1 << 1) #define EXYNOS3_OPTION_SKIP_DEACTIVATE_ACEACP_IN_PWDN (1 << 7) +#define EXYNOS3_OPTION_USE_RETENTION (1 << 4) /* For EXYNOS5 */ 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; diff --git a/arch/arm/plat-samsung/include/plat/map-s5p.h b/arch/arm/plat-samsung/include/plat/map-s5p.h index c18678610bc..791734aa1b1 100644 --- a/arch/arm/plat-samsung/include/plat/map-s5p.h +++ b/arch/arm/plat-samsung/include/plat/map-s5p.h @@ -41,6 +41,9 @@ #define S5P_VA_GIC_CPU S3C_ADDR(0x02810000) #define S5P_VA_GIC_DIST S3C_ADDR(0x02820000) +#define S5P_VA_CMU_ACP S3C_ADDR(0x02B20000) +#define S5P_VA_CMU_DMC S3C_ADDR(0x02B30000) + #define VA_VIC(x) (S3C_VA_IRQ + ((x) * 0x10000)) #define VA_VIC0 VA_VIC(0) #define VA_VIC1 VA_VIC(1) |