From 114168fca85ba38421c84a7163ea4ae98b452577 Mon Sep 17 00:00:00 2001
From: Paul Walmsley <paul@pwsan.com>
Date: Mon, 20 Apr 2009 17:49:04 -0600
Subject: [PATCH] OMAP3 PM: improve PRCM interrupt handler

This test patch fixes a few obvious bugs in the OMAP3xxx PRCM
interrupt handler.

Clearing wakeup sources is now only done when the PRM indicates a
wakeup source interrupt.  Since we don't handle any other types of
PRCM interrupts right now, warn if we get any other type of PRCM
interrupt.  Either code needs to be added to the PRCM interrupt
handler to react to these, or these other interrupts should be masked
off at init.

Also, PM_WKST register contents should be ANDed with the contents of
the MPUGRPSEL registers.  Otherwise the MPU PRCM interrupt handler
could wind up clearing wakeup events meant for the IVA PRCM interrupt
handler.  For a production version of this patch, we should not read
MPUGRPSEL from the PRM, since those reads are very slow; rather, we
should use a cached version from struct powerdomain (not yet
implemented)

Signed-off-by: Kevin Hilman <khilman@deeprootsystems.com>
---
 arch/arm/mach-omap2/pm34xx.c |   78 +++++++++++++++++++++++++++++++++--------
 1 files changed, 62 insertions(+), 16 deletions(-)

diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c
index 7474cfa..7e78a3b 100644
--- a/arch/arm/mach-omap2/pm34xx.c
+++ b/arch/arm/mach-omap2/pm34xx.c
@@ -194,14 +194,18 @@ static void omap3_save_secure_ram_context(u32 target_mpu_state)
 	}
 }
 
