diff mbox

omap: prcm: switch to a chained IRQ handler mechanism

Message ID 1289996179-17698-1-git-send-email-thomas.petazzoni@free-electrons.com (mailing list archive)
State New, archived
Delegated to: Paul Walmsley
Headers show

Commit Message

Thomas Petazzoni Nov. 17, 2010, 12:16 p.m. UTC
None
diff mbox

Patch

diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c
index d068348..77a9a49 100644
--- a/arch/arm/mach-omap2/pm34xx.c
+++ b/arch/arm/mach-omap2/pm34xx.c
@@ -28,6 +28,7 @@ 
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
+#include <linux/irq.h>
 
 #include <plat/sram.h>
 #include <plat/clockdomain.h>
@@ -243,7 +244,7 @@  static int prcm_clear_mod_irqs(s16 module, u8 regs)
 	return c;
 }
 
-static int _prcm_int_handle_wakeup(void)
+static irqreturn_t _prcm_int_handle_wakeup(int irq, void *unused)
 {
 	int c;
 
@@ -255,64 +256,10 @@  static int _prcm_int_handle_wakeup(void)
 		c += prcm_clear_mod_irqs(OMAP3430ES2_USBHOST_MOD, 1);
 	}
 
-	return c;
-}
-
-/*
- * PRCM Interrupt Handler
- *
- * The PRM_IRQSTATUS_MPU register indicates if there are any pending
- * interrupts from the PRCM for the MPU. These bits must be cleared in
- * order to clear the PRCM interrupt. The PRCM interrupt handler is
- * implemented to simply clear the PRM_IRQSTATUS_MPU in order to clear
- * the PRCM interrupt. Please note that bit 0 of the PRM_IRQSTATUS_MPU
- * register indicates that a wake-up event is pending for the MPU and
- * this bit can only be cleared if the all the wake-up events latched
- * in the various PM_WKST_x registers have been cleared. The interrupt
- * handler is implemented using a do-while loop so that if a wake-up
- * event occurred during the processing of the prcm interrupt handler
- * (setting a bit in the corresponding PM_WKST_x register and thus
- * preventing us from clearing bit 0 of the PRM_IRQSTATUS_MPU register)
- * this would be handled.
- */
-static irqreturn_t prcm_interrupt_handler (int irq, void *dev_id)
-{
-	u32 irqenable_mpu, irqstatus_mpu;
-	int c = 0;
-
-	irqenable_mpu = prm_read_mod_reg(OCP_MOD,
-					 OMAP3_PRM_IRQENABLE_MPU_OFFSET);
-	irqstatus_mpu = prm_read_mod_reg(OCP_MOD,
-					 OMAP3_PRM_IRQSTATUS_MPU_OFFSET);
-	irqstatus_mpu &= irqenable_mpu;
-
-	do {
-		if (irqstatus_mpu & (OMAP3430_WKUP_ST_MASK |
-				     OMAP3430_IO_ST_MASK)) {
-			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,
-					OMAP3_PRM_IRQSTATUS_MPU_OFFSET);
-
-		irqstatus_mpu = prm_read_mod_reg(OCP_MOD,
-					OMAP3_PRM_IRQSTATUS_MPU_OFFSET);
-		irqstatus_mpu &= irqenable_mpu;
-
-	} while (irqstatus_mpu);
-
-	return IRQ_HANDLED;
+	if (c)
+		return IRQ_HANDLED;
+	else
+		return IRQ_NONE;
 }
 
 static void restore_control_register(u32 val)
@@ -998,11 +945,104 @@  void omap_push_sram_idle(void)
 				save_secure_ram_context_sz);
 }
 
