diff options
Diffstat (limited to 'drivers/tty/serial/fsl_lpuart.c')
-rw-r--r-- | drivers/tty/serial/fsl_lpuart.c | 106 |
1 files changed, 99 insertions, 7 deletions
diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 472c9a04ed6b..c141fdbe2a16 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -1213,6 +1213,48 @@ static inline void lpuart_prepare_rx(struct lpuart_port *sport) spin_unlock_irqrestore(&sport->port.lock, flags); } +static void 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) { @@ -1264,6 +1306,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; @@ -1416,8 +1493,10 @@ static void lpuart32_setup_watermark(struct lpuart_port *sport) val = (0x1 << UARTWATER_RXWATER_OFF) | (0x0 << UARTWATER_TXWATER_OFF); } else { - val = lpuart32_read(&sport->port, UARTMODIR); - val = sport->rts_watermark << UARTMODIR_RTSWATER_S; + val = lpuart32_read(&sport->port, UARTMODIR) & + ~UARTMODIR_RTSWATER_M; + val |= (sport->rts_watermark << UARTMODIR_RTSWATER_S) & + UARTMODIR_RTSWATER_M; lpuart32_write(&sport->port, val, UARTMODIR); val = (sport->rxfifo_watermark << UARTWATER_RXWATER_OFF) | (sport->txfifo_watermark << UARTWATER_TXWATER_OFF); @@ -1576,6 +1655,7 @@ static int lpuart32_startup(struct uart_port *port) spin_lock_irqsave(&sport->port.lock, flags); + lpuart32_setup_rs485(sport); lpuart32_setup_watermark(sport); temp = lpuart32_read(&sport->port, UARTCTRL); @@ -1938,6 +2018,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 { @@ -2437,7 +2524,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 cr_32; + unsigned long cr_32, flags; unsigned char cr_8; int ret; @@ -2480,7 +2567,9 @@ static int lpuart_probe(struct platform_device *pdev) sport->port.ops = &lpuart_pops; sport->port.flags = UPF_BOOT_AUTOCONF; - if (!lpuart_is_32(sport)) + if (lpuart_is_32(sport)) + sport->port.rs485_config = lpuart32_config_rs485; + else sport->port.rs485_config = lpuart_config_rs485; sport->ipg_clk = devm_clk_get(&pdev->dev, "ipg"); @@ -2558,11 +2647,13 @@ static int lpuart_probe(struct platform_device *pdev) if (!sport->dma_rx_chan) dev_info(sport->port.dev, "NO DMA rx channel, run at cpu mode\n"); - if (!lpuart_is_32(sport) && - of_property_read_bool(np, "linux,rs485-enabled-at-boot-time")) { + 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); + + spin_lock_irqsave(&sport->port.lock, flags); + sport->port.rs485_config(&sport->port, &sport->port.rs485); + spin_unlock_irqrestore(&sport->port.lock, flags); } return 0; @@ -2800,6 +2891,7 @@ static inline void lpuart32_resume_init(struct lpuart_port *sport) unsigned long flags; spin_lock_irqsave(&sport->port.lock, flags); + lpuart32_setup_rs485(sport); lpuart32_setup_watermark(sport); temp = lpuart32_read(&sport->port, UARTCTRL); |