ARM: tegra: dvfs: Add alternative dvfs frequency limits
Alex Frid [Wed, 14 Dec 2011 21:25:50 +0000 (13:25 -0800)]
Added an option to specify alternative dvfs frequency limits for each
tegra clock domain. These alternative limits can be applied in some
particularly extreme (e.g., slow) corner of process-temperature space
with no effect on regular limits for the rest of the space.

Bug 913884

Change-Id: I05e319b60f6dc6f4e7f15c7e677e5a3bce77f201
Signed-off-by: Alex Frid <afrid@nvidia.com>
Reviewed-on: http://git-master/r/70188
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Diwakar Tundlam <dtundlam@nvidia.com>
Tested-by: Diwakar Tundlam <dtundlam@nvidia.com>
Reviewed-by: Krishna Reddy <vdumpa@nvidia.com>
Reviewed-on: http://git-master/r/75614
Reviewed-by: Varun Wadekar <vwadekar@nvidia.com>
Tested-by: Varun Wadekar <vwadekar@nvidia.com>

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

index 22c6660..8723e6f 100644 (file)
@@ -320,16 +320,23 @@ static int dvfs_rail_connect_to_regulator(struct dvfs_rail *rail)
        return 0;
 }
 
+static inline unsigned long *dvfs_get_freqs(struct dvfs *d)
+{
+       return (d->alt_freqs_state == ALT_FREQS_ENABLED) ?
+               &d->alt_freqs[0] : &d->freqs[0];
+}
+
 static int
 __tegra_dvfs_set_rate(struct dvfs *d, unsigned long rate)
 {
        int i = 0;
        int ret;
+       unsigned long *freqs = dvfs_get_freqs(d);
 
-       if (d->freqs == NULL || d->millivolts == NULL)
+       if (freqs == NULL || d->millivolts == NULL)
                return -ENODEV;
 
-       if (rate > d->freqs[d->num_freqs - 1]) {
+       if (rate > freqs[d->num_freqs - 1]) {
                pr_warn("tegra_dvfs: rate %lu too high for dvfs on %s\n", rate,
                        d->clk_name);
                return -EINVAL;
@@ -338,7 +345,7 @@ __tegra_dvfs_set_rate(struct dvfs *d, unsigned long rate)
        if (rate == 0) {
                d->cur_millivolts = 0;
        } else {
-               while (i < d->num_freqs && rate > d->freqs[i])
+               while (i < d->num_freqs && rate > freqs[i])
                        i++;
 
                if ((d->max_millivolts) &&
@@ -360,6 +367,31 @@ __tegra_dvfs_set_rate(struct dvfs *d, unsigned long rate)
        return ret;
 }
 
+static inline int dvfs_alt_freqs_set(struct dvfs *d, bool enable)
+{
+       if (d->alt_freqs_state == ALT_FREQS_NOT_SUPPORTED)
+               return -ENOSYS;
+
+       d->alt_freqs_state = enable ? ALT_FREQS_ENABLED : ALT_FREQS_DISABLED;
+       return 0;
+}
+
+int tegra_dvfs_alt_freqs_set(struct dvfs *d, bool enable)
+{
+       int ret;
+       enum dvfs_alt_freqs old_state;
+
+       mutex_lock(&dvfs_lock);
+
+       old_state = d->alt_freqs_state;
+       ret = dvfs_alt_freqs_set(d, enable);
+       if (!ret && (old_state != d->alt_freqs_state))
+               ret = __tegra_dvfs_set_rate(d, d->cur_rate);
+
+       mutex_unlock(&dvfs_lock);
+       return ret;
+}
+
 int tegra_dvfs_predict_millivolts(struct clk *c, unsigned long rate)
 {
        int i;
@@ -370,6 +402,14 @@ int tegra_dvfs_predict_millivolts(struct clk *c, unsigned long rate)
        if (!c->dvfs->millivolts)
                return -ENODEV;
 
+       /*
+        * Predicted voltage can not be used across the switch to alternative
+        * frequency limits. For now, just fail the call for clock that has
+        * alternative limits initialized.
+        */
+       if (c->dvfs->alt_freqs_state != ALT_FREQS_NOT_SUPPORTED)
+               return -ENOSYS;
+
        for (i = 0; i < c->dvfs->num_freqs; i++) {
                if (rate <= c->dvfs->freqs[i])
                        break;
index 462eef6..f7e863f 100644 (file)
@@ -73,6 +73,12 @@ struct dvfs_rail {
        struct rail_stats stats;
 };
 
+enum dvfs_alt_freqs {
+       ALT_FREQS_NOT_SUPPORTED = 0,
+       ALT_FREQS_DISABLED,
+       ALT_FREQS_ENABLED,
+};
+
 struct dvfs {
        /* Used only by tegra2_clock.c */
        const char *clk_name;
@@ -82,9 +88,11 @@ struct dvfs {
        /* Must be initialized before tegra_dvfs_init */
        int freqs_mult;
        unsigned long freqs[MAX_DVFS_FREQS];
+       unsigned long alt_freqs[MAX_DVFS_FREQS];
        const int *millivolts;
        struct dvfs_rail *dvfs_rail;
        bool auto_dvfs;
+       enum dvfs_alt_freqs alt_freqs_state;
 
        /* Filled in by tegra_dvfs_init */
        int max_millivolts;
@@ -116,6 +124,7 @@ struct dvfs_rail *tegra_dvfs_get_rail_by_name(const char *reg_id);
 int tegra_dvfs_predict_millivolts(struct clk *c, unsigned long rate);
 void tegra_dvfs_core_cap_enable(bool enable);
 void tegra_dvfs_core_cap_level_set(int level);
+int tegra_dvfs_alt_freqs_set(struct dvfs *d, bool enable);
 #else
 static inline void tegra_soc_init_dvfs(void)
 {}
@@ -150,6 +159,8 @@ static inline void tegra_dvfs_core_cap_enable(bool enable)
 {}
 static inline void tegra_dvfs_core_cap_level_set(int level)
 {}
+static inline int tegra_dvfs_alt_freqs_set(struct dvfs *d, bool enable)
+{ return 0; }
 #endif
 
 #ifndef CONFIG_ARCH_TEGRA_2x_SOC