ARM: tegra11: dvfs: Update switch between DFLL and PLL
Alex Frid [Tue, 12 Feb 2013 19:04:40 +0000 (11:04 -0800)]
Modified procedures for auto-switching between PLL and DFLL CPU clock
sources.

- On switch from PLL to DFLL do not allow legacy DVFS to set voltage
for target rate in one shot. Limit setting to minimum DFLL voltage,
and let DFLL to complete voltage ramp after the switch.

- Similarly on switch from DFLL to PLL, first use DFLL mode to lower
cpu voltage to DFLL minimum, leaving only delta down to target for
legacy DVFS.

This modifications speed up the transitions and make them safer, since
major change of voltage, rate, and consumed current happens in DFLL
mode.

Change-Id: I42eee166510bd74d046bc6b3cb232ca10233ead9
Signed-off-by: Alex Frid <afrid@nvidia.com>
Reviewed-on: http://git-master/r/200382
(cherry picked from commit 4a0c6ef45d3f806835c27fc492a09e2eb254b0a6)
Reviewed-on: http://git-master/r/203613
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Yu-Huan Hsu <yhsu@nvidia.com>

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

index ee6a8bd..e7ece7b 100644 (file)
@@ -402,6 +402,11 @@ __tegra_dvfs_set_rate(struct dvfs *d, unsigned long rate)
        if (freqs == NULL || millivolts == NULL)
                return -ENODEV;
 
+       /* On entry to dfll range limit 1st step to range bottom (full ramp of
+          voltage/rate is completed automatically in dfll mode) */
+       if (tegra_dvfs_is_dfll_range_entry(d, rate))
+               rate = d->dfll_data.use_dfll_rate_min;
+
        if (rate > freqs[d->num_freqs - 1]) {
                pr_warn("tegra_dvfs: rate %lu too high for dvfs on %s\n", rate,
                        d->clk_name);
index 9ae8b32..1deb20b 100644 (file)
@@ -239,13 +239,21 @@ static inline bool tegra_dvfs_rail_is_dfll_mode(struct dvfs_rail *rail)
 {
        return rail ? rail->dfll_mode : false;
 }
+static inline bool tegra_dvfs_is_dfll_range_entry(struct dvfs *d,
+                                                 unsigned long rate)
+{
+       return  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);
+}
+
 static inline bool tegra_dvfs_is_dfll_scale(struct dvfs *d, unsigned long rate)
 {
-       return  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)));
+       return tegra_dvfs_rail_is_dfll_mode(d->dvfs_rail) ||
+               tegra_dvfs_is_dfll_range_entry(d, rate);
 }
+
 static inline bool tegra_dvfs_is_dfll_range(struct dvfs *d, unsigned long rate)
 {
        return (d->dfll_data.range == DFLL_RANGE_ALL_RATES) ||
index 59c7688..60256dd 100644 (file)
@@ -1176,7 +1176,22 @@ static int tegra11_cpu_clk_dfll_off(struct clk *c, unsigned long rate,
 
        rate = min(rate, c->max_rate - c->dvfs->dfll_data.max_rate_boost);
        pll = (rate <= c->u.cpu.backup_rate) ? c->u.cpu.backup : c->u.cpu.main;
+       dfll_rate_min = max(rate, dfll_rate_min);
 
+       /* set target rate last time in dfll mode */
+       if (old_rate != dfll_rate_min) {
+               ret = tegra_dvfs_set_rate(c, dfll_rate_min);
+               if (!ret)
+                       ret = clk_set_rate(dfll, dfll_rate_min);
+
+               if (ret) {
+                       pr_err("Failed to set cpu rate %lu on source %s\n",
+                              dfll_rate_min, dfll->name);
+                       return ret;
+               }
+       }
+
+       /* unlock dfll - release volatge rail control */
        tegra_dvfs_rail_mode_updating(tegra_cpu_rail, true);
        ret = tegra_clk_cfg_ex(dfll, TEGRA_CLK_DFLL_LOCK, 0);
        if (ret) {
@@ -1185,7 +1200,7 @@ static int tegra11_cpu_clk_dfll_off(struct clk *c, unsigned long rate,
        }
 
        /* restore legacy dvfs operations and set appropriate voltage */
-       ret = tegra_dvfs_dfll_mode_clear(c->dvfs, max(rate, dfll_rate_min));
+       ret = tegra_dvfs_dfll_mode_clear(c->dvfs, dfll_rate_min);
        if (ret) {
                pr_err("Failed to set cpu rail for rate %lu\n", rate);
                goto back_to_dfll;