ARM: tegra: clock: Update CPU cluster switch clock control
Alex Frid [Wed, 13 Feb 2013 23:55:56 +0000 (15:55 -0800)]
Updated clock control when cluster round trip changes CPU clock
source from PLL to DFLL: G CPU (PLL) => LP CPU (PLL) => G CPU (DFLL).

- made sure that PLL used as G CPU clock source during the last
residency is enabled across LP-to-G switch
- made sure that initial G CPU voltage is preset before cluster
switch to maximum of voltage levels required for G CPU running on
last residency PLL and next residency DFLL
- changed G CPU rail control from s/w dvfs to h/w cl-dvfs after the
switch to G CPU is completed

Change-Id: I7720cda544ef16a4691534b52315e9128b2b27c3
Signed-off-by: Alex Frid <afrid@nvidia.com>
Reviewed-on: http://git-master/r/201830
(cherry picked from commit fd9b858e4443860b3538f8e85eacdb24bf32c59b)
Reviewed-on: http://git-master/r/207894
Reviewed-by: Harshada Kale <hkale@nvidia.com>
Tested-by: Harshada Kale <hkale@nvidia.com>

arch/arm/mach-tegra/dvfs.h
arch/arm/mach-tegra/tegra11_clocks.c

index a371497..8b35e56 100644 (file)
@@ -243,7 +243,8 @@ static inline bool tegra_dvfs_rail_is_dfll_mode(struct dvfs_rail *rail)
 static inline bool tegra_dvfs_is_dfll_range_entry(struct dvfs *d,
                                                  unsigned long rate)
 {
-       return  d->dvfs_rail && (!d->dvfs_rail->dfll_mode) &&
+       /* make exception for cluster switch (cur_rate = 0) */
+       return  d->cur_rate && d->dvfs_rail && (!d->dvfs_rail->dfll_mode) &&
                (d->dfll_data.range == DFLL_RANGE_HIGH_RATES) &&
                (rate >= d->dfll_data.use_dfll_rate_min) &&
                (d->cur_rate < d->dfll_data.use_dfll_rate_min);
index d8b5358..eb412a8 100644 (file)
@@ -1360,6 +1360,7 @@ static int tegra11_cpu_cmplx_clk_set_parent(struct clk *c, struct clk *p)
        const struct clk_mux_sel *sel;
        unsigned long rate = clk_get_rate(c->parent);
        struct clk *dfll = c->parent->u.cpu.dynamic ? : p->u.cpu.dynamic;
+       struct clk *p_source_old = NULL;
        struct clk *p_source;
 
        pr_debug("%s: %s %s\n", __func__, c->name, p->name);
@@ -1436,23 +1437,29 @@ static int tegra11_cpu_cmplx_clk_set_parent(struct clk *c, struct clk *p)
                ret = clk_set_rate(p_source, rate);
                if (ret)
                        goto abort;
-       } else if (p->parent->parent == dfll) {
+       } else if ((p->parent->parent == dfll) || ((p->u.cpu.dynamic == dfll) &&
+                       (dfll->state != UNINITIALIZED) && use_dfll)) {
                /* LP => G (DFLL selected as clock source) switch:
                 * set DFLL rate ready (DFLL is still disabled)
                 * (set target p_source as dfll, G source is already selected)
                 */
                p_source = dfll;
-               ret = clk_set_rate(p_source, rate);
+               ret = clk_set_rate(dfll,
+                       tegra_dvfs_rail_is_dfll_mode(tegra_cpu_rail) ? rate :
+                       max(rate, p->dvfs->dfll_data.use_dfll_rate_min));
                if (ret)
                        goto abort;
        } else
-               /* DFLL is not selcted on either side of the switch:
+               /* DFLL is not selected on either side of the switch:
                 * set target p_source equal to current clock source
                 */
                p_source = c->parent->parent->parent;
 
        /* Switch new parent to target clock source if necessary */
        if (p->parent->parent != p_source) {
+               clk_enable(p->parent->parent);
+               clk_enable(p->parent);
+               p_source_old = p->parent->parent;
                ret = clk_set_parent(p->parent, p_source);
                if (ret) {
                        pr_err("%s: Failed to set parent %s for %s\n",
@@ -1479,20 +1486,44 @@ static int tegra11_cpu_cmplx_clk_set_parent(struct clk *c, struct clk *p)
        /* Disabling old parent scales old mode voltage rail */
        if (c->refcnt)
                clk_disable(c->parent);
+       if (p_source_old) {
+               clk_disable(p->parent);
+               clk_disable(p_source_old);
+       }
 
        clk_reparent(c, p);
 
-       /* Lock DFLL now (resume closed loop VDD_CPU control) */
-       if (p_source == dfll)
-               tegra_clk_cfg_ex(dfll, TEGRA_CLK_DFLL_LOCK, 1);
+       /*
+        * Lock DFLL now (resume closed loop VDD_CPU control).
+        * G CPU operations are always resumed on DFLL if it can be used, even
+        * when autoswitch between PLL and DFLL is allowed, and resume rate is
+        * low enough to run on PLL. This makes CPU clock source ready for
+        * speedy ramp with cl_dvfs controlling volatge (and that ramp is the
+        * most likely reason for going to G CPU in the 1st place)
+        */
+       if (p_source == dfll) {
+               if (tegra_dvfs_rail_is_dfll_mode(tegra_cpu_rail)) {
+                       tegra_clk_cfg_ex(dfll, TEGRA_CLK_DFLL_LOCK, 1);
+               } else {
+                       clk_set_rate(dfll, rate);
+                       tegra_clk_cfg_ex(dfll, TEGRA_CLK_DFLL_LOCK, 1);
+                       tegra_dvfs_dfll_mode_set(p->dvfs, rate);
+               }
+       }
 
        tegra_dvfs_rail_mode_updating(tegra_cpu_rail, false);
        return 0;
 
 abort:
        /* Re-lock DFLL if necessary after aborted switch */
-       if (c->parent->parent->parent == dfll)
+       if (c->parent->parent->parent == dfll) {
+               clk_set_rate(dfll, rate);
                tegra_clk_cfg_ex(dfll, TEGRA_CLK_DFLL_LOCK, 1);
+       }
+       if (p_source_old) {
+               clk_disable(p->parent);
+               clk_disable(p_source_old);
+       }
        tegra_dvfs_rail_mode_updating(tegra_cpu_rail, false);
 
        pr_err("%s: aborted switch from %s to %s\n",