On Mon, Nov 22, 2021 at 05:01:07PM +0800, Su Bao Cheng wrote: > The new patch works on our setup. Thanks for testing. Actually AM65 supports RS-485 in hardware, so the software emulation which is currently used isn't necessary. Below is a tentative, compile-tested only patch to add support for that. Would you mind giving it a spin and see if it works? Unfortunately I can't test it myself. I do have an AM64 EVM on my desk, but it lacks an on-board RS-485 transceiver. (Only the AM65 EVM has one.) If the RTS pin is controlled by hardware, the delays on assertion and deassertion of RTS are fixed (cannot be configured). Also I'm not sure whether full-duplex (RS-422) works correctly and whether you'll see your own echo when sending in RS-485 mode. Thanks! Lukas -- >8 -- Subject: [PATCH] serial: 8250: 8250_omap: Support native rs485 Recent TI Sitara SoCs such as AM64/AM65 have gained the ability to automatically assert RTS when data is transmitted, obviating the need to emulate this functionality in software. The feature is controlled through new DIR_EN and DIR_POL bits in the Mode Definition Register 3. For details see page 8783 and 8890 of the AM65 TRM: https://www.ti.com/lit/ug/spruid7e/spruid7e.pdf Signed-off-by: Lukas Wunner <lukas@xxxxxxxxx> Cc: Jan Kiszka <jan.kiszka@xxxxxxxxxxx> Cc: Su Bao Cheng <baocheng.su@xxxxxxxxxxx> Cc: Vignesh Raghavendra <vigneshr@xxxxxx> Cc: Peter Ujfalusi <peter.ujfalusi@xxxxxx> Cc: Nishanth Menon <nm@xxxxxx> --- drivers/tty/serial/8250/8250_omap.c | 73 +++++++++++++++++++++++++++-- 1 file changed, 69 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 0fea7bde25ea..46e6c8cb2841 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -44,6 +44,7 @@ #define UART_HAS_EFR2 BIT(4) #define UART_HAS_RHR_IT_DIS BIT(5) #define UART_RX_TIMEOUT_QUIRK BIT(6) +#define UART_HAS_NATIVE_RS485 BIT(7) #define OMAP_UART_FCR_RX_TRIG 6 #define OMAP_UART_FCR_TX_TRIG 4 @@ -101,6 +102,11 @@ #define UART_OMAP_IER2 0x1B #define UART_OMAP_IER2_RHR_IT_DIS BIT(2) +/* Mode Definition Register 3 */ +#define UART_OMAP_MDR3 0x20 +#define UART_OMAP_MDR3_DIR_POL BIT(3) +#define UART_OMAP_MDR3_DIR_EN BIT(4) + /* Enhanced features register 2 */ #define UART_OMAP_EFR2 0x23 #define UART_OMAP_EFR2_TIMEOUT_BEHAVE BIT(6) @@ -807,6 +813,60 @@ static void omap_8250_unthrottle(struct uart_port *port) pm_runtime_put_autosuspend(port->dev); } +static int omap8250_rs485_config(struct uart_port *port, + struct serial_rs485 *rs485) +{ + struct uart_8250_port *up = up_to_u8250p(port); + struct omap8250_priv *priv = port->private_data; + unsigned int baud; + u32 reg; + + /* pick sane settings if the user hasn't */ + if (!!(rs485->flags & SER_RS485_RTS_ON_SEND) == + !!(rs485->flags & SER_RS485_RTS_AFTER_SEND)) { + rs485->flags |= SER_RS485_RTS_ON_SEND; + rs485->flags &= ~SER_RS485_RTS_AFTER_SEND; + } + + /* + * There is a fixed delay of 3 bit clock cycles after the TX shift + * register is going empty to allow time for the stop bit to transition + * through the transceiver before direction is changed to receive. + */ + if (priv->quot) { + if (priv->mdr1 & UART_OMAP_MDR1_16X_MODE) + baud = port->uartclk / (16 * priv->quot); + else + baud = port->uartclk / (13 * priv->quot); + rs485->delay_rts_after_send = 3 * MSEC_PER_SEC / baud; + } else { + rs485->delay_rts_after_send = 0; + } + rs485->delay_rts_before_send = 0; + + memset(rs485->padding, 0, sizeof(rs485->padding)); + port->rs485 = *rs485; + + gpiod_set_value(port->rs485_term_gpio, + rs485->flags & SER_RS485_TERMINATE_BUS); + + reg = serial_in(up, UART_OMAP_MDR3); + + if (rs485->flags & SER_RS485_ENABLED) + reg |= UART_OMAP_MDR3_DIR_EN; + else + reg &= ~UART_OMAP_MDR3_DIR_EN; + + if (rs485->flags & SER_RS485_RTS_ON_SEND) + reg |= UART_OMAP_MDR3_DIR_POL; + else + reg &= ~UART_OMAP_MDR3_DIR_POL; + + serial_out(up, UART_OMAP_MDR3, reg); + + return 0; +} + #ifdef CONFIG_SERIAL_8250_DMA static int omap_8250_rx_dma(struct uart_8250_port *p); @@ -1259,7 +1319,7 @@ static struct omap8250_dma_params am33xx_dma = { static struct omap8250_platdata am654_platdata = { .dma_params = &am654_dma, .habit = UART_HAS_EFR2 | UART_HAS_RHR_IT_DIS | - UART_RX_TIMEOUT_QUIRK, + UART_RX_TIMEOUT_QUIRK | UART_HAS_NATIVE_RS485, }; static struct omap8250_platdata am33xx_platdata = { @@ -1352,9 +1412,6 @@ static int omap8250_probe(struct platform_device *pdev) up.port.shutdown = omap_8250_shutdown; up.port.throttle = omap_8250_throttle; up.port.unthrottle = omap_8250_unthrottle; - up.port.rs485_config = serial8250_em485_config; - up.rs485_start_tx = serial8250_em485_start_tx; - up.rs485_stop_tx = serial8250_em485_stop_tx; up.port.has_sysrq = IS_ENABLED(CONFIG_SERIAL_8250_CONSOLE); ret = of_alias_get_id(np, "serial"); @@ -1393,6 +1450,14 @@ static int omap8250_probe(struct platform_device *pdev) DEFAULT_CLK_SPEED); } + if (priv->habit & UART_HAS_NATIVE_RS485) { + up.port.rs485_config = omap8250_rs485_config; + } else { + up.port.rs485_config = serial8250_em485_config; + up.rs485_start_tx = serial8250_em485_start_tx; + up.rs485_stop_tx = serial8250_em485_stop_tx; + } + priv->latency = PM_QOS_CPU_LATENCY_DEFAULT_VALUE; priv->calc_latency = PM_QOS_CPU_LATENCY_DEFAULT_VALUE; cpu_latency_qos_add_request(&priv->pm_qos_request, priv->latency); -- 2.33.0