+static void prcm_irq_ack(unsigned irq)
+{
+	int prcm_irq = irq - OMAP_PRCM_IRQ_BASE;
+
+	prm_write_mod_reg((1 << prcm_irq), OCP_MOD,
+			  OMAP3_PRM_IRQSTATUS_MPU_OFFSET);
+}
+
+static void prcm_irq_mask(unsigned irq)
+{
+	int prcm_irq = irq - OMAP_PRCM_IRQ_BASE;
+
+	prm_rmw_mod_reg_bits((1 << prcm_irq), 0, OCP_MOD,
+			     OMAP3_PRM_IRQENABLE_MPU_OFFSET);
+}
+
+static void prcm_irq_unmask(unsigned irq)
+{
+	int prcm_irq = irq - OMAP_PRCM_IRQ_BASE;
+
+	prm_rmw_mod_reg_bits(0, (1 << prcm_irq), OCP_MOD,
+			     OMAP3_PRM_IRQENABLE_MPU_OFFSET);
+}
+
+static struct irq_chip prcm_irq_chip = {
+	.name     = "PRCM",
+	.ack      = prcm_irq_ack,
+	.mask     = prcm_irq_mask,
+	.unmask   = prcm_irq_unmask,
+};
+
+static u32 prcm_irq_pending(void)
+{
+	u32 irqenable_mpu, irqstatus_mpu;
+
+	irqenable_mpu = prm_read_mod_reg(OCP_MOD,
+					 OMAP3_PRM_IRQENABLE_MPU_OFFSET);
+	irqstatus_mpu = prm_read_mod_reg(OCP_MOD,
+					 OMAP3_PRM_IRQSTATUS_MPU_OFFSET);
+	return irqstatus_mpu & irqenable_mpu;
+}
+
+static void prcm_irq_handle_virtirqs(u32 pending)
+{
+	int virtirq;
+
+	/* Loop on all currently pending irqs so that new irqs cannot
+	 * starve previously pending irqs
+	 */
+	for (virtirq = 0; virtirq < 32; virtirq++)
+		if (pending & (1 << virtirq))
+			generic_handle_irq(OMAP_PRCM_IRQ_BASE + virtirq);
+}
+
+/*
+ * PRCM Interrupt Handler
+ *
+ * The PRM_IRQSTATUS_MPU register indicates if there are any pending
+ * interrupts from the PRCM for the MPU. These bits must be cleared in
+ * order to clear the PRCM interrupt. The PRCM interrupt handler is
+ * implemented to simply clear the PRM_IRQSTATUS_MPU in order to clear
+ * the PRCM interrupt. Please note that bit 0 of the PRM_IRQSTATUS_MPU
+ * register indicates that a wake-up event is pending for the MPU and
+ * this bit can only be cleared if the all the wake-up events latched
+ * in the various PM_WKST_x registers have been cleared. The interrupt
+ * handler is implemented using a do-while loop so that if a wake-up
+ * event occurred during the processing of the prcm interrupt handler
+ * (setting a bit in the corresponding PM_WKST_x register and thus
+ * preventing us from clearing bit 0 of the PRM_IRQSTATUS_MPU register)
+ * this would be handled.
+ */
+static void prcm_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+	u32 pending;
+
+	/* Loop until all pending irqs are handled, since
+	 * generic_handle_irq(), called by prcm_irq_handle_virtirqs()
+	 * can cause new irqs to come
+	 */
+	while (1) {
+		desc->chip->ack(irq);
+
+		pending = prcm_irq_pending();
+		if (!pending) {
+			desc->chip->unmask(irq);
+			break;
+		}
+
+		prcm_irq_handle_virtirqs(pending);
+		desc->chip->unmask(irq);
+	}
+}
+
 static int __init omap3_pm_init(void)
 {
 	struct power_state *pwrst, *tmp;
 	struct clockdomain *neon_clkdm, *per_clkdm, *mpu_clkdm, *core_clkdm;
-	int ret;
+	int ret, i;
 
 	if (!cpu_is_omap34xx())
 		return -ENODEV;
@@ -1013,19 +1053,34 @@  static int __init omap3_pm_init(void)
 	 * supervised mode for powerdomains */
 	prcm_setup_regs();
 
-	ret = request_irq(INT_34XX_PRCM_MPU_IRQ,
-			  (irq_handler_t)prcm_interrupt_handler,
-			  IRQF_DISABLED, "prcm", NULL);
+	for (i = OMAP_PRCM_IRQ_BASE; i < OMAP_PRCM_IRQ_END; i++) {
+		set_irq_chip(i, &prcm_irq_chip);
+		set_irq_handler(i, handle_level_irq);
+		set_irq_flags(i, IRQF_VALID);
+	}
+
+	set_irq_chained_handler(INT_34XX_PRCM_MPU_IRQ, prcm_irq_handler);
+
+	ret = request_irq(INT_34XX_PRCM_WKUP_EN, _prcm_int_handle_wakeup,
+			  IRQF_NO_SUSPEND, "prcm_wkup", NULL);
 	if (ret) {
 		printk(KERN_ERR "request_irq failed to register for 0x%x\n",
-		       INT_34XX_PRCM_MPU_IRQ);
+		       INT_34XX_PRCM_WKUP_EN);
 		goto err1;
 	}
 
