[OpenWrt-Devel,v3,1/2] ath79: ar9330-uart: add support for half-duplex using DTR signal
diff mbox series

Message ID 20200211163251.GA405600@makrotopia.org
State Superseded
Headers show
Series
  • [OpenWrt-Devel,v3,1/2] ath79: ar9330-uart: add support for half-duplex using DTR signal
Related show

Commit Message

Daniel Golle Feb. 11, 2020, 4:32 p.m. UTC
Add support for RS485 tranceiver with transmit/receive switch hooked
to the DTR GPIO. This is how RS485 is implemented on the Teltonika
RUT955 device and this patch immitates the behaviour of the driver
found in their SDK[1].

[1]: https://wiki.teltonika.lt/gpl/RUT9XX_R_GPL_00.06.05.3.tar.gz
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
v3: use rts-gpios and support active-low

 ...41-tty-serial-ar933x-uart-rs485-gpio.patch | 130 ++++++++++++++++++
 1 file changed, 130 insertions(+)
 create mode 100644 target/linux/ath79/patches-4.19/0041-tty-serial-ar933x-uart-rs485-gpio.patch

Comments

Petr Štetiar Feb. 11, 2020, 5:40 p.m. UTC | #1
Hi,

> Add support for RS485 tranceiver with transmit/receive switch hooked
> to the DTR GPIO.
          ^
now it's RTS, but it could be whatever (ditto with subject).

This should probably be "serial: ar933x_uart: add rs485 support" and taken
through upstream to get more eyes on this.

> This is how RS485 is implemented on the Teltonika

Which doesn't mean, that it's upstream material.

> + static inline void ar933x_uart_putc(struct ar933x_uart_port *up, int ch)
> + {
> + 	unsigned int rdata;
> ++	struct serial_rs485 rs485conf = up->port.rs485;
> + 
> + 	rdata = ch & AR933X_UART_DATA_TX_RX_MASK;
> + 	rdata |= AR933X_UART_DATA_TX_CSR;
> +-	ar933x_uart_write(up, AR933X_UART_DATA_REG, rdata);

	if (unlikely(rs485conf.flags & SER_RS485_ENABLED)) {
		ar933x_rs485_putc(up, rdata);
		return;
	}

	ar933x_uart_write(up, AR933X_UART_DATA_REG, rdata);
}

> ++		struct gpio_desc *rts_gpio = mctrl_gpio_to_gpiod(up->gpios, UART_GPIO_RTS);

No need for this, just use mctrl_gpio_set and you probably dont want to do
this each time for every byte.

> ++		if (!IS_ERR_OR_NULL(rts_gpio))
> ++			gpiod_set_value(rts_gpio, !!(rs485conf.flags & SER_RS485_RTS_ON_SEND));

This should be probably handled in ar933x_uart_set_mctrl and with mctrl_gpio_set.

-- ynezz

Patch
diff mbox series

