arm: tegra: clock: Add dfll clock range control
sreenivasulu velpula [Fri, 18 Jul 2014 07:19:01 +0000 (12:19 +0530)]
Add function tegra_clk_dfll_range_control to
change dfll range for any value of 0, 1 or 2.

Bug 1563635

Change-Id: I0b76fd48d102e2e702d88749d71c1bd65a610611
Signed-off-by: sreenivasulu velpula <svelpula@nvidia.com>
Reviewed-on: http://git-master/r/439761
(cherry picked from commit 465c6b2738b1b4f7b8a6c3672c47e8d1c5157021)
Reviewed-on: http://git-master/r/559389
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Bibek Basu <bbasu@nvidia.com>
Tested-by: Bibek Basu <bbasu@nvidia.com>
Reviewed-by: Aleksandr Frid <afrid@nvidia.com>
GVS: Gerrit_Virtual_Submit
Reviewed-by: Venkat Moganty <vmoganty@nvidia.com>

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

index a3428d2..ab1cf8d 100644 (file)
@@ -1171,6 +1171,61 @@ int tegra_clk_register_export_ops(struct clk *c,
 }
 EXPORT_SYMBOL(tegra_clk_register_export_ops);
 
+
+/* Change DFLL range : Refer enum dfll_range */
+int tegra_clk_dfll_range_control(enum dfll_range use_dfll)
+{
+       int ret = 0;
+       unsigned long c_flags, p_flags;
+       unsigned int old_use_dfll;
+       struct clk *c = tegra_get_clock_by_name("cpu");
+       struct clk *dfll = tegra_get_clock_by_name("dfll_cpu");
+
+       if (!c || !c->parent || !c->parent->dvfs || !dfll)
+               return -ENOSYS;
+
+       ret = tegra_cpu_reg_mode_force_normal(true);
+       if (ret) {
+               pr_err("%s: Failed to force regulator normal mode\n", __func__);
+               return ret;
+       }
+
+       clk_lock_save(c, &c_flags);
+       if (dfll->state == UNINITIALIZED) {
+               pr_err("%s: DFLL is not initialized\n", __func__);
+               goto error_1;
+       }
+       if (c->parent->u.cpu.mode == MODE_LP) {
+               pr_err("%s: DFLL is not used on LP CPU\n", __func__);
+               goto error_1;
+       }
+
+       clk_lock_save(c->parent, &p_flags);
+       old_use_dfll = tegra_dvfs_get_dfll_range(c->parent->dvfs);
+       ret = tegra_dvfs_set_dfll_range(c->parent->dvfs, use_dfll);
+       if (!ret) {
+               /* Get the current parent clock running rate and
+                  set it to new parent clock */
+               ret = clk_set_rate_locked(c->parent,
+                       clk_get_rate_locked(c->parent));
+               if (ret) {
+                       tegra_dvfs_set_dfll_range(
+                               c->parent->dvfs, old_use_dfll);
+               }
+       }
+
+       clk_unlock_restore(c->parent, &p_flags);
+       clk_unlock_restore(c, &c_flags);
+       tegra_update_cpu_edp_limits();
+       return ret;
+
+error_1:
+       clk_unlock_restore(c, &c_flags);
+       tegra_cpu_reg_mode_force_normal(false);
+       return -ENOSYS;
+}
+
+
 #define OSC_FREQ_DET                   0x58
 #define OSC_FREQ_DET_TRIG              BIT(31)
 
index 3286255..a77bdc7 100644 (file)
@@ -352,6 +352,14 @@ static inline bool tegra_dvfs_is_dfll_range(struct dvfs *d, unsigned long rate)
                ((d->dfll_data.range == DFLL_RANGE_HIGH_RATES) &&
                (rate >= d->dfll_data.use_dfll_rate_min));
 }
+
+static inline int tegra_dvfs_get_dfll_range(struct dvfs *d)
+{
+       if (d)
+               return d->dfll_data.range;
+       return -ENOENT;
+}
+
 static inline int tegra_dvfs_set_dfll_range(struct dvfs *d, int range)
 {
        if (!d->dfll_millivolts)
@@ -363,6 +371,7 @@ static inline int tegra_dvfs_set_dfll_range(struct dvfs *d, int range)
        d->dfll_data.range = range;
        return 0;
 }
+
 static inline void tegra_dvfs_rail_mode_updating(struct dvfs_rail *rail,
                                                 bool updating)
 {