summaryrefslogtreecommitdiff
path: root/arch/arm64
diff options
context:
space:
mode:
authorSai Gurrappadi <sgurrappadi@nvidia.com>2014-06-12 13:07:08 -0700
committerMandar Padmawar <mpadmawar@nvidia.com>2014-06-19 07:33:35 -0700
commitdd1314c9a7a35e3f2ccfdfae1d2e1766beda7160 (patch)
tree2d9d373dbcfde8586f3042ac92bd02c9a8b3cf74 /arch/arm64
parent569fc01ace0ceb16212293cf6cf8dc7bd0e26998 (diff)
ARM64: tegra: Set suspend_in_progress flag early
Set the suspend_in_progress flag early in suspend_prepare and call cpu_up on offline CPUs to get them out of C6 on suspend. This ensures that all CPUs (CPU1) are in C7 before LP0 is requested. Bug 1522953 Change-Id: I7f74f0afb2bfda92c03cc20262a6acaf8716d034 Signed-off-by: Sai Gurrappadi <sgurrappadi@nvidia.com> Reviewed-on: http://git-master/r/422815 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Peng Du <pdu@nvidia.com> Reviewed-by: Diwakar Tundlam <dtundlam@nvidia.com> Reviewed-by: Chao Xu <cxu@nvidia.com> GVS: Gerrit_Virtual_Submit
Diffstat (limited to 'arch/arm64')
-rw-r--r--arch/arm64/mach-tegra/pm.c51
1 files changed, 50 insertions, 1 deletions
diff --git a/arch/arm64/mach-tegra/pm.c b/arch/arm64/mach-tegra/pm.c
index 3ddcad592234..36626d0cb9f0 100644
--- a/arch/arm64/mach-tegra/pm.c
+++ b/arch/arm64/mach-tegra/pm.c
@@ -54,6 +54,7 @@
#include <linux/tegra-pm.h>
#include <linux/tegra_pm_domains.h>
#include <linux/kmemleak.h>
+#include <linux/cpu.h>
#include <trace/events/power.h>
#include <trace/events/nvsecurity.h>
@@ -212,6 +213,8 @@ static bool suspend_in_progress;
bool tegra_suspend_in_progress(void)
{
+ smp_rmb();
+
return suspend_in_progress;
}
@@ -741,9 +744,42 @@ static int tegra_suspend_prepare_late(void)
(current_suspend_mode == TEGRA_SUSPEND_LP1))
tegra_suspend_check_pwr_stats();
+ return 0;
+}
+
+/*
+ * LP0 WAR: Bring all CPUs online before LP0 so that they can be put into C7 on
+ * subsequent __cpu_downs otherwise we end up hanging the system by leaving a
+ * core in C6 and requesting LP0 from CPU0
+ */
+static int __cpuinit pm_suspend_notifier(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ int cpu, ret;
+
+ if (event != PM_SUSPEND_PREPARE)
+ return NOTIFY_OK;
+
suspend_in_progress = true;
- return 0;
+ dsb();
+
+ for_each_present_cpu(cpu) {
+ if (!cpu)
+ continue;
+ ret = cpu_up(cpu);
+ if (ret)
+ pr_warn("%s: Couldn't bring up CPU%d on LP0 entry\n",
+ __func__, cpu);
+ /*
+ * Error in getting CPU out of C6. Let -EINVAL through as CPU
+ * could have come online
+ */
+ if (ret && ret != -EINVAL)
+ return NOTIFY_BAD;
+ }
+
+ return NOTIFY_OK;
}
static void tegra_suspend_finish(void)
@@ -762,6 +798,16 @@ static const struct platform_suspend_ops tegra_suspend_ops = {
.enter = tegra_suspend_enter,
};
+/*
+ * Note: The priority of this notifier needs to be higher than cpu_hotplug's
+ * suspend notifier otherwise the subsequent cpu_up operation in
+ * pm_suspend_notifier will fail
+ */
+static struct notifier_block __cpuinitdata suspend_notifier = {
+ .notifier_call = pm_suspend_notifier,
+ .priority = 1,
+};
+
static ssize_t suspend_mode_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
@@ -1078,6 +1124,9 @@ out:
if (pdata->suspend_mode == TEGRA_SUSPEND_LP0)
tegra_lp0_suspend_init();
+ if (register_pm_notifier(&suspend_notifier))
+ pr_err("%s: Failed to register suspend notifier\n", __func__);
+
suspend_set_ops(&tegra_suspend_ops);
/* Create /sys/power/suspend/type */