summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/edp_core.c
diff options
context:
space:
mode:
authorAlex Frid <afrid@nvidia.com>2012-11-24 20:46:54 -0800
committerDan Willemsen <dwillemsen@nvidia.com>2013-09-14 12:43:51 -0700
commitae252b528ddbc6b61460bff64744bb92c6c40de1 (patch)
tree414d55faafab49885d3ca5a689c8560ac5ace665 /arch/arm/mach-tegra/edp_core.c
parent5320580e4885cdfc580ab0a1affa3aadc07e9c83 (diff)
ARM: tegra11: power: Add core EDP suspend/resume
Enforced favor emc profile on entry to suspend state, and restored suspended profile on exit. This is necessary, since EMC rate on exit from suspend is determined at boot, and may exceed edp caps set by balanced or favor gpu profiles. Bug 1165638 Change-Id: I9e938aa4b2296a23953b1653f29d632447c345cb Signed-off-by: Alex Frid <afrid@nvidia.com> Reviewed-on: http://git-master/r/166366 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Prashant Malani <pmalani@nvidia.com> Reviewed-by: Diwakar Tundlam <dtundlam@nvidia.com> Reviewed-by: Yu-Huan Hsu <yhsu@nvidia.com>
Diffstat (limited to 'arch/arm/mach-tegra/edp_core.c')
-rw-r--r--arch/arm/mach-tegra/edp_core.c83
1 files changed, 71 insertions, 12 deletions
diff --git a/arch/arm/mach-tegra/edp_core.c b/arch/arm/mach-tegra/edp_core.c
index ab3fcda16000..378916d63871 100644
--- a/arch/arm/mach-tegra/edp_core.c
+++ b/arch/arm/mach-tegra/edp_core.c
@@ -16,11 +16,11 @@
#include <linux/kernel.h>
#include <linux/init.h>
-#include <linux/string.h>
#include <linux/module.h>
#include <linux/clk.h>
#include <linux/kobject.h>
#include <linux/err.h>
+#include <linux/suspend.h>
#include <mach/edp.h>
@@ -38,6 +38,7 @@ static bool core_edp_scpu_state;
static int core_edp_profile;
static int core_edp_modules_state;
static int core_edp_thermal_idx;
+static int core_edp_suspended_profile = CORE_EDP_PROFILES_NUM;
static const char *profile_names[CORE_EDP_PROFILES_NUM] = {
[CORE_EDP_PROFILE_BALANCED] = "profile_balanced",
@@ -139,8 +140,6 @@ static int update_cap_rates(unsigned long *new_rates, unsigned long *old_rates)
return 0;
}
-/* FIXME: resume sync ? */
-
static int __init start_core_edp(void)
{
int ret;
@@ -225,19 +224,12 @@ int tegra_core_edp_cpu_state_update(bool scpu_state)
}
/* core edp profiles update */
-static int profile_update(int profile)
+static int _profile_update(int profile)
{
int ret = 0;
unsigned long *old_cap_rates;
unsigned long *new_cap_rates;
- if (!limits) {
- core_edp_profile = profile;
- return 0;
- }
-
- mutex_lock(&core_edp_lock);
-
if (core_edp_profile != profile) {
old_cap_rates = get_current_cap_rates();
new_cap_rates = get_cap_rates(core_edp_scpu_state, profile,
@@ -248,11 +240,26 @@ static int profile_update(int profile)
else
core_edp_profile = profile;
}
- mutex_unlock(&core_edp_lock);
return ret;
}
+static int profile_update(int profile)
+{
+ int ret = 0;
+
+ if (!limits) {
+ core_edp_profile = profile;
+ return 0;
+ }
+
+ mutex_lock(&core_edp_lock);
+ if (core_edp_suspended_profile == CORE_EDP_PROFILES_NUM)
+ ret = _profile_update(profile);
+ mutex_unlock(&core_edp_lock);
+ return ret;
+}
+
static ssize_t
core_edp_profile_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
@@ -314,8 +321,60 @@ const struct attribute *core_edp_attributes[] = {
static struct kobject *core_edp_kobj;
+/*
+ * Since EMC rate on suspend exit is set to boot configuration with no regards
+ * to EDP constraints, force profile_favor_emc on suspend entry, and restore
+ * suspended profile after resume. This guarantees that other clocks (GPU)
+ * are throttled enough to prevent regulator over-current.
+ */
+static int core_edp_pm_notify(struct notifier_block *nb, unsigned long event,
+ void *dummy)
+{
+ int ret = 0;
+ if (!limits)
+ return NOTIFY_OK;
+
+ mutex_lock(&core_edp_lock);
+ if (event == PM_SUSPEND_PREPARE) {
+ core_edp_suspended_profile = core_edp_profile;
+ ret = _profile_update(CORE_EDP_PROFILE_FAVOR_EMC);
+ if (ret)
+ pr_err("Core EDP suspend: failed to set %s\n",
+ profile_names[CORE_EDP_PROFILE_FAVOR_EMC]);
+ else
+ pr_info("Core EDP suspend: set %s\n",
+ profile_names[CORE_EDP_PROFILE_FAVOR_EMC]);
+ } else if (event == PM_POST_SUSPEND) {
+ ret = _profile_update(core_edp_suspended_profile);
+ if (ret)
+ pr_err("Core EDP resume: failed to restore %s\n",
+ profile_names[core_edp_suspended_profile]);
+ else
+ pr_info("Core EDP resume: restored %s\n",
+ profile_names[core_edp_suspended_profile]);
+ core_edp_suspended_profile = CORE_EDP_PROFILES_NUM;
+ ret = 0; /* don't stop resume */
+ }
+ mutex_unlock(&core_edp_lock);
+ return notifier_from_errno(ret);
+}
+
+static struct notifier_block core_edp_pm_notifier = {
+ .notifier_call = core_edp_pm_notify,
+};
+
+/* initialize update interfaces */
static int __init tegra_core_edp_late_init(void)
{
+ if (!limits)
+ return 0;
+
+ /* exit on error - prevent changing profile_favor_emc */
+ if (register_pm_notifier(&core_edp_pm_notifier)) {
+ pr_err("%s: failed to register edp pm notifier\n", __func__);
+ return 0;
+ }
+
core_edp_kobj = kobject_create_and_add("tegra_core_edp", kernel_kobj);
if (!core_edp_kobj) {
pr_err("%s: failed to create edp sysfs object\n", __func__);