ARM: tegra11: clock: Add DFLL resume operation
Alex Frid [Sun, 30 Sep 2012 02:58:53 +0000 (19:58 -0700)]
- Implemented DFLL resume including CL-DVFS state restoration.
- Moved G-CPU clock resume after DFLL.
- Made sure that LP-CPU clock is enabled before cluster switch on
entry to LP0 (it can be disabled on tegra11 if G-CPU is on DFLL).

Signed-off-by: Alex Frid <afrid@nvidia.com>
Reviewed-on: http://git-master/r/140004
(cherry picked from commit 09f59e09eab56e855ab36c43244f0e04a3246216)

Change-Id: I90cf3f5c8c54bb99303851b30b42da49b47e208f
Signed-off-by: Bharat Nihalani <bnihalani@nvidia.com>
Reviewed-on: http://git-master/r/146265
Reviewed-by: Automatic_Commit_Validation_User

arch/arm/mach-tegra/pm-t3.c
arch/arm/mach-tegra/tegra11_clocks.c
arch/arm/mach-tegra/tegra_cl_dvfs.c
arch/arm/mach-tegra/tegra_cl_dvfs.h

index 45f9ea2..db0487d 100644 (file)
@@ -483,20 +483,31 @@ void tegra_lp0_resume_mc(void)
 
 void tegra_lp0_cpu_mode(bool enter)
 {
+       static struct clk *cclk_lp;
        static bool entered_on_g = false;
        unsigned int flags;
 
+       if (!cclk_lp)
+               cclk_lp = tegra_get_clock_by_name("cclk_lp");
+
        if (enter)
                entered_on_g = !is_lp_cluster();
 
        if (entered_on_g) {
+               if (enter)
+                       clk_enable(cclk_lp);
+
                flags = enter ? TEGRA_POWER_CLUSTER_LP : TEGRA_POWER_CLUSTER_G;
                flags |= TEGRA_POWER_CLUSTER_IMMEDIATE;
 #if defined(CONFIG_ARCH_TEGRA_HAS_SYMMETRIC_CPU_PWR_GATE)
                flags |= TEGRA_POWER_CLUSTER_PART_DEFAULT;
 #endif
-               tegra_cluster_control(0, flags);
-               pr_info("Tegra: switched to %s cluster\n", enter ? "LP" : "G");
+               if (!tegra_cluster_control(0, flags)) {
+                       if (!enter)
+                               clk_disable(cclk_lp);
+                       pr_info("Tegra: switched to %s cluster\n",
+                               enter ? "LP" : "G");
+               }
        }
 }
 
index 63e22bb..3e1f6d1 100644 (file)
 #define USB_PLLS_USE_LOCKDET                   (1<<6)
 #define USB_PLLS_ENABLE_SWCTL                  ((1<<2) | (1<<0))
 
+/* DFLL */
+#define DFLL_BASE                              0x2f4
+#define DFLL_BASE_RESET                                (1<<0)
+
 #define        LVL2_CLK_GATE_OVRE                      0x554
 
 #define ROUND_DIVIDER_UP       0
@@ -3241,7 +3245,7 @@ static int tegra11_dfll_clk_set_rate(struct clk *c, unsigned long rate)
 
 static void tegra11_dfll_clk_reset(struct clk *c, bool assert)
 {
-       u32 val = assert ? 1 : 0;
+       u32 val = assert ? DFLL_BASE_RESET : 0;
        clk_writel_delay(val, c->reg);
 }
 
@@ -3254,6 +3258,17 @@ tegra11_dfll_clk_cfg_ex(struct clk *c, enum tegra_clk_ex_param p, u32 setting)
        return -EINVAL;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static void tegra11_dfll_clk_resume(struct clk *c)
