msm: pm-8x60: Check for SPM status to verify CPU_DEAD
On newer MSM targets including 8960, the clocks for the cpu being
hot unplugged is turned off during the CPU_DEAD callback on the
cpu initiating the hotplug. The initiating CPU polls for the SPM sleep
status of the secondary cpu in platform_cpu_kill to ensure that the
secondary cpu has been power collapsed before returning to process the
CPU_DEAD callbacks.
Change-Id: I5085c353f1b5d08622504cea8430bb8bdb1192f6
Signed-off-by: Maheshkumar Sivasubramanian <msivasub@codeaurora.org>
diff --git a/arch/arm/mach-msm/board-8960.c b/arch/arm/mach-msm/board-8960.c
index 91bee0b..b19445d 100644
--- a/arch/arm/mach-msm/board-8960.c
+++ b/arch/arm/mach-msm/board-8960.c
@@ -1491,6 +1491,12 @@
.msm_apps_ipc_rpm_val = 4,
};
+static struct msm_pm_sleep_status_data msm_pm_slp_sts_data = {
+ .base_addr = MSM_ACC0_BASE + 0x08,
+ .cpu_offset = MSM_ACC1_BASE - MSM_ACC0_BASE,
+ .mask = 1UL << 13,
+};
+
static struct ks8851_pdata spi_eth_pdata = {
.irq_gpio = KS8851_IRQ_GPIO,
.rst_gpio = KS8851_RST_GPIO,
@@ -2167,6 +2173,7 @@
msm_cpuidle_set_states(msm_cstates, ARRAY_SIZE(msm_cstates),
msm_pm_data);
BUG_ON(msm_pm_boot_init(&msm_pm_boot_pdata));
+ msm_pm_init_sleep_status_data(&msm_pm_slp_sts_data);
}
static void __init msm8960_rumi3_init(void)
@@ -2198,6 +2205,7 @@
msm_cpuidle_set_states(msm_cstates, ARRAY_SIZE(msm_cstates),
msm_pm_data);
BUG_ON(msm_pm_boot_init(&msm_pm_boot_pdata));
+ msm_pm_init_sleep_status_data(&msm_pm_slp_sts_data);
}
static void __init msm8960_cdp_init(void)
@@ -2273,6 +2281,7 @@
msm_pm_data);
change_memory_power = &msm8960_change_memory_power;
BUG_ON(msm_pm_boot_init(&msm_pm_boot_pdata));
+ msm_pm_init_sleep_status_data(&msm_pm_slp_sts_data);
if (PLATFORM_IS_CHARM25())
platform_add_devices(mdm_devices, ARRAY_SIZE(mdm_devices));
}
diff --git a/arch/arm/mach-msm/include/mach/pm.h b/arch/arm/mach-msm/include/mach/pm.h
index 813ece1..689cd17 100644
--- a/arch/arm/mach-msm/include/mach/pm.h
+++ b/arch/arm/mach-msm/include/mach/pm.h
@@ -51,11 +51,19 @@
staying in the low power mode saves power */
};
+struct msm_pm_sleep_status_data {
+ void *base_addr;
+ uint32_t cpu_offset;
+ uint32_t mask;
+};
+
void msm_pm_set_platform_data(struct msm_pm_platform_data *data, int count);
int msm_pm_idle_prepare(struct cpuidle_device *dev);
int msm_pm_idle_enter(enum msm_pm_sleep_mode sleep_mode);
void msm_pm_cpu_enter_lowpower(unsigned int cpu);
+void __init msm_pm_init_sleep_status_data(
+ struct msm_pm_sleep_status_data *sleep_data);
#ifdef CONFIG_PM
void msm_pm_set_rpm_wakeup_irq(unsigned int irq);
#else
diff --git a/arch/arm/mach-msm/pm-8x60.c b/arch/arm/mach-msm/pm-8x60.c
index a6644d6..754d76f 100644
--- a/arch/arm/mach-msm/pm-8x60.c
+++ b/arch/arm/mach-msm/pm-8x60.c
@@ -27,6 +27,7 @@
#include <linux/tick.h>
#include <linux/uaccess.h>
#include <linux/wakelock.h>
+#include <linux/delay.h>
#include <mach/msm_iomap.h>
#include <mach/system.h>
#include <asm/cacheflush.h>
@@ -55,6 +56,7 @@
* Debug Definitions
*****************************************************************************/
+
enum {
MSM_PM_DEBUG_SUSPEND = BIT(0),
MSM_PM_DEBUG_POWER_COLLAPSE = BIT(1),
@@ -953,6 +955,23 @@
return 0;
}
+static struct msm_pm_sleep_status_data *msm_pm_slp_sts;
+
+static DEFINE_PER_CPU_SHARED_ALIGNED(enum msm_pm_sleep_mode,
+ msm_pm_last_slp_mode);
+
+bool msm_pm_verify_cpu_pc(unsigned int cpu)
+{
+ enum msm_pm_sleep_mode mode = per_cpu(msm_pm_last_slp_mode, cpu);
+
+ if (msm_pm_slp_sts)
+ if ((mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE) ||
+ (mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE))
+ return true;
+
+ return false;
+}
+
void msm_pm_cpu_enter_lowpower(unsigned int cpu)
{
int i;
@@ -968,14 +987,50 @@
if (MSM_PM_DEBUG_HOTPLUG & msm_pm_debug_mask)
pr_notice("CPU%u: %s: shutting down cpu\n", cpu, __func__);
- if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE])
+ if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE]) {
+ per_cpu(msm_pm_last_slp_mode, cpu)
+ = MSM_PM_SLEEP_MODE_POWER_COLLAPSE;
msm_pm_power_collapse(false);
- else if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE])
+ } else if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) {
+ per_cpu(msm_pm_last_slp_mode, cpu)
+ = MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE;
msm_pm_power_collapse_standalone(false);
- else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT])
+ } else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) {
+ per_cpu(msm_pm_last_slp_mode, cpu)
+ = MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE;
msm_pm_swfi();
+ } else
+ per_cpu(msm_pm_last_slp_mode, cpu) = MSM_PM_SLEEP_MODE_NR;
+}
+int msm_pm_wait_cpu_shutdown(unsigned int cpu)
+{
+ int timeout = 10;
+
+ if (!msm_pm_slp_sts)
+ return 0;
+
+ while (timeout--) {
+
+ /*
+ * Check for the SPM of the core being hotplugged to set
+ * its sleep state.The SPM sleep state indicates that the
+ * core has been power collapsed.
+ */
+
+ int acc_sts = __raw_readl(msm_pm_slp_sts->base_addr
+ + cpu * msm_pm_slp_sts->cpu_offset);
+ mb();
+
+ if (acc_sts & msm_pm_slp_sts->mask)
+ return 0;
+
+ usleep(100);
+ }
+ pr_warn("%s(): Timed out waiting for CPU %u SPM to enter sleep state",
+ __func__, cpu);
+ return -EBUSY;
}
static int msm_pm_enter(suspend_state_t state)
@@ -1085,10 +1140,14 @@
.valid = suspend_valid_only_mem,
};
-
/******************************************************************************
* Initialization routine
*****************************************************************************/
+void __init msm_pm_init_sleep_status_data(
+ struct msm_pm_sleep_status_data *data)
+{
+ msm_pm_slp_sts = data;
+}
static int __init msm_pm_init(void)
{