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];
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.
*/
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);
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);
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.
*/
/* 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;
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
*/
/*
* 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)
/*
* 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.
atmel_port->rs485 = pdata->rs485;
}
+ atmel_set_ops(port);
+
port->iotype = UPIO_MEM;
port->flags = UPF_BOOT_AUTOCONF;
port->ops = &atmel_pops;