misc: tspp: Enable notification of TSIF status and expose it in debugfs

Enable TSIF status interrupt to expose the following
information in debugfs:
- stat_rx_chunks: Counts number of TS packets chunks received from HW.
- stat_overflow: Counts number of times buffer has overflowed.
- stat_lost_sync: Counts number of times TSIF lost sync with input.
- stat_timeout: Counts number of times TSIF reached timeout
waiting for packets.

All counters can read and reset by writing to the respective file.

Change-Id: I42e71d072d8b92bdc8ecf941daef4ac0faa94de6
Signed-off-by: Hamad Kadmany <hkadmany@codeaurora.org>
Signed-off-by: Neha Pandey <nehap@codeaurora.org>
diff --git a/drivers/misc/tspp.c b/drivers/misc/tspp.c
index 3b678c5..f310524 100644
--- a/drivers/misc/tspp.c
+++ b/drivers/misc/tspp.c
@@ -327,10 +327,15 @@
 	int data_inverse;
 	int sync_inverse;
 	int enable_inverse;
+	u32 tsif_irq;
 
 	/* debugfs */
 	struct dentry *dent_tsif;
 	struct dentry *debugfs_tsif_regs[ARRAY_SIZE(debugfs_tsif_regs)];
+	u32 stat_rx;
+	u32 stat_overflow;
+	u32 stat_lost_sync;
+	u32 stat_timeout;
 };
 
 enum tspp_buf_state {
@@ -480,6 +485,49 @@
 		dev_info(&device->pdev->dev, "broken pipe %i", status & 0xffff);
 
 	writel_relaxed(status, device->base + TSPP_IRQ_CLEAR);
+
+	/*
+	 * Before returning IRQ_HANDLED to the generic interrupt handling
+	 * framework need to make sure all operations including clearing of
+	 * interrupt status registers in the hardware is performed.
+	 * Thus a barrier after clearing the interrupt status register
+	 * is required to guarantee that the interrupt status register has
+	 * really been cleared by the time we return from this handler.
+	 */
+	wmb();
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t tsif_isr(int irq, void *dev)
+{
+	struct tspp_tsif_device *tsif_device = dev;
+	u32 sts_ctl = ioread32(tsif_device->base + TSIF_STS_CTL_OFF);
+
+	if (!(sts_ctl & (TSIF_STS_CTL_PACK_AVAIL |
+			 TSIF_STS_CTL_OVERFLOW |
+			 TSIF_STS_CTL_LOST_SYNC |
+			 TSIF_STS_CTL_TIMEOUT)))
+		return IRQ_NONE;
+
+	if (sts_ctl & TSIF_STS_CTL_OVERFLOW)
+		tsif_device->stat_overflow++;
+
+	if (sts_ctl & TSIF_STS_CTL_LOST_SYNC)
+		tsif_device->stat_lost_sync++;
+
+	if (sts_ctl & TSIF_STS_CTL_TIMEOUT)
+		tsif_device->stat_timeout++;
+
+	iowrite32(sts_ctl, tsif_device->base + TSIF_STS_CTL_OFF);
+
+	/*
+	 * Before returning IRQ_HANDLED to the generic interrupt handling
+	 * framework need to make sure all operations including clearing of
+	 * interrupt status registers in the hardware is performed.
+	 * Thus a barrier after clearing the interrupt status register
+	 * is required to guarantee that the interrupt status register has
+	 * really been cleared by the time we return from this handler.
+	 */
 	wmb();
 	return IRQ_HANDLED;
 }
@@ -527,6 +575,11 @@
 			channel->waiting->filled = iovec.size;
 			channel->waiting->read_index = 0;
 
+			if (channel->src == TSPP_SOURCE_TSIF0)
+				device->tsif[0].stat_rx++;
+			else if (channel->src == TSPP_SOURCE_TSIF1)
+				device->tsif[1].stat_rx++;
+
 			/* update the pointers */
 			channel->waiting = channel->waiting->next;
 		}
@@ -2326,6 +2379,31 @@
 				base + debugfs_tsif_regs[i].offset,
 				&fops_iomem_x32);
 		}
+
+		debugfs_create_u32(
+			"stat_rx_chunks",
+			S_IRUGO|S_IWUGO,
+			tsif_device->dent_tsif,
+			&tsif_device->stat_rx);
+
+		debugfs_create_u32(
+			"stat_overflow",
+			S_IRUGO|S_IWUGO,
+			tsif_device->dent_tsif,
+			&tsif_device->stat_overflow);
+
+		debugfs_create_u32(
+			"stat_lost_sync",
+			S_IRUGO|S_IWUGO,
+			tsif_device->dent_tsif,
+			&tsif_device->stat_lost_sync);
+
+		debugfs_create_u32(
+			"stat_timeout",
+			S_IRUGO|S_IWUGO,
+			tsif_device->dent_tsif,
+			&tsif_device->stat_timeout);
+
 	}
 }
 
@@ -2504,6 +2582,21 @@
 		goto err_irq;
 	}
 
+	/* map TSIF IRQs */
+	device->tsif[0].tsif_irq = TSIF1_IRQ;
+	device->tsif[1].tsif_irq = TSIF2_IRQ;
+
+	for (i = 0; i < TSPP_TSIF_INSTANCES; i++) {
+		rc = request_irq(device->tsif[i].tsif_irq,
+				tsif_isr, IRQF_SHARED,
+				dev_name(&pdev->dev), &device->tsif[i]);
+		if (rc) {
+			dev_warn(&pdev->dev, "failed to request TSIF%d IRQ: %d",
+				i, rc);
+			device->tsif[i].tsif_irq = 0;
+		}
+	}
+
 	/* BAM IRQ */
 	device->bam_irq = TSIF_BAM_IRQ;
 
@@ -2635,8 +2728,11 @@
 
 	sps_deregister_bam_device(device->bam_handle);
 
-	for (i = 0; i < TSPP_TSIF_INSTANCES; i++)
+	for (i = 0; i < TSPP_TSIF_INSTANCES; i++) {
 		tsif_debugfs_exit(&device->tsif[i]);
+		if (device->tsif[i].tsif_irq)
+			free_irq(device->tsif[i].tsif_irq,  &device->tsif[i]);
+	}
 
 	wake_lock_destroy(&device->wake_lock);
 	free_irq(device->tspp_irq, device);