summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/dvfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-tegra/dvfs.c')
-rw-r--r--arch/arm/mach-tegra/dvfs.c73
1 files changed, 73 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/dvfs.c b/arch/arm/mach-tegra/dvfs.c
index 47c38be2dfd4..b1414a100350 100644
--- a/arch/arm/mach-tegra/dvfs.c
+++ b/arch/arm/mach-tegra/dvfs.c
@@ -830,6 +830,7 @@ int tegra_dvfs_predict_millivolts(struct clk *c, unsigned long rate)
tegra_dvfs_get_millivolts_pll(c->dvfs);
return predict_millivolts(c, millivolts, rate);
}
+EXPORT_SYMBOL(tegra_dvfs_predict_millivolts);
int tegra_dvfs_predict_peak_millivolts(struct clk *c, unsigned long rate)
{
@@ -1004,12 +1005,74 @@ int tegra_dvfs_rail_get_override_floor(struct dvfs_rail *rail)
}
return -ENOENT;
}
+
+static int dvfs_set_fmax_at_vmin(struct clk *c, unsigned long f_max, int v_min)
+{
+ int i, ret = 0;
+ struct dvfs *d = c->dvfs;
+ unsigned long f_min = 1000; /* 1kHz min rate in DVFS tables */
+
+ mutex_lock(&rail_override_lock);
+ mutex_lock(&dvfs_lock);
+
+ if (v_min > d->dvfs_rail->override_millivolts) {
+ pr_err("%s: new %s vmin %dmV is above override voltage %dmV\n",
+ __func__, c->name, v_min,
+ d->dvfs_rail->override_millivolts);
+ ret = -EPERM;
+ goto out;
+ }
+
+ if (v_min >= d->max_millivolts) {
+ pr_err("%s: new %s vmin %dmV is at/above max voltage %dmV\n",
+ __func__, c->name, v_min, d->max_millivolts);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * dvfs table update:
+ * - for voltages below new v_min the respective frequencies are shifted
+ * below new f_max to the levels already present in the table; if the
+ * 1st table entry has frequency above new fmax, all entries below v_min
+ * are filled in with 1kHz (min rate used in DVFS tables).
+ * - for voltages above new v_min, the respective frequencies are
+ * increased to at least new f_max
+ * - if new v_min is already in the table set the respective frequency
+ * to new f_max
+ */
+ for (i = 0; i < d->num_freqs; i++) {
+ int mv = d->millivolts[i];
+ unsigned long f = d->freqs[i];
+
+ if (mv < v_min) {
+ if (d->freqs[i] >= f_max)
+ d->freqs[i] = i ? d->freqs[i-1] : f_min;
+ } else if (mv > v_min) {
+ d->freqs[i] = max(f, f_max);
+ } else {
+ d->freqs[i] = f_max;
+ }
+ ret = __tegra_dvfs_set_rate(d, d->cur_rate);
+ }
+out:
+ mutex_unlock(&dvfs_lock);
+ mutex_unlock(&rail_override_lock);
+
+ return ret;
+}
#else
static int dvfs_override_core_voltage(int override_mv)
{
pr_err("%s: vdd core override is not supported\n", __func__);
return -ENOSYS;
}
+
+static int dvfs_set_fmax_at_vmin(struct clk *c, unsigned long f_max, int v_min)
+{
+ pr_err("%s: vdd core override is not supported\n", __func__);
+ return -ENOSYS;
+}
#endif
int tegra_dvfs_override_core_voltage(struct clk *c, int override_mv)
@@ -1022,6 +1085,16 @@ int tegra_dvfs_override_core_voltage(struct clk *c, int override_mv)
}
EXPORT_SYMBOL(tegra_dvfs_override_core_voltage);
+int tegra_dvfs_set_fmax_at_vmin(struct clk *c, unsigned long f_max, int v_min)
+{
+ if (!c->dvfs || !c->dvfs->can_override) {
+ pr_err("%s: %s cannot set fmax_at_vmin)\n", __func__, c->name);
+ return -EPERM;
+ }
+ return dvfs_set_fmax_at_vmin(c, f_max, v_min);
+}
+EXPORT_SYMBOL(tegra_dvfs_set_fmax_at_vmin);
+
/* May only be called during clock init, does not take any locks on clock c. */
int __init tegra_enable_dvfs_on_clk(struct clk *c, struct dvfs *d)
{