msm: rpm: panic() on an rpm error interrupt

If the RPM triggers the LOW interrupt it means the RPM has error
fataled. Handle this case so we don't sit spinning on the RPM to
respond until our watchdog times out. We also check this while
polling the ack interrupt in msm_rpm_busy_wait_for_request_completion().

This should allow us to have a nice panic output in the logs
whenever the RPM has crashed and is no longer responsive.

Change-Id: Ia40b95eaffe5bbaa10b62f85ae849eb36b1384e6
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
diff --git a/arch/arm/mach-msm/devices-8064.c b/arch/arm/mach-msm/devices-8064.c
index b660906..3632d80 100644
--- a/arch/arm/mach-msm/devices-8064.c
+++ b/arch/arm/mach-msm/devices-8064.c
@@ -1671,6 +1671,7 @@
 		[MSM_RPM_PAGE_ACK] = MSM_RPM_BASE + 0xa00,
 	},
 	.irq_ack = RPM_APCC_CPU0_GP_HIGH_IRQ,
+	.irq_err = RPM_APCC_CPU0_GP_LOW_IRQ,
 	.ipc_rpm_reg = MSM_APCS_GCC_BASE + 0x008,
 	.ipc_rpm_val = 4,
 	.target_id =  {
diff --git a/arch/arm/mach-msm/devices-8930.c b/arch/arm/mach-msm/devices-8930.c
index 0ad25c0..81c52d1 100644
--- a/arch/arm/mach-msm/devices-8930.c
+++ b/arch/arm/mach-msm/devices-8930.c
@@ -33,6 +33,7 @@
 		[MSM_RPM_PAGE_ACK] = MSM_RPM_BASE + 0xa00,
 	},
 	.irq_ack = RPM_APCC_CPU0_GP_HIGH_IRQ,
+	.irq_err = RPM_APCC_CPU0_GP_LOW_IRQ,
 	.ipc_rpm_reg = MSM_APCS_GCC_BASE + 0x008,
 	.ipc_rpm_val = 4,
 	.target_id = {
diff --git a/arch/arm/mach-msm/devices-8960.c b/arch/arm/mach-msm/devices-8960.c
index 11f8e1f0..f4af677 100644
--- a/arch/arm/mach-msm/devices-8960.c
+++ b/arch/arm/mach-msm/devices-8960.c
@@ -2805,6 +2805,7 @@
 		[MSM_RPM_PAGE_ACK] = MSM_RPM_BASE + 0xa00,
 	},
 	.irq_ack = RPM_APCC_CPU0_GP_HIGH_IRQ,
+	.irq_err = RPM_APCC_CPU0_GP_LOW_IRQ,
 	.ipc_rpm_reg = MSM_APCS_GCC_BASE + 0x008,
 	.ipc_rpm_val = 4,
 	.target_id = {
diff --git a/arch/arm/mach-msm/devices-9615.c b/arch/arm/mach-msm/devices-9615.c
index 0b204a0..28dceb0 100644
--- a/arch/arm/mach-msm/devices-9615.c
+++ b/arch/arm/mach-msm/devices-9615.c
@@ -772,6 +772,7 @@
 		[MSM_RPM_PAGE_ACK] = MSM_RPM_BASE + 0xa00,
 	},
 	.irq_ack = RPM_APCC_CPU0_GP_HIGH_IRQ,
+	.irq_err = RPM_APCC_CPU0_GP_LOW_IRQ,
 	.ipc_rpm_reg = MSM_APCS_GCC_BASE + 0x008,
 	.ipc_rpm_val = 4,
 	.target_id = {
diff --git a/arch/arm/mach-msm/devices-msm8x60.c b/arch/arm/mach-msm/devices-msm8x60.c
index 4670ce8..ae90301 100644
--- a/arch/arm/mach-msm/devices-msm8x60.c
+++ b/arch/arm/mach-msm/devices-msm8x60.c
@@ -2466,6 +2466,7 @@
 		[MSM_RPM_PAGE_ACK] = MSM_RPM_BASE + 0xa00,
 	},
 	.irq_ack = RPM_SCSS_CPU0_GP_HIGH_IRQ,
