Mantis driver patch: use interrupt for I2C traffic instead of busy registry polling

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




Hi

Here is a maybe too ugly patch for direct inclusion:
The code should be reworked to be nicer first.
This seems to work though.

What do you think the idea?

Problem:

I figured out that Mantis I2C read and write functions do busy waiting,
by reading via PCI bus a 32 bit word about 350 times in a tight loop.

I suspected that PCI DMA transfer from Mantis card to
CPU memory might get disturbed (FRTG DMA error message by Mantis)
because this heavy polling might cause the RISC processor's
DMA transfer to slow down so much, that there
comes glitches into the DVB stream (some internal FIFO gets full).

So that's why I wrote this patch: by reducing PCI bus contention,
the RISC DMA transfer should be more robust.

The heavy I2C command seems to be that dvb_core checks whether FE_LOCK still holds.
I think that DVB card's I2C transfers aren't so performance critical,
that the heavy PCI contention by polling is justified. Or is it?

I looked at saa7146_i2c.c from Linux kernel, how to use wait_event_interruptible_timeout.
Signed-off-by: Marko M Ristola <marko.ristola@xxxxxxxxxxx>

Regards,
Marko Ristola

diff --git a/drivers/media/dvb/mantis/mantis_cards.c b/drivers/media/dvb/mantis/mantis_cards.c
index cf4b39f..56c5c30 100644
--- a/drivers/media/dvb/mantis/mantis_cards.c
+++ b/drivers/media/dvb/mantis/mantis_cards.c
@@ -58,7 +58,7 @@ static int devs;

 #define DRIVER_NAME    "Mantis"

