atmel_serial: fix uart/console concurrent access
[sfrench/cifs-2.6.git] / drivers / serial / atmel_serial.c
index 8cea6068f03ed51caa768a78a9c5f7d5a17207c2..430997e33fc434cef3f6a93e7de30ba0c2e50480 100644 (file)
@@ -96,6 +96,7 @@
 
  /* PDC registers */
 #define UART_PUT_PTCR(port,v)  __raw_writel(v, (port)->membase + ATMEL_PDC_PTCR)
+#define UART_GET_TCR(port)      __raw_readl((port)->membase + ATMEL_PDC_TCR)
 #define UART_GET_PTSR(port)    __raw_readl((port)->membase + ATMEL_PDC_PTSR)
 
 #define UART_PUT_RPR(port,v)   __raw_writel(v, (port)->membase + ATMEL_PDC_RPR)
 
 #define UART_PUT_TPR(port,v)   __raw_writel(v, (port)->membase + ATMEL_PDC_TPR)
 #define UART_PUT_TCR(port,v)   __raw_writel(v, (port)->membase + ATMEL_PDC_TCR)
+#define UART_GET_TCR(port)     __raw_readl((port)->membase + ATMEL_PDC_TCR)
 
 static int (*atmel_open_hook)(struct uart_port *);
 static void (*atmel_close_hook)(struct uart_port *);
@@ -549,7 +551,7 @@ static irqreturn_t atmel_interrupt(int irq, void *dev_id)
                atmel_handle_transmit(port, pending);
        } while (pass_counter++ < ATMEL_ISR_PASS_LIMIT);
 
-       return IRQ_HANDLED;
+       return pass_counter ? IRQ_HANDLED : IRQ_NONE;
 }
 
 /*
@@ -562,17 +564,22 @@ static void atmel_tx_dma(struct uart_port *port)
        struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx;
        int count;
 
+       /* nothing left to transmit? */
+       if (UART_GET_TCR(port))
+               return;
+
        xmit->tail += pdc->ofs;
        xmit->tail &= UART_XMIT_SIZE - 1;
 
        port->icount.tx += pdc->ofs;
        pdc->ofs = 0;
 
-       if (!uart_circ_empty(xmit)) {
-               /* more to transmit - setup next transfer */
+       /* more to transmit - setup next transfer */
 
-               /* disable PDC transmit */
-               UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS);
+       /* disable PDC transmit */
+       UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS);
+
+       if (!uart_circ_empty(xmit)) {
                dma_sync_single_for_device(port->dev,
                                           pdc->dma_addr,
                                           pdc->dma_size,
@@ -586,11 +593,6 @@ static void atmel_tx_dma(struct uart_port *port)
                /* re-enable PDC transmit and interrupts */
                UART_PUT_PTCR(port, ATMEL_PDC_TXTEN);
                UART_PUT_IER(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE);
-       } else {
-               /* nothing left to transmit - disable the transmitter */
-
-               /* disable PDC transmit */
-               UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS);
        }
 
        if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
@@ -793,6 +795,7 @@ static void atmel_tasklet_func(unsigned long data)
 static int atmel_startup(struct uart_port *port)
 {
        struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+       struct tty_struct *tty = port->info->tty;
        int retval;
 
        /*
@@ -806,7 +809,7 @@ static int atmel_startup(struct uart_port *port)
         * Allocate the IRQ
         */
        retval = request_irq(port->irq, atmel_interrupt, IRQF_SHARED,
-                       "atmel_serial", port);
+                       tty ? tty->name : "atmel_serial", port);
        if (retval) {
                printk("atmel_serial: atmel_startup - Can't get irq\n");
                return retval;
@@ -1273,6 +1276,7 @@ static void atmel_console_write(struct console *co, const char *s, u_int count)
 {
        struct uart_port *port = &atmel_ports[co->index].uart;
        unsigned int status, imr;
+       unsigned int pdc_tx;
 
        /*
         * First, save IMR and then disable interrupts
@@ -1280,6 +1284,10 @@ static void atmel_console_write(struct console *co, const char *s, u_int count)
        imr = UART_GET_IMR(port);
        UART_PUT_IDR(port, ATMEL_US_RXRDY | ATMEL_US_TXRDY);
 
+       /* Store PDC transmit status and disable it */
+       pdc_tx = UART_GET_PTSR(port) & ATMEL_PDC_TXTEN;
+       UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS);
+
        uart_console_write(port, s, count, atmel_console_putchar);
 
        /*
@@ -1289,6 +1297,11 @@ static void atmel_console_write(struct console *co, const char *s, u_int count)
        do {
                status = UART_GET_CSR(port);
        } while (!(status & ATMEL_US_TXRDY));
+
+       /* Restore PDC transmit status */
+       if (pdc_tx)
+               UART_PUT_PTCR(port, ATMEL_PDC_TXTEN);
+
        /* set interrupts back the way they were */
        UART_PUT_IER(port, imr);
 }