+	.irq_err = RPM_SCSS_CPU0_GP_LOW_IRQ,
 	.ipc_rpm_reg = MSM_GCC_BASE + 0x008,
 	.ipc_rpm_val = 4,
 	.target_id = {
diff --git a/arch/arm/mach-msm/include/mach/rpm.h b/arch/arm/mach-msm/include/mach/rpm.h
index 95a2ccf..140b7c9 100644
--- a/arch/arm/mach-msm/include/mach/rpm.h
+++ b/arch/arm/mach-msm/include/mach/rpm.h
@@ -882,6 +882,7 @@
 struct msm_rpm_platform_data {
 	void __iomem *reg_base_addrs[MSM_RPM_PAGE_COUNT];
 	unsigned int irq_ack;
+	unsigned int irq_err;
 	void *ipc_rpm_reg;
 	unsigned int ipc_rpm_val;
 	struct msm_rpm_map_data target_id[MSM_RPM_ID_LAST];
diff --git a/arch/arm/mach-msm/rpm.c b/arch/arm/mach-msm/rpm.c
index 05b2953..2eac3c1 100644
--- a/arch/arm/mach-msm/rpm.c
+++ b/arch/arm/mach-msm/rpm.c
@@ -239,6 +239,19 @@
 	return 2;
 }
 
+static void msm_rpm_err_fatal(void)
+{
+	/* Tell RPM that we're handling the interrupt */
+	__raw_writel(0x1, msm_rpm_data.ipc_rpm_reg);
+	panic("RPM error fataled");
+}
+
+static irqreturn_t msm_rpm_err_interrupt(int irq, void *dev_id)
+{
+	msm_rpm_err_fatal();
+	return IRQ_HANDLED;
+}
+
 static irqreturn_t msm_rpm_ack_interrupt(int irq, void *dev_id)
 {
 	unsigned long flags;
@@ -267,6 +280,9 @@
 				msm_rpm_request) {
 			if (allow_async_completion)
 				spin_unlock(&msm_rpm_irq_lock);
+			if (gic_is_spi_pending(msm_rpm_data.irq_err))
+				msm_rpm_err_fatal();
+			gic_clear_spi_pending(msm_rpm_data.irq_err);
 			udelay(1);
 			if (allow_async_completion)
 				spin_lock(&msm_rpm_irq_lock);
@@ -355,7 +371,7 @@
 	uint32_t ctx_mask = msm_rpm_get_ctx_mask(ctx);
 	uint32_t ctx_mask_ack = 0;
 	uint32_t sel_masks_ack[SEL_MASK_SIZE];
-	struct irq_chip *irq_chip = NULL;
+	struct irq_chip *irq_chip, *err_chip;
 	int i;
 
 	msm_rpm_request_poll_mode.req = req;
@@ -371,6 +387,13 @@
 		return -ENOSPC;
 	}
 	irq_chip->irq_mask(irq_get_irq_data(irq));
+	err_chip = irq_get_chip(msm_rpm_data.irq_err);
+	if (!err_chip) {
+		irq_chip->irq_unmask(irq_get_irq_data(irq));
+		spin_unlock_irqrestore(&msm_rpm_irq_lock, flags);
+		return -ENOSPC;
+	}
+	err_chip->irq_mask(irq_get_irq_data(msm_rpm_data.irq_err));
 
 	if (msm_rpm_request) {
 		msm_rpm_busy_wait_for_request_completion(true);
@@ -398,6 +421,7 @@
 	msm_rpm_busy_wait_for_request_completion(false);
 	BUG_ON(msm_rpm_request);
 
+	err_chip->irq_unmask(irq_get_irq_data(msm_rpm_data.irq_err));
 	irq_chip->irq_unmask(irq_get_irq_data(irq));
 	spin_unlock_irqrestore(&msm_rpm_irq_lock, flags);
 
@@ -983,6 +1007,14 @@
 		return rc;
 	}
 
+	rc = request_irq(data->irq_err, msm_rpm_err_interrupt,
+			IRQF_TRIGGER_RISING, "rpm_err", NULL);
+	if (rc) {
+		pr_err("%s: failed to request error interrupt: %d\n",
+			__func__, rc);
+		return rc;
+	}
+
 	msm_rpm_populate_map(data);
 
 	return platform_driver_register(&msm_rpm_platform_driver);