diff options
author | Alex Frid <afrid@nvidia.com> | 2013-11-15 23:16:06 -0800 |
---|---|---|
committer | Yu-Huan Hsu <yhsu@nvidia.com> | 2013-11-19 19:49:58 -0800 |
commit | 4d194fc765cf6dbc96bc261880b7aaadd0e56d98 (patch) | |
tree | bcad46e100dc6da90a445b94c46bebc4cff80688 /arch/arm/mach-tegra | |
parent | 9fa1be6e12d82bcc72c44d03c52ae7c6c8078798 (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.c | 21 | ||||
-rw-r--r-- | arch/arm/mach-tegra/dvfs.c | 68 | ||||
-rw-r--r-- | arch/arm/mach-tegra/dvfs.h | 4 |
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); |