-static char *label[10] = {
+static char *label[11] = {
     "DMA",
     "IRQ-0",
     "IRQ-1",
@@ -68,6 +68,7 @@ static char *label[10] = {
     "PPERR",
     "FTRGT",
     "RISCI",
+    "I2CDONE",
     "RACK"
 };

@@ -137,8 +138,15 @@ static irqreturn_t mantis_irq_handler(int irq, void *dev_id)
         mantis->finished_block = (stat & MANTIS_INT_RISCSTAT) >> 28;
         tasklet_schedule(&mantis->tasklet);
     }
-    if (stat & MANTIS_INT_I2CDONE) {
-        dprintk(MANTIS_DEBUG, 0, "<%s>", label[9]);
+    if (stat & (MANTIS_INT_I2CDONE | MANTIS_INT_I2CRACK)) {
+        if (stat & MANTIS_INT_I2CDONE) {
+            dprintk(MANTIS_DEBUG, 0, "<%s>", label[9]);
+            mantis->i2c_status |= MANTIS_INT_I2CDONE;
+        }
+        if (stat & MANTIS_INT_I2CRACK) {
+            dprintk(MANTIS_DEBUG, 0, "<%s>", label[10]);
+            mantis->i2c_status |= MANTIS_INT_I2CRACK;
+        }
         wake_up(&mantis->i2c_wq);
     }
     mmwrite(stat, MANTIS_INT_STAT);
diff --git a/drivers/media/dvb/mantis/mantis_common.h b/drivers/media/dvb/mantis/mantis_common.h
index d0b645a..3773ad0 100644
--- a/drivers/media/dvb/mantis/mantis_common.h
+++ b/drivers/media/dvb/mantis/mantis_common.h
@@ -77,7 +77,8 @@

 enum mantis_i2c_mode {
     MANTIS_PAGE_MODE = 0,
-    MANTIS_BYTE_MODE,
+    MANTIS_BYTE_MODE = 1,
+    MANTIS_IRQ_PAGE_MODE = 2,
 };

 struct mantis_pci;
@@ -136,6 +137,7 @@ struct mantis_pci {

     struct i2c_adapter    adapter;
     int            i2c_rc;
+    int            i2c_status;
     wait_queue_head_t    i2c_wq;
     struct mutex        i2c_lock;

diff --git a/drivers/media/dvb/mantis/mantis_i2c.c b/drivers/media/dvb/mantis/mantis_i2c.c
index 7870bcf..76de8f8 100644
--- a/drivers/media/dvb/mantis/mantis_i2c.c
+++ b/drivers/media/dvb/mantis/mantis_i2c.c
@@ -38,6 +38,8 @@
static int mantis_i2c_read(struct mantis_pci *mantis, const struct i2c_msg *msg)
 {
     u32 rxd, i, stat, trials;
+    u32 intstat;
+    long timeout;

     dprintk(MANTIS_INFO, 0, "        %s:  Address=[0x%02x] <R>[ ",
         __func__, msg->addr);
@@ -51,27 +53,53 @@ static int mantis_i2c_read(struct mantis_pci *mantis, const struct i2c_msg *msg)
         if (i == (msg->len - 1))
             rxd &= ~MANTIS_I2C_STOP;

-        mmwrite(MANTIS_INT_I2CDONE, MANTIS_INT_STAT);
+        mantis->i2c_status = 0;
+        intstat = MANTIS_INT_I2CDONE | MANTIS_INT_I2CRACK;
+        mmwrite(intstat, MANTIS_INT_STAT);
         mmwrite(rxd, MANTIS_I2CDATA_CTL);

         /* wait for xfer completion */
-        for (trials = 0; trials < TRIALS; trials++) {
-            stat = mmread(MANTIS_INT_STAT);
-            if (stat & MANTIS_INT_I2CDONE)
-                break;
+        if (mantis->hwconfig->i2c_mode == MANTIS_IRQ_PAGE_MODE) {
+            timeout = HZ / 64;
+ timeout = wait_event_interruptible_timeout(mantis->i2c_wq, mantis->i2c_status & MANTIS_INT_I2CDONE, timeout);
+            if (timeout == -ERESTARTSYS) {
+ dprintk(MANTIS_DEBUG, 1, "Mantis I2C read: got -ERESTARTSYS.\n");
+                return -ERESTARTSYS;
+            } if (timeout == 0L) {
+ dprintk(MANTIS_DEBUG, 1, "Mantis I2C read: waiting I2CDONE failed.\n");
+                return -EIO;
+            }
+ dprintk(MANTIS_TMG, 0, "I2CDONE: time remained %ld/%d jiffies\n", timeout, HZ / 64);
+        } else {
+            for (trials = 0; trials < TRIALS; trials++) {
+                stat = mmread(MANTIS_INT_STAT);
+                if (stat & MANTIS_INT_I2CDONE)
+                    break;
+            }
+            dprintk(MANTIS_TMG, 0, "I2CDONE: trials=%d\n", trials);
         }

-        dprintk(MANTIS_TMG, 0, "I2CDONE: trials=%d\n", trials);
-
         /* wait for xfer completion */
-        for (trials = 0; trials < TRIALS; trials++) {
-            stat = mmread(MANTIS_INT_STAT);
-            if (stat & MANTIS_INT_I2CRACK)
-                break;
-        }
-
-        dprintk(MANTIS_TMG, 0, "I2CRACK: trials=%d\n", trials);
+        if (mantis->hwconfig->i2c_mode == MANTIS_IRQ_PAGE_MODE) {
+            timeout = HZ / 64;
+ timeout = wait_event_interruptible_timeout(mantis->i2c_wq, mantis->i2c_status & MANTIS_INT_I2CRACK, timeout);
+            if (timeout == -ERESTARTSYS) {
+ dprintk(MANTIS_DEBUG, 1, "Mantis I2C read: got -ERESTARTSYS.\n");
+                return -ERESTARTSYS;
+            } if (timeout == 0L) {
+ dprintk(MANTIS_DEBUG, 1, "Mantis I2C read: waiting I2CRACK failed.\n");
+                return -EIO;
+            }
+ dprintk(MANTIS_TMG, 0, "I2CRACK: time remained %ld/%d jiffies\n", timeout, HZ / 64);
+        } else {
+            for (trials = 0; trials < TRIALS; trials++) {
+                stat = mmread(MANTIS_INT_STAT);
+                if (stat & MANTIS_INT_I2CRACK)
+                    break;
+            }

+            dprintk(MANTIS_TMG, 0, "I2CRACK: trials=%d\n", trials);
+        }
         rxd = mmread(MANTIS_I2CDATA_CTL);
         msg->buf[i] = (u8)((rxd >> 8) & 0xFF);
         dprintk(MANTIS_INFO, 0, "%02x ", msg->buf[i]);
@@ -85,6 +113,8 @@ static int mantis_i2c_write(struct mantis_pci *mantis, const struct i2c_msg *msg
 {
     int i;
     u32 txd = 0, stat, trials;
+    u32 intstat;
+    long timeout;

     dprintk(MANTIS_INFO, 0, "        %s: Address=[0x%02x] <W>[ ",
         __func__, msg->addr);
@@ -99,26 +129,54 @@ static int mantis_i2c_write(struct mantis_pci *mantis, const struct i2c_msg *msg
         if (i == (msg->len - 1))
             txd &= ~MANTIS_I2C_STOP;

-        mmwrite(MANTIS_INT_I2CDONE, MANTIS_INT_STAT);
+        mantis->i2c_status = 0;
+        intstat = MANTIS_INT_I2CDONE | MANTIS_INT_I2CRACK;
+        mmwrite(intstat, MANTIS_INT_STAT);
         mmwrite(txd, MANTIS_I2CDATA_CTL);

         /* wait for xfer completion */
-        for (trials = 0; trials < TRIALS; trials++) {
-            stat = mmread(MANTIS_INT_STAT);
-            if (stat & MANTIS_INT_I2CDONE)
-                break;
-        }
+        if (mantis->hwconfig->i2c_mode == MANTIS_IRQ_PAGE_MODE) {
+            timeout = HZ / 64;
+ timeout = wait_event_interruptible_timeout(mantis->i2c_wq, mantis->i2c_status & MANTIS_INT_I2CDONE, timeout);
+            if (timeout == -ERESTARTSYS) {
+ dprintk(MANTIS_DEBUG, 1, "Mantis I2C write: got -ERESTARTSYS.\n");
+                return -ERESTARTSYS;
+            } if (timeout == 0L) {
+ dprintk(MANTIS_DEBUG, 1, "Mantis I2C write: waiting I2CDONE failed.\n");
+                return -EIO;
+            }
+ dprintk(MANTIS_TMG, 0, "I2CDONE: time remained %ld/%d jiffies\n", timeout, HZ / 64);
+        } else {
+            for (trials = 0; trials < TRIALS; trials++) {
+                stat = mmread(MANTIS_INT_STAT);
+                if (stat & MANTIS_INT_I2CDONE)
+                    break;
+            }

-        dprintk(MANTIS_TMG, 0, "I2CDONE: trials=%d\n", trials);
+            dprintk(MANTIS_TMG, 0, "I2CDONE: trials=%d\n", trials);
+        }

         /* wait for xfer completion */
-        for (trials = 0; trials < TRIALS; trials++) {
-            stat = mmread(MANTIS_INT_STAT);
-            if (stat & MANTIS_INT_I2CRACK)
-                break;
-        }
+        if (mantis->hwconfig->i2c_mode == MANTIS_IRQ_PAGE_MODE) {
+            timeout = HZ / 64;
+ timeout = wait_event_interruptible_timeout(mantis->i2c_wq, mantis->i2c_status & MANTIS_INT_I2CRACK, timeout);
+            if (timeout == -ERESTARTSYS) {
+ dprintk(MANTIS_DEBUG, 1, "Mantis I2C write: got -ERESTARTSYS.\n");
+                return -ERESTARTSYS;
+            } if (timeout == 0L) {
+ dprintk(MANTIS_DEBUG, 1, "Mantis I2C write: waiting I2CRACK failed.\n");
+                return -EIO;
+            }
+ dprintk(MANTIS_TMG, 0, "I2CRACK: time remained %ld/%d jiffies\n", timeout, HZ / 64);
+        } else {
+            for (trials = 0; trials < TRIALS; trials++) {
+                stat = mmread(MANTIS_INT_STAT);
+                if (stat & MANTIS_INT_I2CRACK)
+                    break;
+            }

-        dprintk(MANTIS_TMG, 0, "I2CRACK: trials=%d\n", trials);
+            dprintk(MANTIS_TMG, 0, "I2CRACK: trials=%d\n", trials);
+        }
     }
     dprintk(MANTIS_INFO, 0, "]\n");

@@ -245,9 +303,17 @@ int __devinit mantis_i2c_init(struct mantis_pci *mantis)
     intstat = mmread(MANTIS_INT_STAT);
     intmask = mmread(MANTIS_INT_MASK);
     mmwrite(intstat, MANTIS_INT_STAT);
-    dprintk(MANTIS_DEBUG, 1, "Disabling I2C interrupt");
-    intmask = mmread(MANTIS_INT_MASK);
-    mmwrite((intmask & ~MANTIS_INT_I2CDONE), MANTIS_INT_MASK);
+    if (mantis->hwconfig->i2c_mode == MANTIS_IRQ_PAGE_MODE) {
+        dprintk(MANTIS_DEBUG, 1, "Enabling I2C interrupt");
+        intmask = mmread(MANTIS_INT_MASK);
+        intmask |= MANTIS_INT_I2CDONE;
+        intmask |= MANTIS_INT_I2CRACK;
+        mmwrite(intmask, MANTIS_INT_MASK);
+    } else {
+        dprintk(MANTIS_DEBUG, 1, "Disabling I2C interrupt");
+        intmask = mmread(MANTIS_INT_MASK);
+        mmwrite((intmask & ~MANTIS_INT_I2CDONE), MANTIS_INT_MASK);
+    }

     return 0;
 }
diff --git a/drivers/media/dvb/mantis/mantis_vp2033.c b/drivers/media/dvb/mantis/mantis_vp2033.c
index 10ce817..77fc21f 100644
--- a/drivers/media/dvb/mantis/mantis_vp2033.c
+++ b/drivers/media/dvb/mantis/mantis_vp2033.c
@@ -184,4 +184,5 @@ struct mantis_hwconfig vp2033_config = {
     .frontend_init    = vp2033_frontend_init,
     .power        = GPIF_A12,
     .reset        = GPIF_A13,
+    .i2c_mode    = MANTIS_IRQ_PAGE_MODE,
 };

--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux