V4L/DVB (9510): cx18: Fix write retries for registers that always change - part 2.

cx18: Fix write retries for registers that always change - part 2.
Some registers, especially interrupt related ones, will never read
back the value just written.  Modified interrupt register readback
checks to make sure the intended effect was achieved.

Signed-off-by: Andy Walls <awalls@radix.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
diff --git a/drivers/media/video/cx18/cx18-io.c b/drivers/media/video/cx18/cx18-io.c
index 31be5e8..220fae8 100644
--- a/drivers/media/video/cx18/cx18-io.c
+++ b/drivers/media/video/cx18/cx18-io.c
@@ -88,6 +88,19 @@
 	cx18_log_write_retries(cx, i, addr);
 }
 
+void _cx18_writel_expect(struct cx18 *cx, u32 val, void __iomem *addr,
+			 u32 eval, u32 mask)
+{
+	int i;
+	eval &= mask;
+	for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) {
+		cx18_writel_noretry(cx, val, addr);
+		if (eval == (cx18_readl_noretry(cx, addr) & mask))
+			break;
+	}
+	cx18_log_write_retries(cx, i, addr);
+}
+
 void cx18_writew_retry(struct cx18 *cx, u16 val, void __iomem *addr)
 {
 	int i;
@@ -218,7 +231,7 @@
 void cx18_sw1_irq_enable(struct cx18 *cx, u32 val)
 {
 	u32 r;
-	cx18_write_reg_noretry(cx, val, SW1_INT_STATUS);
+	cx18_write_reg_expect(cx, val, SW1_INT_STATUS, ~val, val);
 	r = cx18_read_reg(cx, SW1_INT_ENABLE_PCI);
 	cx18_write_reg(cx, r | val, SW1_INT_ENABLE_PCI);
 }
@@ -233,7 +246,7 @@
 void cx18_sw2_irq_enable(struct cx18 *cx, u32 val)
 {
 	u32 r;
-	cx18_write_reg_noretry(cx, val, SW2_INT_STATUS);
+	cx18_write_reg_expect(cx, val, SW2_INT_STATUS, ~val, val);
 	r = cx18_read_reg(cx, SW2_INT_ENABLE_PCI);
 	cx18_write_reg(cx, r | val, SW2_INT_ENABLE_PCI);
 }
diff --git a/drivers/media/video/cx18/cx18-io.h b/drivers/media/video/cx18/cx18-io.h
index 287a5e8..4252444 100644
--- a/drivers/media/video/cx18/cx18-io.h
+++ b/drivers/media/video/cx18/cx18-io.h
@@ -133,6 +133,8 @@
 		cx18_writel_noretry(cx, val, addr);
 }
 
+void _cx18_writel_expect(struct cx18 *cx, u32 val, void __iomem *addr,
+			 u32 eval, u32 mask);
 
 static inline
 void cx18_writew_noretry(struct cx18 *cx, u16 val, void __iomem *addr)
@@ -271,6 +273,21 @@
 		cx18_write_reg_noretry(cx, val, reg);
 }
 
+static inline void _cx18_write_reg_expect(struct cx18 *cx, u32 val, u32 reg,
+					  u32 eval, u32 mask)
+{
+	_cx18_writel_expect(cx, val, cx->reg_mem + reg, eval, mask);
+}
+
+static inline void cx18_write_reg_expect(struct cx18 *cx, u32 val, u32 reg,
+					 u32 eval, u32 mask)
+{
+	if (cx18_retry_mmio)
+		_cx18_write_reg_expect(cx, val, reg, eval, mask);
+	else
+		cx18_write_reg_noretry(cx, val, reg);
+}
+
 
 static inline u32 cx18_read_reg_noretry(struct cx18 *cx, u32 reg)
 {
diff --git a/drivers/media/video/cx18/cx18-irq.c b/drivers/media/video/cx18/cx18-irq.c
index 447fc9c..a366259 100644
--- a/drivers/media/video/cx18/cx18-irq.c
+++ b/drivers/media/video/cx18/cx18-irq.c
@@ -142,16 +142,19 @@
 
 	spin_lock(&cx->dma_reg_lock);
 
-	hw2_mask = cx18_read_reg(cx, HW2_INT_MASK5_PCI);
-	hw2 = cx18_read_reg(cx, HW2_INT_CLR_STATUS) & hw2_mask;
-	sw2_mask = cx18_read_reg(cx, SW2_INT_ENABLE_PCI) | IRQ_EPU_TO_HPU_ACK;
-	sw2 = cx18_read_reg(cx, SW2_INT_STATUS) & sw2_mask;
 	sw1_mask = cx18_read_reg(cx, SW1_INT_ENABLE_PCI) | IRQ_EPU_TO_HPU;
 	sw1 = cx18_read_reg(cx, SW1_INT_STATUS) & sw1_mask;
+	sw2_mask = cx18_read_reg(cx, SW2_INT_ENABLE_PCI) | IRQ_EPU_TO_HPU_ACK;
+	sw2 = cx18_read_reg(cx, SW2_INT_STATUS) & sw2_mask;
+	hw2_mask = cx18_read_reg(cx, HW2_INT_MASK5_PCI);
+	hw2 = cx18_read_reg(cx, HW2_INT_CLR_STATUS) & hw2_mask;
 
-	cx18_write_reg_noretry(cx, sw2&sw2_mask, SW2_INT_STATUS);
-	cx18_write_reg_noretry(cx, sw1&sw1_mask, SW1_INT_STATUS);
-	cx18_write_reg_noretry(cx, hw2&hw2_mask, HW2_INT_CLR_STATUS);
+	if (sw1)
+		cx18_write_reg_expect(cx, sw1, SW1_INT_STATUS, ~sw1, sw1);
+	if (sw2)
+		cx18_write_reg_expect(cx, sw2, SW2_INT_STATUS, ~sw2, sw2);
+	if (hw2)
+		cx18_write_reg_expect(cx, hw2, HW2_INT_CLR_STATUS, ~hw2, hw2);
 
 	if (sw1 || sw2 || hw2)
 		CX18_DEBUG_HI_IRQ("SW1: %x  SW2: %x  HW2: %x\n", sw1, sw2, hw2);
@@ -178,5 +181,5 @@
 		hpu_cmd(cx, sw1);
 	spin_unlock(&cx->dma_reg_lock);
 
-	return (hw2 | sw1 | sw2) ? IRQ_HANDLED : IRQ_NONE;
+	return (sw1 || sw2 || hw2) ? IRQ_HANDLED : IRQ_NONE;
 }
diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c
index 87f7c8e..851a905 100644
--- a/drivers/media/video/cx18/cx18-mailbox.c
+++ b/drivers/media/video/cx18/cx18-mailbox.c
@@ -176,7 +176,7 @@
 
 	cx18_setup_page(cx, SCB_OFFSET);
 	cx18_write_sync(cx, mb->request, &ack_mb->ack);
-	cx18_write_reg_noretry(cx, ack_irq, SW2_INT_SET);
+	cx18_write_reg_expect(cx, ack_irq, SW2_INT_SET, ack_irq, ack_irq);
 	return 0;
 }
 
@@ -225,7 +225,7 @@
 	}
 	if (info->flags & API_FAST)
 		timeout /= 2;
-	cx18_write_reg_noretry(cx, irq, SW1_INT_SET);
+	cx18_write_reg_expect(cx, irq, SW1_INT_SET, irq, irq);
 
 	while (!sig && cx18_readl(cx, &mb->ack) != cx18_readl(cx, &mb->request)
 	       && cnt < 660) {