Commit 9a4d8a8c authored by Marek Szyprowski's avatar Marek Szyprowski Committed by Sylwester Nawrocki

clk: samsung: exynos4: Use generic helper for handling suspend/resume

Replace common suspend/resume handling code by generic helper.
Handling of PLLs is a bit different in generic code, as they are handled
in the same way as other clock registers. Such approach was already used
on later Exynos SoCs and worked fine. Tests have shown that it works also
on Exynos4 SoCs and significantly simplifies the code.
Signed-off-by: default avatarMarek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: default avatarSylwester Nawrocki <snawrocki@kernel.org>
parent 8bf27eaa
......@@ -16,7 +16,6 @@
#include <linux/clk-provider.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/syscore_ops.h>
#include "clk.h"
#include "clk-cpu.h"
......@@ -157,14 +156,6 @@ enum exynos4_plls {
static void __iomem *reg_base;
static enum exynos4_soc exynos4_soc;
/*
* Support for CMU save/restore across system suspends
*/
#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
* suspend/resume cycle.
......@@ -192,7 +183,7 @@ static const unsigned long exynos4x12_clk_save[] __initconst = {
E4X12_PWR_CTRL2,
};
static const unsigned long exynos4_clk_pll_regs[] __initconst = {
static const unsigned long exynos4_clk_regs[] __initconst = {
EPLL_LOCK,
VPLL_LOCK,
EPLL_CON0,
......@@ -201,9 +192,6 @@ static const unsigned long exynos4_clk_pll_regs[] __initconst = {
VPLL_CON0,
VPLL_CON1,
VPLL_CON2,
};
static const unsigned long exynos4_clk_regs[] __initconst = {
SRC_LEFTBUS,
DIV_LEFTBUS,
GATE_IP_LEFTBUS,
......@@ -276,6 +264,8 @@ static const unsigned long exynos4_clk_regs[] __initconst = {
};
static const struct samsung_clk_reg_dump src_mask_suspend[] = {
{ .offset = VPLL_CON0, .value = 0x80600302, },
{ .offset = EPLL_CON0, .value = 0x806F0302, },
{ .offset = SRC_MASK_TOP, .value = 0x00000001, },
{ .offset = SRC_MASK_CAM, .value = 0x11111111, },
{ .offset = SRC_MASK_TV, .value = 0x00000111, },
......@@ -291,123 +281,6 @@ 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_enable_pll(u32 reg)
{
u32 pll_con = readl(reg_base + reg);
pll_con |= PLL_ENABLED;
writel(pll_con, reg_base + reg);
while (!(pll_con & PLL_LOCKED)) {
cpu_relax();
pll_con = readl(reg_base + reg);
}
}
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));
exynos4_clk_enable_pll(EPLL_CON0);
exynos4_clk_enable_pll(VPLL_CON0);
if (exynos4_soc == EXYNOS4210) {
samsung_clk_save(reg_base, exynos4_save_soc,
ARRAY_SIZE(exynos4210_clk_save));
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));
if (exynos4_soc == EXYNOS4210)
samsung_clk_restore(reg_base, exynos4_save_soc,
ARRAY_SIZE(exynos4210_clk_save));
else
samsung_clk_restore(reg_base, exynos4_save_soc,
ARRAY_SIZE(exynos4x12_clk_save));
}
static struct syscore_ops exynos4_clk_syscore_ops = {
.suspend = exynos4_clk_suspend,
.resume = exynos4_clk_resume,
};
static void __init exynos4_clk_sleep_init(void)
{
exynos4_save_common = samsung_clk_alloc_reg_dump(exynos4_clk_regs,
ARRAY_SIZE(exynos4_clk_regs));
if (!exynos4_save_common)
goto err_warn;
if (exynos4_soc == EXYNOS4210)
exynos4_save_soc = samsung_clk_alloc_reg_dump(
exynos4210_clk_save,
ARRAY_SIZE(exynos4210_clk_save));
else
exynos4_save_soc = samsung_clk_alloc_reg_dump(
exynos4x12_clk_save,
ARRAY_SIZE(exynos4x12_clk_save));
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:
pr_warn("%s: failed to allocate sleep save data, no sleep support!\n",
__func__);
}
#else
static void __init exynos4_clk_sleep_init(void) {}
#endif
/* list of all parent clock list */
PNAME(mout_apll_p) = { "fin_pll", "fout_apll", };
PNAME(mout_mpll_p) = { "fin_pll", "fout_mpll", };
......@@ -1532,7 +1405,17 @@ static void __init exynos4_clk_init(struct device_node *np,
if (soc == EXYNOS4X12)
exynos4x12_core_down_clock();
exynos4_clk_sleep_init();
samsung_clk_extended_sleep_init(reg_base,
exynos4_clk_regs, ARRAY_SIZE(exynos4_clk_regs),
src_mask_suspend, ARRAY_SIZE(src_mask_suspend));
if (exynos4_soc == EXYNOS4210)
samsung_clk_extended_sleep_init(reg_base,
exynos4210_clk_save, ARRAY_SIZE(exynos4210_clk_save),
src_mask_suspend_e4210, ARRAY_SIZE(src_mask_suspend_e4210));
else
samsung_clk_sleep_init(reg_base, exynos4x12_clk_save,
ARRAY_SIZE(exynos4x12_clk_save));
samsung_clk_of_add_provider(np, ctx);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment