ARM: tegra: clock: Update CPU cluster switch clock control
[linux-3.10.git] / arch / arm / mach-tegra / tegra11_clocks.c
index d310b81..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",
@@ -1816,15 +1847,28 @@ static int tegra11_pll_clk_wait_for_lock(
 #ifndef CONFIG_TEGRA_SIMULATION_PLATFORM
 #if USE_PLL_LOCK_BITS
        int i;
+       u32 val = 0;
+
        for (i = 0; i < (c->u.pll.lock_delay / PLL_PRE_LOCK_DELAY + 1); i++) {
                udelay(PLL_PRE_LOCK_DELAY);
-               if ((clk_readl(lock_reg) & lock_bits) == lock_bits) {
+               val = clk_readl(lock_reg);
+               if ((val & lock_bits) == lock_bits) {
                        udelay(PLL_POST_LOCK_DELAY);
                        return 0;
                }
        }
-       pr_err("Timed out waiting for lock bit on pll %s\n", c->name);
-       return -1;
+
+       /* PLLCX lock bits may fluctuate after the lock - don't report timeout
+          in this case (phase lock bit happens to uniquely identify PLLCX) */
+       if (lock_bits & PLLCX_BASE_PHASE_LOCK) {
+               pr_debug("Timed out waiting for %s lock bit ([0x%x] = 0x%x)\n",
+                        c->name, lock_reg, val);
+               return 0;
+       } else {
+               pr_err("Timed out waiting for %s lock bit ([0x%x] = 0x%x)\n",
+                      c->name, lock_reg, val);
+               return -ETIMEDOUT;
+       }
 #endif
        udelay(c->u.pll.lock_delay);
 #endif
@@ -6636,6 +6680,7 @@ struct clk tegra_list_clks[] = {
        SHARED_CLK("usb2.sclk", "tegra-ehci.1",         "sclk", &tegra_clk_sbus_cmplx, NULL, 0, 0),
        SHARED_CLK("usb3.sclk", "tegra-ehci.2",         "sclk", &tegra_clk_sbus_cmplx, NULL, 0, 0),
        SHARED_CLK("wake.sclk", "wake_sclk",            "sclk", &tegra_clk_sbus_cmplx, NULL, 0, 0),
+       SHARED_CLK("camera.sclk",       "vi",           "sclk", &tegra_clk_sbus_cmplx, NULL, 0, 0),
        SHARED_CLK("mon.avp",   "tegra_actmon",         "avp",  &tegra_clk_sbus_cmplx, NULL, 0, 0),
        SHARED_CLK("cap.sclk",  "cap_sclk",             NULL,   &tegra_clk_sbus_cmplx, NULL, 0, SHARED_CEILING),
        SHARED_CLK("cap.throttle.sclk", "cap_throttle", NULL,   &tegra_clk_sbus_cmplx, NULL, 0, SHARED_CEILING),
@@ -6821,6 +6866,8 @@ struct clk_duplicate tegra_clk_duplicates[] = {
        CLK_DUPLICATE("i2c3", "tegra-i2c-slave.2", NULL),
        CLK_DUPLICATE("i2c4", "tegra-i2c-slave.3", NULL),
        CLK_DUPLICATE("i2c5", "tegra-i2c-slave.4", NULL),
+       CLK_DUPLICATE("cl_dvfs_soc", "tegra11-i2c.4", NULL),
+       CLK_DUPLICATE("cl_dvfs_ref", "tegra11-i2c.4", NULL),
        CLK_DUPLICATE("sbc1", "tegra11-spi-slave.0", NULL),
        CLK_DUPLICATE("sbc2", "tegra11-spi-slave.1", NULL),
        CLK_DUPLICATE("sbc3", "tegra11-spi-slave.2", NULL),
@@ -7001,7 +7048,7 @@ static void tegra11_init_one_clock(struct clk *c)
 }
 
 /* Direct access to CPU clock sources fot CPU idle driver */
-int tegra11_cpu_dfll_rate_exchange(unsigned long *rate)
+int tegra11_cpu_g_idle_rate_exchange(unsigned long *rate)
 {
        int ret = 0;
        struct clk *dfll = tegra_clk_cpu_cmplx.parent->u.cpu.dynamic;
@@ -7024,7 +7071,7 @@ int tegra11_cpu_dfll_rate_exchange(unsigned long *rate)
        return ret;
 }
 
-int tegra11_cpu_backup_rate_exchange(unsigned long *rate)
+int tegra11_cpu_lp_idle_rate_exchange(unsigned long *rate)
 {
        int ret = 0;
        struct clk *backup = tegra_clk_cpu_cmplx.parent->u.cpu.backup;
@@ -7065,9 +7112,11 @@ bool tegra_clk_is_parent_allowed(struct clk *c, struct clk *p)
         * source for EMC only when pll_m is fixed, or as a general fixed rate
         * clock source for EMC and other peripherals if pll_m is scaled. In
         * configuration with single cbus pll_c can be used as a scaled cbus
-        * clock source only.
+        * clock source only. No direct use for pll_c by super clocks.
         */
        if ((p == &tegra_pll_c) && (c != &tegra_pll_c_out1)) {
+               if (c->ops == &tegra_super_ops)
+                       return false;
 #ifdef CONFIG_TEGRA_DUAL_CBUS
 #ifndef CONFIG_TEGRA_PLLM_SCALED
                return c->flags & PERIPH_EMC_ENB;
@@ -7081,9 +7130,12 @@ bool tegra_clk_is_parent_allowed(struct clk *c, struct clk *p)
         * In any configuration pll_m must not be used as a clock source for
         * cbus modules. If pll_m is scaled it can be used as EMC source only.
         * Otherwise fixed rate pll_m can be used as clock source for EMC and
-        * other peripherals.
+        * other peripherals. No direct use for pll_m by super clocks.
         */
        if ((p == &tegra_pll_m) && (c != &tegra_pll_m_out1)) {
+               if (c->ops == &tegra_super_ops)
+                       return false;
+
                if (c->flags & PERIPH_ON_CBUS)
                        return false;
 #ifdef CONFIG_TEGRA_PLLM_SCALED
@@ -7626,18 +7678,6 @@ void __init tegra11x_init_clocks(void)
        for (i = 0; i < ARRAY_SIZE(tegra_list_clks); i++)
                tegra11_init_one_clock(&tegra_list_clks[i]);
 
-       for (i = 0; i < ARRAY_SIZE(tegra_clk_duplicates); i++) {
-               c = tegra_get_clock_by_name(tegra_clk_duplicates[i].name);
-               if (!c) {
-                       pr_err("%s: Unknown duplicate clock %s\n", __func__,
-                               tegra_clk_duplicates[i].name);
-                       continue;
-               }
-
-               tegra_clk_duplicates[i].lookup.clk = c;
-               clkdev_add(&tegra_clk_duplicates[i].lookup);
-       }
-
        for (i = 0; i < ARRAY_SIZE(tegra_sync_source_list); i++)
                tegra11_init_one_clock(&tegra_sync_source_list[i]);
        for (i = 0; i < ARRAY_SIZE(tegra_clk_audio_list); i++)
@@ -7655,6 +7695,18 @@ void __init tegra11x_init_clocks(void)
        for (i = 0; i < ARRAY_SIZE(tegra_xusb_coupled_clks); i++)
                tegra11_init_one_clock(&tegra_xusb_coupled_clks[i]);
 
+       for (i = 0; i < ARRAY_SIZE(tegra_clk_duplicates); i++) {
+               c = tegra_get_clock_by_name(tegra_clk_duplicates[i].name);
+               if (!c) {
+                       pr_err("%s: Unknown duplicate clock %s\n", __func__,
+                               tegra_clk_duplicates[i].name);
+                       continue;
+               }
+
+               tegra_clk_duplicates[i].lookup.clk = c;
+               clkdev_add(&tegra_clk_duplicates[i].lookup);
+       }
+
        /* Initialize to default */
        tegra_init_cpu_edp_limits(0);