ARM: OMAP: PRCM: add suspend prepare / finish support

PRCM chain handler needs to disable forwarding of interrupts during
suspend, because runtime PM is disabled and most of the drivers
are potentially not able to handle interrupts coming at this time.

This patch masks all the PRCM interrupt events if a PRCM interrupt
occurs during suspend, but does not ack them. Once suspend finish
is called, all the masked events will be re-enabled, which causes
immediate PRCM interrupt and handles the postponed event.

The suspend prepare and complete  callbacks will be called from
pm34xx.c / pm44xx.c files in the following patches.

The functions defined in this patch should eventually be moved to
suspend->prepare and suspend->finish driver hooks, once the PRCM
chain handler will be made as its own driver.

Signed-off-by: Tero Kristo <t-kristo@ti.com>
Cc: Kevin Hilman <khilman@ti.com>
Cc: Paul Walmsley <paul@pwsan.com>
Tested-by: Kevin Hilman <khilman@ti.com>
Reviewed-by: Kevin Hilman <khilman@ti.com>
[paul@pwsan.com: add kerneldoc, add omap_prcm_irq_setup.saved_mask, add fn
 ptrs for save_and_clear_irqen() and restore_irqen()]
Signed-off-by: Paul Walmsley <paul@pwsan.com>
diff --git a/arch/arm/mach-omap2/prm_common.c b/arch/arm/mach-omap2/prm_common.c
index 5694be5..860118a 100644
--- a/arch/arm/mach-omap2/prm_common.c
+++ b/arch/arm/mach-omap2/prm_common.c
@@ -89,10 +89,25 @@
 	int nr_irqs = prcm_irq_setup->nr_regs * 32;
 
 	/*
+	 * If we are suspended, mask all interrupts from PRCM level,
+	 * this does not ack them, and they will be pending until we
+	 * re-enable the interrupts, at which point the
+	 * omap_prcm_irq_handler will be executed again.  The
+	 * _save_and_clear_irqen() function must ensure that the PRM
+	 * write to disable all IRQs has reached the PRM before
+	 * returning, or spurious PRCM interrupts may occur during
+	 * suspend.
+	 */
+	if (prcm_irq_setup->suspended) {
+		prcm_irq_setup->save_and_clear_irqen(prcm_irq_setup->saved_mask);
+		prcm_irq_setup->suspend_save_flag = true;
+	}
+
+	/*
 	 * Loop until all pending irqs are handled, since
 	 * generic_handle_irq() can cause new irqs to come
 	 */
-	while (1) {
+	while (!prcm_irq_setup->suspended) {
 		prcm_irq_setup->read_pending_irqs(pending);
 
 		/* No bit set, then all IRQs are handled */
@@ -174,6 +189,9 @@
 		prcm_irq_chips = NULL;
 	}
 
+	kfree(prcm_irq_setup->saved_mask);
+	prcm_irq_setup->saved_mask = NULL;
+
 	kfree(prcm_irq_setup->priority_mask);
 	prcm_irq_setup->priority_mask = NULL;
 
@@ -185,6 +203,29 @@
 	prcm_irq_setup->base_irq = 0;
 }
 
+void omap_prcm_irq_prepare(void)
+{
+	prcm_irq_setup->suspended = true;
+}
+
+void omap_prcm_irq_complete(void)
+{
+	prcm_irq_setup->suspended = false;
+
+	/* If we have not saved the masks, do not attempt to restore */
+	if (!prcm_irq_setup->suspend_save_flag)
+		return;
+
+	prcm_irq_setup->suspend_save_flag = false;
+
+	/*
+	 * Re-enable all masked PRCM irq sources, this causes the PRCM
+	 * interrupt to fire immediately if the events were masked
+	 * previously in the chain handler
+	 */
+	prcm_irq_setup->restore_irqen(prcm_irq_setup->saved_mask);
+}
+
 /**
  * omap_prcm_register_chain_handler - initializes the prcm chained interrupt
  * handler based on provided parameters
@@ -219,10 +260,12 @@
 	prcm_irq_setup = irq_setup;
 
 	prcm_irq_chips = kzalloc(sizeof(void *) * nr_regs, GFP_KERNEL);
+	prcm_irq_setup->saved_mask = kzalloc(sizeof(u32) * nr_regs, GFP_KERNEL);
 	prcm_irq_setup->priority_mask = kzalloc(sizeof(u32) * nr_regs,
 		GFP_KERNEL);
 
-	if (!prcm_irq_chips || !prcm_irq_setup->priority_mask) {
+	if (!prcm_irq_chips || !prcm_irq_setup->saved_mask ||
+	    !prcm_irq_setup->priority_mask) {
 		pr_err("PRCM: kzalloc failed\n");
 		goto err;
 	}