msm: pm: Add API to enable/disable retention mode

On 8974 targets, retention mode voltage is controlled by the LDO
regulator, which in turn is powered through a ganged PMIC regulators. At
certain low voltage levels, the ganged PMIC will not be able to power
the LDO retention voltage. The regulator framework would invoke this
API to enable/disable retention mode in these scenarios.

When disabling retention modes, send a IPI to wakeup all online cores
out of idle, to ensure that no core is in a retention low power mode.
Also, disabled support for retention during suspend/hotplug operations
to ensure that a CPU isn't already in retention when the voltage is
lowered. This shouldn't have an effect on suspend, as the driver always
chooses the deepest sleep mode for suspend/hotplug.

Change-Id: I0af18168968c2aaf05ca99188d30762612c87de6
Signed-off-by: Mahesh Sivasubramanian <msivasub@codeaurora.org>
diff --git a/arch/arm/mach-msm/pm-8x60.c b/arch/arm/mach-msm/pm-8x60.c
index e35d843..261d433 100644
--- a/arch/arm/mach-msm/pm-8x60.c
+++ b/arch/arm/mach-msm/pm-8x60.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -118,6 +118,7 @@
 static struct msm_pm_init_data_type msm_pm_init_data;
 static struct hrtimer pm_hrtimer;
 static struct msm_pm_sleep_ops pm_sleep_ops;
+static bool msm_pm_ldo_retention_enabled = true;
 /*
  * Write out the attribute.
  */
@@ -820,6 +821,14 @@
 			}
 			/* fall through */
 		case MSM_PM_SLEEP_MODE_RETENTION:
+			/*
+			 * The Krait BHS regulator doesn't have enough head
+			 * room to drive the retention voltage on LDO and so
+			 * has disabled retention
+			 */
+			if (!msm_pm_ldo_retention_enabled)
+				break;
+
 			if (!allow)
 				break;
 
@@ -995,6 +1004,34 @@
 		msm_pm_swfi();
 }
 
+static void msm_pm_ack_retention_disable(void *data)
+{
+	/*
+	 * This is a NULL function to ensure that the core has woken up
+	 * and is safe to disable retention.
+	 */
+}
+/**
+ * msm_pm_enable_retention() - Disable/Enable retention on all cores
+ * @enable: Enable/Disable retention
+ *
+ */
+void msm_pm_enable_retention(bool enable)
+{
+	msm_pm_ldo_retention_enabled = enable;
+	/*
+	 * If retention is being disabled, wakeup all online core to ensure
+	 * that it isn't executing retention. Offlined cores need not be woken
+	 * up as they enter the deepest sleep mode, namely RPM assited power
+	 * collapse
+	 */
+	if (!enable)
+		smp_call_function_many(cpu_online_mask,
+				msm_pm_ack_retention_disable,
+				NULL, true);
+}
+EXPORT_SYMBOL(msm_pm_enable_retention);
+
 static int msm_pm_enter(suspend_state_t state)
 {
 	bool allow[MSM_PM_SLEEP_MODE_NR];