Merge tag 'samsung-pm-1' of http://git.kernel.org/pub/scm/linux/kernel/git/kgene...
authorArnd Bergmann <arnd@arndb.de>
Sat, 29 Mar 2014 00:47:15 +0000 (01:47 +0100)
committerArnd Bergmann <arnd@arndb.de>
Sat, 29 Mar 2014 00:47:15 +0000 (01:47 +0100)
Merge "Samsung S2R PM updates for v3.15" from Kukjin Kim:

From Tomasz Figa:
This series reworks suspend/resume handling of Samsung clock drivers
to cover more SoC specific aspects that are beyond simple register
save and restore. The goal is to have all the suspend/resume code
that touches the clock controller in single place, which is the clock
driver.

* tag 'samsung-pm-1' of http://git.kernel.org/pub/scm/linux/kernel/git/kgene/linux-samsung:
  ARM: EXYNOS: Drop legacy Exynos4 clock suspend/resume code
  clk: samsung: exynos4: Add remaining suspend/resume handling
  clk: samsung: Drop old suspend/resume code
  clk: samsung: s3c64xx: Move suspend/resume handling to SoC driver
  clk: samsung: exynos5420: Move suspend/resume handling to SoC driver
  clk: samsung: exynos5250: Move suspend/resume handling to SoC driver
  clk: samsung: exynos4: Move suspend/resume handling to SoC driver
  clk: samsung: Provide common helpers for register save/restore
  clk: exynos4: Remove remnants of non-DT support

Acked-by: Mike Turquette <mturquette@linaro.org>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
arch/arm/mach-exynos/pm.c
drivers/clk/samsung/clk-exynos4.c
drivers/clk/samsung/clk-exynos5250.c
drivers/clk/samsung/clk-exynos5420.c
drivers/clk/samsung/clk-exynos5440.c
drivers/clk/samsung/clk-s3c64xx.c
drivers/clk/samsung/clk.c
drivers/clk/samsung/clk.h

index e00025b..ba18214 100644 (file)
 #include "common.h"
 #include "regs-pmu.h"
 
-#define EXYNOS4_EPLL_LOCK                      (S5P_VA_CMU + 0x0C010)
-#define EXYNOS4_VPLL_LOCK                      (S5P_VA_CMU + 0x0C020)
-
-#define EXYNOS4_EPLL_CON0                      (S5P_VA_CMU + 0x0C110)
-#define EXYNOS4_EPLL_CON1                      (S5P_VA_CMU + 0x0C114)
-#define EXYNOS4_VPLL_CON0                      (S5P_VA_CMU + 0x0C120)
-#define EXYNOS4_VPLL_CON1                      (S5P_VA_CMU + 0x0C124)
-
-#define EXYNOS4_CLKSRC_MASK_TOP                        (S5P_VA_CMU + 0x0C310)
-#define EXYNOS4_CLKSRC_MASK_CAM                        (S5P_VA_CMU + 0x0C320)
-#define EXYNOS4_CLKSRC_MASK_TV                 (S5P_VA_CMU + 0x0C324)
-#define EXYNOS4_CLKSRC_MASK_LCD0               (S5P_VA_CMU + 0x0C334)
-#define EXYNOS4_CLKSRC_MASK_MAUDIO             (S5P_VA_CMU + 0x0C33C)
-#define EXYNOS4_CLKSRC_MASK_FSYS               (S5P_VA_CMU + 0x0C340)
-#define EXYNOS4_CLKSRC_MASK_PERIL0             (S5P_VA_CMU + 0x0C350)
-#define EXYNOS4_CLKSRC_MASK_PERIL1             (S5P_VA_CMU + 0x0C354)
-
-#define EXYNOS4_CLKSRC_MASK_DMC                        (S5P_VA_CMU + 0x10300)
-
-#define EXYNOS4_EPLLCON0_LOCKED_SHIFT          (29)
-#define EXYNOS4_VPLLCON0_LOCKED_SHIFT          (29)
-
-#define EXYNOS4210_CLKSRC_MASK_LCD1            (S5P_VA_CMU + 0x0C338)
-
-static const struct sleep_save exynos4_set_clksrc[] = {
-       { .reg = EXYNOS4_CLKSRC_MASK_TOP                , .val = 0x00000001, },
-       { .reg = EXYNOS4_CLKSRC_MASK_CAM                , .val = 0x11111111, },
-       { .reg = EXYNOS4_CLKSRC_MASK_TV                 , .val = 0x00000111, },
-       { .reg = EXYNOS4_CLKSRC_MASK_LCD0               , .val = 0x00001111, },
-       { .reg = EXYNOS4_CLKSRC_MASK_MAUDIO             , .val = 0x00000001, },
-       { .reg = EXYNOS4_CLKSRC_MASK_FSYS               , .val = 0x01011111, },
-       { .reg = EXYNOS4_CLKSRC_MASK_PERIL0             , .val = 0x01111111, },
-       { .reg = EXYNOS4_CLKSRC_MASK_PERIL1             , .val = 0x01110111, },
-       { .reg = EXYNOS4_CLKSRC_MASK_DMC                , .val = 0x00010000, },
-};
-
-static const struct sleep_save exynos4210_set_clksrc[] = {
-       { .reg = EXYNOS4210_CLKSRC_MASK_LCD1            , .val = 0x00001111, },
-};
-
-static struct sleep_save exynos4_epll_save[] = {
-       SAVE_ITEM(EXYNOS4_EPLL_CON0),
-       SAVE_ITEM(EXYNOS4_EPLL_CON1),
-};
-
-static struct sleep_save exynos4_vpll_save[] = {
-       SAVE_ITEM(EXYNOS4_VPLL_CON0),
-       SAVE_ITEM(EXYNOS4_VPLL_CON1),
-};
-
 static struct sleep_save exynos5_sys_save[] = {
        SAVE_ITEM(EXYNOS5_SYS_I2C_CFG),
 };
