summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra
diff options
context:
space:
mode:
authorAlex Frid <afrid@nvidia.com>2013-11-15 23:16:06 -0800
committerYu-Huan Hsu <yhsu@nvidia.com>2013-11-19 19:49:58 -0800
commit4d194fc765cf6dbc96bc261880b7aaadd0e56d98 (patch)
treebcad46e100dc6da90a445b94c46bebc4cff80688 /arch/arm/mach-tegra
parent9fa1be6e12d82bcc72c44d03c52ae7c6c8078798 (diff)
ARM: tegra: dvfs: Support persistent alt frequencies
Existing interface to dvfs alternative frequencies combined enable control with alternative ladder installation. Hence, the only way to switch back to primary frequencies was to de-install alternative ones. This commit provided separate install and enable interfaces, so that alternative ladder can be installed once, and then enabled/disabled under client control. Also made sure alternative voltage-frequency mapping results in lower voltage at the same frequency (this was an implicit assumption of alternative frequencies support - now it is enforced explicitly). Bug 1397158 Change-Id: I4663602d1882f72156e02590f41ff04e7e69fbf8 Signed-off-by: Alex Frid <afrid@nvidia.com> Reviewed-on: http://git-master/r/333090 Reviewed-by: Yu-Huan Hsu <yhsu@nvidia.com>
Diffstat (limited to 'arch/arm/mach-tegra')
-rw-r--r--arch/arm/mach-tegra/clock.c21
-rw-r--r--arch/arm/mach-tegra/dvfs.c68
-rw-r--r--arch/arm/mach-tegra/dvfs.h4
3 files changed, 88 insertions, 5 deletions
diff --git a/arch/arm/mach-tegra/clock.c b/arch/arm/mach-tegra/clock.c
index 2678315877f5..ca18a8c65e5a 100644
--- a/arch/arm/mach-tegra/clock.c
+++ b/arch/arm/mach-tegra/clock.c
@@ -1694,6 +1694,20 @@ static const struct file_operations possible_rates_fops = {
.release = single_release,
};
+static int use_alt_freq_get(void *data, u64 *val)
+{
+ struct clk *c = (struct clk *)data;
+ *val = c->dvfs->use_alt_freqs;
+ return 0;
+}
+static int use_alt_freq_set(void *data, u64 val)
+{
+ struct clk *c = (struct clk *)data;
+ return tegra_dvfs_use_alt_freqs_on_clk(c, val);
+}
+DEFINE_SIMPLE_ATTRIBUTE(use_alt_freq_fops,
+ use_alt_freq_get, use_alt_freq_set, "%llu\n");
+
static int clk_debugfs_register_one(struct clk *c)
{
struct dentry *d;
@@ -1762,6 +1776,13 @@ static int clk_debugfs_register_one(struct clk *c)
goto err_out;
}
+ if (c->dvfs) {
+ d = debugfs_create_file("use_alt_freq", S_IRUGO | S_IWUSR,
+ c->dent, c, &use_alt_freq_fops);
+ if (!d)
+ goto err_out;
+ }
+
return 0;
err_out:
diff --git a/arch/arm/mach-tegra/dvfs.c b/arch/arm/mach-tegra/dvfs.c
index 8fae879b4ca2..6a16a2fc8c98 100644
--- a/arch/arm/mach-tegra/dvfs.c
+++ b/arch/arm/mach-tegra/dvfs.c
@@ -559,7 +559,7 @@ static int dvfs_rail_connect_to_regulator(struct dvfs_rail *rail)
static inline unsigned long *dvfs_get_freqs(struct dvfs *d)
{
- return d->alt_freqs ? : &d->freqs[0];
+ return d->alt_freqs && d->use_alt_freqs ? d->alt_freqs : &d->freqs[0];
}
static inline const int *dvfs_get_millivolts(struct dvfs *d, unsigned long rate)
@@ -647,6 +647,61 @@ __tegra_dvfs_set_rate(struct dvfs *d, unsigned long rate)
return ret;
}
+/*
+ * Some clocks may have alternative frequency ladder that provides lower minimum
+ * voltage at the same rate (or complimentary: higher maximum rate at the same
+ * voltage). Interfaces below allows dvfs clients to install such ladder, and
+ * switch between primary and alternative frequencies in flight.
+ */
+static int alt_freqs_validate(struct dvfs *d, unsigned long *alt_freqs)
+{
+ int i;
+
+ if (alt_freqs) {
+ for (i = 0; i < d->num_freqs; i++) {
+ if (d->freqs[i] > alt_freqs[i]) {
+ pr_err("%s: Invalid alt freqs for %s\n",
+ __func__, d->clk_name);
+ return -EINVAL;
+ }
+ }
+ }
+ return 0;
+}
+
+int tegra_dvfs_alt_freqs_install(struct dvfs *d, unsigned long *alt_freqs)
+{
+ int ret = 0;
+
+ mutex_lock(&dvfs_lock);
+
+ ret = alt_freqs_validate(d, alt_freqs);
+ if (!ret)
+ d->alt_freqs = alt_freqs;
+
+ mutex_unlock(&dvfs_lock);
+ return ret;
+}
+
+int tegra_dvfs_use_alt_freqs_on_clk(struct clk *c, bool use_alt_freq)
+{
+ int ret = -ENOENT;
+ struct dvfs *d = c->dvfs;
+
+ mutex_lock(&dvfs_lock);
+
+ if (d && d->alt_freqs) {
+ ret = 0;
+ if (d->use_alt_freqs != use_alt_freq) {
+ d->use_alt_freqs = use_alt_freq;
+ ret = __tegra_dvfs_set_rate(d, d->cur_rate);
+ }
+ }
+
+ mutex_unlock(&dvfs_lock);
+ return ret;
+}
+
int tegra_dvfs_alt_freqs_set(struct dvfs *d, unsigned long *alt_freqs)
{
int ret = 0;
@@ -654,8 +709,12 @@ int tegra_dvfs_alt_freqs_set(struct dvfs *d, unsigned long *alt_freqs)
mutex_lock(&dvfs_lock);
if (d->alt_freqs != alt_freqs) {
- d->alt_freqs = alt_freqs;
- ret = __tegra_dvfs_set_rate(d, d->cur_rate);
+ ret = alt_freqs_validate(d, alt_freqs);
+ if (!ret) {
+ d->use_alt_freqs = !!alt_freqs;
+ d->alt_freqs = alt_freqs;
+ ret = __tegra_dvfs_set_rate(d, d->cur_rate);
+ }
}
mutex_unlock(&dvfs_lock);
@@ -2046,7 +2105,8 @@ static int dvfs_table_show(struct seq_file *s, void *data)
seq_printf(s, "%-16s", d->clk_name);
for (i = 0; i < d->num_freqs; i++) {
- unsigned int f = d->freqs[i]/100000;
+ unsigned long *freqs = dvfs_get_freqs(d);
+ unsigned int f = freqs[i]/100000;
seq_printf(s, " %4u.%u", f/10, f%10);
}
seq_printf(s, "\n");
diff --git a/arch/arm/mach-tegra/dvfs.h b/arch/arm/mach-tegra/dvfs.h
index aadf9668cb4b..bcb1f62fc086 100644
--- a/arch/arm/mach-tegra/dvfs.h
+++ b/arch/arm/mach-tegra/dvfs.h
@@ -144,7 +144,6 @@ struct dvfs {
/* Must be initialized before tegra_dvfs_init */
int freqs_mult;
unsigned long freqs[MAX_DVFS_FREQS];
- unsigned long *alt_freqs;
const int *millivolts;
const int *peak_millivolts;
const int *dfll_millivolts;
@@ -161,6 +160,8 @@ struct dvfs {
int cur_millivolts;
unsigned long cur_rate;
+ unsigned long *alt_freqs;
+ bool use_alt_freqs;
struct list_head node;
struct list_head debug_node;
struct list_head reg_node;
@@ -250,6 +251,7 @@ int tegra_dvfs_predict_millivolts_dfll(struct clk *c, unsigned long rate);
const int *tegra_dvfs_get_millivolts_pll(struct dvfs *d);
int tegra_dvfs_override_core_cap_apply(int level);
+int tegra_dvfs_alt_freqs_install(struct dvfs *d, unsigned long *alt_freqs);
int tegra_dvfs_alt_freqs_set(struct dvfs *d, unsigned long *alt_freqs);
int tegra_cpu_dvfs_alter(int edp_thermal_index, const cpumask_t *cpus,
bool before_clk_update, int cpu_event);