summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra
diff options
context:
space:
mode:
authorPavan Kunapuli <pkunapuli@nvidia.com>2013-04-22 14:54:05 +0530
committerSimone Willett <swillett@nvidia.com>2013-04-23 12:01:14 -0700
commit8fa21bd5ceb9bbe86baecb8935c719128953da5e (patch)
tree33a3d91e7109173736fd4c750089097155aa2c2b /arch/arm/mach-tegra
parentbaa0f5d83640831a4a5a380687c6a845507d3e05 (diff)
ARM: tegra: power: Re-factor core voltage capping
Re-factored core voltage capping APIs: - Collapsed set level and enable APIs into one set level interface; non-zero level automatically enables capping, and zero level disables capping. Attempt to apply new non-zero level before the previous one is disabled is rejected. - Added error reporting and propagation through api layers - Allowed to set cap voltage at nominal vdd core level even if voltage to frequency mapping is not ready (no clock rates are capped at nominal voltage, anyway). Core voltage capping APIs are not used by any kernel client (designated to vdd core override mechanism, yet to be added). Hence, function names and signatures changes are transparent, for now. User space vdd core capping sysfs nodes are not affected and kept backward compatible. Bug 1246712 Signed-off-by: Alex Frid <afrid@nvidia.com> Change-Id: I66343c77e1bae337b8c829d98fb98dc75fc9a971 Signed-off-by: Pavan Kunapuli <pkunapuli@nvidia.com> Reviewed-on: http://git-master/r/221526 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Tao Xie <txie@nvidia.com> Tested-by: Tao Xie <txie@nvidia.com> Reviewed-by: Aleksandr Frid <afrid@nvidia.com> Reviewed-by: Matt Wagner <mwagner@nvidia.com>
Diffstat (limited to 'arch/arm/mach-tegra')
-rw-r--r--arch/arm/mach-tegra/dvfs.h3
-rw-r--r--arch/arm/mach-tegra/tegra_core_volt_cap.c79
2 files changed, 48 insertions, 34 deletions
diff --git a/arch/arm/mach-tegra/dvfs.h b/arch/arm/mach-tegra/dvfs.h
index a532c82f92ff..b6bda2e4f8f6 100644
--- a/arch/arm/mach-tegra/dvfs.h
+++ b/arch/arm/mach-tegra/dvfs.h
@@ -210,8 +210,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);
int tegra_dvfs_predict_millivolts_pll(struct clk *c, unsigned long rate);
int tegra_dvfs_predict_millivolts_dfll(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_core_cap_level_apply(int level);
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);
diff --git a/arch/arm/mach-tegra/tegra_core_volt_cap.c b/arch/arm/mach-tegra/tegra_core_volt_cap.c
index 30eaa64cfc73..4627ef97360b 100644
--- a/arch/arm/mach-tegra/tegra_core_volt_cap.c
+++ b/arch/arm/mach-tegra/tegra_core_volt_cap.c
@@ -51,14 +51,18 @@ static int core_cap_table_size;
static const int *cap_millivolts;
static int cap_millivolts_num;
-static int core_nominal_mv;
-
-static void core_cap_level_set(int level)
+static int core_cap_level_set(int level, int core_nominal_mv)
{
int i, j;
+ int ret = 0;
- if (!core_cap_table)
- return;
+ if (!core_cap_table) {
+ if (level == core_nominal_mv) {
+ core_buses_cap.level = level;
+ return 0;
+ }
+ return -ENOENT;
+ }
for (j = 0; j < cap_millivolts_num; j++) {
int v = cap_millivolts[j];
@@ -71,38 +75,47 @@ static void core_cap_level_set(int level)
if (level < core_buses_cap.level) {
for (i = 0; i < core_cap_table_size; i++)
if (core_cap_table[i].cap_clk)
- clk_set_rate(core_cap_table[i].cap_clk,
+ ret |= clk_set_rate(core_cap_table[i].cap_clk,
core_cap_table[i].freqs[j]);
} else if (level > core_buses_cap.level) {
for (i = core_cap_table_size - 1; i >= 0; i--)
if (core_cap_table[i].cap_clk)
- clk_set_rate(core_cap_table[i].cap_clk,
+ ret |= clk_set_rate(core_cap_table[i].cap_clk,
core_cap_table[i].freqs[j]);
}
core_buses_cap.level = level;
+ if (ret)
+ ret = -EAGAIN;
+ return ret;
}
-static void core_cap_update(void)
+static int core_cap_update(void)
{
- int new_level = core_nominal_mv;
+ int new_level;
+ int core_nominal_mv =
+ tegra_dvfs_rail_get_nominal_millivolts(tegra_core_rail);
+ if (core_nominal_mv <= 0)
+ return -ENOENT;
+ new_level = core_nominal_mv;
if (kdvfs_core_cap.refcnt)
new_level = min(new_level, kdvfs_core_cap.level);
if (user_core_cap.refcnt)
new_level = min(new_level, user_core_cap.level);
if (core_buses_cap.level != new_level)
- core_cap_level_set(new_level);
+ return core_cap_level_set(new_level, core_nominal_mv);
+ return 0;
}
-static void core_cap_enable(bool enable)
+static int core_cap_enable(bool enable)
{
if (enable)
core_buses_cap.refcnt++;
else if (core_buses_cap.refcnt)
core_buses_cap.refcnt--;
- core_cap_update();
+ return core_cap_update();
}
static ssize_t
@@ -171,28 +184,32 @@ const struct attribute *cap_attributes[] = {
NULL,
};
-void tegra_dvfs_core_cap_enable(bool enable)
+int tegra_dvfs_core_cap_level_apply(int level)
{
+ int ret = 0;
+
mutex_lock(&core_cap_lock);
- if (enable) {
- kdvfs_core_cap.refcnt++;
- if (kdvfs_core_cap.refcnt == 1)
- core_cap_enable(true);
+ if (level) {
+ if (kdvfs_core_cap.refcnt) {
+ pr_err("%s: core cap is already set\n", __func__);
+ ret = -EPERM;
+ } else {
+ kdvfs_core_cap.level = level;
+ kdvfs_core_cap.refcnt = 1;
+ ret = core_cap_enable(true);
+ if (ret) {
+ kdvfs_core_cap.refcnt = 0;
+ core_cap_enable(false);
+ }
+ }
} else if (kdvfs_core_cap.refcnt) {
- kdvfs_core_cap.refcnt--;
- if (kdvfs_core_cap.refcnt == 0)
- core_cap_enable(false);
+ kdvfs_core_cap.refcnt = 0;
+ core_cap_enable(false);
}
- mutex_unlock(&core_cap_lock);
-}
-void tegra_dvfs_core_cap_level_set(int level)
-{
- mutex_lock(&core_cap_lock);
- kdvfs_core_cap.level = level;
- core_cap_update();
mutex_unlock(&core_cap_lock);
+ return ret;
}
static int __init init_core_cap_one(struct clk *c, unsigned long *freqs)
@@ -249,15 +266,13 @@ int __init tegra_init_core_cap(
if (!table || !table_size || !millivolts || !millivolts_num)
return -EINVAL;
- core_nominal_mv =
+ user_core_cap.level =
tegra_dvfs_rail_get_nominal_millivolts(tegra_core_rail);
- if (core_nominal_mv <= 0)
- return -ENODATA;
+ if (user_core_cap.level <= 0)
+ return -ENOENT;
cap_millivolts = millivolts;
cap_millivolts_num = millivolts_num;
- core_buses_cap.level = kdvfs_core_cap.level = user_core_cap.level =
- core_nominal_mv;
for (i = 0; i < table_size; i++) {
c = tegra_get_clock_by_name(table[i].cap_name);