summaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorAlex Frid <afrid@nvidia.com>2013-03-11 21:58:05 -0700
committerSimone Willett <swillett@nvidia.com>2013-04-23 12:01:07 -0700
commitbaa0f5d83640831a4a5a380687c6a845507d3e05 (patch)
tree03ea4e829758ccb089883ff26f2fe9382b0c1490 /arch
parentbd85824b9cab30d50d8960fb03cd5f30636f64d4 (diff)
ARM: tegra: dvfs: Add VDD_CORE override mechanism
Added mechanism to fix and lock (override) VDD_CORE rail voltage at particular level. When override mode is entered, all scalable shared buses and bus users (memory, graphics cbus clocks, system bus clocks) are throttled to/below the rate safe at override voltage. Other clocks, however, continue to run at rates set by the respective drivers. Hence override voltage must be high enough to allow maximum rates of all core clocks outside shared buses. The lowest possible override level is determined by core dvfs tables, and the supported override range is defined accordingly. Attempt to set override voltage outside the range will fail, with the exception of level 0 that is interpreted as request to exit override mode. Override voltage cannot be changed if VDD_CORE is already locked: first override mode has to be exited via zero level request, and then a new override voltage can be set. No other override arbitration is provided. Bug 1246712 Signed-off-by: Alex Frid <afrid@nvidia.com> Change-Id: I34975774fbf05025e06d9db2a0de74da7f31a73f Signed-off-by: Pavan Kunapuli <pkunapuli@nvidia.com> Reviewed-on: http://git-master/r/221525 Reviewed-by: Simone Willett <swillett@nvidia.com> Tested-by: Simone Willett <swillett@nvidia.com>
Diffstat (limited to 'arch')
-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