@@ -140,6 +140,7 @@ static enum exynos4_soc exynos4_soc;
#ifdef CONFIG_PM_SLEEP
static struct samsung_clk_reg_dump *exynos4_save_common;
static struct samsung_clk_reg_dump *exynos4_save_soc;
+static struct samsung_clk_reg_dump *exynos4_save_pll;
/*
* list of controller registers to be saved and restored during a
@@ -165,6 +166,17 @@ static unsigned long exynos4x12_clk_save[] __initdata = {
E4X12_MPLL_CON0,
};
+static unsigned long exynos4_clk_pll_regs[] __initdata = {
+ EPLL_LOCK,
+ VPLL_LOCK,
+ EPLL_CON0,
+ EPLL_CON1,
+ EPLL_CON2,
+ VPLL_CON0,
+ VPLL_CON1,
+ VPLL_CON2,
+};
+
static unsigned long exynos4_clk_regs[] __initdata = {
SRC_LEFTBUS,
DIV_LEFTBUS,
@@ -172,12 +184,6 @@ static unsigned long exynos4_clk_regs[] __initdata = {
SRC_RIGHTBUS,
DIV_RIGHTBUS,
GATE_IP_RIGHTBUS,
- EPLL_CON0,
- EPLL_CON1,
- EPLL_CON2,
- VPLL_CON0,
- VPLL_CON1,
- VPLL_CON2,
SRC_TOP0,
SRC_TOP1,
SRC_CAM,
@@ -238,23 +244,70 @@ static unsigned long exynos4_clk_regs[] __initdata = {
GATE_IP_CPU,
};
+static const struct samsung_clk_reg_dump src_mask_suspend[] = {
+ { .offset = SRC_MASK_TOP, .value = 0x00000001, },
+ { .offset = SRC_MASK_CAM, .value = 0x11111111, },
+ { .offset = SRC_MASK_TV, .value = 0x00000111, },
+ { .offset = SRC_MASK_LCD0, .value = 0x00001111, },
+ { .offset = SRC_MASK_MAUDIO, .value = 0x00000001, },
+ { .offset = SRC_MASK_FSYS, .value = 0x01011111, },
+ { .offset = SRC_MASK_PERIL0, .value = 0x01111111, },
+ { .offset = SRC_MASK_PERIL1, .value = 0x01110111, },
+ { .offset = SRC_MASK_DMC, .value = 0x00010000, },
+};
+
+static const struct samsung_clk_reg_dump src_mask_suspend_e4210[] = {
+ { .offset = E4210_SRC_MASK_LCD1, .value = 0x00001111, },
+};
+
+#define PLL_ENABLED (1 << 31)
+#define PLL_LOCKED (1 << 29)
+
+static void exynos4_clk_wait_for_pll(u32 reg)
+{
+ u32 pll_con;
+
+ pll_con = readl(reg_base + reg);
+ if (!(pll_con & PLL_ENABLED))
+ return;
+
+ while (!(pll_con & PLL_LOCKED)) {
+ cpu_relax();
+ pll_con = readl(reg_base + reg);
+ }
+}
+
static int exynos4_clk_suspend(void)
{
samsung_clk_save(reg_base, exynos4_save_common,
ARRAY_SIZE(exynos4_clk_regs));
+ samsung_clk_save(reg_base, exynos4_save_pll,
+ ARRAY_SIZE(exynos4_clk_pll_regs));
- if (exynos4_soc == EXYNOS4210)
+ if (exynos4_soc == EXYNOS4210) {
samsung_clk_save(reg_base, exynos4_save_soc,
ARRAY_SIZE(exynos4210_clk_save));
- else
+ samsung_clk_restore(reg_base, src_mask_suspend_e4210,
+ ARRAY_SIZE(src_mask_suspend_e4210));
+ } else {
samsung_clk_save(reg_base, exynos4_save_soc,
ARRAY_SIZE(exynos4x12_clk_save));
+ }
+
+ samsung_clk_restore(reg_base, src_mask_suspend,
+ ARRAY_SIZE(src_mask_suspend));
return 0;
}
static void exynos4_clk_resume(void)
{
+ samsung_clk_restore(reg_base, exynos4_save_pll,
+ ARRAY_SIZE(exynos4_clk_pll_regs));
+
+ exynos4_clk_wait_for_pll(EPLL_CON0);
+ exynos4_clk_wait_for_pll(VPLL_CON0);
+
samsung_clk_restore(reg_base, exynos4_save_common,
ARRAY_SIZE(exynos4_clk_regs));
@@ -289,9 +342,16 @@ static void exynos4_clk_sleep_init(void)
if (!exynos4_save_soc)
goto err_common;
+ exynos4_save_pll = samsung_clk_alloc_reg_dump(exynos4_clk_pll_regs,
+ ARRAY_SIZE(exynos4_clk_pll_regs));
+ if (!exynos4_save_pll)
+ goto err_soc;
+
register_syscore_ops(&exynos4_clk_syscore_ops);
return;
+err_soc:
+ kfree(exynos4_save_soc);
err_common:
kfree(exynos4_save_common);
err_warn: