diff options
-rw-r--r-- | arch/arm/mach-tegra/dvfs.c | 46 | ||||
-rw-r--r-- | arch/arm/mach-tegra/dvfs.h | 11 |
2 files changed, 54 insertions, 3 deletions
diff --git a/arch/arm/mach-tegra/dvfs.c b/arch/arm/mach-tegra/dvfs.c index 22c666081c90..8723e6fa60df 100644 --- a/arch/arm/mach-tegra/dvfs.c +++ b/arch/arm/mach-tegra/dvfs.c @@ -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; diff --git a/arch/arm/mach-tegra/dvfs.h b/arch/arm/mach-tegra/dvfs.h index 462eef645a4f..f7e863f14f39 100644 --- a/arch/arm/mach-tegra/dvfs.h +++ b/arch/arm/mach-tegra/dvfs.h @@ -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 |