serial: at91: use function pointer to choose pdc or pio
authorElen Song <elen.song@atmel.com>
Mon, 22 Jul 2013 08:30:25 +0000 (16:30 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 29 Jul 2013 20:03:29 +0000 (13:03 -0700)
use function pointer can avoid define atmel_use_pdc_tx/rx everywhere.
(*prepare_rx/tx)() is in setup transfer stage.
(*schedule_rx/tx)() is in tasklet schedule stage.
(*release_rx/tx)() is used when shutdown the transfer.

Signed-off-by: Elen Song <elen.song@atmel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/serial/atmel_serial.c

index 1d59563016fb6b6cffa8c6869244692764ea4f27..b56123d3affefc45b9055e1110fa9bed94b3cd14 100644 (file)
@@ -154,6 +154,12 @@ struct atmel_uart_port {
 
        struct serial_rs485     rs485;          /* rs485 settings */
        unsigned int            tx_done_mask;
+       int (*prepare_rx)(struct uart_port *port);
+       int (*prepare_tx)(struct uart_port *port);
+       void (*schedule_rx)(struct uart_port *port);
+       void (*schedule_tx)(struct uart_port *port);
+       void (*release_rx)(struct uart_port *port);
+       void (*release_tx)(struct uart_port *port);
 };
 
 static struct atmel_uart_port atmel_ports[ATMEL_MAX_UART];
@@ -657,6 +663,17 @@ static irqreturn_t atmel_interrupt(int irq, void *dev_id)
        return pass_counter ? IRQ_HANDLED : IRQ_NONE;
 }
 
+static void atmel_release_tx_pdc(struct uart_port *port)
+{
+       struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+       struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx;
+
+       dma_unmap_single(port->dev,
+                        pdc->dma_addr,
+                        pdc->dma_size,
+                        DMA_TO_DEVICE);
+}
+
 /*
  * Called from tasklet with ENDTX and TXBUFE interrupts disabled.
  */
@@ -709,6 +726,23 @@ static void atmel_tx_pdc(struct uart_port *port)
                uart_write_wakeup(port);
 }
 
+static int atmel_prepare_tx_pdc(struct uart_port *port)
+{
+       struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+       struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx;
+       struct circ_buf *xmit = &port->state->xmit;
+
+       pdc->buf = xmit->buf;
+       pdc->dma_addr = dma_map_single(port->dev,
+                                       pdc->buf,
+                                       UART_XMIT_SIZE,
+                                       DMA_TO_DEVICE);
+       pdc->dma_size = UART_XMIT_SIZE;
+       pdc->ofs = 0;
+
+       return 0;
+}
+
 static void atmel_rx_from_ring(struct uart_port *port)
 {
        struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
@@ -777,6 +811,22 @@ static void atmel_rx_from_ring(struct uart_port *port)
        spin_lock(&port->lock);
 }
 
+static void atmel_release_rx_pdc(struct uart_port *port)
+{
+       struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+       int i;
+
+       for (i = 0; i < 2; i++) {
+               struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i];
+
+               dma_unmap_single(port->dev,
+                                pdc->dma_addr,
+                                pdc->dma_size,
+                                DMA_FROM_DEVICE);
+               kfree(pdc->buf);
+       }
+}
+
 static void atmel_rx_from_pdc(struct uart_port *port)
 {
        struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
@@ -854,6 +904,45 @@ static void atmel_rx_from_pdc(struct uart_port *port)
        UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT);
 }
 
+static int atmel_prepare_rx_pdc(struct uart_port *port)
+{
+       struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+       int i;
+
+       for (i = 0; i < 2; i++) {
+               struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i];
+
+               pdc->buf = kmalloc(PDC_BUFFER_SIZE, GFP_KERNEL);
+               if (pdc->buf == NULL) {
+                       if (i != 0) {
+                               dma_unmap_single(port->dev,
+                                       atmel_port->pdc_rx[0].dma_addr,
+                                       PDC_BUFFER_SIZE,
+                                       DMA_FROM_DEVICE);
+                               kfree(atmel_port->pdc_rx[0].buf);
+                       }
+                       atmel_port->use_pdc_rx = 0;
+                       return -ENOMEM;
+               }
+               pdc->dma_addr = dma_map_single(port->dev,
+                                               pdc->buf,
+                                               PDC_BUFFER_SIZE,
+                                               DMA_FROM_DEVICE);
+               pdc->dma_size = PDC_BUFFER_SIZE;
+               pdc->ofs = 0;
+       }
+
+       atmel_port->pdc_rx_idx = 0;
+
+       UART_PUT_RPR(port, atmel_port->pdc_rx[0].dma_addr);
+       UART_PUT_RCR(port, PDC_BUFFER_SIZE);
+
+       UART_PUT_RNPR(port, atmel_port->pdc_rx[1].dma_addr);
+       UART_PUT_RNCR(port, PDC_BUFFER_SIZE);
+
+       return 0;
+}
+
 /*
  * tasklet handling tty stuff outside the interrupt handler.
  */
@@ -867,10 +956,7 @@ static void atmel_tasklet_func(unsigned long data)
        /* The interrupt handler does not take the lock */
        spin_lock(&port->lock);
 
-       if (atmel_use_pdc_tx(port))
-               atmel_tx_pdc(port);
-       else
-               atmel_tx_chars(port);
+       atmel_port->schedule_tx(port);
 
        status = atmel_port->irq_status;
        status_change = status ^ atmel_port->irq_status_prev;
