summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-tegra/Kconfig10
-rw-r--r--arch/arm/mach-tegra/dvfs.c110
-rw-r--r--arch/arm/mach-tegra/dvfs.h10
-rw-r--r--arch/arm/mach-tegra/include/mach/clk.h1
4 files changed, 129 insertions, 2 deletions
diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig
index 1568e85a91e2..0224d4a48323 100644
--- a/arch/arm/mach-tegra/Kconfig
+++ b/arch/arm/mach-tegra/Kconfig
@@ -127,6 +127,7 @@ config ARCH_TEGRA_11x_SOC
select TEGRA_ISOMGR_DEBUG
select TEGRA_ERRATA_1157520
select TEGRA_CORE_EDP_LIMITS
+ select TEGRA_VDD_CORE_OVERRIDE
select TEGRA_THERMAL_THROTTLE_EXACT_FREQ
select ARM_ERRATA_799270
help
@@ -899,4 +900,13 @@ config TEGRA_PLLM_SCALED
granularity of possible memory rate steps. In this case PLLC
provides a backup memory clock while PLLM is re-locking to the
new rate.
+
+config TEGRA_VDD_CORE_OVERRIDE
+ bool "Enable core rail override support"
+ depends on TEGRA_SILICON_PLATFORM
+ default n
+ help
+ When enabled, core rail can be fixed and locked at specified voltage
+ within override range, and core modules clocks are capped at rates
+ safe at override level.
endif
diff --git a/arch/arm/mach-tegra/dvfs.c b/arch/arm/mach-tegra/dvfs.c
index 50fbb406f4b0..66877de687f3 100644
--- a/arch/arm/mach-tegra/dvfs.c
+++ b/arch/arm/mach-tegra/dvfs.c
@@ -289,7 +289,10 @@ static inline int dvfs_rail_apply_limits(struct dvfs_rail *rail, int millivolts)
min_mv = max(min_mv, rail->thermal_idx ?
0 : rail->min_millivolts_cold);
- millivolts += rail->offs_millivolts;
+ if (rail->override_millivolts)
+ millivolts = rail->override_millivolts;
+ else
+ millivolts += rail->offs_millivolts;
if (millivolts > rail->max_millivolts)
millivolts = rail->max_millivolts;
else if (millivolts < min_mv)
@@ -529,6 +532,71 @@ int tegra_dvfs_set_rate(struct clk *c, unsigned long rate)
}
EXPORT_SYMBOL(tegra_dvfs_set_rate);
+#ifdef CONFIG_TEGRA_VDD_CORE_OVERRIDE
+static DEFINE_MUTEX(rail_override_lock);
+
+int tegra_dvfs_override_core_voltage(int override_mv)
+{
+ int ret, floor, ceiling;
+ struct dvfs_rail *rail = tegra_core_rail;
+
+ if (!rail)
+ return -ENOENT;
+
+ floor = rail->min_override_millivolts;
+ ceiling = rail->nominal_millivolts;
+ if (override_mv && ((override_mv < floor) || (override_mv > ceiling))) {
+ pr_err("%s: override level %d outside the range [%d...%d]\n",
+ __func__, override_mv, floor, ceiling);
+ return -EINVAL;
+ }
+
+ mutex_lock(&rail_override_lock);
+
+ if (override_mv) {
+ ret = tegra_dvfs_core_cap_level_apply(override_mv);
+ if (ret) {
+ pr_err("%s: failed to set cap for override level %d\n",
+ __func__, override_mv);
+ goto out;
+ }
+ }
+
+ mutex_lock(&dvfs_lock);
+ if (rail->disabled || rail->suspended) {
+ pr_err("%s: cannot scale %s rail\n", __func__,
+ rail->disabled ? "disabled" : "suspended");
+ ret = -EPERM;
+ if (!override_mv) {
+ mutex_unlock(&dvfs_lock);
+ goto out;
+ }
+ } else {
+ rail->override_millivolts = override_mv;
+ ret = dvfs_rail_update(rail);
+ if (ret) {
+ pr_err("%s: failed to set override level %d\n",
+ __func__, override_mv);
+ rail->override_millivolts = 0;
+ dvfs_rail_update(rail);
+ }
+ }
+ mutex_unlock(&dvfs_lock);
+
+ if (!override_mv || ret)
+ tegra_dvfs_core_cap_level_apply(0);
+out:
+ mutex_unlock(&rail_override_lock);
+ return ret;
+}
+#else
+int tegra_dvfs_override_core_voltage(int override_mv)
+{
+ return -ENOSYS;
+}
+#endif
+EXPORT_SYMBOL(tegra_dvfs_override_core_voltage);
+
/* 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)
{
@@ -561,6 +629,18 @@ int __init tegra_enable_dvfs_on_clk(struct clk *c, struct dvfs *d)
c->dvfs = d;
+ /*
+ * Minimum core override level is determined as maximum voltage required
+ * for clocks outside shared buses (shared bus rates can be capped to
+ * safe levels when override limit is set)
+ */
+ if (i && c->ops && !c->ops->shared_bus_update &&
+ !(c->flags & PERIPH_ON_CBUS)) {
+ int mv = tegra_dvfs_predict_millivolts(c, d->freqs[i-1]);
+ if (d->dvfs_rail->min_override_millivolts < mv)
+ d->dvfs_rail->min_override_millivolts = mv;
+ }
+
mutex_lock(&dvfs_lock);
list_add_tail(&d->reg_node, &d->dvfs_rail->dvfs);
mutex_unlock(&dvfs_lock);
@@ -1056,6 +1136,13 @@ static int dvfs_tree_show(struct seq_file *s, void *data)
}
seq_printf(s, " offset %-7d mV\n", rail->offs_millivolts);
+ if (rail == tegra_core_rail) {
+ seq_printf(s, " override %-7d mV [%-4d...%-4d]\n",
+ rail->override_millivolts,
+ rail->min_override_millivolts,
+ rail->nominal_millivolts);
+ }
+
list_sort(NULL, &rail->dvfs, dvfs_tree_sort_cmp);
list_for_each_entry(d, &rail->dvfs, reg_node) {
@@ -1151,6 +1238,22 @@ static int core_offs_set(void *data, u64 val)
}
DEFINE_SIMPLE_ATTRIBUTE(core_offs_fops, core_offs_get, core_offs_set, "%lld\n");
+static int core_override_get(void *data, u64 *val)
+{
+ if (tegra_core_rail) {
+ *val = (u64)tegra_core_rail->override_millivolts;
+ return 0;
+ }
+ *val = 0;
+ return -ENOENT;
+}
+static int core_override_set(void *data, u64 val)
+{
+ return tegra_dvfs_override_core_voltage((int)val);
+}
+DEFINE_SIMPLE_ATTRIBUTE(core_override_fops,
+ core_override_get, core_override_set, "%llu\n");
+
int __init dvfs_debugfs_init(struct dentry *clk_debugfs_root)
{
struct dentry *d;
@@ -1175,6 +1278,11 @@ int __init dvfs_debugfs_init(struct dentry *clk_debugfs_root)
if (!d)
return -ENOMEM;
+ d = debugfs_create_file("vdd_core_override", S_IRUGO | S_IWUSR,
+ clk_debugfs_root, NULL, &core_override_fops);
+ if (!d)
+ return -ENOMEM;
+
return 0;
}
diff --git a/arch/arm/mach-tegra/dvfs.h b/arch/arm/mach-tegra/dvfs.h
index 2b73b6d200a3..a532c82f92ff 100644
--- a/arch/arm/mach-tegra/dvfs.h
+++ b/arch/arm/mach-tegra/dvfs.h
@@ -63,7 +63,8 @@ struct dvfs_rail {
int reg_max_millivolts;
int nominal_millivolts;
int min_millivolts_cold;
-
+ int override_millivolts;
+ int min_override_millivolts;
int step;
bool jmp_to_zero;
bool disabled;
@@ -292,4 +293,11 @@ static inline int tegra_dvfs_rail_get_nominal_millivolts(struct dvfs_rail *rail)
return -ENOENT;
}
+static inline int tegra_dvfs_rail_get_override_floor(struct dvfs_rail *rail)
+{
+ if (rail)
+ return rail->min_override_millivolts;
+ return -ENOENT;
+}
+
#endif
diff --git a/arch/arm/mach-tegra/include/mach/clk.h b/arch/arm/mach-tegra/include/mach/clk.h
index ea9dbae6c7e7..bcce817cc1b3 100644
--- a/arch/arm/mach-tegra/include/mach/clk.h
+++ b/arch/arm/mach-tegra/include/mach/clk.h
@@ -40,6 +40,7 @@ void tegra_periph_reset_deassert(struct clk *c);
void tegra_periph_reset_assert(struct clk *c);
int tegra_dvfs_set_rate(struct clk *c, unsigned long rate);
+int tegra_dvfs_override_core_voltage(int override_mv);
unsigned long clk_get_rate_all_locked(struct clk *c);
#ifdef CONFIG_ARCH_TEGRA_2x_SOC