diff options
author | Sai Gurrappadi <sgurrappadi@nvidia.com> | 2014-06-12 13:07:08 -0700 |
---|---|---|
committer | Mandar Padmawar <mpadmawar@nvidia.com> | 2014-06-19 07:33:35 -0700 |
commit | dd1314c9a7a35e3f2ccfdfae1d2e1766beda7160 (patch) | |
tree | 2d9d373dbcfde8586f3042ac92bd02c9a8b3cf74 | |
parent | 569fc01ace0ceb16212293cf6cf8dc7bd0e26998 (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
-rw-r--r-- | arch/arm64/mach-tegra/pm.c | 51 |
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 */ |