diff options
author | Bhuvanchandra DV <bhuvanchandra.dv@toradex.com> | 2015-08-10 19:46:14 +0530 |
---|---|---|
committer | Stefan Agner <stefan.agner@toradex.com> | 2015-08-11 09:53:15 +0200 |
commit | dc92514c086119369c8c07b5b17c5312e0711d75 (patch) | |
tree | 942825217e125999ca2b0f6ec92d1cc192f0a491 | |
parent | 21cd9f04484738f7a4d6440aa29c741b0fe8c0fd (diff) |
tty: serial: fsl_lpuart: Add support for RS-485
Enable Vybrid's build-in support for RS-485 auto RTS for
controlling line direction of RS-485 transceiver driver.
Enable RS485 feature by either using ioctrl 'TIOCSRS485'
or enable it in the device tree by setting
'linux,rs485-enabled-at-boot-time' property.
e.g.
&uart2 {
status = "okay";
linux,rs485-enabled-at-boot-time;
};
Signed-off-by: Bhuvanchandra DV <bhuvanchandra.dv@toradex.com>
-rw-r--r-- | drivers/tty/serial/fsl_lpuart.c | 77 |
1 files changed, 71 insertions, 6 deletions
diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index e9700823052b..1f52532ce943 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -794,6 +794,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; @@ -826,18 +872,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) @@ -1217,6 +1268,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 { @@ -1775,6 +1832,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); @@ -1815,6 +1874,12 @@ static int lpuart_probe(struct platform_device *pdev) 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; } |