summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChanwoo Choi <cw00.choi@samsung.com>2014-07-10 21:37:13 +0900
committerChanho Park <chanho61.park@samsung.com>2014-11-18 12:00:12 +0900
commit9a81acaa551c9dbc47e5129381d924882f158b30 (patch)
treee050e5697f5a54528128b2dbea61af6c0b087691
parent43b8bfbf2fcfcf54431518392d0115a3c98352f6 (diff)
downloadlinux-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>
-rw-r--r--arch/arm/mach-exynos/common.c10
-rw-r--r--arch/arm/mach-exynos/include/mach/regs-clock.h3
-rw-r--r--arch/arm/mach-exynos/include/mach/regs-pmu.h6
-rw-r--r--arch/arm/mach-exynos/pm.c134
-rw-r--r--arch/arm/plat-samsung/include/plat/map-s5p.h3
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)