This patch enables support for SW half-duplex mode. Synopsys
DesignWare UART has a build-in support for the RS485 protocol
from IP version 4.0 onward with dedicated RE/DE_EN registers.
This patch enables RS485 either using dedicated registers or
em485 as fallback.
In order to select preference for SW half-duplex mode (em485 +
RE/DE_EN) over HW managed one, as both are supported under
some configurations, SER_RS485_SW_RX_OR_TX flag is added to
serial_rs485.
This patch depends on UART_CAP_NOTEMT which is not provided
by this series but another one:
https://lore.kernel.org/all/[email protected]/
Cc: Eric Tremblay <[email protected]>
Cc: Uwe Kleine-König <[email protected]>
Signed-off-by: Ilpo Järvinen <[email protected]>
---
drivers/tty/serial/8250/8250_dwlib.c | 60 +++++++++++++++++++++++++---
include/uapi/linux/serial.h | 2 +
2 files changed, 56 insertions(+), 6 deletions(-)
diff --git a/drivers/tty/serial/8250/8250_dwlib.c b/drivers/tty/serial/8250/8250_dwlib.c
index d26792999984..51b0f55ee9d0 100644
--- a/drivers/tty/serial/8250/8250_dwlib.c
+++ b/drivers/tty/serial/8250/8250_dwlib.c
@@ -101,8 +101,26 @@ void dw8250_do_set_termios(struct uart_port *p, struct ktermios *termios, struct
}
EXPORT_SYMBOL_GPL(dw8250_do_set_termios);
+static void dw8250_rs485_start_tx(struct uart_8250_port *up)
+{
+ struct uart_port *p = &(up->port);
+
+ dw8250_writel_ext(p, DW_UART_RE_EN, 0);
+ dw8250_writel_ext(p, DW_UART_DE_EN, 1);
+}
+
+static void dw8250_rs485_stop_tx(struct uart_8250_port *up)
+{
+ struct uart_port *p = &(up->port);
+
+ dw8250_writel_ext(p, DW_UART_DE_EN, 0);
+ dw8250_writel_ext(p, DW_UART_RE_EN, 1);
+}
+
static int dw8250_rs485_config(struct uart_port *p, struct serial_rs485 *rs485)
{
+ struct uart_8250_port *up = up_to_u8250p(p);
+ u32 re_en, de_en;
u32 tcr;
tcr = dw8250_readl_ext(p, DW_UART_TCR);
@@ -110,16 +128,29 @@ static int dw8250_rs485_config(struct uart_port *p, struct serial_rs485 *rs485)
if (rs485->flags & SER_RS485_ENABLED) {
/* Clearing unsupported flags. */
- rs485->flags &= SER_RS485_ENABLED | SER_RS485_RX_DURING_TX;
+ rs485->flags &= SER_RS485_ENABLED | SER_RS485_RX_DURING_TX | SER_RS485_SW_RX_OR_TX;
tcr |= DW_UART_TCR_RS485_EN;
if (rs485->flags & SER_RS485_RX_DURING_TX) {
tcr |= DW_UART_TCR_XFER_MODE_DE_DURING_RE;
+ re_en = 1;
+ de_en = 1;
+ rs485->flags &= ~SER_RS485_SW_RX_OR_TX;
+ } else if (rs485->flags & SER_RS485_SW_RX_OR_TX) {
+ tcr |= DW_UART_TCR_XFER_MODE_SW_DE_OR_RE;
+ re_en = 1;
+ de_en = 0;
+ if (up->em485 && !up->em485->tx_stopped) {
+ re_en = 0;
+ de_en = 1;
+ }
} else {
tcr |= DW_UART_TCR_XFER_MODE_DE_OR_RE;
+ re_en = 1;
+ de_en = 1;
}
- dw8250_writel_ext(p, DW_UART_DE_EN, 1);
- dw8250_writel_ext(p, DW_UART_RE_EN, 1);
+ dw8250_writel_ext(p, DW_UART_DE_EN, de_en);
+ dw8250_writel_ext(p, DW_UART_RE_EN, re_en);
} else {
rs485->flags = 0;
@@ -147,7 +178,16 @@ static int dw8250_rs485_config(struct uart_port *p, struct serial_rs485 *rs485)
rs485->delay_rts_before_send = 0;
rs485->delay_rts_after_send = 0;
- p->rs485 = *rs485;
+ if (rs485->flags & SER_RS485_SW_RX_OR_TX) {
+ int ret;
+
+ ret = serial8250_em485_config(p, rs485);
+ if (ret)
+ return ret;
+ } else {
+ serial8250_em485_destroy(up_to_u8250p(p));
+ p->rs485 = *rs485;
+ }
return 0;
}
@@ -159,8 +199,16 @@ void dw8250_setup_port(struct uart_port *p)
u32 reg;
d->hw_rs485_support = device_property_read_bool(p->dev, "snps,rs485-interface-en");
- if (d->hw_rs485_support)
+ if (d->hw_rs485_support) {
p->rs485_config = dw8250_rs485_config;
+ up->rs485_start_tx = dw8250_rs485_start_tx;
+ up->rs485_stop_tx = dw8250_rs485_stop_tx;
+ } else {
+ p->rs485_config = serial8250_em485_config;
+ up->rs485_start_tx = serial8250_em485_start_tx;
+ up->rs485_stop_tx = serial8250_em485_stop_tx;
+ }
+ up->capabilities |= UART_CAP_NOTEMT;
/*
* If the Component Version Register returns zero, we know that
@@ -192,7 +240,7 @@ void dw8250_setup_port(struct uart_port *p)
p->type = PORT_16550A;
p->flags |= UPF_FIXED_TYPE;
p->fifosize = DW_UART_CPR_FIFO_SIZE(reg);
- up->capabilities = UART_CAP_FIFO;
+ up->capabilities = UART_CAP_FIFO | UART_CAP_NOTEMT;
}
if (reg & DW_UART_CPR_AFCE_MODE)
diff --git a/include/uapi/linux/serial.h b/include/uapi/linux/serial.h
index fa6b16e5fdd8..f868685b35a0 100644
--- a/include/uapi/linux/serial.h
+++ b/include/uapi/linux/serial.h
@@ -126,6 +126,8 @@ struct serial_rs485 {
#define SER_RS485_TERMINATE_BUS (1 << 5) /* Enable bus
termination
(if supported) */
+#define SER_RS485_SW_RX_OR_TX (1 << 6) /* Prefer SW half-duplex
+ mode over HW one */
__u32 delay_rts_before_send; /* Delay before send (milliseconds) */
__u32 delay_rts_after_send; /* Delay after send (milliseconds) */
__u32 padding[5]; /* Memory is cheap, new structs
--
2.30.2
On Wed, Mar 02, 2022 at 11:56:02AM +0200, Ilpo J?rvinen wrote:
> This patch enables support for SW half-duplex mode. Synopsys
> DesignWare UART has a build-in support for the RS485 protocol
> from IP version 4.0 onward with dedicated RE/DE_EN registers.
> This patch enables RS485 either using dedicated registers or
> em485 as fallback.
>
> In order to select preference for SW half-duplex mode (em485 +
> RE/DE_EN) over HW managed one, as both are supported under
> some configurations, SER_RS485_SW_RX_OR_TX flag is added to
> serial_rs485.
>
> This patch depends on UART_CAP_NOTEMT which is not provided
> by this series but another one:
> https://lore.kernel.org/all/[email protected]/
I don't see any benefit in using software emulated RTS assertion
if hardware support is present. It just consumes more CPU time
and is slower to deassert RTS, thereby increasing bus turn-around time.
So if hardware support is present, I think you always want to
use that and you need to fallback to software emulation only
if hardware support is missing (i.e. on IP versions < 4).
The registers you're using here, DW_UART_RE_EN and DW_UART_DE_EN
don't seem to be present on older IP versions. I'm looking at
the databook for version 3.04a and those registers aren't mentioned:
https://linux-sunxi.org/images/d/d2/Dw_apb_uart_db.pdf
So the software emulation you've implemented here won't help with
older IP and the newer IP doesn't need it because it supports
RTS assertion in hardware. Is that correct? If so, I'd suggest
not supporting DW_UART_TCR_XFER_MODE_SW_DE_OR_RE at all.
A number of people have attempted to add rs485 software emulation
to 8250_dw.c but noone ever pursued it into mainline. The last
attempt was by Heiko St?bner, who used patches by Giulio Benetti (+cc):
https://lore.kernel.org/linux-serial/[email protected]/
FWIW, newer TI Sitara SoCs have also added hardware support for
rs485 and in this (not yet upstreamed) patch, I likewise chose
to use software emulation only if hardware support is not available:
https://github.com/l1k/linux/commit/82c989617a05
Thanks,
Lukas
On Mon, Mar 7, 2022 at 12:01 AM Lukas Wunner <[email protected]> wrote:
> On Wed, Mar 02, 2022 at 11:56:02AM +0200, Ilpo Järvinen wrote:
> > This patch enables support for SW half-duplex mode. Synopsys
> > DesignWare UART has a build-in support for the RS485 protocol
> > from IP version 4.0 onward with dedicated RE/DE_EN registers.
> > This patch enables RS485 either using dedicated registers or
> > em485 as fallback.
> >
> > In order to select preference for SW half-duplex mode (em485 +
> > RE/DE_EN) over HW managed one, as both are supported under
> > some configurations, SER_RS485_SW_RX_OR_TX flag is added to
> > serial_rs485.
> >
> > This patch depends on UART_CAP_NOTEMT which is not provided
> > by this series but another one:
> > https://lore.kernel.org/all/[email protected]/
>
> I don't see any benefit in using software emulated RTS assertion
> if hardware support is present. It just consumes more CPU time
> and is slower to deassert RTS, thereby increasing bus turn-around time.
>
> So if hardware support is present, I think you always want to
> use that and you need to fallback to software emulation only
> if hardware support is missing (i.e. on IP versions < 4).
No, this is not a good idea because presence of the HW support does
not guarantee we will have pins on PCB level and/or properly muxed by
firmware, or be able to be properly muxed by software (OS). Or am I
missing something here and you have an idea how to enable RS485 in
that case?
> The registers you're using here, DW_UART_RE_EN and DW_UART_DE_EN
> don't seem to be present on older IP versions. I'm looking at
> the databook for version 3.04a and those registers aren't mentioned:
>
> https://linux-sunxi.org/images/d/d2/Dw_apb_uart_db.pdf
>
> So the software emulation you've implemented here won't help with
> older IP and the newer IP doesn't need it because it supports
> RTS assertion in hardware. Is that correct? If so, I'd suggest
> not supporting DW_UART_TCR_XFER_MODE_SW_DE_OR_RE at all.
>
> A number of people have attempted to add rs485 software emulation
> to 8250_dw.c but noone ever pursued it into mainline. The last
> attempt was by Heiko Stübner, who used patches by Giulio Benetti (+cc):
>
> https://lore.kernel.org/linux-serial/[email protected]/
>
> FWIW, newer TI Sitara SoCs have also added hardware support for
> rs485 and in this (not yet upstreamed) patch, I likewise chose
> to use software emulation only if hardware support is not available:
>
> https://github.com/l1k/linux/commit/82c989617a05
--
With Best Regards,
Andy Shevchenko