diff --git a/target/linux/ath79/patches-4.19/0041-tty-serial-ar933x-uart-rs485-gpio.patch b/target/linux/ath79/patches-4.19/0041-tty-serial-ar933x-uart-rs485-gpio.patch
new file mode 100644
index 0000000000..fd6131c944
--- /dev/null
+++ b/target/linux/ath79/patches-4.19/0041-tty-serial-ar933x-uart-rs485-gpio.patch
@@ -0,0 +1,130 @@ 
+--- a/drivers/tty/serial/Kconfig
++++ b/drivers/tty/serial/Kconfig
+@@ -1296,6 +1296,7 @@ config SERIAL_AR933X
+ 	tristate "AR933X serial port support"
+ 	depends on HAVE_CLK && ATH79
+ 	select SERIAL_CORE
++	select SERIAL_MCTRL_GPIO if GPIOLIB
+ 	help
+ 	  If you have an Atheros AR933X SOC based board and want to use the
+ 	  built-in UART of the SoC, say Y to this option.
+--- a/drivers/tty/serial/ar933x_uart.c
++++ b/drivers/tty/serial/ar933x_uart.c
+@@ -13,6 +13,7 @@
+ #include <linux/console.h>
+ #include <linux/sysrq.h>
+ #include <linux/delay.h>
++#include <linux/gpio/consumer.h>
+ #include <linux/platform_device.h>
+ #include <linux/of.h>
+ #include <linux/of_platform.h>
+@@ -29,6 +30,8 @@
+ 
+ #include <asm/mach-ath79/ar933x_uart.h>
+ 
++#include "serial_mctrl_gpio.h"
++
+ #define DRIVER_NAME "ar933x-uart"
+ 
+ #define AR933X_UART_MAX_SCALE	0xff
+@@ -47,6 +50,7 @@ struct ar933x_uart_port {
+ 	unsigned int		min_baud;
+ 	unsigned int		max_baud;
+ 	struct clk		*clk;
++	struct mctrl_gpios	*gpios;
+ };
+ 
+ static inline unsigned int ar933x_uart_read(struct ar933x_uart_port *up,
+@@ -103,10 +107,49 @@ static inline void ar933x_uart_stop_tx_i
+ static inline void ar933x_uart_putc(struct ar933x_uart_port *up, int ch)
+ {
+ 	unsigned int rdata;
++	struct serial_rs485 rs485conf = up->port.rs485;
+ 
+ 	rdata = ch & AR933X_UART_DATA_TX_RX_MASK;
+ 	rdata |= AR933X_UART_DATA_TX_CSR;
+-	ar933x_uart_write(up, AR933X_UART_DATA_REG, rdata);
++
++	if (rs485conf.flags & SER_RS485_ENABLED) {
++		unsigned int timeout = 60000;
++		unsigned long flags;
++		unsigned int status;
++		struct gpio_desc *rts_gpio = mctrl_gpio_to_gpiod(up->gpios, UART_GPIO_RTS);
++
++		/* Disable RX interrupt */
++		spin_lock_irqsave(&up->port.lock, flags);
++		up->ier &= ~AR933X_UART_INT_RX_VALID;
++		ar933x_uart_write(up, AR933X_UART_INT_EN_REG, up->ier);
++
++		if (!IS_ERR_OR_NULL(rts_gpio))
++			gpiod_set_value(rts_gpio, !!(rs485conf.flags & SER_RS485_RTS_ON_SEND));
++
++		ar933x_uart_write(up, AR933X_UART_DATA_REG, rdata);
++
++		/* wait for transmission to end */
++		do {
++			status = ar933x_uart_read(up, AR933X_UART_CS_REG);
++			if (--timeout == 0)
++				break;
++			udelay(1);
++		} while ((status & AR933X_UART_CS_TX_BUSY) != 0);
++
++		ar933x_uart_write(up, AR933X_UART_INT_REG, AR933X_UART_INT_RX_VALID);
++		/* remove the character from the FIFO */
++		ar933x_uart_write(up, AR933X_UART_DATA_REG, AR933X_UART_DATA_RX_CSR);
++		/* Enable RX interrupt */
++		up->ier |= AR933X_UART_INT_RX_VALID;
++		ar933x_uart_write(up, AR933X_UART_INT_EN_REG, up->ier);
++
++		if (!IS_ERR_OR_NULL(rts_gpio))
++			gpiod_set_value(rts_gpio, !!(rs485conf.flags & SER_RS485_RTS_AFTER_SEND));
++
++		spin_unlock_irqrestore(&up->port.lock, flags);
++	} else {
++		ar933x_uart_write(up, AR933X_UART_DATA_REG, rdata);
++	}
+ }
+ 
+ static unsigned int ar933x_uart_tx_empty(struct uart_port *port)
+@@ -511,6 +554,13 @@ static const struct uart_ops ar933x_uart
+ 	.verify_port	= ar933x_uart_verify_port,
+ };
+ 
++static int ar933x_config_rs485(struct uart_port *port,
++				struct serial_rs485 *rs485conf)
++{
++	port->rs485 = *rs485conf;
++	return 0;
++}
++
+ #ifdef CONFIG_SERIAL_AR933X_CONSOLE
+ static struct ar933x_uart_port *
+ ar933x_console_ports[CONFIG_SERIAL_AR933X_NR_UARTS];
+@@ -680,6 +730,8 @@ static int ar933x_uart_probe(struct plat
+ 		goto err_disable_clk;
+ 	}
+ 
++	uart_get_rs485_mode(&pdev->dev, &port->rs485);
++
+ 	port->mapbase = mem_res->start;
+ 	port->line = id;
+ 	port->irq = irq_res->start;
+@@ -690,6 +742,7 @@ static int ar933x_uart_probe(struct plat
+ 	port->regshift = 2;
+ 	port->fifosize = AR933X_UART_FIFO_SIZE;
+ 	port->ops = &ar933x_uart_ops;
++	port->rs485_config = ar933x_config_rs485;
+ 
+ 	baud = ar933x_uart_get_baud(port->uartclk, AR933X_UART_MAX_SCALE, 1);
+ 	up->min_baud = max_t(unsigned int, baud, AR933X_UART_MIN_BAUD);
+@@ -697,6 +750,10 @@ static int ar933x_uart_probe(struct plat
+ 	baud = ar933x_uart_get_baud(port->uartclk, 0, AR933X_UART_MAX_STEP);
+ 	up->max_baud = min_t(unsigned int, baud, AR933X_UART_MAX_BAUD);
+ 
++	up->gpios = mctrl_gpio_init(port, 0);
++	if (IS_ERR(up->gpios) && PTR_ERR(up->gpios) != -ENOSYS)
++		return PTR_ERR(up->gpios);
++
+ #ifdef CONFIG_SERIAL_AR933X_CONSOLE
+ 	ar933x_console_ports[up->port.line] = up;
+ #endif