@@ -892,14 +978,36 @@ static void atmel_tasklet_func(unsigned long data)
                atmel_port->irq_status_prev = status;
        }
 
-       if (atmel_use_pdc_rx(port))
-               atmel_rx_from_pdc(port);
-       else
-               atmel_rx_from_ring(port);
+       atmel_port->schedule_rx(port);
 
        spin_unlock(&port->lock);
 }
 
+static void atmel_set_ops(struct uart_port *port)
+{
+       struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
+       if (atmel_use_pdc_rx(port)) {
+               atmel_port->prepare_rx = &atmel_prepare_rx_pdc;
+               atmel_port->schedule_rx = &atmel_rx_from_pdc;
+               atmel_port->release_rx = &atmel_release_rx_pdc;
+       } else {
+               atmel_port->prepare_rx = NULL;
+               atmel_port->schedule_rx = &atmel_rx_from_ring;
+               atmel_port->release_rx = NULL;
+       }
+
+       if (atmel_use_pdc_tx(port)) {
+               atmel_port->prepare_tx = &atmel_prepare_tx_pdc;
+               atmel_port->schedule_tx = &atmel_tx_pdc;
+               atmel_port->release_tx = &atmel_release_tx_pdc;
+       } else {
+               atmel_port->prepare_tx = NULL;
+               atmel_port->schedule_tx = &atmel_tx_chars;
+               atmel_port->release_tx = NULL;
+       }
+}
+
 /*
  * Perform initialization and enable port for reception
  */
@@ -929,53 +1037,17 @@ static int atmel_startup(struct uart_port *port)
        /*
         * Initialize DMA (if necessary)
         */
-       if (atmel_use_pdc_rx(port)) {
-               int i;
-
-               for (i = 0; i < 2; i++) {
-                       struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i];
-
-                       pdc->buf = kmalloc(PDC_BUFFER_SIZE, GFP_KERNEL);
-                       if (pdc->buf == NULL) {
-                               if (i != 0) {
-                                       dma_unmap_single(port->dev,
-                                               atmel_port->pdc_rx[0].dma_addr,
-                                               PDC_BUFFER_SIZE,
-                                               DMA_FROM_DEVICE);
-                                       kfree(atmel_port->pdc_rx[0].buf);
-                               }
-                               free_irq(port->irq, port);
-                               return -ENOMEM;
-                       }
-                       pdc->dma_addr = dma_map_single(port->dev,
-                                                      pdc->buf,
-                                                      PDC_BUFFER_SIZE,
-                                                      DMA_FROM_DEVICE);
-                       pdc->dma_size = PDC_BUFFER_SIZE;
-                       pdc->ofs = 0;
-               }
-
-               atmel_port->pdc_rx_idx = 0;
-
-               UART_PUT_RPR(port, atmel_port->pdc_rx[0].dma_addr);
-               UART_PUT_RCR(port, PDC_BUFFER_SIZE);
-
-               UART_PUT_RNPR(port, atmel_port->pdc_rx[1].dma_addr);
-               UART_PUT_RNCR(port, PDC_BUFFER_SIZE);
+       if (atmel_port->prepare_rx) {
+               retval = atmel_port->prepare_rx(port);
+               if (retval < 0)
+                       atmel_set_ops(port);
        }
-       if (atmel_use_pdc_tx(port)) {
-               struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx;
-               struct circ_buf *xmit = &port->state->xmit;
 
-               pdc->buf = xmit->buf;
-               pdc->dma_addr = dma_map_single(port->dev,
-                                              pdc->buf,
-                                              UART_XMIT_SIZE,
-                                              DMA_TO_DEVICE);
-               pdc->dma_size = UART_XMIT_SIZE;
-               pdc->ofs = 0;
+       if (atmel_port->prepare_tx) {
+               retval = atmel_port->prepare_tx(port);
+               if (retval < 0)
+                       atmel_set_ops(port);
        }
-
        /*
         * If there is a specific "open" function (to register
         * control line interrupts)
@@ -1030,27 +1102,10 @@ static void atmel_shutdown(struct uart_port *port)
        /*
         * Shut-down the DMA.
         */
-       if (atmel_use_pdc_rx(port)) {
-               int i;
-
-               for (i = 0; i < 2; i++) {
-                       struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i];
-
-                       dma_unmap_single(port->dev,
-                                        pdc->dma_addr,
-                                        pdc->dma_size,
-                                        DMA_FROM_DEVICE);
-                       kfree(pdc->buf);
-               }
-       }
-       if (atmel_use_pdc_tx(port)) {
-               struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx;
-
-               dma_unmap_single(port->dev,
-                                pdc->dma_addr,
-                                pdc->dma_size,
-                                DMA_TO_DEVICE);
-       }
+       if (atmel_port->release_rx)
+               atmel_port->release_rx(port);
+       if (atmel_port->release_tx)
+               atmel_port->release_tx(port);
 
        /*
         * Disable all interrupts, port and break condition.
@@ -1473,6 +1528,8 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port,
                atmel_port->rs485       = pdata->rs485;
        }
 
+       atmel_set_ops(port);
+
        port->iotype            = UPIO_MEM;
        port->flags             = UPF_BOOT_AUTOCONF;
        port->ops               = &atmel_pops;