[media] nuvoton-cir: add proper rx fifo overrun handling
authorJarod Wilson <jarod@redhat.com>
Fri, 8 Oct 2010 19:16:23 +0000 (16:16 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Thu, 21 Oct 2010 03:18:32 +0000 (01:18 -0200)
Per discussion with Andy Walls on irc, rx fifo overruns are not all that
uncommon on a busy system, and the initial posting of the nuvoton-cir
driver doesn't handle them well enough. With this addition, we'll drain
the hw fifo, attempt to process any ir pulse trains completed with that
flush, then we'll issue a hw rx fifo clear and reset the raw ir sample
kfifo and start over collecting raw ir data.

Also slightly refactors the cir interrupt enabling so that we always get
consistent flags set and only have to modify them in one place, should
they need to be altered.

Signed-off-by: Jarod Wilson <jarod@redhat.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/IR/nuvoton-cir.c

index 1ce93599ff569a1573fa452de8211667d1c9ad7a..fdb280ed01ce8415fec9cba14bbb87f8d5770945 100644 (file)
@@ -339,6 +339,15 @@ static void nvt_clear_tx_fifo(struct nvt_dev *nvt)
        nvt_cir_reg_write(nvt, val | CIR_FIFOCON_TXFIFOCLR, CIR_FIFOCON);
 }
 
+/* enable RX Trigger Level Reach and Packet End interrupts */
+static void nvt_set_cir_iren(struct nvt_dev *nvt)
+{
+       u8 iren;
+
+       iren = CIR_IREN_RTR | CIR_IREN_PE;
+       nvt_cir_reg_write(nvt, iren, CIR_IREN);
+}
+
 static void nvt_cir_regs_init(struct nvt_dev *nvt)
 {
        /* set sample limit count (PE interrupt raised when reached) */
@@ -363,8 +372,8 @@ static void nvt_cir_regs_init(struct nvt_dev *nvt)
        /* clear any and all stray interrupts */
        nvt_cir_reg_write(nvt, 0xff, CIR_IRSTS);
 
-       /* and finally, enable RX Trigger Level Read and Packet End interrupts */
-       nvt_cir_reg_write(nvt, CIR_IREN_RTR | CIR_IREN_PE, CIR_IREN);
+       /* and finally, enable interrupts */
+       nvt_set_cir_iren(nvt);
 }
 
 static void nvt_cir_wake_regs_init(struct nvt_dev *nvt)
@@ -639,12 +648,22 @@ static void nvt_process_rx_ir_data(struct nvt_dev *nvt)
        nvt_dbg_verbose("%s done", __func__);
 }
 
+static void nvt_handle_rx_fifo_overrun(struct nvt_dev *nvt)
+{
+       nvt_pr(KERN_WARNING, "RX FIFO overrun detected, flushing data!");
+
+       nvt->pkts = 0;
+       nvt_clear_cir_fifo(nvt);
+       ir_raw_event_reset(nvt->rdev);
+}
+
 /* copy data from hardware rx fifo into driver buffer */
 static void nvt_get_rx_ir_data(struct nvt_dev *nvt)
 {
        unsigned long flags;
        u8 fifocount, val;
        unsigned int b_idx;
+       bool overrun = false;
        int i;
 
        /* Get count of how many bytes to read from RX FIFO */
@@ -652,11 +671,10 @@ static void nvt_get_rx_ir_data(struct nvt_dev *nvt)
        /* if we get 0xff, probably means the logical dev is disabled */
        if (fifocount == 0xff)
                return;
-       /* this would suggest a fifo overrun, not good... */
+       /* watch out for a fifo overrun condition */
        else if (fifocount > RX_BUF_LEN) {
-               nvt_pr(KERN_WARNING, "fifocount %d over fifo len (%d)!",
-                      fifocount, RX_BUF_LEN);
-               return;
+               overrun = true;
+               fifocount = RX_BUF_LEN;
        }
 
        nvt_dbg("attempting to fetch %u bytes from hw rx fifo", fifocount);
@@ -682,6 +700,9 @@ static void nvt_get_rx_ir_data(struct nvt_dev *nvt)
 
        nvt_process_rx_ir_data(nvt);
 
+       if (overrun)
+               nvt_handle_rx_fifo_overrun(nvt);
+
        spin_unlock_irqrestore(&nvt->nvt_lock, flags);
 }
 
@@ -886,7 +907,7 @@ static void nvt_enable_cir(struct nvt_dev *nvt)
        nvt_cir_reg_write(nvt, 0xff, CIR_IRSTS);
 
        /* enable interrupts */
-       nvt_cir_reg_write(nvt, CIR_IREN_RTR | CIR_IREN_PE, CIR_IREN);
+       nvt_set_cir_iren(nvt);
 }
 
 static void nvt_disable_cir(struct nvt_dev *nvt)
@@ -1155,7 +1176,7 @@ static int nvt_resume(struct pnp_dev *pdev)
        nvt_dbg("%s called", __func__);
 
        /* open interrupt */
-       nvt_cir_reg_write(nvt, CIR_IREN_RTR | CIR_IREN_PE, CIR_IREN);
+       nvt_set_cir_iren(nvt);
 
        /* Enable CIR logical device */
        nvt_efm_enable(nvt);