diff options
Diffstat (limited to 'drivers/tty/serial/fsl_lpuart.c')
-rw-r--r-- | drivers/tty/serial/fsl_lpuart.c | 102 |
1 files changed, 98 insertions, 4 deletions
diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index d1f8840e871d..b822e54f2d13 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -207,6 +207,7 @@ #define UARTMODIR_IREN 0x00020000 #define UARTMODIR_RTSWATER_S 0x8 +#define UARTMODIR_RTSWATER_M 0x0000ff00 #define UARTMODIR_TXCTSSRC 0x00000020 #define UARTMODIR_TXCTSC 0x00000010 #define UARTMODIR_RXRTSE 0x00000008 @@ -1424,6 +1425,48 @@ static void lpuart_dma_rx_free(struct uart_port *port) sport->dma_rx_cookie = -EINVAL; } +static void __maybe_unused lpuart_setup_rs485(struct lpuart_port *sport) +{ + /* Todo implement for 8-bit registers */ +} + +static void lpuart32_setup_rs485(struct lpuart_port *sport) +{ + struct serial_rs485 *rs485 = &sport->port.rs485; + unsigned long val, ctrl, ctrl_saved; + + val = lpuart32_read(&sport->port, UARTMODIR) & + ~(UARTMODIR_TXRTSPOL | UARTMODIR_TXRTSE); + + /* Make sure transmitter is disabled */ + ctrl = lpuart32_read(&sport->port, UARTCTRL); + ctrl_saved = ctrl; + ctrl &= ~(UARTCTRL_TIE | UARTCTRL_TCIE | UARTCTRL_TE | + UARTCTRL_RIE | UARTCTRL_RE); + lpuart32_write(&sport->port, ctrl, UARTCTRL); + + if (rs485->flags & SER_RS485_ENABLED) { + /* Enable auto RS-485 RTS mode */ + val |= UARTMODIR_TXRTSE; + + /* + * 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) + val &= ~UARTMODIR_TXRTSPOL; + else if (rs485->flags & SER_RS485_RTS_AFTER_SEND) + val |= UARTMODIR_TXRTSPOL; + } + + lpuart32_write(&sport->port, val, UARTMODIR); + + /* Restore cr2 */ + lpuart32_write(&sport->port, ctrl_saved, UARTCTRL); +} + static int lpuart_config_rs485(struct uart_port *port, struct serial_rs485 *rs485) { @@ -1475,6 +1518,41 @@ static int lpuart_config_rs485(struct uart_port *port, return 0; } +static int lpuart32_config_rs485(struct uart_port *port, + struct serial_rs485 *rs485) +{ + struct lpuart_port *sport = container_of(port, + struct lpuart_port, port); + + /* clear unsupported configurations */ + rs485->delay_rts_before_send = 0; + rs485->delay_rts_after_send = 0; + rs485->flags &= ~SER_RS485_RX_DURING_TX; + + if (rs485->flags & SER_RS485_ENABLED) { + /* + * 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; + } + + /* Store the new configuration */ + sport->port.rs485 = *rs485; + + /* config_rs485 gets called in irqsave context so do not lock again */ + lpuart32_setup_rs485(sport); + + return 0; +} + static unsigned int lpuart_get_mctrl(struct uart_port *port) { unsigned int temp = 0; @@ -1650,8 +1728,10 @@ static void lpuart32_setup_watermark(struct lpuart_port *sport) /* set RTS watermark */ if (!uart_console(&sport->port)) { - val = lpuart32_read(&sport->port, UARTMODIR); - val = (sport->rxfifo_size >> 1) << UARTMODIR_RTSWATER_S; + val = lpuart32_read(&sport->port, UARTMODIR) & + ~UARTMODIR_RTSWATER_M; + val |= ((sport->rxfifo_size >> 1) << UARTMODIR_RTSWATER_S) & + UARTMODIR_RTSWATER_M; lpuart32_write(&sport->port, val, UARTMODIR); } @@ -1791,6 +1871,7 @@ static void lpuart32_hw_setup(struct lpuart_port *sport) lpuart_rx_dma_startup(sport); lpuart_tx_dma_startup(sport); + lpuart32_setup_rs485(sport); lpuart32_setup_watermark_enable(sport); lpuart32_configure(sport); @@ -2170,6 +2251,13 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios, ctrl |= UARTCTRL_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 { @@ -2713,6 +2801,7 @@ static int lpuart_probe(struct platform_device *pdev) struct device_node *np = pdev->dev.of_node; struct lpuart_port *sport; struct resource *res; + unsigned long flags; int ret; sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL); @@ -2744,7 +2833,10 @@ 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; + if (lpuart_is_32(sport)) + sport->port.rs485_config = lpuart32_config_rs485; + else + sport->port.rs485_config = lpuart_config_rs485; ret = lpuart_attach_pd(&pdev->dev); if (ret) @@ -2827,7 +2919,9 @@ static int lpuart_probe(struct platform_device *pdev) sport->port.rs485.delay_rts_after_send) dev_err(&pdev->dev, "driver doesn't support RTS delays\n"); - lpuart_config_rs485(&sport->port, &sport->port.rs485); + spin_lock_irqsave(&sport->port.lock, flags); + sport->port.rs485_config(&sport->port, &sport->port.rs485); + spin_unlock_irqrestore(&sport->port.lock, flags); sport->dma_tx_chan = dma_request_slave_channel(sport->port.dev, "tx"); if (!sport->dma_tx_chan) |