serial: MPSC: Remove race between Rx stop & restart
authorCarlos Sanchez <carlos.sanchez@gecoinc.com>
Tue, 31 Jul 2007 07:38:59 +0000 (00:38 -0700)
committerLinus Torvalds <torvalds@woody.linux-foundation.org>
Tue, 31 Jul 2007 22:39:41 +0000 (15:39 -0700)
The patch in commit ID f7232056bff5fe2d3bfeab35252a66ebaeb5bbde stops (aborts)
the MPSC's receive engine just before restarting it.  Unfortunately, it
doesn't wait for the abort to complete before restarting it which creates a
race between the abort and the restart.  If the restart occurs first, the
in-progress abort stops it again and the rx engine remains stopped.

Instead, do the abort when the SDMA engine is being stopped.  Make sure to
wait for the abort to complete before continuing.

Signed-off-by: Carlos Sanchez <carlos.sanchez@gecoinc.com>
Signed-off-by: Mark A. Greer <mgreer@mvista.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
drivers/serial/mpsc.c

index 00924feaf621831caa46267ad8b9b278a48a8552..567fa789c5c7753762d12de98540de60cd7624fc 100644 (file)
@@ -1363,8 +1363,6 @@ mpsc_start_rx(struct mpsc_port_info *pi)
 {
        pr_debug("mpsc_start_rx[%d]: Starting...\n", pi->port.line);
 
-       /* Issue a Receive Abort to clear any receive errors */
-       writel(MPSC_CHR_2_RA, pi->mpsc_base + MPSC_CHR_2);
        if (pi->rcv_data) {
                mpsc_enter_hunt(pi);
                mpsc_sdma_cmd(pi, SDMA_SDCM_ERD);
@@ -1379,6 +1377,20 @@ mpsc_stop_rx(struct uart_port *port)
 
        pr_debug("mpsc_stop_rx[%d]: Stopping...\n", port->line);
 
+       if (pi->mirror_regs) {
+               writel(pi->MPSC_CHR_2_m | MPSC_CHR_2_RA,
+                               pi->mpsc_base + MPSC_CHR_2);
+               /* Erratum prevents reading CHR_2 so just delay for a while */
+               udelay(100);
+       }
+       else {
+               writel(readl(pi->mpsc_base + MPSC_CHR_2) | MPSC_CHR_2_RA,
+                       pi->mpsc_base + MPSC_CHR_2);
+
+               while (readl(pi->mpsc_base + MPSC_CHR_2) & MPSC_CHR_2_RA)
+                       udelay(10);
+       }
+
        mpsc_sdma_cmd(pi, SDMA_SDCM_AR);
        return;
 }