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.c102
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)