Commit a930e528 authored by Elen Song's avatar Elen Song Committed by Greg Kroah-Hartman

serial: at91: use function pointer to choose pdc or pio

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: default avatarElen Song <elen.song@atmel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 64e22ebe
...@@ -154,6 +154,12 @@ struct atmel_uart_port { ...@@ -154,6 +154,12 @@ struct atmel_uart_port {
struct serial_rs485 rs485; /* rs485 settings */ struct serial_rs485 rs485; /* rs485 settings */
unsigned int tx_done_mask; 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]; static struct atmel_uart_port atmel_ports[ATMEL_MAX_UART];
...@@ -657,6 +663,17 @@ static irqreturn_t atmel_interrupt(int irq, void *dev_id) ...@@ -657,6 +663,17 @@ static irqreturn_t atmel_interrupt(int irq, void *dev_id)
return pass_counter ? IRQ_HANDLED : IRQ_NONE; 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. * Called from tasklet with ENDTX and TXBUFE interrupts disabled.
*/ */
...@@ -709,6 +726,23 @@ static void atmel_tx_pdc(struct uart_port *port) ...@@ -709,6 +726,23 @@ static void atmel_tx_pdc(struct uart_port *port)
uart_write_wakeup(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) static void atmel_rx_from_ring(struct uart_port *port)
{ {
struct atmel_uart_port *atmel_port = to_atmel_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) ...@@ -777,6 +811,22 @@ static void atmel_rx_from_ring(struct uart_port *port)
spin_lock(&port->lock); 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) static void atmel_rx_from_pdc(struct uart_port *port)
{ {
struct atmel_uart_port *atmel_port = to_atmel_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) ...@@ -854,6 +904,45 @@ static void atmel_rx_from_pdc(struct uart_port *port)
UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT); 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. * tasklet handling tty stuff outside the interrupt handler.
*/ */
...@@ -867,10 +956,7 @@ static void atmel_tasklet_func(unsigned long data) ...@@ -867,10 +956,7 @@ static void atmel_tasklet_func(unsigned long data)
/* The interrupt handler does not take the lock */ /* The interrupt handler does not take the lock */
spin_lock(&port->lock); spin_lock(&port->lock);
if (atmel_use_pdc_tx(port)) atmel_port->schedule_tx(port);
atmel_tx_pdc(port);
else
atmel_tx_chars(port);
status = atmel_port->irq_status; status = atmel_port->irq_status;
status_change = status ^ atmel_port->irq_status_prev; status_change = status ^ atmel_port->irq_status_prev;
...@@ -892,14 +978,36 @@ static void atmel_tasklet_func(unsigned long data) ...@@ -892,14 +978,36 @@ static void atmel_tasklet_func(unsigned long data)
atmel_port->irq_status_prev = status; atmel_port->irq_status_prev = status;
} }
if (atmel_use_pdc_rx(port)) atmel_port->schedule_rx(port);
atmel_rx_from_pdc(port);
else
atmel_rx_from_ring(port);
spin_unlock(&port->lock); 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 * Perform initialization and enable port for reception
*/ */
...@@ -929,53 +1037,17 @@ static int atmel_startup(struct uart_port *port) ...@@ -929,53 +1037,17 @@ static int atmel_startup(struct uart_port *port)
/* /*
* Initialize DMA (if necessary) * Initialize DMA (if necessary)
*/ */
if (atmel_use_pdc_rx(port)) { if (atmel_port->prepare_rx) {
int i; retval = atmel_port->prepare_rx(port);
if (retval < 0)
for (i = 0; i < 2; i++) { atmel_set_ops(port);
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_use_pdc_tx(port)) {
struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx;
struct circ_buf *xmit = &port->state->xmit;
pdc->buf = xmit->buf; if (atmel_port->prepare_tx) {
pdc->dma_addr = dma_map_single(port->dev, retval = atmel_port->prepare_tx(port);
pdc->buf, if (retval < 0)
UART_XMIT_SIZE, atmel_set_ops(port);
DMA_TO_DEVICE);
pdc->dma_size = UART_XMIT_SIZE;
pdc->ofs = 0;
} }
/* /*
* If there is a specific "open" function (to register * If there is a specific "open" function (to register
* control line interrupts) * control line interrupts)
...@@ -1030,27 +1102,10 @@ static void atmel_shutdown(struct uart_port *port) ...@@ -1030,27 +1102,10 @@ static void atmel_shutdown(struct uart_port *port)
/* /*
* Shut-down the DMA. * Shut-down the DMA.
*/ */
if (atmel_use_pdc_rx(port)) { if (atmel_port->release_rx)
int i; atmel_port->release_rx(port);
if (atmel_port->release_tx)
for (i = 0; i < 2; i++) { atmel_port->release_tx(port);
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);
}
/* /*
* Disable all interrupts, port and break condition. * Disable all interrupts, port and break condition.
...@@ -1473,6 +1528,8 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port, ...@@ -1473,6 +1528,8 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port,
atmel_port->rs485 = pdata->rs485; atmel_port->rs485 = pdata->rs485;
} }
atmel_set_ops(port);
port->iotype = UPIO_MEM; port->iotype = UPIO_MEM;
port->flags = UPF_BOOT_AUTOCONF; port->flags = UPF_BOOT_AUTOCONF;
port->ops = &atmel_pops; port->ops = &atmel_pops;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment