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.c106
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);