+{
+       if (!(clk_readl(c->reg) & DFLL_BASE_RESET))
+               return;         /* already resumed */
+
+       tegra_periph_reset_deassert(c);
+       tegra_cl_dvfs_resume(c->u.dfll.cl_dvfs);
+}
+#endif
+
 static struct clk_ops tegra_dfll_ops = {
        .enable                 = tegra11_dfll_clk_enable,
        .disable                = tegra11_dfll_clk_disable,
@@ -6850,8 +6865,6 @@ static int tegra11_clk_suspend(void)
        *ctx++ = clk_readl(tegra_pll_a_out0.reg);
        *ctx++ = clk_readl(tegra_pll_c_out1.reg);
 
-       *ctx++ = clk_readl(tegra_clk_cclk_g.reg);
-       *ctx++ = clk_readl(tegra_clk_cclk_g.reg + SUPER_CLK_DIVIDER);
        *ctx++ = clk_readl(tegra_clk_cclk_lp.reg);
        *ctx++ = clk_readl(tegra_clk_cclk_lp.reg + SUPER_CLK_DIVIDER);
 
@@ -6890,6 +6903,9 @@ static int tegra11_clk_suspend(void)
        *ctx++ = clk_readl(CLK_OUT_ENB_W);
        *ctx++ = clk_readl(CLK_OUT_ENB_X);
 
+       *ctx++ = clk_readl(tegra_clk_cclk_g.reg);
+       *ctx++ = clk_readl(tegra_clk_cclk_g.reg + SUPER_CLK_DIVIDER);
+
        *ctx++ = clk_readl(SPARE_REG);
        *ctx++ = clk_readl(MISC_CLK_ENB);
        *ctx++ = clk_readl(CLK_MASK_ARM);
@@ -6957,9 +6973,6 @@ static void tegra11_clk_resume(void)
        pll_c_out1 = *ctx++;
        clk_writel(pll_c_out1 | val, tegra_pll_c_out1.reg);
 
-       clk_writel(*ctx++, tegra_clk_cclk_g.reg);
-       clk_writel(*ctx++, tegra_clk_cclk_g.reg + SUPER_CLK_DIVIDER);
-
        val = *ctx++;
        tegra11_super_clk_resume(&tegra_clk_cclk_lp,
                tegra_clk_virtual_cpu_lp.u.cpu.backup, val);
@@ -7016,6 +7029,13 @@ static void tegra11_clk_resume(void)
        clk_writel(*ctx++, CLK_OUT_ENB_X);
        wmb();
 
+       /* DFLL resume after cl_dvfs and i2c5 clocks are resumed */
+       tegra11_dfll_clk_resume(&tegra_dfll_cpu);
+
+       /* CPU G clock restored after DFLL and PLLs */
+       clk_writel(*ctx++, tegra_clk_cclk_g.reg);
+       clk_writel(*ctx++, tegra_clk_cclk_g.reg + SUPER_CLK_DIVIDER);
+
        clk_writel(*ctx++, SPARE_REG);
        clk_writel(*ctx++, MISC_CLK_ENB);
        clk_writel(*ctx++, CLK_MASK_ARM);
index 219684a..d84059d 100644 (file)
@@ -473,6 +473,28 @@ int __init tegra_init_cl_dvfs(struct tegra_cl_dvfs *cld)
        return 0;
 }
 
+void tegra_cl_dvfs_resume(struct tegra_cl_dvfs *cld)
+{
+       enum tegra_cl_dvfs_ctrl_mode mode = cld->mode;
+       struct dfll_rate_req req = cld->last_req;
+
+       cl_dvfs_enable_clocks(cld);
+
+       /* Setup PMU interface, and configure controls in disabled mode */
+       cl_dvfs_init_out_if(cld);
+       cl_dvfs_init_cntrl_logic(cld);
+
+       cl_dvfs_disable_clocks(cld);
+
+       /* Restore last request and mode */
+       cld->last_req = req;
+       if (mode != TEGRA_CL_DVFS_DISABLED) {
+               set_mode(cld, TEGRA_CL_DVFS_OPEN_LOOP);
+               WARN(mode > TEGRA_CL_DVFS_OPEN_LOOP,
+                    "DFLL was left locked in suspend\n");
+       }
+}
+
 void tegra_cl_dvfs_set_platform_data(struct tegra_cl_dvfs_platform_data *data)
 {
        struct clk *c = tegra_get_clock_by_name(data->dfll_clk_name);
index b200009..ab10635 100644 (file)
@@ -119,6 +119,7 @@ struct tegra_cl_dvfs {
 #ifdef CONFIG_ARCH_TEGRA_HAS_CL_DVFS
 void tegra_cl_dvfs_set_platform_data(struct tegra_cl_dvfs_platform_data *data);
 int tegra_init_cl_dvfs(struct tegra_cl_dvfs *cld);
+void tegra_cl_dvfs_resume(struct tegra_cl_dvfs *cld);
 
 void tegra_cl_dvfs_disable(struct tegra_cl_dvfs *cld);
 int tegra_cl_dvfs_enable(struct tegra_cl_dvfs *cld);
@@ -132,6 +133,8 @@ static inline void tegra_cl_dvfs_set_platform_data(
 {}
 static inline int tegra_init_cl_dvfs(struct tegra_cl_dvfs *cld)
 { return -ENOSYS; }
+static inline void tegra_cl_dvfs_resume(struct tegra_cl_dvfs *cld)
+{}
 
 static inline void tegra_cl_dvfs_disable(struct tegra_cl_dvfs *cld)
 {}