@@ -124,10 +74,7 @@ static void exynos_pm_prepare(void)
 
        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 {
+       if (soc_is_exynos5250()) {
                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);
@@ -143,15 +90,6 @@ static void exynos_pm_prepare(void)
        /* 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())
-               s3c_pm_do_restore_core(exynos4_set_clksrc, ARRAY_SIZE(exynos4_set_clksrc));
-
-       if (soc_is_exynos4210())
-               s3c_pm_do_restore_core(exynos4210_set_clksrc, ARRAY_SIZE(exynos4210_set_clksrc));
-
 }
 
 static int exynos_pm_add(struct device *dev, struct subsys_interface *sif)
@@ -162,73 +100,6 @@ static int exynos_pm_add(struct device *dev, struct subsys_interface *sif)
        return 0;
 }
 
-static unsigned long pll_base_rate;
-
-static void exynos4_restore_pll(void)
-{
-       unsigned long pll_con, locktime, lockcnt;
-       unsigned long pll_in_rate;
-       unsigned int p_div, epll_wait = 0, vpll_wait = 0;
-
-       if (pll_base_rate == 0)
-               return;
-
-       pll_in_rate = pll_base_rate;
-
-       /* EPLL */
-       pll_con = exynos4_epll_save[0].val;
-
-       if (pll_con & (1 << 31)) {
-               pll_con &= (PLL46XX_PDIV_MASK << PLL46XX_PDIV_SHIFT);
-               p_div = (pll_con >> PLL46XX_PDIV_SHIFT);
-
-               pll_in_rate /= 1000000;
-
-               locktime = (3000 / pll_in_rate) * p_div;
-               lockcnt = locktime * 10000 / (10000 / pll_in_rate);
-
-               __raw_writel(lockcnt, EXYNOS4_EPLL_LOCK);
-
-               s3c_pm_do_restore_core(exynos4_epll_save,
-                                       ARRAY_SIZE(exynos4_epll_save));
-               epll_wait = 1;
-       }
-
-       pll_in_rate = pll_base_rate;
-
-       /* VPLL */
-       pll_con = exynos4_vpll_save[0].val;
-
-       if (pll_con & (1 << 31)) {
-               pll_in_rate /= 1000000;
-               /* 750us */
-               locktime = 750;
-               lockcnt = locktime * 10000 / (10000 / pll_in_rate);
-
-               __raw_writel(lockcnt, EXYNOS4_VPLL_LOCK);
-
-               s3c_pm_do_restore_core(exynos4_vpll_save,
-                                       ARRAY_SIZE(exynos4_vpll_save));
-               vpll_wait = 1;
-       }
-
-       /* Wait PLL locking */
-
-       do {
-               if (epll_wait) {
-                       pll_con = __raw_readl(EXYNOS4_EPLL_CON0);
-                       if (pll_con & (1 << EXYNOS4_EPLLCON0_LOCKED_SHIFT))
-                               epll_wait = 0;
-               }
-
-               if (vpll_wait) {
-                       pll_con = __raw_readl(EXYNOS4_VPLL_CON0);
-                       if (pll_con & (1 << EXYNOS4_VPLLCON0_LOCKED_SHIFT))
-                               vpll_wait = 0;
-               }
-       } while (epll_wait || vpll_wait);
-}
-
 static struct subsys_interface exynos_pm_interface = {
        .name           = "exynos_pm",
        .subsys         = &exynos_subsys,
@@ -237,7 +108,6 @@ static struct subsys_interface exynos_pm_interface = {
 
 static __init int exynos_pm_drvinit(void)
 {
-       struct clk *pll_base;
        unsigned int tmp;
 
        if (soc_is_exynos5440())
@@ -251,15 +121,6 @@ static __init int exynos_pm_drvinit(void)
        tmp |= ((0xFF << 8) | (0x1F << 1));
        __raw_writel(tmp, S5P_WAKEUP_MASK);
 
-       if (!soc_is_exynos5250()) {
-               pll_base = clk_get(NULL, "xtal");
-
-               if (!IS_ERR(pll_base)) {
-                       pll_base_rate = clk_get_rate(pll_base);
-                       clk_put(pll_base);
-               }
-       }
-
        return subsys_interface_register(&exynos_pm_interface);
 }
 arch_initcall(exynos_pm_drvinit);
@@ -343,13 +204,8 @@ static void exynos_pm_resume(void)
 
        s3c_pm_do_restore_core(exynos_core_save, ARRAY_SIZE(exynos_core_save));
 
-       if (!soc_is_exynos5250()) {
-               exynos4_restore_pll();
-
-#ifdef CONFIG_SMP
+       if (IS_ENABLED(CONFIG_SMP) && !soc_is_exynos5250())
                scu_enable(S5P_VA_SCU);
-#endif
-       }
 
 early_wakeup:
 
index 010f071..b4f9672 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/clk-provider.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
+#include <linux/syscore_ops.h>
 
 #include "clk.h"
 
@@ -130,6 +131,17 @@ enum exynos4_plls {
        nr_plls                 /* number of 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.
@@ -154,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,
@@ -161,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,
@@ -227,6 +244,124 @@ 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) {
+               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 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 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", };
@@ -908,12 +1043,13 @@ static unsigned long exynos4_get_xom(void)
        return xom;
 }
 
-static void __init exynos4_clk_register_finpll(unsigned long xom)
+static void __init exynos4_clk_register_finpll(void)
 {
        struct samsung_fixed_rate_clock fclk;
        struct clk *clk;
        unsigned long finpll_f = 24000000;
        char *parent_name;
+       unsigned int xom = exynos4_get_xom();
 
        parent_name = xom & 1 ? "xusbxti" : "xxti";
        clk = clk_get(NULL, parent_name);
@@ -1038,27 +1174,21 @@ static struct samsung_pll_clock exynos4x12_plls[nr_plls] __initdata = {
 
 /* register exynos4 clocks */
 static void __init exynos4_clk_init(struct device_node *np,
-                                   enum exynos4_soc exynos4_soc,
-                                   void __iomem *reg_base, unsigned long xom)
+                                   enum exynos4_soc soc)
 {
+       exynos4_soc = soc;
+
        reg_base = of_iomap(np, 0);
        if (!reg_base)
                panic("%s: failed to map registers\n", __func__);
 
-       if (exynos4_soc == EXYNOS4210)
-               samsung_clk_init(np, reg_base, CLK_NR_CLKS,
-                       exynos4_clk_regs, ARRAY_SIZE(exynos4_clk_regs),
-                       exynos4210_clk_save, ARRAY_SIZE(exynos4210_clk_save));
-       else
-               samsung_clk_init(np, reg_base, CLK_NR_CLKS,
-                       exynos4_clk_regs, ARRAY_SIZE(exynos4_clk_regs),
-                       exynos4x12_clk_save, ARRAY_SIZE(exynos4x12_clk_save));
+       samsung_clk_init(np, reg_base, CLK_NR_CLKS);
 
        samsung_clk_of_register_fixed_ext(exynos4_fixed_rate_ext_clks,
                        ARRAY_SIZE(exynos4_fixed_rate_ext_clks),
                        ext_clk_match);
 
-       exynos4_clk_register_finpll(xom);
+       exynos4_clk_register_finpll();
 
        if (exynos4_soc == EXYNOS4210) {
                samsung_clk_register_mux(exynos4210_mux_early,
@@ -1125,6 +1255,8 @@ static void __init exynos4_clk_init(struct device_node *np,
        samsung_clk_register_alias(exynos4_aliases,
                        ARRAY_SIZE(exynos4_aliases));
 
+       exynos4_clk_sleep_init();
+
        pr_info("%s clocks: sclk_apll = %ld, sclk_mpll = %ld\n"
                "\tsclk_epll = %ld, sclk_vpll = %ld, arm_clk = %ld\n",
                exynos4_soc == EXYNOS4210 ? "Exynos4210" : "Exynos4x12",
@@ -1136,12 +1268,12 @@ static void __init exynos4_clk_init(struct device_node *np,
 
 static void __init exynos4210_clk_init(struct device_node *np)
 {
-       exynos4_clk_init(np, EXYNOS4210, NULL, exynos4_get_xom());
+       exynos4_clk_init(np, EXYNOS4210);
 }
 CLK_OF_DECLARE(exynos4210_clk, "samsung,exynos4210-clock", exynos4210_clk_init);
 
 static void __init exynos4412_clk_init(struct device_node *np)
 {
-       exynos4_clk_init(np, EXYNOS4X12, NULL, exynos4_get_xom());
+       exynos4_clk_init(np, EXYNOS4X12);
 }
 CLK_OF_DECLARE(exynos4412_clk, "samsung,exynos4412-clock", exynos4412_clk_init);
index ff4beeb..e7ee442 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/clk-provider.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
+#include <linux/syscore_ops.h>
 
 #include "clk.h"
 
@@ -85,6 +86,11 @@ enum exynos5250_plls {
        nr_plls                 /* number of PLLs */
 };
 
+static void __iomem *reg_base;
+
+#ifdef CONFIG_PM_SLEEP
+static struct samsung_clk_reg_dump *exynos5250_save;
+
 /*
  * list of controller registers to be saved and restored during a
  * suspend/resume cycle.
@@ -137,6 +143,41 @@ static unsigned long exynos5250_clk_regs[] __initdata = {
        GATE_IP_ACP,
 };
 
+static int exynos5250_clk_suspend(void)
+{
+       samsung_clk_save(reg_base, exynos5250_save,
+                               ARRAY_SIZE(exynos5250_clk_regs));
+
+       return 0;
+}
+
+static void exynos5250_clk_resume(void)
+{
+       samsung_clk_restore(reg_base, exynos5250_save,
+                               ARRAY_SIZE(exynos5250_clk_regs));
+}
+
+static struct syscore_ops exynos5250_clk_syscore_ops = {
+       .suspend = exynos5250_clk_suspend,
+       .resume = exynos5250_clk_resume,
+};
+
+static void exynos5250_clk_sleep_init(void)
+{
+       exynos5250_save = samsung_clk_alloc_reg_dump(exynos5250_clk_regs,
+                                       ARRAY_SIZE(exynos5250_clk_regs));
+       if (!exynos5250_save) {
+               pr_warn("%s: failed to allocate sleep save data, no sleep support!\n",
+                       __func__);
+               return;
+       }
+
+       register_syscore_ops(&exynos5250_clk_syscore_ops);
+}
+#else
+static void exynos5250_clk_sleep_init(void) {}
+#endif
+
 /* list of all parent clock list */
 PNAME(mout_apll_p)     = { "fin_pll", "fout_apll", };
 PNAME(mout_cpu_p)      = { "mout_apll", "mout_mpll", };
@@ -645,8 +686,6 @@ static struct of_device_id ext_clk_match[] __initdata = {
 /* register exynox5250 clocks */
 static void __init exynos5250_clk_init(struct device_node *np)
 {
-       void __iomem *reg_base;
-
        if (np) {
                reg_base = of_iomap(np, 0);
                if (!reg_base)
@@ -655,9 +694,7 @@ static void __init exynos5250_clk_init(struct device_node *np)
                panic("%s: unable to determine soc\n", __func__);
        }
 
-       samsung_clk_init(np, reg_base, CLK_NR_CLKS,
-                       exynos5250_clk_regs, ARRAY_SIZE(exynos5250_clk_regs),
-                       NULL, 0);
+       samsung_clk_init(np, reg_base, CLK_NR_CLKS);
        samsung_clk_of_register_fixed_ext(exynos5250_fixed_rate_ext_clks,
                        ARRAY_SIZE(exynos5250_fixed_rate_ext_clks),
                        ext_clk_match);
@@ -685,6 +722,8 @@ static void __init exynos5250_clk_init(struct device_node *np)
        samsung_clk_register_gate(exynos5250_gate_clks,
                        ARRAY_SIZE(exynos5250_gate_clks));
 
+       exynos5250_clk_sleep_init();
+
        pr_info("Exynos5250: clock setup completed, armclk=%ld\n",
                        _get_rate("div_arm2"));
 }
index ab4f2f7..60b2681 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/clk-provider.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
+#include <linux/syscore_ops.h>
 
 #include "clk.h"
 
@@ -108,6 +109,11 @@ enum exynos5420_plls {
        nr_plls                 /* number of PLLs */
 };
 
+static void __iomem *reg_base;
+
+#ifdef CONFIG_PM_SLEEP
+static struct samsung_clk_reg_dump *exynos5420_save;
+
 /*
  * list of controller registers to be saved and restored during a
  * suspend/resume cycle.
@@ -174,6 +180,41 @@ static unsigned long exynos5420_clk_regs[] __initdata = {
        DIV_KFC0,
 };
 
+static int exynos5420_clk_suspend(void)
+{
+       samsung_clk_save(reg_base, exynos5420_save,
+                               ARRAY_SIZE(exynos5420_clk_regs));
+
+       return 0;
+}
+
+static void exynos5420_clk_resume(void)
+{
+       samsung_clk_restore(reg_base, exynos5420_save,
+                               ARRAY_SIZE(exynos5420_clk_regs));
+}
+
+static struct syscore_ops exynos5420_clk_syscore_ops = {
+       .suspend = exynos5420_clk_suspend,
+       .resume = exynos5420_clk_resume,
+};
+
+static void exynos5420_clk_sleep_init(void)
+{
+       exynos5420_save = samsung_clk_alloc_reg_dump(exynos5420_clk_regs,
+                                       ARRAY_SIZE(exynos5420_clk_regs));
+       if (!exynos5420_save) {
+               pr_warn("%s: failed to allocate sleep save data, no sleep support!\n",
+                       __func__);
+               return;
+       }
+
+       register_syscore_ops(&exynos5420_clk_syscore_ops);
+}
+#else
+static void exynos5420_clk_sleep_init(void) {}
+#endif
+
 /* list of all parent clocks */
 PNAME(mspll_cpu_p)     = { "sclk_cpll", "sclk_dpll",
                                "sclk_mpll", "sclk_spll" };
@@ -737,8 +778,6 @@ static struct of_device_id ext_clk_match[] __initdata = {
 /* register exynos5420 clocks */
 static void __init exynos5420_clk_init(struct device_node *np)
 {
-       void __iomem *reg_base;
-
        if (np) {
                reg_base = of_iomap(np, 0);
                if (!reg_base)
@@ -747,9 +786,7 @@ static void __init exynos5420_clk_init(struct device_node *np)
                panic("%s: unable to determine soc\n", __func__);
        }
 
-       samsung_clk_init(np, reg_base, CLK_NR_CLKS,
-                       exynos5420_clk_regs, ARRAY_SIZE(exynos5420_clk_regs),
-                       NULL, 0);
+       samsung_clk_init(np, reg_base, CLK_NR_CLKS);
        samsung_clk_of_register_fixed_ext(exynos5420_fixed_rate_ext_clks,
                        ARRAY_SIZE(exynos5420_fixed_rate_ext_clks),
                        ext_clk_match);
@@ -765,5 +802,7 @@ static void __init exynos5420_clk_init(struct device_node *np)
                        ARRAY_SIZE(exynos5420_div_clks));
        samsung_clk_register_gate(exynos5420_gate_clks,
                        ARRAY_SIZE(exynos5420_gate_clks));
+
+       exynos5420_clk_sleep_init();
 }
 CLK_OF_DECLARE(exynos5420_clk, "samsung,exynos5420-clock", exynos5420_clk_init);
index cbc15b5..2bfad5a 100644 (file)
@@ -101,7 +101,7 @@ static void __init exynos5440_clk_init(struct device_node *np)
                return;
        }
 
-       samsung_clk_init(np, reg_base, CLK_NR_CLKS, NULL, 0, NULL, 0);
+       samsung_clk_init(np, reg_base, CLK_NR_CLKS);
        samsung_clk_of_register_fixed_ext(exynos5440_fixed_rate_ext_clks,
                ARRAY_SIZE(exynos5440_fixed_rate_ext_clks), ext_clk_match);
 
index 8e27aee..8bda658 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/clk-provider.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
+#include <linux/syscore_ops.h>
 
 #include <dt-bindings/clock/samsung,s3c64xx-clock.h>
 
@@ -61,6 +62,13 @@ enum s3c64xx_plls {
        apll, mpll, epll,
 };
 
+static void __iomem *reg_base;
+static bool is_s3c6400;
+
+#ifdef CONFIG_PM_SLEEP
+static struct samsung_clk_reg_dump *s3c64xx_save_common;
+static struct samsung_clk_reg_dump *s3c64xx_save_soc;
+
 /*
  * List of controller registers to be saved and restored during
  * a suspend/resume cycle.
@@ -87,6 +95,60 @@ static unsigned long s3c6410_clk_regs[] __initdata = {
        MEM0_GATE,
 };
 
+static int s3c64xx_clk_suspend(void)
+{
+       samsung_clk_save(reg_base, s3c64xx_save_common,
+                               ARRAY_SIZE(s3c64xx_clk_regs));
+
+       if (!is_s3c6400)
+               samsung_clk_save(reg_base, s3c64xx_save_soc,
+                                       ARRAY_SIZE(s3c6410_clk_regs));
+
+       return 0;
+}
+
+static void s3c64xx_clk_resume(void)
+{
+       samsung_clk_restore(reg_base, s3c64xx_save_common,
+                               ARRAY_SIZE(s3c64xx_clk_regs));
+
+       if (!is_s3c6400)
+               samsung_clk_restore(reg_base, s3c64xx_save_soc,
+                                       ARRAY_SIZE(s3c6410_clk_regs));
+}
+
+static struct syscore_ops s3c64xx_clk_syscore_ops = {
+       .suspend = s3c64xx_clk_suspend,
+       .resume = s3c64xx_clk_resume,
+};
+
+static void s3c64xx_clk_sleep_init(void)
+{
+       s3c64xx_save_common = samsung_clk_alloc_reg_dump(s3c64xx_clk_regs,
+                                               ARRAY_SIZE(s3c64xx_clk_regs));
+       if (!s3c64xx_save_common)
+               goto err_warn;
+
+       if (!is_s3c6400) {
+               s3c64xx_save_soc = samsung_clk_alloc_reg_dump(s3c6410_clk_regs,
+                                               ARRAY_SIZE(s3c6410_clk_regs));
+               if (!s3c64xx_save_soc)
+                       goto err_soc;
+       }
+
+       register_syscore_ops(&s3c64xx_clk_syscore_ops);
+       return;
+
+err_soc:
+       kfree(s3c64xx_save_common);
+err_warn:
+       pr_warn("%s: failed to allocate sleep save data, no sleep support!\n",
+               __func__);
+}
+#else
+static void s3c64xx_clk_sleep_init(void) {}
+#endif
+
 /* List of parent clocks common for all S3C64xx SoCs. */
 PNAME(spi_mmc_p)       = { "mout_epll", "dout_mpll", "fin_pll", "clk27m" };
 PNAME(uart_p)          = { "mout_epll", "dout_mpll" };
@@ -391,11 +453,11 @@ static void __init s3c64xx_clk_register_fixed_ext(unsigned long fin_pll_f,
 
 /* Register s3c64xx clocks. */
 void __init s3c64xx_clk_init(struct device_node *np, unsigned long xtal_f,
-                            unsigned long xusbxti_f, bool is_s3c6400,
-                            void __iomem *reg_base)
+                            unsigned long xusbxti_f, bool s3c6400,
+                            void __iomem *base)
 {
-       unsigned long *soc_regs = NULL;
-       unsigned long nr_soc_regs = 0;
+       reg_base = base;
+       is_s3c6400 = s3c6400;
 
        if (np) {
                reg_base = of_iomap(np, 0);
@@ -403,13 +465,7 @@ void __init s3c64xx_clk_init(struct device_node *np, unsigned long xtal_f,
                        panic("%s: failed to map registers\n", __func__);
        }
 
-       if (!is_s3c6400) {
-               soc_regs = s3c6410_clk_regs;
-               nr_soc_regs = ARRAY_SIZE(s3c6410_clk_regs);
-       }
-
-       samsung_clk_init(np, reg_base, NR_CLKS, s3c64xx_clk_regs,
-                       ARRAY_SIZE(s3c64xx_clk_regs), soc_regs, nr_soc_regs);
+       samsung_clk_init(np, reg_base, NR_CLKS);
 
        /* Register external clocks. */
        if (!np)
@@ -452,6 +508,7 @@ void __init s3c64xx_clk_init(struct device_node *np, unsigned long xtal_f,
 
        samsung_clk_register_alias(s3c64xx_clock_aliases,
                                        ARRAY_SIZE(s3c64xx_clock_aliases));
+       s3c64xx_clk_sleep_init();
 
        pr_info("%s clocks: apll = %lu, mpll = %lu\n"
                "\tepll = %lu, arm_clk = %lu\n",
index f503f32..91bec3e 100644 (file)
@@ -21,64 +21,45 @@ static void __iomem *reg_base;
 static struct clk_onecell_data clk_data;
 #endif
 
-#ifdef CONFIG_PM_SLEEP
-static struct samsung_clk_reg_dump *reg_dump;
-static unsigned long nr_reg_dump;
-
-static int samsung_clk_suspend(void)
+void samsung_clk_save(void __iomem *base,
+                                   struct samsung_clk_reg_dump *rd,
+                                   unsigned int num_regs)
 {
-       struct samsung_clk_reg_dump *rd = reg_dump;
-       unsigned long i;
-
-       for (i = 0; i < nr_reg_dump; i++, rd++)
-               rd->value = __raw_readl(reg_base + rd->offset);
+       for (; num_regs > 0; --num_regs, ++rd)
+               rd->value = readl(base + rd->offset);
+}
 
-       return 0;
+void samsung_clk_restore(void __iomem *base,
+                                     const struct samsung_clk_reg_dump *rd,
+                                     unsigned int num_regs)
+{
+       for (; num_regs > 0; --num_regs, ++rd)
+               writel(rd->value, base + rd->offset);
 }
 
-static void samsung_clk_resume(void)
+struct samsung_clk_reg_dump *samsung_clk_alloc_reg_dump(
+                                               const unsigned long *rdump,
+                                               unsigned long nr_rdump)
 {
-       struct samsung_clk_reg_dump *rd = reg_dump;
-       unsigned long i;
+       struct samsung_clk_reg_dump *rd;
+       unsigned int i;
 
-       for (i = 0; i < nr_reg_dump; i++, rd++)
-               __raw_writel(rd->value, reg_base + rd->offset);
-}
+       rd = kcalloc(nr_rdump, sizeof(*rd), GFP_KERNEL);
+       if (!rd)
+               return NULL;
+
+       for (i = 0; i < nr_rdump; ++i)
+               rd[i].offset = rdump[i];
 
-static struct syscore_ops samsung_clk_syscore_ops = {
-       .suspend        = samsung_clk_suspend,
-       .resume         = samsung_clk_resume,
-};
-#endif /* CONFIG_PM_SLEEP */
+       return rd;
+}
 
 /* setup the essentials required to support clock lookup using ccf */
 void __init samsung_clk_init(struct device_node *np, void __iomem *base,
-               unsigned long nr_clks, unsigned long *rdump,
-               unsigned long nr_rdump, unsigned long *soc_rdump,
-               unsigned long nr_soc_rdump)
+                            unsigned long nr_clks)
 {
        reg_base = base;
 
-#ifdef CONFIG_PM_SLEEP
-       if (rdump && nr_rdump) {
-               unsigned int idx;
-               reg_dump = kzalloc(sizeof(struct samsung_clk_reg_dump)
-                               * (nr_rdump + nr_soc_rdump), GFP_KERNEL);
-               if (!reg_dump) {
-                       pr_err("%s: memory alloc for register dump failed\n",
-                                       __func__);
-                       return;
-               }
-
-               for (idx = 0; idx < nr_rdump; idx++)
-                       reg_dump[idx].offset = rdump[idx];
-               for (idx = 0; idx < nr_soc_rdump; idx++)
-                       reg_dump[nr_rdump + idx].offset = soc_rdump[idx];
-               nr_reg_dump = nr_rdump + nr_soc_rdump;
-               register_syscore_ops(&samsung_clk_syscore_ops);
-       }
-#endif
-
        clk_table = kzalloc(sizeof(struct clk *) * nr_clks, GFP_KERNEL);
        if (!clk_table)
                panic("could not allocate clock lookup table\n");
index 31b4174..c7141ba 100644 (file)
@@ -313,9 +313,7 @@ struct samsung_pll_clock {
                _lock, _con, _rtable, _alias)
 
 extern void __init samsung_clk_init(struct device_node *np, void __iomem *base,
-               unsigned long nr_clks, unsigned long *rdump,
-               unsigned long nr_rdump, unsigned long *soc_rdump,
-               unsigned long nr_soc_rdump);
+                                   unsigned long nr_clks);
 extern void __init samsung_clk_of_register_fixed_ext(
                struct samsung_fixed_rate_clock *fixed_rate_clk,
                unsigned int nr_fixed_rate_clk,
@@ -340,4 +338,14 @@ extern void __init samsung_clk_register_pll(struct samsung_pll_clock *pll_list,
 
 extern unsigned long _get_rate(const char *clk_name);
 
+extern void samsung_clk_save(void __iomem *base,
+                            struct samsung_clk_reg_dump *rd,
+                            unsigned int num_regs);
+extern void samsung_clk_restore(void __iomem *base,
+                               const struct samsung_clk_reg_dump *rd,
+                               unsigned int num_regs);
+extern struct samsung_clk_reg_dump *samsung_clk_alloc_reg_dump(
+                                               const unsigned long *rdump,
+                                               unsigned long nr_rdump);
+
 #endif /* __SAMSUNG_CLK_H */