+	ret = request_irq(INT_34XX_PRCM_IO_EN, _prcm_int_handle_wakeup,
+			  IRQF_NO_SUSPEND, "prcm_io", NULL);
+	if (ret) {
+		printk(KERN_ERR "request_irq failed to register for 0x%x\n",
+		       INT_34XX_PRCM_IO_EN);
+		goto err2;
+	}
+
 	ret = pwrdm_for_each(pwrdms_setup, NULL);
 	if (ret) {
 		printk(KERN_ERR "Failed to setup powerdomains\n");
-		goto err2;
+		goto err3;
 	}
 
 	(void) clkdm_for_each(clkdms_setup, NULL);
@@ -1033,7 +1088,7 @@  static int __init omap3_pm_init(void)
 	mpu_pwrdm = pwrdm_lookup("mpu_pwrdm");
 	if (mpu_pwrdm == NULL) {
 		printk(KERN_ERR "Failed to get mpu_pwrdm\n");
-		goto err2;
+		goto err3;
 	}
 
 	neon_pwrdm = pwrdm_lookup("neon_pwrdm");
@@ -1080,7 +1135,9 @@  static int __init omap3_pm_init(void)
 err1:
 	return ret;
 err2:
-	free_irq(INT_34XX_PRCM_MPU_IRQ, NULL);
+	free_irq(INT_34XX_PRCM_WKUP_EN, NULL);
+err3:
+	free_irq(INT_34XX_PRCM_IO_EN, NULL);
 	list_for_each_entry_safe(pwrst, tmp, &pwrst_list, node) {
 		list_del(&pwrst->node);
 		kfree(pwrst);
diff --git a/arch/arm/plat-omap/include/plat/irqs.h b/arch/arm/plat-omap/include/plat/irqs.h
index 65e20a6..6c5eb1c 100644
--- a/arch/arm/plat-omap/include/plat/irqs.h
+++ b/arch/arm/plat-omap/include/plat/irqs.h
@@ -363,7 +363,43 @@ 
 #define OMAP_MAX_GPIO_LINES	192
 #define IH_GPIO_BASE		(128 + IH2_BASE)
 #define IH_MPUIO_BASE		(OMAP_MAX_GPIO_LINES + IH_GPIO_BASE)
-#define OMAP_IRQ_END		(IH_MPUIO_BASE + 16)
+#define OMAP_MPUIO_IRQ_END	(IH_MPUIO_BASE + 16)
+
+/* 32 IRQs for the PRCM */
+#define OMAP_PRCM_IRQ_BASE                 (OMAP_MPUIO_IRQ_END)
+#define INT_34XX_PRCM_WKUP_EN              (OMAP_PRCM_IRQ_BASE +  0)
+#define INT_34XX_PRCM_EVGENON_EN           (OMAP_PRCM_IRQ_BASE +  2)
+#define INT_34XX_PRCM_EVGENOFF_EN          (OMAP_PRCM_IRQ_BASE +  3)
+#define INT_34XX_PRCM_TRANSITION_EN        (OMAP_PRCM_IRQ_BASE +  4)
+#define INT_34XX_PRCM_CORE_DPLL_RECAL_EN   (OMAP_PRCM_IRQ_BASE +  5)
+#define INT_34XX_PRCM_PERIPH_DPLL_RECAL_EN (OMAP_PRCM_IRQ_BASE +  6)
+#define INT_34XX_PRCM_MPU_DPLL_RECAL_EN_EN (OMAP_PRCM_IRQ_BASE +  7)
+#define INT_34XX_PRCM_IVA2_DPLL_RECAL_EN   (OMAP_PRCM_IRQ_BASE +  8)
+#define INT_34XX_PRCM_IO_EN                (OMAP_PRCM_IRQ_BASE +  9)
+#define INT_34XX_PRCM_VP1_OPPCHANGEDONE_EN (OMAP_PRCM_IRQ_BASE + 10)
+#define INT_34XX_PRCM_VP1_MINVDD_EN        (OMAP_PRCM_IRQ_BASE + 11)
+#define INT_34XX_PRCM_VP1_MAXVDD_EN        (OMAP_PRCM_IRQ_BASE + 12)
+#define INT_34XX_PRCM_VP1_NOSMPSACK_EN     (OMAP_PRCM_IRQ_BASE + 13)
+#define INT_34XX_PRCM_VP1_EQVALUE_EN       (OMAP_PRCM_IRQ_BASE + 14)
+#define INT_34XX_PRCM_VP1_TRANXDONE_EN     (OMAP_PRCM_IRQ_BASE + 15)
+#define INT_34XX_PRCM_VP2_OPPCHANGEDONE_EN (OMAP_PRCM_IRQ_BASE + 16)
+#define INT_34XX_PRCM_VP2_MINVDD_EN        (OMAP_PRCM_IRQ_BASE + 17)
+#define INT_34XX_PRCM_VP2_MAXVDD_EN        (OMAP_PRCM_IRQ_BASE + 18)
+#define INT_34XX_PRCM_VP2_NOSMPSACK_EN     (OMAP_PRCM_IRQ_BASE + 19)
+#define INT_34XX_PRCM_VP2_EQVALUE_EN       (OMAP_PRCM_IRQ_BASE + 20)
+#define INT_34XX_PRCM_VP2_TRANXDONE_EN     (OMAP_PRCM_IRQ_BASE + 21)
+#define INT_34XX_PRCM_VC_SAERR_EN          (OMAP_PRCM_IRQ_BASE + 22)
+#define INT_34XX_PRCM_VC_RAERR_EN          (OMAP_PRCM_IRQ_BASE + 23)
+#define INT_34XX_PRCM_VC_TIMEOUT_ERR_EN    (OMAP_PRCM_IRQ_BASE + 24)
+#define INT_34XX_PRCM_SND_PERIPH_RECAL_EN  (OMAP_PRCM_IRQ_BASE + 25)
+#define INT_36XX_PRCM_ABB_LDO_TRANXDONE_EN (OMAP_PRCM_IRQ_BASE + 26)
+#define INT_36XX_PRCM_VC_VP1_ACK_EN        (OMAP_PRCM_IRQ_BASE + 27)
+#define INT_36XX_PRCM_VC_BYPASS_ACK_EN     (OMAP_PRCM_IRQ_BASE + 28)
+#define OMAP_PRCM_NR_IRQS                  32
+#define OMAP_PRCM_IRQ_END                  (OMAP_PRCM_IRQ_BASE + \
+					    OMAP_PRCM_NR_IRQS)
+
+#define OMAP_IRQ_END                       (OMAP_PRCM_IRQ_END)
 
 /* External FPGA handles interrupts on Innovator boards */
 #define	OMAP_FPGA_IRQ_BASE	(OMAP_IRQ_END)