summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-tegra/dvfs.c46
-rw-r--r--arch/arm/mach-tegra/dvfs.h11
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