summaryrefslogtreecommitdiff
path: root/drivers/tty/serial/fsl_lpuart.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/serial/fsl_lpuart.c')
-rw-r--r--drivers/tty/serial/fsl_lpuart.c140
1 files changed, 82 insertions, 58 deletions
diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index e7cde3a9566d..3ad1458bfeb0 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -237,7 +237,8 @@ struct lpuart_port {
unsigned int rxfifo_size;
bool lpuart32;
- bool lpuart_dma_use;
+ bool lpuart_dma_tx_use;
+ bool lpuart_dma_rx_use;
struct dma_chan *dma_tx_chan;
struct dma_chan *dma_rx_chan;
struct dma_async_tx_descriptor *dma_tx_desc;
@@ -454,6 +455,15 @@ static int lpuart_dma_rx(struct lpuart_port *sport)
return 0;
}
+static void lpuart_flush_buffer(struct uart_port *port)
+{
+ struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
+ if (sport->lpuart_dma_tx_use) {
+ dmaengine_terminate_all(sport->dma_tx_chan);
+ sport->dma_tx_in_progress = 0;
+ }
+}
+
static void lpuart_dma_rx_complete(void *arg)
{
struct lpuart_port *sport = arg;
@@ -461,6 +471,7 @@ static void lpuart_dma_rx_complete(void *arg)
unsigned long flags;
async_tx_ack(sport->dma_rx_desc);
+ mod_timer(&sport->lpuart_timer, jiffies + sport->dma_rx_timeout);
spin_lock_irqsave(&sport->port.lock, flags);
@@ -506,9 +517,6 @@ static inline void lpuart_prepare_rx(struct lpuart_port *sport)
spin_lock_irqsave(&sport->port.lock, flags);
- init_timer(&sport->lpuart_timer);
- sport->lpuart_timer.function = lpuart_timer_func;
- sport->lpuart_timer.data = (unsigned long)sport;
sport->lpuart_timer.expires = jiffies + sport->dma_rx_timeout;
add_timer(&sport->lpuart_timer);
@@ -571,7 +579,7 @@ static void lpuart_start_tx(struct uart_port *port)
temp = readb(port->membase + UARTCR2);
writeb(temp | UARTCR2_TIE, port->membase + UARTCR2);
- if (sport->lpuart_dma_use) {
+ if (sport->lpuart_dma_tx_use) {
if (!uart_circ_empty(xmit) && !sport->dma_tx_in_progress)
lpuart_prepare_tx(sport);
} else {
@@ -758,19 +766,19 @@ out:
static irqreturn_t lpuart_int(int irq, void *dev_id)
{
struct lpuart_port *sport = dev_id;
- unsigned char sts;
+ unsigned char sts, crdma;
sts = readb(sport->port.membase + UARTSR1);
+ crdma = readb(sport->port.membase + UARTCR5);
- if (sts & UARTSR1_RDRF) {
- if (sport->lpuart_dma_use)
+ if (sts & UARTSR1_RDRF && !(crdma & UARTCR5_RDMAS)) {
+ if (sport->lpuart_dma_rx_use)
lpuart_prepare_rx(sport);
else
lpuart_rxint(irq, dev_id);
}
- if (sts & UARTSR1_TDRE &&
- !(readb(sport->port.membase + UARTCR5) & UARTCR5_TDMAS)) {
- if (sport->lpuart_dma_use)
+ if (sts & UARTSR1_TDRE && !(crdma & UARTCR5_TDMAS)) {
+ if (sport->lpuart_dma_tx_use)
lpuart_pio_tx(sport);
else
lpuart_txint(irq, dev_id);
@@ -913,6 +921,9 @@ static void lpuart_setup_watermark(struct lpuart_port *sport)
writeb(val | UARTPFIFO_TXFE | UARTPFIFO_RXFE,
sport->port.membase + UARTPFIFO);
+ /* explicitly clear RDRF */
+ readb(sport->port.membase + UARTSR1);
+
/* flush Tx and Rx FIFO */
writeb(UARTCFIFO_TXFLUSH | UARTCFIFO_RXFLUSH,
sport->port.membase + UARTCFIFO);
@@ -953,26 +964,17 @@ static int lpuart_dma_tx_request(struct uart_port *port)
{
struct lpuart_port *sport = container_of(port,
struct lpuart_port, port);
- struct dma_chan *tx_chan;
struct dma_slave_config dma_tx_sconfig;
dma_addr_t dma_bus;
unsigned char *dma_buf;
int ret;
- tx_chan = dma_request_slave_channel(sport->port.dev, "tx");
-
- if (!tx_chan) {
- dev_err(sport->port.dev, "Dma tx channel request failed!\n");
- return -ENODEV;
- }
-
- dma_bus = dma_map_single(tx_chan->device->dev,
+ dma_bus = dma_map_single(sport->dma_tx_chan->device->dev,
sport->port.state->xmit.buf,
UART_XMIT_SIZE, DMA_TO_DEVICE);
- if (dma_mapping_error(tx_chan->device->dev, dma_bus)) {
+ if (dma_mapping_error(sport->dma_tx_chan->device->dev, dma_bus)) {
dev_err(sport->port.dev, "dma_map_single tx failed\n");
- dma_release_channel(tx_chan);
return -ENOMEM;
}
@@ -981,16 +983,14 @@ static int lpuart_dma_tx_request(struct uart_port *port)
dma_tx_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
dma_tx_sconfig.dst_maxburst = sport->txfifo_size;
dma_tx_sconfig.direction = DMA_MEM_TO_DEV;
- ret = dmaengine_slave_config(tx_chan, &dma_tx_sconfig);
+ ret = dmaengine_slave_config(sport->dma_tx_chan, &dma_tx_sconfig);
if (ret < 0) {
dev_err(sport->port.dev,
"Dma slave config failed, err = %d\n", ret);
- dma_release_channel(tx_chan);
return ret;
}
- sport->dma_tx_chan = tx_chan;
sport->dma_tx_buf_virt = dma_buf;
sport->dma_tx_buf_bus = dma_bus;
sport->dma_tx_in_progress = 0;
@@ -1002,34 +1002,24 @@ static int lpuart_dma_rx_request(struct uart_port *port)
{
struct lpuart_port *sport = container_of(port,
struct lpuart_port, port);
- struct dma_chan *rx_chan;
struct dma_slave_config dma_rx_sconfig;
dma_addr_t dma_bus;
unsigned char *dma_buf;
int ret;
- rx_chan = dma_request_slave_channel(sport->port.dev, "rx");
-
- if (!rx_chan) {
- dev_err(sport->port.dev, "Dma rx channel request failed!\n");
- return -ENODEV;
- }
-
dma_buf = devm_kzalloc(sport->port.dev,
FSL_UART_RX_DMA_BUFFER_SIZE, GFP_KERNEL);
if (!dma_buf) {
dev_err(sport->port.dev, "Dma rx alloc failed\n");
- dma_release_channel(rx_chan);
return -ENOMEM;
}
- dma_bus = dma_map_single(rx_chan->device->dev, dma_buf,
+ dma_bus = dma_map_single(sport->dma_rx_chan->device->dev, dma_buf,
FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
- if (dma_mapping_error(rx_chan->device->dev, dma_bus)) {
+ if (dma_mapping_error(sport->dma_rx_chan->device->dev, dma_bus)) {
dev_err(sport->port.dev, "dma_map_single rx failed\n");
- dma_release_channel(rx_chan);
return -ENOMEM;
}
@@ -1037,16 +1027,14 @@ static int lpuart_dma_rx_request(struct uart_port *port)
dma_rx_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
dma_rx_sconfig.src_maxburst = 1;
dma_rx_sconfig.direction = DMA_DEV_TO_MEM;
- ret = dmaengine_slave_config(rx_chan, &dma_rx_sconfig);
+ ret = dmaengine_slave_config(sport->dma_rx_chan, &dma_rx_sconfig);
if (ret < 0) {
dev_err(sport->port.dev,
"Dma slave config failed, err = %d\n", ret);
- dma_release_channel(rx_chan);
return ret;
}
- sport->dma_rx_chan = rx_chan;
sport->dma_rx_buf_virt = dma_buf;
sport->dma_rx_buf_bus = dma_bus;
sport->dma_rx_in_progress = 0;
@@ -1058,31 +1046,24 @@ static void lpuart_dma_tx_free(struct uart_port *port)
{
struct lpuart_port *sport = container_of(port,
struct lpuart_port, port);
- struct dma_chan *dma_chan;
dma_unmap_single(sport->port.dev, sport->dma_tx_buf_bus,
UART_XMIT_SIZE, DMA_TO_DEVICE);
- dma_chan = sport->dma_tx_chan;
- sport->dma_tx_chan = NULL;
+
sport->dma_tx_buf_bus = 0;
sport->dma_tx_buf_virt = NULL;
- dma_release_channel(dma_chan);
}
static void lpuart_dma_rx_free(struct uart_port *port)
{
struct lpuart_port *sport = container_of(port,
struct lpuart_port, port);
- struct dma_chan *dma_chan;
dma_unmap_single(sport->port.dev, sport->dma_rx_buf_bus,
FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
- dma_chan = sport->dma_rx_chan;
- sport->dma_rx_chan = NULL;
sport->dma_rx_buf_bus = 0;
sport->dma_rx_buf_virt = NULL;
- dma_release_channel(dma_chan);
}
static int lpuart_startup(struct uart_port *port)
@@ -1098,17 +1079,26 @@ static int lpuart_startup(struct uart_port *port)
sport->txfifo_size = 0x1 << (((temp >> UARTPFIFO_TXSIZE_OFF) &
UARTPFIFO_FIFOSIZE_MASK) + 1);
+ sport->port.fifosize = sport->txfifo_size;
+
sport->rxfifo_size = 0x1 << (((temp >> UARTPFIFO_RXSIZE_OFF) &
UARTPFIFO_FIFOSIZE_MASK) + 1);
- /* Whether use dma support by dma request results */
- if (lpuart_dma_tx_request(port) || lpuart_dma_rx_request(port)) {
- sport->lpuart_dma_use = false;
- } else {
- sport->lpuart_dma_use = true;
+ if (sport->dma_rx_chan && !lpuart_dma_rx_request(port)) {
+ sport->lpuart_dma_rx_use = true;
+ setup_timer(&sport->lpuart_timer, lpuart_timer_func,
+ (unsigned long)sport);
+ } else
+ sport->lpuart_dma_rx_use = false;
+
+
+ if (sport->dma_tx_chan && !lpuart_dma_tx_request(port)) {
+ sport->lpuart_dma_tx_use = true;
temp = readb(port->membase + UARTCR5);
+ temp &= ~UARTCR5_RDMAS;
writeb(temp | UARTCR5_TDMAS, port->membase + UARTCR5);
- }
+ } else
+ sport->lpuart_dma_tx_use = false;
ret = devm_request_irq(port->dev, port->irq, lpuart_int, 0,
DRIVER_NAME, sport);
@@ -1179,10 +1169,13 @@ static void lpuart_shutdown(struct uart_port *port)
devm_free_irq(port->dev, port->irq, sport);
- if (sport->lpuart_dma_use) {
- lpuart_dma_tx_free(port);
- lpuart_dma_rx_free(port);
+ if (sport->lpuart_dma_rx_use) {
+ lpuart_dma_rx_free(&sport->port);
+ del_timer_sync(&sport->lpuart_timer);
}
+
+ if (sport->lpuart_dma_tx_use)
+ lpuart_dma_tx_free(&sport->port);
}
static void lpuart32_shutdown(struct uart_port *port)
@@ -1304,7 +1297,7 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios,
/* update the per-port timeout */
uart_update_timeout(port, termios->c_cflag, baud);
- if (sport->lpuart_dma_use) {
+ if (sport->lpuart_dma_rx_use) {
/* Calculate delay for 1.5 DMA buffers */
sport->dma_rx_timeout = (sport->port.timeout - HZ / 50) *
FSL_UART_RX_DMA_BUFFER_SIZE * 3 /
@@ -1517,6 +1510,7 @@ static struct uart_ops lpuart_pops = {
.release_port = lpuart_release_port,
.config_port = lpuart_config_port,
.verify_port = lpuart_verify_port,
+ .flush_buffer = lpuart_flush_buffer,
};
static struct uart_ops lpuart32_pops = {
@@ -1535,6 +1529,7 @@ static struct uart_ops lpuart32_pops = {
.release_port = lpuart_release_port,
.config_port = lpuart_config_port,
.verify_port = lpuart_verify_port,
+ .flush_buffer = lpuart_flush_buffer,
};
static struct lpuart_port *lpuart_ports[UART_NR];
@@ -1833,6 +1828,16 @@ static int lpuart_probe(struct platform_device *pdev)
return ret;
}
+ sport->dma_tx_chan = dma_request_slave_channel(sport->port.dev, "tx");
+ if (!sport->dma_tx_chan)
+ dev_info(sport->port.dev, "DMA tx channel request failed, "
+ "operating without tx DMA\n");
+
+ sport->dma_rx_chan = dma_request_slave_channel(sport->port.dev, "rx");
+ if (!sport->dma_rx_chan)
+ dev_info(sport->port.dev, "DMA rx channel request failed, "
+ "operating without rx DMA\n");
+
return 0;
}
@@ -1844,6 +1849,12 @@ static int lpuart_remove(struct platform_device *pdev)
clk_disable_unprepare(sport->clk);
+ if (sport->dma_tx_chan)
+ dma_release_channel(sport->dma_tx_chan);
+
+ if (sport->dma_rx_chan)
+ dma_release_channel(sport->dma_rx_chan);
+
return 0;
}
@@ -1851,6 +1862,19 @@ static int lpuart_remove(struct platform_device *pdev)
static int lpuart_suspend(struct device *dev)
{
struct lpuart_port *sport = dev_get_drvdata(dev);
+ unsigned long temp;
+
+ if (sport->lpuart32) {
+ /* disable Rx/Tx and interrupts */
+ temp = lpuart32_read(sport->port.membase + UARTCTRL);
+ temp &= ~(UARTCTRL_TE | UARTCTRL_TIE | UARTCTRL_TCIE);
+ lpuart32_write(temp, sport->port.membase + UARTCTRL);
+ } else {
+ /* disable Rx/Tx and interrupts */
+ temp = readb(sport->port.membase + UARTCR2);
+ temp &= ~(UARTCR2_TE | UARTCR2_TIE | UARTCR2_TCIE);
+ writeb(temp, sport->port.membase + UARTCR2);
+ }
uart_suspend_port(&lpuart_reg, &sport->port);