diff options
Diffstat (limited to 'drivers/tty/serial/fsl_lpuart.c')
-rw-r--r-- | drivers/tty/serial/fsl_lpuart.c | 208 |
1 files changed, 134 insertions, 74 deletions
diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 08ce76f4f261..be6c011b642d 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -230,6 +230,9 @@ #define DEV_NAME "ttyLP" #define UART_NR 6 +static bool nodma = true; +module_param(nodma, bool, S_IRUGO); + struct lpuart_port { struct uart_port port; struct clk *clk; @@ -342,38 +345,19 @@ static void lpuart_copy_rx_to_tty(struct lpuart_port *sport, FSL_UART_RX_DMA_BUFFER_SIZE, DMA_TO_DEVICE); } -static void lpuart_pio_tx(struct lpuart_port *sport) -{ - struct circ_buf *xmit = &sport->port.state->xmit; - unsigned long flags; - - spin_lock_irqsave(&sport->port.lock, flags); - - while (!uart_circ_empty(xmit) && - readb(sport->port.membase + UARTTCFIFO) < sport->txfifo_size) { - writeb(xmit->buf[xmit->tail], sport->port.membase + UARTDR); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - sport->port.icount.tx++; - } - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&sport->port); - - if (uart_circ_empty(xmit)) - writeb(readb(sport->port.membase + UARTCR5) | UARTCR5_TDMAS, - sport->port.membase + UARTCR5); - - spin_unlock_irqrestore(&sport->port.lock, flags); -} - -static int lpuart_dma_tx(struct lpuart_port *sport, unsigned long count) +static int lpuart_dma_tx(struct lpuart_port *sport) { struct circ_buf *xmit = &sport->port.state->xmit; + unsigned long count = CIRC_CNT_TO_END(xmit->head, + xmit->tail, UART_XMIT_SIZE); dma_addr_t tx_bus_addr; + if (!count) + return 0; + dma_sync_single_for_device(sport->port.dev, sport->dma_tx_buf_bus, UART_XMIT_SIZE, DMA_TO_DEVICE); - sport->dma_tx_bytes = count & ~(sport->txfifo_size - 1); + sport->dma_tx_bytes = count; tx_bus_addr = sport->dma_tx_buf_bus + xmit->tail; sport->dma_tx_desc = dmaengine_prep_slave_single(sport->dma_tx_chan, tx_bus_addr, sport->dma_tx_bytes, @@ -393,25 +377,6 @@ static int lpuart_dma_tx(struct lpuart_port *sport, unsigned long count) return 0; } -static void lpuart_prepare_tx(struct lpuart_port *sport) -{ - struct circ_buf *xmit = &sport->port.state->xmit; - unsigned long count = CIRC_CNT_TO_END(xmit->head, - xmit->tail, UART_XMIT_SIZE); - - if (!count) - return; - - if (count < sport->txfifo_size) - writeb(readb(sport->port.membase + UARTCR5) & ~UARTCR5_TDMAS, - sport->port.membase + UARTCR5); - else { - writeb(readb(sport->port.membase + UARTCR5) | UARTCR5_TDMAS, - sport->port.membase + UARTCR5); - lpuart_dma_tx(sport, count); - } -} - static void lpuart_dma_tx_complete(void *arg) { struct lpuart_port *sport = arg; @@ -428,7 +393,7 @@ static void lpuart_dma_tx_complete(void *arg) if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(&sport->port); - lpuart_prepare_tx(sport); + lpuart_dma_tx(sport); spin_unlock_irqrestore(&sport->port.lock, flags); } @@ -483,9 +448,8 @@ static void lpuart_dma_rx_complete(void *arg) spin_unlock_irqrestore(&sport->port.lock, flags); } -static void lpuart_timer_func(unsigned long data) +static void lpuart_dma_rx_terminate(struct lpuart_port *sport) { - struct lpuart_port *sport = (struct lpuart_port *)data; struct tty_port *port = &sport->port.state->port; struct dma_tx_state state; unsigned long flags; @@ -510,6 +474,11 @@ static void lpuart_timer_func(unsigned long data) spin_unlock_irqrestore(&sport->port.lock, flags); } +static void lpuart_timer_func(unsigned long data) +{ + lpuart_dma_rx_terminate((struct lpuart_port *)data); +} + static inline void lpuart_prepare_rx(struct lpuart_port *sport) { unsigned long flags; @@ -581,7 +550,7 @@ static void lpuart_start_tx(struct uart_port *port) if (sport->lpuart_dma_tx_use) { if (!uart_circ_empty(xmit) && !sport->dma_tx_in_progress) - lpuart_prepare_tx(sport); + lpuart_dma_tx(sport); } else { if (readb(port->membase + UARTSR1) & UARTSR1_TDRE) lpuart_transmit_buffer(sport); @@ -778,10 +747,8 @@ static irqreturn_t lpuart_int(int irq, void *dev_id) lpuart_rxint(irq, dev_id); } if (sts & UARTSR1_TDRE && !(crdma & UARTCR5_TDMAS)) { - if (sport->lpuart_dma_tx_use) - lpuart_pio_tx(sport); - else - lpuart_txint(irq, dev_id); + BUG_ON(sport->lpuart_dma_tx_use); + lpuart_txint(irq, dev_id); } return IRQ_HANDLED; @@ -810,8 +777,18 @@ static irqreturn_t lpuart32_int(int irq, void *dev_id) /* return TIOCSER_TEMT when transmitter is not busy */ static unsigned int lpuart_tx_empty(struct uart_port *port) { - return (readb(port->membase + UARTSR1) & UARTSR1_TC) ? - TIOCSER_TEMT : 0; + struct lpuart_port *sport = container_of(port, + struct lpuart_port, port); + unsigned char sr1 = readb(port->membase + UARTSR1); + unsigned char sfifo = readb(port->membase + UARTSFIFO); + + if (sport->dma_tx_in_progress) + return 0; + + if (sr1 & UARTSR1_TC && sfifo & UARTSFIFO_TXEMPT) + return TIOCSER_TEMT; + + return 0; } static unsigned int lpuart32_tx_empty(struct uart_port *port) @@ -820,6 +797,52 @@ static unsigned int lpuart32_tx_empty(struct uart_port *port) TIOCSER_TEMT : 0; } +static int lpuart_config_rs485(struct uart_port *port, + struct serial_rs485 *rs485) +{ + struct lpuart_port *sport = container_of(port, + struct lpuart_port, port); + u8 modem = readb(sport->port.membase + UARTMODEM) & + ~(UARTMODEM_TXRTSPOL | UARTMODEM_TXRTSE); + writeb(modem, sport->port.membase + UARTMODEM); + + if (rs485->flags & SER_RS485_ENABLED) { + /* Enable auto RS-485 RTS mode */ + modem |= UARTMODEM_TXRTSE; + + /* + * RTS needs to be logic HIGH either during transer _or_ after + * transfer, other variants are not supported by the hardware. + */ + + if (!(rs485->flags & (SER_RS485_RTS_ON_SEND | + SER_RS485_RTS_AFTER_SEND))) + rs485->flags |= SER_RS485_RTS_ON_SEND; + + if (rs485->flags & SER_RS485_RTS_ON_SEND && + rs485->flags & SER_RS485_RTS_AFTER_SEND) + + rs485->flags &= ~SER_RS485_RTS_AFTER_SEND; + + /* + * The hardware defaults to RTS logic HIGH while transfer. + * Switch polarity in case RTS shall be logic HIGH + * after transfer. + * Note: UART is assumed to be active high. + */ + if (rs485->flags & SER_RS485_RTS_ON_SEND) + modem &= ~UARTMODEM_TXRTSPOL; + else if (rs485->flags & SER_RS485_RTS_AFTER_SEND) + modem |= UARTMODEM_TXRTSPOL; + } + + /* Store the new configuration */ + sport->port.rs485 = *rs485; + + writeb(modem, sport->port.membase + UARTMODEM); + return 0; +} + static unsigned int lpuart_get_mctrl(struct uart_port *port) { unsigned int temp = 0; @@ -852,18 +875,23 @@ static unsigned int lpuart32_get_mctrl(struct uart_port *port) static void lpuart_set_mctrl(struct uart_port *port, unsigned int mctrl) { + struct lpuart_port *sport = container_of(port, + struct lpuart_port, port); unsigned char temp; - temp = readb(port->membase + UARTMODEM) & + /* Make sure RXRTSE bit is not set when RS485 is enabled */ + if (!(sport->port.rs485.flags & SER_RS485_ENABLED)) { + temp = readb(sport->port.membase + UARTMODEM) & ~(UARTMODEM_RXRTSE | UARTMODEM_TXCTSE); - if (mctrl & TIOCM_RTS) - temp |= UARTMODEM_RXRTSE; + if (mctrl & TIOCM_RTS) + temp |= UARTMODEM_RXRTSE; - if (mctrl & TIOCM_CTS) - temp |= UARTMODEM_TXCTSE; + if (mctrl & TIOCM_CTS) + temp |= UARTMODEM_TXCTSE; - writeb(temp, port->membase + UARTMODEM); + writeb(temp, sport->port.membase + UARTMODEM); + } } static void lpuart32_set_mctrl(struct uart_port *port, unsigned int mctrl) @@ -921,14 +949,17 @@ 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); - writeb(0, sport->port.membase + UARTTWFIFO); + /* explicitly clear RDRF */ + if (readb(sport->port.membase + UARTSR1) & UARTSR1_RDRF) { + readb(sport->port.membase + UARTDR); + writeb(UARTSFIFO_RXUF, sport->port.membase + UARTSFIFO); + } + + writeb(sport->txfifo_size / 2, sport->port.membase + UARTTWFIFO); writeb(1, sport->port.membase + UARTRWFIFO); /* Restore cr2 */ @@ -981,7 +1012,7 @@ static int lpuart_dma_tx_request(struct uart_port *port) dma_buf = sport->port.state->xmit.buf; dma_tx_sconfig.dst_addr = sport->port.mapbase + UARTDR; dma_tx_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; - dma_tx_sconfig.dst_maxburst = sport->txfifo_size; + dma_tx_sconfig.dst_maxburst = 1; dma_tx_sconfig.direction = DMA_MEM_TO_DEV; ret = dmaengine_slave_config(sport->dma_tx_chan, &dma_tx_sconfig); @@ -1240,6 +1271,12 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios, cr1 |= UARTCR1_M; } + /* When auto RS-485 RTS mode is enabled, + * hardware flow control need to be disabled. + */ + if (sport->port.rs485.flags & SER_RS485_ENABLED) + termios->c_cflag &= ~CRTSCTS; + if (termios->c_cflag & CRTSCTS) { modem |= (UARTMODEM_RXRTSE | UARTMODEM_TXCTSE); } else { @@ -1761,6 +1798,18 @@ static struct uart_driver lpuart_reg = { .cons = LPUART_CONSOLE, }; +static struct dma_chan *lpuart_request_dma_chan(struct lpuart_port *sport, + const char *name) +{ + struct dma_chan *chan; + + chan = dma_request_slave_channel(sport->port.dev, name); + if (!chan) + dev_info(sport->port.dev, "DMA %s channel request failed, " + "operating without %s DMA\n", name, name); + return chan; +} + static int lpuart_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -1798,6 +1847,8 @@ static int lpuart_probe(struct platform_device *pdev) sport->port.ops = &lpuart_pops; sport->port.flags = UPF_BOOT_AUTOCONF; + sport->port.rs485_config = lpuart_config_rs485; + sport->clk = devm_clk_get(&pdev->dev, "ipg"); if (IS_ERR(sport->clk)) { ret = PTR_ERR(sport->clk); @@ -1828,15 +1879,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"); + if (!nodma) { + sport->dma_tx_chan = lpuart_request_dma_chan(sport, "tx"); + sport->dma_rx_chan = lpuart_request_dma_chan(sport, "rx"); + } - 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"); + if (of_property_read_bool(np, "linux,rs485-enabled-at-boot-time")) { + sport->port.rs485.flags |= SER_RS485_ENABLED; + sport->port.rs485.flags |= SER_RS485_RTS_ON_SEND; + writeb(UARTMODEM_TXRTSE, sport->port.membase + UARTMODEM); + } return 0; } @@ -1876,7 +1928,12 @@ static int lpuart_suspend(struct device *dev) writeb(temp, sport->port.membase + UARTCR2); } + if (sport->dma_rx_in_progress) + lpuart_dma_rx_terminate(sport); + uart_suspend_port(&lpuart_reg, &sport->port); + if (sport->port.suspended && !sport->port.irq_wake) + clk_disable_unprepare(sport->clk); return 0; } @@ -1886,6 +1943,9 @@ static int lpuart_resume(struct device *dev) struct lpuart_port *sport = dev_get_drvdata(dev); unsigned long temp; + if (sport->port.suspended && !sport->port.irq_wake) + clk_prepare_enable(sport->clk); + if (sport->lpuart32) { lpuart32_setup_watermark(sport); temp = lpuart32_read(sport->port.membase + UARTCTRL); |