summaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorAlex Frid <afrid@nvidia.com>2013-07-12 19:58:41 -0700
committerHarshada Kale <hkale@nvidia.com>2013-09-12 02:21:25 -0700
commit1e821c4257c85b8d2dddc3eb67ca1b61445f9eba (patch)
tree759b4a6bb3fc1f810301b31cb2eb5d2e5df65ae1 /arch
parent5d927cc305aa8d0c2e6af9fedda9f5b10d378e16 (diff)
ARM: tegra: dvfs: Decouple nominal and detached voltages
DVFS rail nominal voltage is minimum voltage required to run all associated clocks at maximum allowed rates. DVFS rail can be detached from clocks during initial boot, on suspend entry/exit, or when voltage scaling is disabled. So far, rail voltage in any detached mode was set to nominal level. This commit introduced separate voltages for each detached mode. If any of these levels is not specified, backward compatible nominal voltage is used. Since, suspend voltage may now be different from nominal (below), it is important for dvfs to suspend after suspend edp rate caps are set, and resume before edp. Hence, priorities of dvfs suspend notifiers were adjusted accordingly. Change-Id: Id05e0b16f24dc7d28b1ee9e87afd63d98a9ab86e Signed-off-by: Alex Frid <afrid@nvidia.com> Reviewed-on: http://git-master/r/250029 (cherry picked from commit 57d1ea085f098f43db40a9484e5f9d13ec49a45b) Reviewed-on: http://git-master/r/253648 Tested-by: Shaoming Feng <shaomingf@nvidia.com> GVS: Gerrit_Virtual_Submit Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/mach-tegra/dvfs.c129
-rw-r--r--arch/arm/mach-tegra/dvfs.h11
-rw-r--r--arch/arm/mach-tegra/tegra_core_volt_cap.c3
3 files changed, 116 insertions, 27 deletions
diff --git a/arch/arm/mach-tegra/dvfs.c b/arch/arm/mach-tegra/dvfs.c
index 12fe0d01f273..46fe8bb94e8e 100644
--- a/arch/arm/mach-tegra/dvfs.c
+++ b/arch/arm/mach-tegra/dvfs.c
@@ -51,6 +51,16 @@ static DEFINE_MUTEX(rail_disable_lock);
static int dvfs_rail_update(struct dvfs_rail *rail);
+static inline int tegra_dvfs_rail_get_disable_level(struct dvfs_rail *rail)
+{
+ return rail->disable_millivolts ? : rail->nominal_millivolts;
+}
+
+static inline int tegra_dvfs_rail_get_suspend_level(struct dvfs_rail *rail)
+{
+ return rail->suspend_millivolts ? : rail->nominal_millivolts;
+}
+
void tegra_dvfs_add_relationships(struct dvfs_relationship *rels, int n)
{
int i;
@@ -69,7 +79,7 @@ void tegra_dvfs_add_relationships(struct dvfs_relationship *rels, int n)
int tegra_dvfs_init_rails(struct dvfs_rail *rails[], int n)
{
- int i;
+ int i, mv;
mutex_lock(&dvfs_lock);
@@ -77,8 +87,19 @@ int tegra_dvfs_init_rails(struct dvfs_rail *rails[], int n)
INIT_LIST_HEAD(&rails[i]->dvfs);
INIT_LIST_HEAD(&rails[i]->relationships_from);
INIT_LIST_HEAD(&rails[i]->relationships_to);
- rails[i]->millivolts = rails[i]->nominal_millivolts;
- rails[i]->new_millivolts = rails[i]->nominal_millivolts;
+
+ mv = rails[i]->nominal_millivolts;
+ if (rails[i]->boot_millivolts > mv)
+ WARN(1, "%s: boot voltage %d above nominal %d\n",
+ rails[i]->reg_id, rails[i]->boot_millivolts, mv);
+ if (rails[i]->disable_millivolts > mv)
+ rails[i]->disable_millivolts = mv;
+ if (rails[i]->suspend_millivolts > mv)
+ rails[i]->suspend_millivolts = mv;
+
+ mv = tegra_dvfs_rail_get_boot_level(rails[i]);
+ rails[i]->millivolts = mv;
+ rails[i]->new_millivolts = mv;
if (!rails[i]->step)
rails[i]->step = rails[i]->max_millivolts;
@@ -380,6 +401,13 @@ static int dvfs_rail_connect_to_regulator(struct dvfs_rail *rail)
rail->millivolts = v / 1000;
rail->new_millivolts = rail->millivolts;
dvfs_rail_stats_init(rail, rail->millivolts);
+
+ if (rail->boot_millivolts &&
+ (rail->boot_millivolts != rail->millivolts)) {
+ WARN(1, "%s boot voltage %d does not match expected %d\n",
+ rail->reg_id, rail->millivolts, rail->boot_millivolts);
+ rail->boot_millivolts = rail->millivolts;
+ }
return 0;
}
@@ -400,7 +428,7 @@ static int
__tegra_dvfs_set_rate(struct dvfs *d, unsigned long rate)
{
int i = 0;
- int ret;
+ int ret, mv, detach_mv;
unsigned long *freqs = dvfs_get_freqs(d);
const int *millivolts = dvfs_get_millivolts(d, rate);
@@ -430,6 +458,28 @@ __tegra_dvfs_set_rate(struct dvfs *d, unsigned long rate)
" %s\n", millivolts[i], d->clk_name);
return -EINVAL;
}
+
+ mv = millivolts[i];
+ detach_mv = tegra_dvfs_rail_get_boot_level(d->dvfs_rail);
+ if (!d->dvfs_rail->reg && (mv > detach_mv)) {
+ pr_warn("%s: %s: voltage %d above boot limit %d\n",
+ __func__, d->clk_name, mv, detach_mv);
+ return -EINVAL;
+ }
+
+ detach_mv = tegra_dvfs_rail_get_disable_level(d->dvfs_rail);
+ if (d->dvfs_rail->disabled && (mv > detach_mv)) {
+ pr_warn("%s: %s: voltage %d above disable limit %d\n",
+ __func__, d->clk_name, mv, detach_mv);
+ return -EINVAL;
+ }
+
+ detach_mv = tegra_dvfs_rail_get_suspend_level(d->dvfs_rail);
+ if (d->dvfs_rail->suspended && (mv > detach_mv)) {
+ pr_warn("%s: %s: voltage %d above disable limit %d\n",
+ __func__, d->clk_name, mv, detach_mv);
+ return -EINVAL;
+ }
d->cur_millivolts = millivolts[i];
}
@@ -686,11 +736,19 @@ static int tegra_dvfs_suspend_one(void)
list_for_each_entry(rail, &dvfs_rail_list, node) {
if (!rail->suspended && !rail->disabled &&
tegra_dvfs_from_rails_suspended_or_solved(rail)) {
- int mv = dvfs_rail_apply_limits(
- rail, rail->nominal_millivolts);
- ret = dvfs_rail_set_voltage(rail, mv);
- if (ret)
+ /* apply suspend limit only if it is above current mv */
+ int mv = tegra_dvfs_rail_get_suspend_level(rail);
+ mv = dvfs_rail_apply_limits(rail, mv);
+ ret = -EPERM;
+
+ if (mv >= rail->millivolts)
+ ret = dvfs_rail_set_voltage(rail, mv);
+ if (ret) {
+ pr_err("tegra_dvfs: failed %s suspend at %d\n",
+ rail->reg_id, rail->millivolts);
return ret;
+ }
+
rail->suspended = true;
return 0;
}
@@ -734,24 +792,35 @@ static int tegra_dvfs_suspend(void)
return ret;
}
-static int tegra_dvfs_pm_notify(struct notifier_block *nb,
- unsigned long event, void *data)
+static int tegra_dvfs_pm_suspend(struct notifier_block *nb,
+ unsigned long event, void *data)
{
- switch (event) {
- case PM_SUSPEND_PREPARE:
+ if (event == PM_SUSPEND_PREPARE) {
if (tegra_dvfs_suspend())
return NOTIFY_STOP;
- break;
- case PM_POST_SUSPEND:
- tegra_dvfs_resume();
- break;
+ pr_info("tegra_dvfs: suspended\n");
}
+ return NOTIFY_OK;
+};
+static int tegra_dvfs_pm_resume(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ if (event == PM_POST_SUSPEND) {
+ tegra_dvfs_resume();
+ pr_info("tegra_dvfs: resumed\n");
+ }
return NOTIFY_OK;
};
-static struct notifier_block tegra_dvfs_nb = {
- .notifier_call = tegra_dvfs_pm_notify,
+static struct notifier_block tegra_dvfs_suspend_nb = {
+ .notifier_call = tegra_dvfs_pm_suspend,
+ .priority = -1,
+};
+
+static struct notifier_block tegra_dvfs_resume_nb = {
+ .notifier_call = tegra_dvfs_pm_resume,
+ .priority = 1,
};
static int tegra_dvfs_reboot_notify(struct notifier_block *nb,
@@ -774,7 +843,8 @@ static struct notifier_block tegra_dvfs_reboot_nb = {
/* must be called with dvfs lock held */
static void __tegra_dvfs_rail_disable(struct dvfs_rail *rail)
{
- int ret;
+ int ret = -EPERM;
+ int mv;
/* don't set voltage in DFLL mode - won't work, but break stats */
if (rail->dfll_mode) {
@@ -782,12 +852,15 @@ static void __tegra_dvfs_rail_disable(struct dvfs_rail *rail)
return;
}
- ret = dvfs_rail_set_voltage(rail,
- dvfs_rail_apply_limits(rail, rail->nominal_millivolts));
+ /* apply detach mode limit provided it is above current volatge */
+ mv = tegra_dvfs_rail_get_disable_level(rail);
+ mv = dvfs_rail_apply_limits(rail, mv);
+
+ if (mv >= rail->millivolts)
+ ret = dvfs_rail_set_voltage(rail, mv);
if (ret) {
- pr_info("dvfs: failed to set regulator %s to disable "
- "voltage %d\n", rail->reg_id,
- rail->nominal_millivolts);
+ pr_err("tegra_dvfs: failed to disable %s at %d\n",
+ rail->reg_id, rail->millivolts);
return;
}
rail->disabled = true;
@@ -1049,10 +1122,14 @@ int __init tegra_dvfs_late_init(void)
mutex_unlock(&dvfs_lock);
#ifdef CONFIG_TEGRA_SILICON_PLATFORM
- if (!connected)
+ if (!connected) {
+ pr_warn("tegra_dvfs: DVFS regulators connection failed\n"
+ " !!!! voltage scaling is disabled !!!!\n");
return -ENODEV;
+ }
#endif
- register_pm_notifier(&tegra_dvfs_nb);
+ register_pm_notifier(&tegra_dvfs_suspend_nb);
+ register_pm_notifier(&tegra_dvfs_resume_nb);
register_reboot_notifier(&tegra_dvfs_reboot_nb);
list_for_each_entry(rail, &dvfs_rail_list, node)
diff --git a/arch/arm/mach-tegra/dvfs.h b/arch/arm/mach-tegra/dvfs.h
index b6bda2e4f8f6..3105606f9372 100644
--- a/arch/arm/mach-tegra/dvfs.h
+++ b/arch/arm/mach-tegra/dvfs.h
@@ -79,6 +79,10 @@ struct dvfs_rail {
int millivolts;
int new_millivolts;
int offs_millivolts;
+ int boot_millivolts;
+ int disable_millivolts;
+ int suspend_millivolts;
+
bool suspended;
bool dfll_mode;
bool dfll_mode_updating;
@@ -292,6 +296,13 @@ static inline int tegra_dvfs_rail_get_nominal_millivolts(struct dvfs_rail *rail)
return -ENOENT;
}
+static inline int tegra_dvfs_rail_get_boot_level(struct dvfs_rail *rail)
+{
+ if (rail)
+ return rail->boot_millivolts ? : rail->nominal_millivolts;
+ return -ENOENT;
+}
+
static inline int tegra_dvfs_rail_get_override_floor(struct dvfs_rail *rail)
{
if (rail)
diff --git a/arch/arm/mach-tegra/tegra_core_volt_cap.c b/arch/arm/mach-tegra/tegra_core_volt_cap.c
index 4627ef97360b..c842158c2b19 100644
--- a/arch/arm/mach-tegra/tegra_core_volt_cap.c
+++ b/arch/arm/mach-tegra/tegra_core_volt_cap.c
@@ -57,7 +57,8 @@ static int core_cap_level_set(int level, int core_nominal_mv)
int ret = 0;
if (!core_cap_table) {
- if (level == core_nominal_mv) {
+ int mv = tegra_dvfs_rail_get_boot_level(tegra_core_rail);
+ if (level == mv) {
core_buses_cap.level = level;
return 0;
}