-/* PRCM Interrupt Handler for wakeups */
-static irqreturn_t prcm_interrupt_handler (int irq, void *dev_id)
+static int _prcm_int_handle_wakeup(void)
 {
-	u32 wkst, irqstatus_mpu;
-	u32 fclk, iclk;
+	u32 wkst, fclk, iclk;
+	int c = 0;
+
+        /* FIXME: MPUGRPSEL reads beow should be done from an SDRAM
+	 *        local copy in the struct powerdomain since PRM reads
+	 *        are very slow. */
 
 	/* WKUP */
 	wkst = prm_read_mod_reg(WKUP_MOD, PM_WKST);
+	wkst &= prm_read_mod_reg(WKUP_MOD, OMAP3430_PM_MPUGRPSEL);
 	if (wkst) {
 		iclk = cm_read_mod_reg(WKUP_MOD, CM_ICLKEN);
 		fclk = cm_read_mod_reg(WKUP_MOD, CM_FCLKEN);
@@ -211,10 +215,12 @@ static irqreturn_t prcm_interrupt_handler (int irq, void *dev_id)
 		while (prm_read_mod_reg(WKUP_MOD, PM_WKST));
 		cm_write_mod_reg(iclk, WKUP_MOD, CM_ICLKEN);
 		cm_write_mod_reg(fclk, WKUP_MOD, CM_FCLKEN);
+		c++;
 	}
 
 	/* CORE */
 	wkst = prm_read_mod_reg(CORE_MOD, PM_WKST1);
+	wkst &= prm_read_mod_reg(CORE_MOD, OMAP3430_PM_MPUGRPSEL1);
 	if (wkst) {
 		iclk = cm_read_mod_reg(CORE_MOD, CM_ICLKEN1);
 		fclk = cm_read_mod_reg(CORE_MOD, CM_FCLKEN1);
@@ -224,21 +230,32 @@ static irqreturn_t prcm_interrupt_handler (int irq, void *dev_id)
 		while (prm_read_mod_reg(CORE_MOD, PM_WKST1));
 		cm_write_mod_reg(iclk, CORE_MOD, CM_ICLKEN1);
 		cm_write_mod_reg(fclk, CORE_MOD, CM_FCLKEN1);
+		c++;
 	}
-	wkst = prm_read_mod_reg(CORE_MOD, OMAP3430ES2_PM_WKST3);
-	if (wkst) {
-		iclk = cm_read_mod_reg(CORE_MOD, CM_ICLKEN3);
-		fclk = cm_read_mod_reg(CORE_MOD, OMAP3430ES2_CM_FCLKEN3);
-		cm_set_mod_reg_bits(wkst, CORE_MOD, CM_ICLKEN3);
-		cm_set_mod_reg_bits(wkst, CORE_MOD, OMAP3430ES2_CM_FCLKEN3);
-		prm_write_mod_reg(wkst, CORE_MOD, OMAP3430ES2_PM_WKST3);
-		while (prm_read_mod_reg(CORE_MOD, OMAP3430ES2_PM_WKST3));
-		cm_write_mod_reg(iclk, CORE_MOD, CM_ICLKEN3);
-		cm_write_mod_reg(fclk, CORE_MOD, OMAP3430ES2_CM_FCLKEN3);
+	if (omap_rev() > OMAP3430_REV_ES1_0) {
+		wkst = prm_read_mod_reg(CORE_MOD, OMAP3430ES2_PM_WKST3);
+		wkst &= prm_read_mod_reg(CORE_MOD, OMAP3430ES2_PM_MPUGRPSEL3);
+		if (wkst) {
+			iclk = cm_read_mod_reg(CORE_MOD, CM_ICLKEN3);
+			fclk = cm_read_mod_reg(CORE_MOD,
+					       OMAP3430ES2_CM_FCLKEN3);
+			cm_set_mod_reg_bits(wkst, CORE_MOD, CM_ICLKEN3);
+			cm_set_mod_reg_bits(wkst, CORE_MOD,
+					    OMAP3430ES2_CM_FCLKEN3);
+			prm_write_mod_reg(wkst, CORE_MOD,
+					  OMAP3430ES2_PM_WKST3);
+			while (prm_read_mod_reg(CORE_MOD,
+						OMAP3430ES2_PM_WKST3));
+			cm_write_mod_reg(iclk, CORE_MOD, CM_ICLKEN3);
+			cm_write_mod_reg(fclk, CORE_MOD,
+					 OMAP3430ES2_CM_FCLKEN3);
+			c++;
+		}
 	}
 
 	/* PER */
 	wkst = prm_read_mod_reg(OMAP3430_PER_MOD, PM_WKST);
+	wkst &= prm_read_mod_reg(OMAP3430_PER_MOD, OMAP3430_PM_MPUGRPSEL);
 	if (wkst) {
 		iclk = cm_read_mod_reg(OMAP3430_PER_MOD, CM_ICLKEN);
 		fclk = cm_read_mod_reg(OMAP3430_PER_MOD, CM_FCLKEN);
@@ -248,11 +265,14 @@ static irqreturn_t prcm_interrupt_handler (int irq, void *dev_id)
 		while (prm_read_mod_reg(OMAP3430_PER_MOD, PM_WKST));
 		cm_write_mod_reg(iclk, OMAP3430_PER_MOD, CM_ICLKEN);
 		cm_write_mod_reg(fclk, OMAP3430_PER_MOD, CM_FCLKEN);
+		c++;
 	}
 
 	if (omap_rev() > OMAP3430_REV_ES1_0) {
 		/* USBHOST */
 		wkst = prm_read_mod_reg(OMAP3430ES2_USBHOST_MOD, PM_WKST);
+		wkst &= prm_read_mod_reg(OMAP3430ES2_USBHOST_MOD,
+					 OMAP3430_PM_MPUGRPSEL);
 		if (wkst) {
 			iclk = cm_read_mod_reg(OMAP3430ES2_USBHOST_MOD,
 					       CM_ICLKEN);
@@ -271,12 +291,38 @@ static irqreturn_t prcm_interrupt_handler (int irq, void *dev_id)
 			cm_write_mod_reg(fclk, OMAP3430ES2_USBHOST_MOD,
 					 CM_FCLKEN);
 		}
+		c++;
 	}
 
+	return c;
+}
+
+/* PRCM Interrupt Handler for wakeups */
+static irqreturn_t prcm_interrupt_handler (int irq, void *dev_id)
+{
+	u32 irqstatus_mpu;
+	int c;
+
 	irqstatus_mpu = prm_read_mod_reg(OCP_MOD,
 					OMAP2_PRM_IRQSTATUS_MPU_OFFSET);
+
+	if (irqstatus_mpu & OMAP3430_WKUP_ST) {
+		c = _prcm_int_handle_wakeup();
+
+		/*
+		 * Is the MPU PRCM interrupt handler racing with the
+		 * IVA2 PRCM interrupt handler ?
+		 */
+		WARN(c == 0, "prcm: WARNING: PRCM indicated MPU wakeup "
+		     "but no wakeup sources are marked\n");
+	} else {
+		/* XXX we need to expand our PRCM interrupt handler */
+		WARN(1, "prcm: WARNING: PRCM interrupt received, but no "
+		     "code to handle it (%08x)\n", irqstatus_mpu);
+	}
+
 	prm_write_mod_reg(irqstatus_mpu, OCP_MOD,
-					OMAP2_PRM_IRQSTATUS_MPU_OFFSET);
+			  OMAP2_PRM_IRQSTATUS_MPU_OFFSET);
 
 	while (prm_read_mod_reg(OCP_MOD, OMAP2_PRM_IRQSTATUS_MPU_OFFSET));
 
@@ -969,7 +1015,7 @@ int __init omap3_pm_init(void)
 	 * supervised mode for powerdomains */
 	prcm_setup_regs();
 
-	ret = request_irq(INT_34XX_PRCM_MPU_IRQ,
+	ret = request_irq(INT_34XX_PRCM_MPU_IRQ,	
 			  (irq_handler_t)prcm_interrupt_handler,
 			  IRQF_DISABLED, "prcm", NULL);
 	if (ret) {
-- 
1.6.2.2

