msm: restart: Handle reset interrupt from pmic

The reset interrupt in the pmic indicates the pmic will shutdown
the msm in few seconds - it does so by lowering the reset_n line
to the msm.

When this interrupt is triggered it is required that no more ssbi
transactions are initiated and the msm should cleanup and prepare
for the impending shutdown. There is no need to lower ps_hold in
this case as the pmic will shutdown the msm regardless.

Also since we don't want any ssbi transactions, force shutdown
nonboot cpus. This will prevent ssbi transactions to the pmic for
lower/raising nonboot cpu's voltages as they enter/exit idle states.

Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
diff --git a/arch/arm/mach-msm/restart.c b/arch/arm/mach-msm/restart.c
index 2b82bf1..dba575c 100644
--- a/arch/arm/mach-msm/restart.c
+++ b/arch/arm/mach-msm/restart.c
@@ -19,6 +19,8 @@
 #include <linux/io.h>
 #include <linux/delay.h>
 #include <linux/pm.h>
+#include <linux/cpu.h>
+#include <linux/interrupt.h>
 #include <linux/mfd/pmic8058.h>
 #include <linux/mfd/pmic8901.h>
 #include <linux/mfd/pm8xxx/misc.h>
@@ -28,6 +30,7 @@
 #include <mach/msm_iomap.h>
 #include <mach/restart.h>
 #include <mach/socinfo.h>
+#include <mach/irqs.h>
 
 #define WDT0_RST       (MSM_TMR0_BASE + 0x38)
 #define WDT0_EN        (MSM_TMR0_BASE + 0x40)
@@ -42,6 +45,8 @@
 static int restart_mode;
 void *restart_reason;
 
+int pmic_reset_irq;
+
 #ifdef CONFIG_MSM_DLOAD_MODE
 static int in_panic;
 static void *dload_mode_addr;
@@ -103,9 +108,9 @@
 }
 EXPORT_SYMBOL(msm_set_restart_mode);
 
-static void msm_power_off(void)
+static void __msm_power_off(int lower_pshold)
 {
-	printk(KERN_NOTICE "Powering off the SoC\n");
+	printk(KERN_CRIT "Powering off the SoC\n");
 #ifdef CONFIG_MSM_DLOAD_MODE
 	set_dload_mode(0);
 #endif
@@ -114,12 +119,48 @@
 		pm8901_reset_pwr_off(0);
 	}
 	pm8xxx_reset_pwr_off(0);
-	__raw_writel(0, PSHOLD_CTL_SU);
+	if (lower_pshold)
+		__raw_writel(0, PSHOLD_CTL_SU);
 	mdelay(10000);
 	printk(KERN_ERR "Powering off has failed\n");
 	return;
 }
 
+static void msm_power_off(void)
+{
+	/* MSM initiated power off, lower ps_hold */
+	__msm_power_off(1);
+}
+
+static void cpu_power_off(void *data)
+{
+	pr_err("PMIC Initiated shutdown %s cpu=%d\n", __func__,
+						smp_processor_id());
+	if (smp_processor_id() == 0)
+		/*
+		 * PMIC initiated power off, do not lower ps_hold, pmic will
+		 * shut msm down
+		 */
+		__msm_power_off(0);
+
+	preempt_disable();
+	while (1)
+		;
+}
+
+static irqreturn_t resout_irq_handler(int irq, void *dev_id)
+{
+	pr_warn("%s PMIC Initiated shutdown\n", __func__);
+	oops_in_progress = 1;
+	smp_call_function_many(cpu_online_mask, cpu_power_off, NULL, 0);
+	if (smp_processor_id() == 0)
+		cpu_power_off(NULL);
+	preempt_disable();
+	while (1)
+		;
+	return IRQ_HANDLED;
+}
+
 void arch_reset(char mode, const char *cmd)
 {
 
@@ -179,6 +220,8 @@
 
 static int __init msm_restart_init(void)
 {
+	int rc;
+
 #ifdef CONFIG_MSM_DLOAD_MODE
 	atomic_notifier_chain_register(&panic_notifier_list, &panic_blk);
 	dload_mode_addr = MSM_IMEM_BASE + DLOAD_MODE_ADDR;
@@ -189,6 +232,16 @@
 	restart_reason = MSM_IMEM_BASE + RESTART_REASON_ADDR;
 	pm_power_off = msm_power_off;
 
+	if (pmic_reset_irq != 0) {
+		rc = request_any_context_irq(pmic_reset_irq,
+					resout_irq_handler, IRQF_TRIGGER_HIGH,
+					"restart_from_pmic", NULL);
+		if (rc < 0)
+			pr_err("pmic restart irq fail rc = %d\n", rc);
+	} else {
+		pr_warn("no pmic restart interrupt specified\n");
+	}
+
 	return 0;
 }