msm: kgsl: refcount irq to avoid racing against idle check

Current irq handler clears the pending interrupt bits in interrupt
status register before serving the interrupts. This leads to a race
condition with the idle check which checks the interrupt status
register to determine whether any interrupt is pending or not. As
the interrupt status register is already cleared, idle check goes
ahead and switch off the GPU clocks even when irq is yet to be served
causing NOC errors.

This change refcounts each irq handler call and uses this reference
count to determine if any irq is still pending or not along with
interrupt status register to avoid this race condition.

Change-Id: I030d52c52055f836ea4c7519ce2d8db94a2a09a0
Signed-off-by: Deepak Kumar <dkumar@codeaurora.org>
Signed-off-by: Lynus Vaz <lvaz@codeaurora.org>
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index 2480187..96b01e9 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -599,6 +599,10 @@
 	unsigned int status = 0, tmp, int_bit;
 	int i;
 
+	atomic_inc(&adreno_dev->pending_irq_refcnt);
+	/* Ensure this increment is done before the IRQ status is updated */
+	smp_mb__after_atomic();
+
 	adreno_readreg(adreno_dev, ADRENO_REG_RBBM_INT_0_STATUS, &status);
 
 	/*
@@ -637,6 +641,12 @@
 		adreno_writereg(adreno_dev, ADRENO_REG_RBBM_INT_CLEAR_CMD,
 				int_bit);
 
+	/* Make sure the regwrites are done before the decrement */
+	smp_mb__before_atomic();
+	atomic_dec(&adreno_dev->pending_irq_refcnt);
+	/* Ensure other CPUs see the decrement */
+	smp_mb__after_atomic();
+
 	return ret;
 
 }
@@ -2086,7 +2096,18 @@
 
 	adreno_readreg(adreno_dev, ADRENO_REG_RBBM_INT_0_STATUS, &status);
 
-	return (status & gpudev->irq->mask) ? 1 : 0;
+	/*
+	 * IRQ handler clears the RBBM INT0 status register immediately
+	 * entering the ISR before actually serving the interrupt because
+	 * of this we can't rely only on RBBM INT0 status only.
+	 * Use pending_irq_refcnt along with RBBM INT0 to correctly
+	 * determine whether any IRQ is pending or not.
+	 */
+	if ((status & gpudev->irq->mask) ||
+		atomic_read(&adreno_dev->pending_irq_refcnt))
+		return 1;
+	else
+		return 0;
 }
 
 
diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h
index 48ddb96..fe4f56d 100644
--- a/drivers/gpu/msm/adreno.h
+++ b/drivers/gpu/msm/adreno.h
@@ -375,6 +375,7 @@
  * @ram_cycles_lo: Number of DDR clock cycles for the monitor session
  * @perfctr_pwr_lo: Number of cycles VBIF is stalled by DDR
  * @halt: Atomic variable to check whether the GPU is currently halted
+ * @pending_irq_refcnt: Atomic variable to keep track of running IRQ handlers
  * @ctx_d_debugfs: Context debugfs node
  * @pwrctrl_flag: Flag to hold adreno specific power attributes
  * @profile_buffer: Memdesc holding the drawobj profiling buffer
@@ -425,6 +426,7 @@
 	unsigned int starved_ram_lo;
 	unsigned int perfctr_pwr_lo;
 	atomic_t halt;
+	atomic_t pending_irq_refcnt;
 	struct dentry *ctx_d_debugfs;
 	unsigned long pwrctrl_flag;