Initial Contribution

msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142

Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/arch/arm/mach-msm/pm2.c b/arch/arm/mach-msm/pm2.c
new file mode 100644
index 0000000..6b24a02
--- /dev/null
+++ b/arch/arm/mach-msm/pm2.c
@@ -0,0 +1,1894 @@
+/* arch/arm/mach-msm/pm2.c
+ *
+ * MSM Power Management Routines
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2008-2011 Code Aurora Forum. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/pm.h>
+#include <linux/pm_qos_params.h>
+#include <linux/proc_fs.h>
+#include <linux/suspend.h>
+#include <linux/reboot.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/memory.h>
+#ifdef CONFIG_HAS_WAKELOCK
+#include <linux/wakelock.h>
+#endif
+#include <mach/msm_iomap.h>
+#include <mach/system.h>
+#ifdef CONFIG_CPU_V7
+#include <asm/pgtable.h>
+#include <asm/pgalloc.h>
+#endif
+#ifdef CONFIG_CACHE_L2X0
+#include <asm/hardware/cache-l2x0.h>
+#endif
+#ifdef CONFIG_VFP
+#include <asm/vfp.h>
+#endif
+
+#ifdef CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_DEEP_POWER_DOWN
+#include <mach/msm_migrate_pages.h>
+#endif
+
+#include "smd_private.h"
+#include "smd_rpcrouter.h"
+#include "acpuclock.h"
+#include "clock.h"
+#include "proc_comm.h"
+#include "idle.h"
+#include "irq.h"
+#include "gpio.h"
+#include "timer.h"
+#include "pm.h"
+#include "spm.h"
+#include "sirc.h"
+
+/******************************************************************************
+ * Debug Definitions
+ *****************************************************************************/
+
+enum {
+	MSM_PM_DEBUG_SUSPEND = 1U << 0,
+	MSM_PM_DEBUG_POWER_COLLAPSE = 1U << 1,
+	MSM_PM_DEBUG_STATE = 1U << 2,
+	MSM_PM_DEBUG_CLOCK = 1U << 3,
+	MSM_PM_DEBUG_RESET_VECTOR = 1U << 4,
+	MSM_PM_DEBUG_SMSM_STATE = 1U << 5,
+	MSM_PM_DEBUG_IDLE = 1U << 6,
+};
+
+static int msm_pm_debug_mask;
+module_param_named(
+	debug_mask, msm_pm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP
+);
+
+#define MSM_PM_DPRINTK(mask, level, message, ...) \
+	do { \
+		if ((mask) & msm_pm_debug_mask) \
+			printk(level message, ## __VA_ARGS__); \
+	} while (0)
+
+#define MSM_PM_DEBUG_PRINT_STATE(tag) \
+	do { \
+		MSM_PM_DPRINTK(MSM_PM_DEBUG_STATE, \
+			KERN_INFO, "%s: " \
+			"APPS_CLK_SLEEP_EN %x, APPS_PWRDOWN %x, " \
+			"SMSM_POWER_MASTER_DEM %x, SMSM_MODEM_STATE %x, " \
+			"SMSM_APPS_DEM %x\n", \
+			tag, \
+			__raw_readl(APPS_CLK_SLEEP_EN), \
+			__raw_readl(APPS_PWRDOWN), \
+			smsm_get_state(SMSM_POWER_MASTER_DEM), \
+			smsm_get_state(SMSM_MODEM_STATE), \
+			smsm_get_state(SMSM_APPS_DEM)); \
+	} while (0)
+
+#define MSM_PM_DEBUG_PRINT_SLEEP_INFO() \
+	do { \
+		if (msm_pm_debug_mask & MSM_PM_DEBUG_SMSM_STATE) \
+			smsm_print_sleep_info(msm_pm_smem_data->sleep_time, \
+				msm_pm_smem_data->resources_used, \
+				msm_pm_smem_data->irq_mask, \
+				msm_pm_smem_data->wakeup_reason, \
+				msm_pm_smem_data->pending_irqs); \
+	} while (0)
+
+
+/******************************************************************************
+ * Sleep Modes and Parameters
+ *****************************************************************************/
+
+static int msm_pm_sleep_mode = CONFIG_MSM7X00A_SLEEP_MODE;
+module_param_named(
+	sleep_mode, msm_pm_sleep_mode,
+	int, S_IRUGO | S_IWUSR | S_IWGRP
+);
+
+static int msm_pm_idle_sleep_mode = CONFIG_MSM7X00A_IDLE_SLEEP_MODE;
+module_param_named(
+	idle_sleep_mode, msm_pm_idle_sleep_mode,
+	int, S_IRUGO | S_IWUSR | S_IWGRP
+);
+
+static int msm_pm_idle_sleep_min_time = CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME;
+module_param_named(
+	idle_sleep_min_time, msm_pm_idle_sleep_min_time,
+	int, S_IRUGO | S_IWUSR | S_IWGRP
+);
+
+enum {
+	MSM_PM_MODE_ATTR_SUSPEND,
+	MSM_PM_MODE_ATTR_IDLE,
+	MSM_PM_MODE_ATTR_LATENCY,
+	MSM_PM_MODE_ATTR_RESIDENCY,
+	MSM_PM_MODE_ATTR_NR,
+};
+
+static char *msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_NR] = {
+	[MSM_PM_MODE_ATTR_SUSPEND] = "suspend_enabled",
+	[MSM_PM_MODE_ATTR_IDLE] = "idle_enabled",
+	[MSM_PM_MODE_ATTR_LATENCY] = "latency",
+	[MSM_PM_MODE_ATTR_RESIDENCY] = "residency",
+};
+
+static char *msm_pm_sleep_mode_labels[MSM_PM_SLEEP_MODE_NR] = {
+	[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_SUSPEND] = " ",
+	[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = "power_collapse",
+	[MSM_PM_SLEEP_MODE_APPS_SLEEP] = "apps_sleep",
+	[MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT] =
+		"ramp_down_and_wfi",
+	[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT] = "wfi",
+	[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN] =
+		"power_collapse_no_xo_shutdown",
+	[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE] =
+		"standalone_power_collapse",
+};
+
+static struct msm_pm_platform_data *msm_pm_modes;
+
+static struct kobject *msm_pm_mode_kobjs[MSM_PM_SLEEP_MODE_NR];
+static struct attribute_group *msm_pm_mode_attr_group[MSM_PM_SLEEP_MODE_NR];
+static struct attribute **msm_pm_mode_attrs[MSM_PM_SLEEP_MODE_NR];
+static struct kobj_attribute *msm_pm_mode_kobj_attrs[MSM_PM_SLEEP_MODE_NR];
+
+/*
+ * Write out the attribute.
+ */
+static ssize_t msm_pm_mode_attr_show(
+	struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	int ret = -EINVAL;
+	int i;
+
+	for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
+		struct kernel_param kp;
+
+		if (msm_pm_sleep_mode_labels[i] == NULL)
+			continue;
+
+		if (strcmp(kobj->name, msm_pm_sleep_mode_labels[i]))
+			continue;
+
+		if (!strcmp(attr->attr.name,
+			msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_SUSPEND])) {
+			u32 arg = msm_pm_modes[i].suspend_enabled;
+			kp.arg = &arg;
+			ret = param_get_ulong(buf, &kp);
+		} else if (!strcmp(attr->attr.name,
+			msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_IDLE])) {
+			u32 arg = msm_pm_modes[i].idle_enabled;
+			kp.arg = &arg;
+			ret = param_get_ulong(buf, &kp);
+		} else if (!strcmp(attr->attr.name,
+			msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_LATENCY])) {
+			kp.arg = &msm_pm_modes[i].latency;
+			ret = param_get_ulong(buf, &kp);
+		} else if (!strcmp(attr->attr.name,
+			msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_RESIDENCY])) {
+			kp.arg = &msm_pm_modes[i].residency;
+			ret = param_get_ulong(buf, &kp);
+		}
+
+		break;
+	}
+
+	if (ret > 0) {
+		strcat(buf, "\n");
+		ret++;
+	}
+
+	return ret;
+}
+
+/*
+ * Read in the new attribute value.
+ */
+static ssize_t msm_pm_mode_attr_store(struct kobject *kobj,
+	struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	int ret = -EINVAL;
+	int i;
+
+	for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
+		struct kernel_param kp;
+
+		if (msm_pm_sleep_mode_labels[i] == NULL)
+			continue;
+
+		if (strcmp(kobj->name, msm_pm_sleep_mode_labels[i]))
+			continue;
+
+		if (!strcmp(attr->attr.name,
+			msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_SUSPEND])) {
+			kp.arg = &msm_pm_modes[i].suspend_enabled;
+			ret = param_set_byte(buf, &kp);
+		} else if (!strcmp(attr->attr.name,
+			msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_IDLE])) {
+			kp.arg = &msm_pm_modes[i].idle_enabled;
+			ret = param_set_byte(buf, &kp);
+		} else if (!strcmp(attr->attr.name,
+			msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_LATENCY])) {
+			kp.arg = &msm_pm_modes[i].latency;
+			ret = param_set_ulong(buf, &kp);
+		} else if (!strcmp(attr->attr.name,
+			msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_RESIDENCY])) {
+			kp.arg = &msm_pm_modes[i].residency;
+			ret = param_set_ulong(buf, &kp);
+		}
+
+		break;
+	}
+
+	return ret ? ret : count;
+}
+
+/*
+ * Add sysfs entries for the sleep modes.
+ */
+static int __init msm_pm_mode_sysfs_add(void)
+{
+	struct kobject *module_kobj = NULL;
+	struct kobject *modes_kobj = NULL;
+
+	struct kobject *kobj;
+	struct attribute_group *attr_group;
+	struct attribute **attrs;
+	struct kobj_attribute *kobj_attrs;
+
+	int i, j, k;
+	int ret;
+
+	module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
+	if (!module_kobj) {
+		printk(KERN_ERR "%s: cannot find kobject for module %s\n",
+			__func__, KBUILD_MODNAME);
+		ret = -ENOENT;
+		goto mode_sysfs_add_cleanup;
+	}
+
+	modes_kobj = kobject_create_and_add("modes", module_kobj);
+	if (!modes_kobj) {
+		printk(KERN_ERR "%s: cannot create modes kobject\n", __func__);
+		ret = -ENOMEM;
+		goto mode_sysfs_add_cleanup;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(msm_pm_mode_kobjs); i++) {
+		if (!msm_pm_modes[i].suspend_supported &&
+				!msm_pm_modes[i].idle_supported)
+			continue;
+
+		kobj = kobject_create_and_add(
+				msm_pm_sleep_mode_labels[i], modes_kobj);
+		attr_group = kzalloc(sizeof(*attr_group), GFP_KERNEL);
+		attrs = kzalloc(sizeof(*attrs) * (MSM_PM_MODE_ATTR_NR + 1),
+				GFP_KERNEL);
+		kobj_attrs = kzalloc(sizeof(*kobj_attrs) * MSM_PM_MODE_ATTR_NR,
+				GFP_KERNEL);
+
+		if (!kobj || !attr_group || !attrs || !kobj_attrs) {
+			printk(KERN_ERR
+				"%s: cannot create kobject or attributes\n",
+				__func__);
+			ret = -ENOMEM;
+			goto mode_sysfs_add_abort;
+		}
+
+		for (k = 0, j = 0; k < MSM_PM_MODE_ATTR_NR; k++) {
+			if ((k == MSM_PM_MODE_ATTR_SUSPEND) &&
+				(!msm_pm_modes[i].suspend_supported))
+				continue;
+			if ((k == MSM_PM_MODE_ATTR_IDLE) &&
+				(!msm_pm_modes[i].idle_supported))
+				continue;
+
+			kobj_attrs[j].attr.mode = 0644;
+			kobj_attrs[j].show = msm_pm_mode_attr_show;
+			kobj_attrs[j].store = msm_pm_mode_attr_store;
+			kobj_attrs[j].attr.name = msm_pm_mode_attr_labels[k];
+			attrs[j] = &kobj_attrs[j].attr;
+			j++;
+		}
+		attrs[j] = NULL;
+
+		attr_group->attrs = attrs;
+		ret = sysfs_create_group(kobj, attr_group);
+		if (ret) {
+			printk(KERN_ERR
+				"%s: cannot create kobject attribute group\n",
+				__func__);
+			goto mode_sysfs_add_abort;
+		}
+
+		msm_pm_mode_kobjs[i] = kobj;
+		msm_pm_mode_attr_group[i] = attr_group;
+		msm_pm_mode_attrs[i] = attrs;
+		msm_pm_mode_kobj_attrs[i] = kobj_attrs;
+	}
+
+	return 0;
+
+mode_sysfs_add_abort:
+	kfree(kobj_attrs);
+	kfree(attrs);
+	kfree(attr_group);
+	kobject_put(kobj);
+
+mode_sysfs_add_cleanup:
+	for (i = ARRAY_SIZE(msm_pm_mode_kobjs) - 1; i >= 0; i--) {
+		if (!msm_pm_mode_kobjs[i])
+			continue;
+
+		sysfs_remove_group(
+			msm_pm_mode_kobjs[i], msm_pm_mode_attr_group[i]);
+
+		kfree(msm_pm_mode_kobj_attrs[i]);
+		kfree(msm_pm_mode_attrs[i]);
+		kfree(msm_pm_mode_attr_group[i]);
+		kobject_put(msm_pm_mode_kobjs[i]);
+	}
+
+	return ret;
+}
+
+void __init msm_pm_set_platform_data(
+	struct msm_pm_platform_data *data, int count)
+{
+	BUG_ON(MSM_PM_SLEEP_MODE_NR != count);
+	msm_pm_modes = data;
+}
+
+
+/******************************************************************************
+ * Sleep Limitations
+ *****************************************************************************/
+enum {
+	SLEEP_LIMIT_NONE = 0,
+	SLEEP_LIMIT_NO_TCXO_SHUTDOWN = 2,
+	SLEEP_LIMIT_MASK = 0x03,
+};
+
+#ifdef CONFIG_MSM_MEMORY_LOW_POWER_MODE
+enum {
+	SLEEP_RESOURCE_MEMORY_BIT0 = 0x0200,
+	SLEEP_RESOURCE_MEMORY_BIT1 = 0x0010,
+};
+#endif
+
+
+/******************************************************************************
+ * Configure Hardware for Power Down/Up
+ *****************************************************************************/
+
+#if defined(CONFIG_ARCH_MSM7X30)
+#define APPS_CLK_SLEEP_EN (MSM_GCC_BASE + 0x020)
+#define APPS_PWRDOWN      (MSM_ACC_BASE + 0x01c)
+#define APPS_SECOP        (MSM_TCSR_BASE + 0x038)
+#else /* defined(CONFIG_ARCH_MSM7X30) */
+#define APPS_CLK_SLEEP_EN (MSM_CSR_BASE + 0x11c)
+#define APPS_PWRDOWN      (MSM_CSR_BASE + 0x440)
+#define APPS_STANDBY_CTL  (MSM_CSR_BASE + 0x108)
+#endif /* defined(CONFIG_ARCH_MSM7X30) */
+
+/*
+ * Configure hardware registers in preparation for Apps power down.
+ */
+static void msm_pm_config_hw_before_power_down(void)
+{
+#if defined(CONFIG_ARCH_MSM7X30)
+	__raw_writel(1, APPS_PWRDOWN);
+	mb();
+	__raw_writel(4, APPS_SECOP);
+	mb();
+#elif defined(CONFIG_ARCH_MSM7X27)
+	__raw_writel(0x1f, APPS_CLK_SLEEP_EN);
+	mb();
+	__raw_writel(1, APPS_PWRDOWN);
+	mb();
+#elif defined(CONFIG_ARCH_MSM7x27A)
+	__raw_writel(0x7, APPS_CLK_SLEEP_EN);
+	mb();
+	__raw_writel(1, APPS_PWRDOWN);
+	mb();
+#else
+	__raw_writel(0x1f, APPS_CLK_SLEEP_EN);
+	mb();
+	__raw_writel(1, APPS_PWRDOWN);
+	mb();
+	__raw_writel(0, APPS_STANDBY_CTL);
+	mb();
+#endif
+}
+
+/*
+ * Clear hardware registers after Apps powers up.
+ */
+static void msm_pm_config_hw_after_power_up(void)
+{
+#if defined(CONFIG_ARCH_MSM7X30)
+	__raw_writel(0, APPS_SECOP);
+	mb();
+	__raw_writel(0, APPS_PWRDOWN);
+	mb();
+	msm_spm_reinit();
+#elif defined(CONFIG_ARCH_MSM7x27A)
+	__raw_writel(0, APPS_PWRDOWN);
+	mb();
+	__raw_writel(0, APPS_CLK_SLEEP_EN);
+	mb();
+#else
+	__raw_writel(0, APPS_PWRDOWN);
+	mb();
+	__raw_writel(0, APPS_CLK_SLEEP_EN);
+	mb();
+#endif
+}
+
+/*
+ * Configure hardware registers in preparation for SWFI.
+ */
+static void msm_pm_config_hw_before_swfi(void)
+{
+#if defined(CONFIG_ARCH_QSD8X50)
+	__raw_writel(0x1f, APPS_CLK_SLEEP_EN);
+	mb();
+#elif defined(CONFIG_ARCH_MSM7X27)
+	__raw_writel(0x0f, APPS_CLK_SLEEP_EN);
+	mb();
+#elif defined(CONFIG_ARCH_MSM7X27A)
+	__raw_writel(0x7, APPS_CLK_SLEEP_EN);
+	mb();
+#endif
+}
+
+/*
+ * Respond to timing out waiting for Modem
+ *
+ * NOTE: The function never returns.
+ */
+static void msm_pm_timeout(void)
+{
+#if defined(CONFIG_MSM_PM_TIMEOUT_RESET_CHIP)
+	printk(KERN_EMERG "%s(): resetting chip\n", __func__);
+	msm_proc_comm(PCOM_RESET_CHIP_IMM, NULL, NULL);
+#elif defined(CONFIG_MSM_PM_TIMEOUT_RESET_MODEM)
+	printk(KERN_EMERG "%s(): resetting modem\n", __func__);
+	msm_proc_comm_reset_modem_now();
+#elif defined(CONFIG_MSM_PM_TIMEOUT_HALT)
+	printk(KERN_EMERG "%s(): halting\n", __func__);
+#endif
+	for (;;)
+		;
+}
+
+
+/******************************************************************************
+ * State Polling Definitions
+ *****************************************************************************/
+
+struct msm_pm_polled_group {
+	uint32_t group_id;
+
+	uint32_t bits_all_set;
+	uint32_t bits_all_clear;
+	uint32_t bits_any_set;
+	uint32_t bits_any_clear;
+
+	uint32_t value_read;
+};
+
+/*
+ * Return true if all bits indicated by flag are set in source.
+ */
+static inline bool msm_pm_all_set(uint32_t source, uint32_t flag)
+{
+	return (source & flag) == flag;
+}
+
+/*
+ * Return true if any bit indicated by flag are set in source.
+ */
+static inline bool msm_pm_any_set(uint32_t source, uint32_t flag)
+{
+	return !flag || (source & flag);
+}
+
+/*
+ * Return true if all bits indicated by flag are cleared in source.
+ */
+static inline bool msm_pm_all_clear(uint32_t source, uint32_t flag)
+{
+	return (~source & flag) == flag;
+}
+
+/*
+ * Return true if any bit indicated by flag are cleared in source.
+ */
+static inline bool msm_pm_any_clear(uint32_t source, uint32_t flag)
+{
+	return !flag || (~source & flag);
+}
+
+/*
+ * Poll the shared memory states as indicated by the poll groups.
+ *
+ * nr_grps: number of groups in the array
+ * grps: array of groups
+ *
+ * The function returns when conditions specified by any of the poll
+ * groups become true.  The conditions specified by a poll group are
+ * deemed true when 1) at least one bit from bits_any_set is set OR one
+ * bit from bits_any_clear is cleared; and 2) all bits in bits_all_set
+ * are set; and 3) all bits in bits_all_clear are cleared.
+ *
+ * Return value:
+ *      >=0: index of the poll group whose conditions have become true
+ *      -ETIMEDOUT: timed out
+ */
+static int msm_pm_poll_state(int nr_grps, struct msm_pm_polled_group *grps)
+{
+	int i, k;
+
+	for (i = 0; i < 50000; i++) {
+		for (k = 0; k < nr_grps; k++) {
+			bool all_set, all_clear;
+			bool any_set, any_clear;
+
+			grps[k].value_read = smsm_get_state(grps[k].group_id);
+
+			all_set = msm_pm_all_set(grps[k].value_read,
+					grps[k].bits_all_set);
+			all_clear = msm_pm_all_clear(grps[k].value_read,
+					grps[k].bits_all_clear);
+			any_set = msm_pm_any_set(grps[k].value_read,
+					grps[k].bits_any_set);
+			any_clear = msm_pm_any_clear(grps[k].value_read,
+					grps[k].bits_any_clear);
+
+			if (all_set && all_clear && (any_set || any_clear))
+				return k;
+		}
+		udelay(50);
+	}
+
+	printk(KERN_ERR "%s failed:\n", __func__);
+	for (k = 0; k < nr_grps; k++)
+		printk(KERN_ERR "(%x, %x, %x, %x) %x\n",
+			grps[k].bits_all_set, grps[k].bits_all_clear,
+			grps[k].bits_any_set, grps[k].bits_any_clear,
+			grps[k].value_read);
+
+	return -ETIMEDOUT;
+}
+
+
+/******************************************************************************
+ * Suspend Max Sleep Time
+ *****************************************************************************/
+
+#define SCLK_HZ (32768)
+#define MSM_PM_SLEEP_TICK_LIMIT (0x6DDD000)
+
+#ifdef CONFIG_MSM_SLEEP_TIME_OVERRIDE
+static int msm_pm_sleep_time_override;
+module_param_named(sleep_time_override,
+	msm_pm_sleep_time_override, int, S_IRUGO | S_IWUSR | S_IWGRP);
+#endif
+
+static uint32_t msm_pm_max_sleep_time;
+
+/*
+ * Convert time from nanoseconds to slow clock ticks, then cap it to the
+ * specified limit
+ */
+static int64_t msm_pm_convert_and_cap_time(int64_t time_ns, int64_t limit)
+{
+	do_div(time_ns, NSEC_PER_SEC / SCLK_HZ);
+	return (time_ns > limit) ? limit : time_ns;
+}
+
+/*
+ * Set the sleep time for suspend.  0 means infinite sleep time.
+ */
+void msm_pm_set_max_sleep_time(int64_t max_sleep_time_ns)
+{
+	unsigned long flags;
+
+	local_irq_save(flags);
+	if (max_sleep_time_ns == 0) {
+		msm_pm_max_sleep_time = 0;
+	} else {
+		msm_pm_max_sleep_time = (uint32_t)msm_pm_convert_and_cap_time(
+			max_sleep_time_ns, MSM_PM_SLEEP_TICK_LIMIT);
+
+		if (msm_pm_max_sleep_time == 0)
+			msm_pm_max_sleep_time = 1;
+	}
+
+	MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND, KERN_INFO,
+		"%s(): Requested %lld ns Giving %u sclk ticks\n", __func__,
+		max_sleep_time_ns, msm_pm_max_sleep_time);
+	local_irq_restore(flags);
+}
+EXPORT_SYMBOL(msm_pm_set_max_sleep_time);
+
+
+/******************************************************************************
+ * CONFIG_MSM_IDLE_STATS
+ *****************************************************************************/
+
+#ifdef CONFIG_MSM_IDLE_STATS
+enum msm_pm_time_stats_id {
+	MSM_PM_STAT_REQUESTED_IDLE,
+	MSM_PM_STAT_IDLE_SPIN,
+	MSM_PM_STAT_IDLE_WFI,
+	MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE,
+	MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE,
+	MSM_PM_STAT_IDLE_SLEEP,
+	MSM_PM_STAT_IDLE_FAILED_SLEEP,
+	MSM_PM_STAT_IDLE_POWER_COLLAPSE,
+	MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE,
+	MSM_PM_STAT_SUSPEND,
+	MSM_PM_STAT_FAILED_SUSPEND,
+	MSM_PM_STAT_NOT_IDLE,
+	MSM_PM_STAT_COUNT
+};
+
+static struct msm_pm_time_stats {
+	const char *name;
+	int64_t first_bucket_time;
+	int bucket[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT];
+	int64_t min_time[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT];
+	int64_t max_time[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT];
+	int count;
+	int64_t total_time;
+} msm_pm_stats[MSM_PM_STAT_COUNT] = {
+	[MSM_PM_STAT_REQUESTED_IDLE].name = "idle-request",
+	[MSM_PM_STAT_REQUESTED_IDLE].first_bucket_time =
+		CONFIG_MSM_IDLE_STATS_FIRST_BUCKET,
+
+	[MSM_PM_STAT_IDLE_SPIN].name = "idle-spin",
+	[MSM_PM_STAT_IDLE_SPIN].first_bucket_time =
+		CONFIG_MSM_IDLE_STATS_FIRST_BUCKET,
+
+	[MSM_PM_STAT_IDLE_WFI].name = "idle-wfi",
+	[MSM_PM_STAT_IDLE_WFI].first_bucket_time =
+		CONFIG_MSM_IDLE_STATS_FIRST_BUCKET,
+
+	[MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE].name =
+		"idle-standalone-power-collapse",
+	[MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE].first_bucket_time =
+		CONFIG_MSM_IDLE_STATS_FIRST_BUCKET,
+
+	[MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE].name =
+		"idle-failed-standalone-power-collapse",
+	[MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE].first_bucket_time =
+		CONFIG_MSM_IDLE_STATS_FIRST_BUCKET,
+
+	[MSM_PM_STAT_IDLE_SLEEP].name = "idle-sleep",
+	[MSM_PM_STAT_IDLE_SLEEP].first_bucket_time =
+		CONFIG_MSM_IDLE_STATS_FIRST_BUCKET,
+
+	[MSM_PM_STAT_IDLE_FAILED_SLEEP].name = "idle-failed-sleep",
+	[MSM_PM_STAT_IDLE_FAILED_SLEEP].first_bucket_time =
+		CONFIG_MSM_IDLE_STATS_FIRST_BUCKET,
+
+	[MSM_PM_STAT_IDLE_POWER_COLLAPSE].name = "idle-power-collapse",
+	[MSM_PM_STAT_IDLE_POWER_COLLAPSE].first_bucket_time =
+		CONFIG_MSM_IDLE_STATS_FIRST_BUCKET,
+
+	[MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE].name =
+		"idle-failed-power-collapse",
+	[MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE].first_bucket_time =
+		CONFIG_MSM_IDLE_STATS_FIRST_BUCKET,
+
+	[MSM_PM_STAT_SUSPEND].name = "suspend",
+	[MSM_PM_STAT_SUSPEND].first_bucket_time =
+		CONFIG_MSM_SUSPEND_STATS_FIRST_BUCKET,
+
+	[MSM_PM_STAT_FAILED_SUSPEND].name = "failed-suspend",
+	[MSM_PM_STAT_FAILED_SUSPEND].first_bucket_time =
+		CONFIG_MSM_IDLE_STATS_FIRST_BUCKET,
+
+	[MSM_PM_STAT_NOT_IDLE].name = "not-idle",
+	[MSM_PM_STAT_NOT_IDLE].first_bucket_time =
+		CONFIG_MSM_IDLE_STATS_FIRST_BUCKET,
+};
+
+static uint32_t msm_pm_sleep_limit = SLEEP_LIMIT_NONE;
+
+/*
+ * Add the given time data to the statistics collection.
+ */
+static void msm_pm_add_stat(enum msm_pm_time_stats_id id, int64_t t)
+{
+	int i;
+	int64_t bt;
+
+	msm_pm_stats[id].total_time += t;
+	msm_pm_stats[id].count++;
+
+	bt = t;
+	do_div(bt, msm_pm_stats[id].first_bucket_time);
+
+	if (bt < 1ULL << (CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT *
+				(CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1)))
+		i = DIV_ROUND_UP(fls((uint32_t)bt),
+					CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT);
+	else
+		i = CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1;
+
+	msm_pm_stats[id].bucket[i]++;
+
+	if (t < msm_pm_stats[id].min_time[i] || !msm_pm_stats[id].max_time[i])
+		msm_pm_stats[id].min_time[i] = t;
+	if (t > msm_pm_stats[id].max_time[i])
+		msm_pm_stats[id].max_time[i] = t;
+}
+
+/*
+ * Helper function of snprintf where buf is auto-incremented, size is auto-
+ * decremented, and there is no return value.
+ *
+ * NOTE: buf and size must be l-values (e.g. variables)
+ */
+#define SNPRINTF(buf, size, format, ...) \
+	do { \
+		if (size > 0) { \
+			int ret; \
+			ret = snprintf(buf, size, format, ## __VA_ARGS__); \
+			if (ret > size) { \
+				buf += size; \
+				size = 0; \
+			} else { \
+				buf += ret; \
+				size -= ret; \
+			} \
+		} \
+	} while (0)
+
+/*
+ * Write out the power management statistics.
+ */
+static int msm_pm_read_proc
+	(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+	int i;
+	char *p = page;
+
+	if (count < 1024) {
+		*start = (char *) 0;
+		*eof = 0;
+		return 0;
+	}
+
+	if (!off) {
+		SNPRINTF(p, count, "Last power collapse voted ");
+		if ((msm_pm_sleep_limit & SLEEP_LIMIT_MASK) ==
+			SLEEP_LIMIT_NONE)
+			SNPRINTF(p, count, "for TCXO shutdown\n\n");
+		else
+			SNPRINTF(p, count, "against TCXO shutdown\n\n");
+
+		*start = (char *) 1;
+		*eof = 0;
+	} else if (--off < ARRAY_SIZE(msm_pm_stats)) {
+		int64_t bucket_time;
+		int64_t s;
+		uint32_t ns;
+
+		s = msm_pm_stats[off].total_time;
+		ns = do_div(s, NSEC_PER_SEC);
+		SNPRINTF(p, count,
+			"%s:\n"
+			"  count: %7d\n"
+			"  total_time: %lld.%09u\n",
+			msm_pm_stats[off].name,
+			msm_pm_stats[off].count,
+			s, ns);
+
+		bucket_time = msm_pm_stats[off].first_bucket_time;
+		for (i = 0; i < CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1; i++) {
+			s = bucket_time;
+			ns = do_div(s, NSEC_PER_SEC);
+			SNPRINTF(p, count,
+				"   <%6lld.%09u: %7d (%lld-%lld)\n",
+				s, ns, msm_pm_stats[off].bucket[i],
+				msm_pm_stats[off].min_time[i],
+				msm_pm_stats[off].max_time[i]);
+
+			bucket_time <<= CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT;
+		}
+
+		SNPRINTF(p, count, "  >=%6lld.%09u: %7d (%lld-%lld)\n",
+			s, ns, msm_pm_stats[off].bucket[i],
+			msm_pm_stats[off].min_time[i],
+			msm_pm_stats[off].max_time[i]);
+
+		*start = (char *) 1;
+		*eof = (off + 1 >= ARRAY_SIZE(msm_pm_stats));
+	}
+
+	return p - page;
+}
+#undef SNPRINTF
+
+#define MSM_PM_STATS_RESET "reset"
+
+/*
+ * Reset the power management statistics values.
+ */
+static int msm_pm_write_proc(struct file *file, const char __user *buffer,
+	unsigned long count, void *data)
+{
+	char buf[sizeof(MSM_PM_STATS_RESET)];
+	int ret;
+	unsigned long flags;
+	int i;
+
+	if (count < strlen(MSM_PM_STATS_RESET)) {
+		ret = -EINVAL;
+		goto write_proc_failed;
+	}
+
+	if (copy_from_user(buf, buffer, strlen(MSM_PM_STATS_RESET))) {
+		ret = -EFAULT;
+		goto write_proc_failed;
+	}
+
+	if (memcmp(buf, MSM_PM_STATS_RESET, strlen(MSM_PM_STATS_RESET))) {
+		ret = -EINVAL;
+		goto write_proc_failed;
+	}
+
+	local_irq_save(flags);
+	for (i = 0; i < ARRAY_SIZE(msm_pm_stats); i++) {
+		memset(msm_pm_stats[i].bucket,
+			0, sizeof(msm_pm_stats[i].bucket));
+		memset(msm_pm_stats[i].min_time,
+			0, sizeof(msm_pm_stats[i].min_time));
+		memset(msm_pm_stats[i].max_time,
+			0, sizeof(msm_pm_stats[i].max_time));
+		msm_pm_stats[i].count = 0;
+		msm_pm_stats[i].total_time = 0;
+	}
+
+	msm_pm_sleep_limit = SLEEP_LIMIT_NONE;
+	local_irq_restore(flags);
+
+	return count;
+
+write_proc_failed:
+	return ret;
+}
+#undef MSM_PM_STATS_RESET
+#endif /* CONFIG_MSM_IDLE_STATS */
+
+
+/******************************************************************************
+ * Shared Memory Bits
+ *****************************************************************************/
+
+#define DEM_MASTER_BITS_PER_CPU             6
+
+/* Power Master State Bits - Per CPU */
+#define DEM_MASTER_SMSM_RUN \
+	(0x01UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
+#define DEM_MASTER_SMSM_RSA \
+	(0x02UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
+#define DEM_MASTER_SMSM_PWRC_EARLY_EXIT \
+	(0x04UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
+#define DEM_MASTER_SMSM_SLEEP_EXIT \
+	(0x08UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
+#define DEM_MASTER_SMSM_READY \
+	(0x10UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
+#define DEM_MASTER_SMSM_SLEEP \
+	(0x20UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE))
+
+/* Power Slave State Bits */
+#define DEM_SLAVE_SMSM_RUN                  (0x0001)
+#define DEM_SLAVE_SMSM_PWRC                 (0x0002)
+#define DEM_SLAVE_SMSM_PWRC_DELAY           (0x0004)
+#define DEM_SLAVE_SMSM_PWRC_EARLY_EXIT      (0x0008)
+#define DEM_SLAVE_SMSM_WFPI                 (0x0010)
+#define DEM_SLAVE_SMSM_SLEEP                (0x0020)
+#define DEM_SLAVE_SMSM_SLEEP_EXIT           (0x0040)
+#define DEM_SLAVE_SMSM_MSGS_REDUCED         (0x0080)
+#define DEM_SLAVE_SMSM_RESET                (0x0100)
+#define DEM_SLAVE_SMSM_PWRC_SUSPEND         (0x0200)
+
+
+/******************************************************************************
+ * Shared Memory Data
+ *****************************************************************************/
+
+#define DEM_MAX_PORT_NAME_LEN (20)
+
+struct msm_pm_smem_t {
+	uint32_t sleep_time;
+	uint32_t irq_mask;
+	uint32_t resources_used;
+	uint32_t reserved1;
+
+	uint32_t wakeup_reason;
+	uint32_t pending_irqs;
+	uint32_t rpc_prog;
+	uint32_t rpc_proc;
+	char     smd_port_name[DEM_MAX_PORT_NAME_LEN];
+	uint32_t reserved2;
+};
+
+
+/******************************************************************************
+ *
+ *****************************************************************************/
+static struct msm_pm_smem_t *msm_pm_smem_data;
+static uint32_t *msm_pm_reset_vector;
+static atomic_t msm_pm_init_done = ATOMIC_INIT(0);
+
+static int msm_pm_modem_busy(void)
+{
+	if (!(smsm_get_state(SMSM_POWER_MASTER_DEM) & DEM_MASTER_SMSM_READY)) {
+		MSM_PM_DPRINTK(MSM_PM_DEBUG_POWER_COLLAPSE,
+			KERN_INFO, "%s(): master not ready\n", __func__);
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+/*
+ * Power collapse the Apps processor.  This function executes the handshake
+ * protocol with Modem.
+ *
+ * Return value:
+ *      -EAGAIN: modem reset occurred or early exit from power collapse
+ *      -EBUSY: modem not ready for our power collapse -- no power loss
+ *      -ETIMEDOUT: timed out waiting for modem's handshake -- no power loss
+ *      0: success
+ */
+static int msm_pm_power_collapse
+	(bool from_idle, uint32_t sleep_delay, uint32_t sleep_limit)
+{
+	struct msm_pm_polled_group state_grps[2];
+	unsigned long saved_acpuclk_rate;
+	uint32_t saved_vector[2];
+	int collapsed = 0;
+	int ret;
+
+	MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
+		KERN_INFO, "%s(): idle %d, delay %u, limit %u\n", __func__,
+		(int)from_idle, sleep_delay, sleep_limit);
+
+	if (!(smsm_get_state(SMSM_POWER_MASTER_DEM) & DEM_MASTER_SMSM_READY)) {
+		MSM_PM_DPRINTK(
+			MSM_PM_DEBUG_SUSPEND | MSM_PM_DEBUG_POWER_COLLAPSE,
+			KERN_INFO, "%s(): master not ready\n", __func__);
+		ret = -EBUSY;
+		goto power_collapse_bail;
+	}
+
+	memset(msm_pm_smem_data, 0, sizeof(*msm_pm_smem_data));
+
+	msm_irq_enter_sleep1(true, from_idle, &msm_pm_smem_data->irq_mask);
+	msm_sirc_enter_sleep();
+	msm_gpio_enter_sleep(from_idle);
+
+	msm_pm_smem_data->sleep_time = sleep_delay;
+	msm_pm_smem_data->resources_used = sleep_limit;
+
+	/* Enter PWRC/PWRC_SUSPEND */
+
+	if (from_idle)
+		smsm_change_state(SMSM_APPS_DEM, DEM_SLAVE_SMSM_RUN,
+			DEM_SLAVE_SMSM_PWRC);
+	else
+		smsm_change_state(SMSM_APPS_DEM, DEM_SLAVE_SMSM_RUN,
+			DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND);
+
+	MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): PWRC");
+	MSM_PM_DEBUG_PRINT_SLEEP_INFO();
+
+	memset(state_grps, 0, sizeof(state_grps));
+	state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
+	state_grps[0].bits_all_set = DEM_MASTER_SMSM_RSA;
+	state_grps[1].group_id = SMSM_MODEM_STATE;
+	state_grps[1].bits_all_set = SMSM_RESET;
+
+	ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
+
+	if (ret < 0) {
+		printk(KERN_EMERG "%s(): power collapse entry "
+			"timed out waiting for Modem's response\n", __func__);
+		msm_pm_timeout();
+	}
+
+	if (ret == 1) {
+		MSM_PM_DPRINTK(
+			MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
+			KERN_INFO,
+			"%s(): msm_pm_poll_state detected Modem reset\n",
+			__func__);
+		goto power_collapse_early_exit;
+	}
+
+	/* DEM Master in RSA */
+
+	MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): PWRC RSA");
+
+	ret = msm_irq_enter_sleep2(true, from_idle);
+	if (ret < 0) {
+		MSM_PM_DPRINTK(
+			MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
+			KERN_INFO,
+			"%s(): msm_irq_enter_sleep2 aborted, %d\n", __func__,
+			ret);
+		goto power_collapse_early_exit;
+	}
+
+	msm_pm_config_hw_before_power_down();
+	MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): pre power down");
+
+	saved_acpuclk_rate = acpuclk_power_collapse();
+	MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
+		"%s(): change clock rate (old rate = %lu)\n", __func__,
+		saved_acpuclk_rate);
+
+	if (saved_acpuclk_rate == 0) {
+		msm_pm_config_hw_after_power_up();
+		goto power_collapse_early_exit;
+	}
+
+	saved_vector[0] = msm_pm_reset_vector[0];
+	saved_vector[1] = msm_pm_reset_vector[1];
+	msm_pm_reset_vector[0] = 0xE51FF004; /* ldr pc, 4 */
+	msm_pm_reset_vector[1] = virt_to_phys(msm_pm_collapse_exit);
+
+	MSM_PM_DPRINTK(MSM_PM_DEBUG_RESET_VECTOR, KERN_INFO,
+		"%s(): vector %x %x -> %x %x\n", __func__,
+		saved_vector[0], saved_vector[1],
+		msm_pm_reset_vector[0], msm_pm_reset_vector[1]);
+
+#ifdef CONFIG_VFP
+	if (from_idle)
+		vfp_flush_context();
+#endif
+
+#ifdef CONFIG_CACHE_L2X0
+	l2x0_suspend();
+#endif
+
+	collapsed = msm_pm_collapse();
+
+#ifdef CONFIG_CACHE_L2X0
+	l2x0_resume(collapsed);
+#endif
+
+	msm_pm_reset_vector[0] = saved_vector[0];
+	msm_pm_reset_vector[1] = saved_vector[1];
+
+	if (collapsed) {
+#ifdef CONFIG_VFP
+		if (from_idle)
+			vfp_reinit();
+#endif
+		cpu_init();
+		local_fiq_enable();
+	}
+
+	MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND | MSM_PM_DEBUG_POWER_COLLAPSE,
+		KERN_INFO,
+		"%s(): msm_pm_collapse returned %d\n", __func__, collapsed);
+
+	MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
+		"%s(): restore clock rate to %lu\n", __func__,
+		saved_acpuclk_rate);
+	if (acpuclk_set_rate(smp_processor_id(), saved_acpuclk_rate,
+			SETRATE_PC) < 0)
+		printk(KERN_ERR "%s(): failed to restore clock rate(%lu)\n",
+			__func__, saved_acpuclk_rate);
+
+	msm_irq_exit_sleep1(msm_pm_smem_data->irq_mask,
+		msm_pm_smem_data->wakeup_reason,
+		msm_pm_smem_data->pending_irqs);
+
+	msm_pm_config_hw_after_power_up();
+	MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): post power up");
+
+	memset(state_grps, 0, sizeof(state_grps));
+	state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
+	state_grps[0].bits_any_set =
+		DEM_MASTER_SMSM_RSA | DEM_MASTER_SMSM_PWRC_EARLY_EXIT;
+	state_grps[1].group_id = SMSM_MODEM_STATE;
+	state_grps[1].bits_all_set = SMSM_RESET;
+
+	ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
+
+	if (ret < 0) {
+		printk(KERN_EMERG "%s(): power collapse exit "
+			"timed out waiting for Modem's response\n", __func__);
+		msm_pm_timeout();
+	}
+
+	if (ret == 1) {
+		MSM_PM_DPRINTK(
+			MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
+			KERN_INFO,
+			"%s(): msm_pm_poll_state detected Modem reset\n",
+			__func__);
+		goto power_collapse_early_exit;
+	}
+
+	/* Sanity check */
+	if (collapsed) {
+		BUG_ON(!(state_grps[0].value_read & DEM_MASTER_SMSM_RSA));
+	} else {
+		BUG_ON(!(state_grps[0].value_read &
+			DEM_MASTER_SMSM_PWRC_EARLY_EXIT));
+		goto power_collapse_early_exit;
+	}
+
+	/* Enter WFPI */
+
+	smsm_change_state(SMSM_APPS_DEM,
+		DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND,
+		DEM_SLAVE_SMSM_WFPI);
+
+	MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): WFPI");
+
+	memset(state_grps, 0, sizeof(state_grps));
+	state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
+	state_grps[0].bits_all_set = DEM_MASTER_SMSM_RUN;
+	state_grps[1].group_id = SMSM_MODEM_STATE;
+	state_grps[1].bits_all_set = SMSM_RESET;
+
+	ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
+
+	if (ret < 0) {
+		printk(KERN_EMERG "%s(): power collapse WFPI "
+			"timed out waiting for Modem's response\n", __func__);
+		msm_pm_timeout();
+	}
+
+	if (ret == 1) {
+		MSM_PM_DPRINTK(
+			MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
+			KERN_INFO,
+			"%s(): msm_pm_poll_state detected Modem reset\n",
+			__func__);
+		ret = -EAGAIN;
+		goto power_collapse_restore_gpio_bail;
+	}
+
+	/* DEM Master == RUN */
+
+	MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): WFPI RUN");
+	MSM_PM_DEBUG_PRINT_SLEEP_INFO();
+
+	msm_irq_exit_sleep2(msm_pm_smem_data->irq_mask,
+		msm_pm_smem_data->wakeup_reason,
+		msm_pm_smem_data->pending_irqs);
+	msm_irq_exit_sleep3(msm_pm_smem_data->irq_mask,
+		msm_pm_smem_data->wakeup_reason,
+		msm_pm_smem_data->pending_irqs);
+	msm_gpio_exit_sleep();
+	msm_sirc_exit_sleep();
+
+	smsm_change_state(SMSM_APPS_DEM,
+		DEM_SLAVE_SMSM_WFPI, DEM_SLAVE_SMSM_RUN);
+
+	MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): RUN");
+
+	smd_sleep_exit();
+	return 0;
+
+power_collapse_early_exit:
+	/* Enter PWRC_EARLY_EXIT */
+
+	smsm_change_state(SMSM_APPS_DEM,
+		DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND,
+		DEM_SLAVE_SMSM_PWRC_EARLY_EXIT);
+
+	MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): EARLY_EXIT");
+
+	memset(state_grps, 0, sizeof(state_grps));
+	state_grps[0].group_id = SMSM_POWER_MASTER_DEM;
+	state_grps[0].bits_all_set = DEM_MASTER_SMSM_PWRC_EARLY_EXIT;
+	state_grps[1].group_id = SMSM_MODEM_STATE;
+	state_grps[1].bits_all_set = SMSM_RESET;
+
+	ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps);
+	MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): EARLY_EXIT EE");
+
+	if (ret < 0) {
+		printk(KERN_EMERG "%s(): power collapse EARLY_EXIT "
+			"timed out waiting for Modem's response\n", __func__);
+		msm_pm_timeout();
+	}
+
+	if (ret == 1) {
+		MSM_PM_DPRINTK(
+			MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
+			KERN_INFO,
+			"%s(): msm_pm_poll_state detected Modem reset\n",
+			__func__);
+	}
+
+	/* DEM Master == RESET or PWRC_EARLY_EXIT */
+
+	ret = -EAGAIN;
+
+power_collapse_restore_gpio_bail:
+	msm_gpio_exit_sleep();
+	msm_sirc_exit_sleep();
+
+	/* Enter RUN */
+	smsm_change_state(SMSM_APPS_DEM,
+		DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND |
+		DEM_SLAVE_SMSM_PWRC_EARLY_EXIT, DEM_SLAVE_SMSM_RUN);
+
+	MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): RUN");
+
+	if (collapsed)
+		smd_sleep_exit();
+
+power_collapse_bail:
+	return ret;
+}
+
+/*
+ * Power collapse the Apps processor without involving Modem.
+ *
+ * Return value:
+ *      0: success
+ */
+static int msm_pm_power_collapse_standalone(void)
+{
+	uint32_t saved_vector[2];
+	int collapsed = 0;
+	int ret;
+
+	MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE,
+		KERN_INFO, "%s()\n", __func__);
+
+	ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_POWER_COLLAPSE, false);
+	WARN_ON(ret);
+
+	saved_vector[0] = msm_pm_reset_vector[0];
+	saved_vector[1] = msm_pm_reset_vector[1];
+	msm_pm_reset_vector[0] = 0xE51FF004; /* ldr pc, 4 */
+	msm_pm_reset_vector[1] = virt_to_phys(msm_pm_collapse_exit);
+
+	MSM_PM_DPRINTK(MSM_PM_DEBUG_RESET_VECTOR, KERN_INFO,
+		"%s(): vector %x %x -> %x %x\n", __func__,
+		saved_vector[0], saved_vector[1],
+		msm_pm_reset_vector[0], msm_pm_reset_vector[1]);
+
+#ifdef CONFIG_VFP
+	vfp_flush_context();
+#endif
+
+#ifdef CONFIG_CACHE_L2X0
+	l2x0_suspend();
+#endif
+
+	collapsed = msm_pm_collapse();
+
+#ifdef CONFIG_CACHE_L2X0
+	l2x0_resume(collapsed);
+#endif
+
+	msm_pm_reset_vector[0] = saved_vector[0];
+	msm_pm_reset_vector[1] = saved_vector[1];
+
+	if (collapsed) {
+#ifdef CONFIG_VFP
+		vfp_reinit();
+#endif
+		cpu_init();
+		local_fiq_enable();
+	}
+
+	MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND | MSM_PM_DEBUG_POWER_COLLAPSE,
+		KERN_INFO,
+		"%s(): msm_pm_collapse returned %d\n", __func__, collapsed);
+
+	ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false);
+	WARN_ON(ret);
+
+	return 0;
+}
+
+/*
+ * Apps-sleep the Apps processor.  This function execute the handshake
+ * protocol with Modem.
+ *
+ * Return value:
+ *      -ENOSYS: function not implemented yet
+ */
+static int msm_pm_apps_sleep(uint32_t sleep_delay, uint32_t sleep_limit)
+{
+	return -ENOSYS;
+}
+
+/*
+ * Bring the Apps processor to SWFI.
+ *
+ * Return value:
+ *      -EIO: could not ramp Apps processor clock
+ *      0: success
+ */
+static int msm_pm_swfi(bool ramp_acpu)
+{
+	unsigned long saved_acpuclk_rate = 0;
+
+	if (ramp_acpu) {
+		saved_acpuclk_rate = acpuclk_wait_for_irq();
+		MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
+			"%s(): change clock rate (old rate = %lu)\n", __func__,
+			saved_acpuclk_rate);
+
+		if (!saved_acpuclk_rate)
+			return -EIO;
+	}
+
+	msm_pm_config_hw_before_swfi();
+	msm_arch_idle();
+
+	if (ramp_acpu) {
+		MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO,
+			"%s(): restore clock rate to %lu\n", __func__,
+			saved_acpuclk_rate);
+		if (acpuclk_set_rate(smp_processor_id(), saved_acpuclk_rate,
+				SETRATE_SWFI) < 0)
+			printk(KERN_ERR
+				"%s(): failed to restore clock rate(%lu)\n",
+				__func__, saved_acpuclk_rate);
+	}
+
+	return 0;
+}
+
+
+/******************************************************************************
+ * External Idle/Suspend Functions
+ *****************************************************************************/
+
+/*
+ * Put CPU in low power mode.
+ */
+void arch_idle(void)
+{
+	bool allow[MSM_PM_SLEEP_MODE_NR];
+	uint32_t sleep_limit = SLEEP_LIMIT_NONE;
+
+	int latency_qos;
+	int64_t timer_expiration;
+
+	int low_power;
+	int ret;
+	int i;
+
+#ifdef CONFIG_MSM_IDLE_STATS
+	int64_t t1;
+	static int64_t t2;
+	int exit_stat;
+#endif /* CONFIG_MSM_IDLE_STATS */
+
+	if (!atomic_read(&msm_pm_init_done))
+		return;
+
+	latency_qos = pm_qos_request(PM_QOS_CPU_DMA_LATENCY);
+	timer_expiration = msm_timer_enter_idle();
+
+#ifdef CONFIG_MSM_IDLE_STATS
+	t1 = ktime_to_ns(ktime_get());
+	msm_pm_add_stat(MSM_PM_STAT_NOT_IDLE, t1 - t2);
+	msm_pm_add_stat(MSM_PM_STAT_REQUESTED_IDLE, timer_expiration);
+#endif /* CONFIG_MSM_IDLE_STATS */
+
+	for (i = 0; i < ARRAY_SIZE(allow); i++)
+		allow[i] = true;
+
+	switch (msm_pm_idle_sleep_mode) {
+	case MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT:
+		allow[MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT] =
+			false;
+		/* fall through */
+	case MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT:
+		allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE] = false;
+		/* fall through */
+	case MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE:
+		allow[MSM_PM_SLEEP_MODE_APPS_SLEEP] = false;
+		/* fall through */
+	case MSM_PM_SLEEP_MODE_APPS_SLEEP:
+		allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN] = false;
+		allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = false;
+		/* fall through */
+	case MSM_PM_SLEEP_MODE_POWER_COLLAPSE_SUSPEND:
+	case MSM_PM_SLEEP_MODE_POWER_COLLAPSE:
+		break;
+	default:
+		printk(KERN_ERR "idle sleep mode is invalid: %d\n",
+			msm_pm_idle_sleep_mode);
+#ifdef CONFIG_MSM_IDLE_STATS
+		exit_stat = MSM_PM_STAT_IDLE_SPIN;
+#endif /* CONFIG_MSM_IDLE_STATS */
+		low_power = 0;
+		goto arch_idle_exit;
+	}
+
+	if ((timer_expiration < msm_pm_idle_sleep_min_time) ||
+#ifdef CONFIG_HAS_WAKELOCK
+		has_wake_lock(WAKE_LOCK_IDLE) ||
+#endif
+		!msm_irq_idle_sleep_allowed()) {
+		allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = false;
+		allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN] = false;
+		allow[MSM_PM_SLEEP_MODE_APPS_SLEEP] = false;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(allow); i++) {
+		struct msm_pm_platform_data *mode = &msm_pm_modes[i];
+		if (!mode->idle_supported || !mode->idle_enabled ||
+			mode->latency >= latency_qos ||
+			mode->residency * 1000ULL >= timer_expiration)
+			allow[i] = false;
+	}
+
+	if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] ||
+		allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]) {
+		uint32_t wait_us = CONFIG_MSM_IDLE_WAIT_ON_MODEM;
+		while (msm_pm_modem_busy() && wait_us) {
+			if (wait_us > 100) {
+				udelay(100);
+				wait_us -= 100;
+			} else {
+				udelay(wait_us);
+				wait_us = 0;
+			}
+		}
+
+		if (msm_pm_modem_busy()) {
+			allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = false;
+			allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]
+				= false;
+		}
+	}
+
+	MSM_PM_DPRINTK(MSM_PM_DEBUG_IDLE, KERN_INFO,
+		"%s(): latency qos %d, next timer %lld, sleep limit %u\n",
+		__func__, latency_qos, timer_expiration, sleep_limit);
+
+	for (i = 0; i < ARRAY_SIZE(allow); i++)
+		MSM_PM_DPRINTK(MSM_PM_DEBUG_IDLE, KERN_INFO,
+			"%s(): allow %s: %d\n", __func__,
+			msm_pm_sleep_mode_labels[i], (int)allow[i]);
+
+	if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] ||
+		allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]) {
+		uint32_t sleep_delay;
+
+		sleep_delay = (uint32_t) msm_pm_convert_and_cap_time(
+			timer_expiration, MSM_PM_SLEEP_TICK_LIMIT);
+		if (sleep_delay == 0) /* 0 would mean infinite time */
+			sleep_delay = 1;
+
+		if (!allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE])
+			sleep_limit = SLEEP_LIMIT_NO_TCXO_SHUTDOWN;
+
+#if defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_IDLE_ACTIVE)
+		sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT1;
+#elif defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_IDLE_RETENTION)
+		sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT0;
+#endif
+
+		ret = msm_pm_power_collapse(true, sleep_delay, sleep_limit);
+		low_power = (ret != -EBUSY && ret != -ETIMEDOUT);
+
+#ifdef CONFIG_MSM_IDLE_STATS
+		if (ret)
+			exit_stat = MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE;
+		else {
+			exit_stat = MSM_PM_STAT_IDLE_POWER_COLLAPSE;
+			msm_pm_sleep_limit = sleep_limit;
+		}
+#endif /* CONFIG_MSM_IDLE_STATS */
+	} else if (allow[MSM_PM_SLEEP_MODE_APPS_SLEEP]) {
+		uint32_t sleep_delay;
+
+		sleep_delay = (uint32_t) msm_pm_convert_and_cap_time(
+			timer_expiration, MSM_PM_SLEEP_TICK_LIMIT);
+		if (sleep_delay == 0) /* 0 would mean infinite time */
+			sleep_delay = 1;
+
+		ret = msm_pm_apps_sleep(sleep_delay, sleep_limit);
+		low_power = 0;
+
+#ifdef CONFIG_MSM_IDLE_STATS
+		if (ret)
+			exit_stat = MSM_PM_STAT_IDLE_FAILED_SLEEP;
+		else
+			exit_stat = MSM_PM_STAT_IDLE_SLEEP;
+#endif /* CONFIG_MSM_IDLE_STATS */
+	} else if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) {
+		ret = msm_pm_power_collapse_standalone();
+		low_power = 0;
+#ifdef CONFIG_MSM_IDLE_STATS
+		exit_stat = ret ?
+			MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE :
+			MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE;
+#endif /* CONFIG_MSM_IDLE_STATS */
+	} else if (allow[MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT]) {
+		ret = msm_pm_swfi(true);
+		if (ret)
+			while (!msm_irq_pending())
+				udelay(1);
+		low_power = 0;
+#ifdef CONFIG_MSM_IDLE_STATS
+		exit_stat = ret ? MSM_PM_STAT_IDLE_SPIN : MSM_PM_STAT_IDLE_WFI;
+#endif /* CONFIG_MSM_IDLE_STATS */
+	} else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) {
+		msm_pm_swfi(false);
+		low_power = 0;
+#ifdef CONFIG_MSM_IDLE_STATS
+		exit_stat = MSM_PM_STAT_IDLE_WFI;
+#endif /* CONFIG_MSM_IDLE_STATS */
+	} else {
+		while (!msm_irq_pending())
+			udelay(1);
+		low_power = 0;
+#ifdef CONFIG_MSM_IDLE_STATS
+		exit_stat = MSM_PM_STAT_IDLE_SPIN;
+#endif /* CONFIG_MSM_IDLE_STATS */
+	}
+
+arch_idle_exit:
+	msm_timer_exit_idle(low_power);
+
+#ifdef CONFIG_MSM_IDLE_STATS
+	t2 = ktime_to_ns(ktime_get());
+	msm_pm_add_stat(exit_stat, t2 - t1);
+#endif /* CONFIG_MSM_IDLE_STATS */
+}
+
+/*
+ * Suspend the Apps processor.
+ *
+ * Return value:
+ *      -EAGAIN: modem reset occurred or early exit from suspend
+ *      -EBUSY: modem not ready for our suspend
+ *      -EINVAL: invalid sleep mode
+ *      -EIO: could not ramp Apps processor clock
+ *      -ETIMEDOUT: timed out waiting for modem's handshake
+ *      0: success
+ */
+static int msm_pm_enter(suspend_state_t state)
+{
+	bool allow[MSM_PM_SLEEP_MODE_NR];
+	uint32_t sleep_limit = SLEEP_LIMIT_NONE;
+	int ret;
+	int i;
+
+#ifdef CONFIG_MSM_IDLE_STATS
+	int64_t period = 0;
+	int64_t time = 0;
+
+	time = msm_timer_get_sclk_time(&period);
+#endif
+
+	MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND, KERN_INFO,
+		"%s(): sleep limit %u\n", __func__, sleep_limit);
+
+	for (i = 0; i < ARRAY_SIZE(allow); i++)
+		allow[i] = true;
+
+	switch (msm_pm_sleep_mode) {
+	case MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT:
+		allow[MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT] =
+			false;
+		/* fall through */
+	case MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT:
+		allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE] = false;
+		/* fall through */
+	case MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE:
+		allow[MSM_PM_SLEEP_MODE_APPS_SLEEP] = false;
+		/* fall through */
+	case MSM_PM_SLEEP_MODE_APPS_SLEEP:
+		allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN] = false;
+		allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = false;
+		/* fall through */
+	case MSM_PM_SLEEP_MODE_POWER_COLLAPSE_SUSPEND:
+	case MSM_PM_SLEEP_MODE_POWER_COLLAPSE:
+		break;
+	default:
+		printk(KERN_ERR "suspend sleep mode is invalid: %d\n",
+			msm_pm_sleep_mode);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(allow); i++) {
+		struct msm_pm_platform_data *mode = &msm_pm_modes[i];
+		if (!mode->suspend_supported || !mode->suspend_enabled)
+			allow[i] = false;
+	}
+
+	ret = 0;
+
+	if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] ||
+		allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]) {
+#ifdef CONFIG_MSM_IDLE_STATS
+		enum msm_pm_time_stats_id id;
+		int64_t end_time;
+#endif
+
+		clock_debug_print_enabled();
+
+#ifdef CONFIG_MSM_SLEEP_TIME_OVERRIDE
+		if (msm_pm_sleep_time_override > 0) {
+			int64_t ns;
+			ns = NSEC_PER_SEC * (int64_t)msm_pm_sleep_time_override;
+			msm_pm_set_max_sleep_time(ns);
+			msm_pm_sleep_time_override = 0;
+		}
+#endif
+		if (!allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE])
+			sleep_limit = SLEEP_LIMIT_NO_TCXO_SHUTDOWN;
+
+#if defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_ACTIVE)
+		sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT1;
+#elif defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_RETENTION)
+		sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT0;
+#elif defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_DEEP_POWER_DOWN)
+		if (get_msm_migrate_pages_status() != MEM_OFFLINE)
+			sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT0;
+#endif
+
+		for (i = 0; i < 30 && msm_pm_modem_busy(); i++)
+			udelay(500);
+
+		ret = msm_pm_power_collapse(
+			false, msm_pm_max_sleep_time, sleep_limit);
+
+#ifdef CONFIG_MSM_IDLE_STATS
+		if (ret)
+			id = MSM_PM_STAT_FAILED_SUSPEND;
+		else {
+			id = MSM_PM_STAT_SUSPEND;
+			msm_pm_sleep_limit = sleep_limit;
+		}
+
+		if (time != 0) {
+			end_time = msm_timer_get_sclk_time(NULL);
+			if (end_time != 0) {
+				time = end_time - time;
+				if (time < 0)
+					time += period;
+			} else
+				time = 0;
+		}
+
+		msm_pm_add_stat(id, time);
+#endif
+	} else if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) {
+		ret = msm_pm_power_collapse_standalone();
+	} else if (allow[MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT]) {
+		ret = msm_pm_swfi(true);
+		if (ret)
+			while (!msm_irq_pending())
+				udelay(1);
+	} else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) {
+		msm_pm_swfi(false);
+	}
+
+	MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND, KERN_INFO,
+		"%s(): return %d\n", __func__, ret);
+
+	return ret;
+}
+
+static struct platform_suspend_ops msm_pm_ops = {
+	.enter = msm_pm_enter,
+	.valid = suspend_valid_only_mem,
+};
+
+
+/******************************************************************************
+ * Restart Definitions
+ *****************************************************************************/
+
+static uint32_t restart_reason = 0x776655AA;
+
+static void msm_pm_power_off(void)
+{
+	msm_rpcrouter_close();
+	msm_proc_comm(PCOM_POWER_DOWN, 0, 0);
+	for (;;)
+		;
+}
+
+static void msm_pm_restart(char str, const char *cmd)
+{
+	msm_rpcrouter_close();
+	msm_proc_comm(PCOM_RESET_CHIP, &restart_reason, 0);
+
+	for (;;)
+		;
+}
+
+static int msm_reboot_call
+	(struct notifier_block *this, unsigned long code, void *_cmd)
+{
+	if ((code == SYS_RESTART) && _cmd) {
+		char *cmd = _cmd;
+		if (!strcmp(cmd, "bootloader")) {
+			restart_reason = 0x77665500;
+		} else if (!strcmp(cmd, "recovery")) {
+			restart_reason = 0x77665502;
+		} else if (!strcmp(cmd, "eraseflash")) {
+			restart_reason = 0x776655EF;
+		} else if (!strncmp(cmd, "oem-", 4)) {
+			unsigned code = simple_strtoul(cmd + 4, 0, 16) & 0xff;
+			restart_reason = 0x6f656d00 | code;
+		} else {
+			restart_reason = 0x77665501;
+		}
+	}
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block msm_reboot_notifier = {
+	.notifier_call = msm_reboot_call,
+};
+
+
+/******************************************************************************
+ *
+ *****************************************************************************/
+
+/*
+ * Initialize the power management subsystem.
+ *
+ * Return value:
+ *      -ENODEV: initialization failed
+ *      0: success
+ */
+static int __init msm_pm_init(void)
+{
+#ifdef CONFIG_MSM_IDLE_STATS
+	struct proc_dir_entry *d_entry;
+#endif
+	int ret;
+#ifdef CONFIG_CPU_V7
+	pgd_t *pc_pgd;
+	pmd_t *pmd;
+	unsigned long pmdval;
+
+	/* Page table for cores to come back up safely. */
+	pc_pgd = pgd_alloc(&init_mm);
+	if (!pc_pgd)
+		return -ENOMEM;
+	pmd = pmd_offset(pc_pgd +
+			 pgd_index(virt_to_phys(msm_pm_collapse_exit)),
+			 virt_to_phys(msm_pm_collapse_exit));
+	pmdval = (virt_to_phys(msm_pm_collapse_exit) & PGDIR_MASK) |
+		     PMD_TYPE_SECT | PMD_SECT_AP_WRITE;
+	pmd[0] = __pmd(pmdval);
+	pmd[1] = __pmd(pmdval + (1 << (PGDIR_SHIFT - 1)));
+
+	/* It is remotely possible that the code in msm_pm_collapse_exit()
+	 * which turns on the MMU with this mapping is in the
+	 * next even-numbered megabyte beyond the
+	 * start of msm_pm_collapse_exit().
+	 * Map this megabyte in as well.
+	 */
+	pmd[2] = __pmd(pmdval + (2 << (PGDIR_SHIFT - 1)));
+	flush_pmd_entry(pmd);
+	msm_pm_pc_pgd = virt_to_phys(pc_pgd);
+#endif
+
+	pm_power_off = msm_pm_power_off;
+	arm_pm_restart = msm_pm_restart;
+	register_reboot_notifier(&msm_reboot_notifier);
+
+	msm_pm_smem_data = smem_alloc(SMEM_APPS_DEM_SLAVE_DATA,
+		sizeof(*msm_pm_smem_data));
+	if (msm_pm_smem_data == NULL) {
+		printk(KERN_ERR "%s: failed to get smsm_data\n", __func__);
+		return -ENODEV;
+	}
+#if defined(CONFIG_ARCH_MSM_SCORPION) && !defined(CONFIG_MSM_SMP)
+	/* The bootloader is responsible for initializing many of Scorpion's
+	 * coprocessor registers for things like cache timing. The state of
+	 * these coprocessor registers is lost on reset, so part of the
+	 * bootloader must be re-executed. Do not overwrite the reset vector
+	 * or bootloader area.
+	 */
+	msm_pm_reset_vector = (uint32_t *) PAGE_OFFSET;
+#else
+	msm_pm_reset_vector = ioremap(0, PAGE_SIZE);
+	if (msm_pm_reset_vector == NULL) {
+		printk(KERN_ERR "%s: failed to map reset vector\n", __func__);
+		return -ENODEV;
+	}
+#endif /* CONFIG_ARCH_MSM_SCORPION */
+
+	ret = msm_timer_init_time_sync(msm_pm_timeout);
+	if (ret)
+		return ret;
+
+	ret = smsm_change_intr_mask(SMSM_POWER_MASTER_DEM, 0xFFFFFFFF, 0);
+	if (ret) {
+		printk(KERN_ERR "%s: failed to clear interrupt mask, %d\n",
+			__func__, ret);
+		return ret;
+	}
+
+#ifdef CONFIG_MSM_MEMORY_LOW_POWER_MODE
+	/* The wakeup_reason field is overloaded during initialization time
+	   to signal Modem that Apps will control the low power modes of
+	   the memory.
+	 */
+	msm_pm_smem_data->wakeup_reason = 1;
+	smsm_change_state(SMSM_APPS_DEM, 0, DEM_SLAVE_SMSM_RUN);
+#endif
+
+	BUG_ON(msm_pm_modes == NULL);
+
+	atomic_set(&msm_pm_init_done, 1);
+	suspend_set_ops(&msm_pm_ops);
+
+	msm_pm_mode_sysfs_add();
+#ifdef CONFIG_MSM_IDLE_STATS
+	d_entry = create_proc_entry("msm_pm_stats",
+			S_IRUGO | S_IWUSR | S_IWGRP, NULL);
+	if (d_entry) {
+		d_entry->read_proc = msm_pm_read_proc;
+		d_entry->write_proc = msm_pm_write_proc;
+		d_entry->data = NULL;
+	}
+#endif
+
+	return 0;
+}
+
+late_initcall_sync(msm_pm_init);