All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>
To: Manikanta Guntupalli <manikanta.guntupalli@amd.com>
Cc: git@amd.com, michal.simek@amd.com,
	 Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	robh+dt@kernel.org,  krzysztof.kozlowski+dt@linaro.org,
	conor+dt@kernel.org,  linux-serial <linux-serial@vger.kernel.org>,
	devicetree@vger.kernel.org,  LKML <linux-kernel@vger.kernel.org>,
	Jiri Slaby <jirislaby@kernel.org>,
	 linux-arm-kernel@lists.infradead.org,
	radhey.shyam.pandey@amd.com,  srinivas.goud@amd.com,
	shubhrajyoti.datta@amd.com, manion05gk@gmail.com
Subject: Re: [PATCH V5 3/3] tty: serial: uartps: Add rs485 support to uartps driver
Date: Wed, 13 Dec 2023 15:57:41 +0200 (EET)	[thread overview]
Message-ID: <7919791e-f52f-eb35-ead-deea90cbe8@linux.intel.com> (raw)
In-Reply-To: <20231213130023.606486-4-manikanta.guntupalli@amd.com>

On Wed, 13 Dec 2023, Manikanta Guntupalli wrote:

> Add rs485 support to uartps driver. Use either rts-gpios or RTS
> to control RS485 phy as driver or a receiver.
> 
> Signed-off-by: Manikanta Guntupalli <manikanta.guntupalli@amd.com>
> ---
> Changes for V2:
> Modify optional gpio name to xlnx,phy-ctrl-gpios.
> Update commit description.
> Add support for RTS, delay_rts_before_send and delay_rts_after_send in RS485 mode.
> 
> Changes for V3:
> Modify optional gpio name to rts-gpios.
> Update commit description.
> Move cdns_uart_tx_empty function to avoid prototype statement.
> Remove assignment of struct serial_rs485 to port->rs485 as
> serial core performs that.
> Switch to native RTS in non GPIO case.
> Handle rs485 during stop tx.
> Remove explicit calls to configure gpio direction and value,
> as devm_gpiod_get_optional performs that by using GPIOD_OUT_LOW argument.
> Update implementation to support configuration of GPIO/RTS value
> based on user configuration of SER_RS485_RTS_ON_SEND and
> SER_RS485_RTS_AFTER_SEND. Move implementation to start_tx from handle_tx.
> 
> Changes for V4:
> Create separate patch for cdns_uart_tx_empty relocation.
> Call cdns_rs485_rx_setup() before uart_add_one_port() in probe.
> Update gpio descriptor name to gpiod_rts.
> Instead of cdns_rs485_config_gpio_rts_high() and
> cdns_rs485_config_gpio_rts_low() functions for RTS/GPIO value
> configuration implement cdns_rts_gpio_enable().
> Disable auto rts and call cdns_uart_stop_tx() from cdns_rs485_config.
> Use timer instead of mdelay for delay_rts_before_send and delay_rts_after_send.
> Update cdns_uart_set_mctrl to support GPIO/RTS.
> 
> Changes for V5:
> None.
> ---
>  drivers/tty/serial/xilinx_uartps.c | 214 +++++++++++++++++++++++++++--
>  1 file changed, 205 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c
> index aafcc2179e0e..3e1045896812 100644
> --- a/drivers/tty/serial/xilinx_uartps.c
> +++ b/drivers/tty/serial/xilinx_uartps.c
> @@ -22,7 +22,9 @@
>  #include <linux/of.h>
>  #include <linux/module.h>
>  #include <linux/pm_runtime.h>
> -#include <linux/iopoll.h>
> +#include <linux/gpio.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/delay.h>
>  
>  #define CDNS_UART_TTY_NAME	"ttyPS"
>  #define CDNS_UART_NAME		"xuartps"
> @@ -193,6 +195,10 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255");
>   * @clk_rate_change_nb:	Notifier block for clock changes
>   * @quirks:		Flags for RXBS support.
>   * @cts_override:	Modem control state override
> + * @gpiod_rts:		Pointer to the gpio descriptor
> + * @rs485_tx_started:	RS485 tx state
> + * @timer:		Timer for tx and rx
> + * @stop_tx_timer:	Timer for stop tx
>   */
>  struct cdns_uart {
>  	struct uart_port	*port;
> @@ -203,10 +209,22 @@ struct cdns_uart {
>  	struct notifier_block	clk_rate_change_nb;
>  	u32			quirks;
>  	bool cts_override;
> +	struct gpio_desc	*gpiod_rts;
> +	bool			rs485_tx_started;
> +	struct timer_list	timer;
> +	struct timer_list	stop_tx_timer;
>  };
>  struct cdns_platform_data {
>  	u32 quirks;
>  };
> +
> +struct serial_rs485 cdns_rs485_supported = {
> +	.flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND |
> +		 SER_RS485_RTS_AFTER_SEND,
> +	.delay_rts_before_send = 1,
> +	.delay_rts_after_send = 1,
> +};
> +
>  #define to_cdns_uart(_nb) container_of(_nb, struct cdns_uart, \
>  		clk_rate_change_nb)
>  
> @@ -305,6 +323,55 @@ static void cdns_uart_handle_rx(void *dev_id, unsigned int isrstatus)
>  	tty_flip_buffer_push(&port->state->port);
>  }
>  
> +/**
> + * cdns_rts_gpio_enable - Configure RTS/GPIO to high/low
> + * @cdns_uart: Handle to the cdns_uart
> + * @enable: Value to be set to RTS/GPIO
> + */
> +static void cdns_rts_gpio_enable(struct cdns_uart *cdns_uart, bool enable)
> +{
> +	u32 val;
> +
> +	if (cdns_uart->gpiod_rts) {
> +		gpiod_set_value(cdns_uart->gpiod_rts, enable);
> +	} else {
> +		val = readl(cdns_uart->port->membase + CDNS_UART_MODEMCR);
> +		if (enable)
> +			val &= ~CDNS_UART_MODEMCR_RTS;
> +		else
> +			val |= CDNS_UART_MODEMCR_RTS;
> +		writel(val, cdns_uart->port->membase + CDNS_UART_MODEMCR);
> +	}
> +}
> +
> +/**
> + * cdns_rs485_tx_setup - Tx setup specific to rs485
> + * @cdns_uart: Handle to the cdns_uart
> + */
> +static void cdns_rs485_tx_setup(struct cdns_uart *cdns_uart)
> +{
> +	if (cdns_uart->port->rs485.flags & SER_RS485_RTS_ON_SEND)
> +		cdns_rts_gpio_enable(cdns_uart, 1);
> +	else
> +		cdns_rts_gpio_enable(cdns_uart, 0);
> +
> +	cdns_uart->rs485_tx_started = true;
> +}
> +
> +/**
> + * cdns_rs485_rx_setup - Rx setup specific to rs485
> + * @cdns_uart: Handle to the cdns_uart
> + */
> +static void cdns_rs485_rx_setup(struct cdns_uart *cdns_uart)
> +{
> +	if (cdns_uart->port->rs485.flags & SER_RS485_RTS_AFTER_SEND)
> +		cdns_rts_gpio_enable(cdns_uart, 1);
> +	else
> +		cdns_rts_gpio_enable(cdns_uart, 0);
> +
> +	cdns_uart->rs485_tx_started = false;
> +}
> +
>  /**
>   * cdns_uart_tx_empty -  Check whether TX is empty
>   * @port: Handle to the uart port structure
> @@ -579,6 +646,42 @@ static int cdns_uart_clk_notifier_cb(struct notifier_block *nb,
>  }
>  #endif
>  
> +/**
> + * cdns_rs485_rx_callback - Timer rx callback handler for rs485.
> + * @t: Handle to the timer list structure
> + */
> +static void cdns_rs485_rx_callback(struct timer_list *t)
> +{
> +	struct cdns_uart *cdns_uart = from_timer(cdns_uart, t, timer);
> +
> +	/*
> +	 * Default Rx should be setup, because Rx signaling path
> +	 * need to enable to receive data.
> +	 */
> +	cdns_rs485_rx_setup(cdns_uart);
> +}
> +
> +/**
> + * cdns_rs485_tx_callback - Timer tx callback handler for rs485.
> + * @t: Handle to the timer list structure
> + */
> +static void cdns_rs485_tx_callback(struct timer_list *t)
> +{
> +	struct cdns_uart *cdns_uart = from_timer(cdns_uart, t, timer);
> +
> +	cdns_uart_handle_tx(cdns_uart->port);
> +
> +	/* Enable the TX Empty interrupt */
> +	writel(CDNS_UART_IXR_TXEMPTY, cdns_uart->port->membase + CDNS_UART_IER);
> +
> +	if (uart_circ_empty(&cdns_uart->port->state->xmit) ||
> +	    uart_tx_stopped(cdns_uart->port)) {
> +		timer_setup(&cdns_uart->timer, cdns_rs485_rx_callback, 0);
> +		mod_timer(&cdns_uart->timer, jiffies +
> +			  msecs_to_jiffies(cdns_uart->port->rs485.delay_rts_after_send));
> +	}
> +}
> +
>  /**
>   * cdns_uart_start_tx -  Start transmitting bytes
>   * @port: Handle to the uart port structure
> @@ -586,6 +689,7 @@ static int cdns_uart_clk_notifier_cb(struct notifier_block *nb,
>  static void cdns_uart_start_tx(struct uart_port *port)
>  {
>  	unsigned int status;
> +	struct cdns_uart *cdns_uart = port->private_data;
>  
>  	if (uart_tx_stopped(port))
>  		return;
> @@ -604,10 +708,40 @@ static void cdns_uart_start_tx(struct uart_port *port)
>  
>  	writel(CDNS_UART_IXR_TXEMPTY, port->membase + CDNS_UART_ISR);
>  
> -	cdns_uart_handle_tx(port);
> +	if (cdns_uart->port->rs485.flags & SER_RS485_ENABLED) {
> +		if (!cdns_uart->rs485_tx_started) {
> +			timer_setup(&cdns_uart->timer,
> +				    cdns_rs485_tx_callback, 0);
> +			cdns_rs485_tx_setup(cdns_uart);
> +			mod_timer(&cdns_uart->timer, jiffies +
> +				  msecs_to_jiffies(port->rs485.delay_rts_before_send));
> +		} else {
> +			if (!timer_pending(&cdns_uart->timer))
> +				mod_timer(&cdns_uart->timer, jiffies);
> +		}
> +	} else {
> +		cdns_uart_handle_tx(port);
>  
> -	/* Enable the TX Empty interrupt */
> -	writel(CDNS_UART_IXR_TXEMPTY, port->membase + CDNS_UART_IER);
> +		/* Enable the TX Empty interrupt */
> +		writel(CDNS_UART_IXR_TXEMPTY, port->membase + CDNS_UART_IER);
> +	}
> +}
> +
> +/**
> + * cdns_rs485_stop_tx_callback - Timer stop tx callback handler for rs485.
> + * @t: Handle to the timer list structure
> + */
> +static void cdns_rs485_stop_tx_callback(struct timer_list *t)
> +{
> +	unsigned int regval;
> +	struct cdns_uart *cdns_uart = from_timer(cdns_uart, t, stop_tx_timer);
> +
> +	cdns_rs485_rx_setup(cdns_uart);
> +
> +	regval = readl(cdns_uart->port->membase + CDNS_UART_CR);
> +	regval |= CDNS_UART_CR_TX_DIS;
> +	/* Disable the transmitter */
> +	writel(regval, cdns_uart->port->membase + CDNS_UART_CR);
>  }
>  
>  /**
> @@ -617,11 +751,19 @@ static void cdns_uart_start_tx(struct uart_port *port)
>  static void cdns_uart_stop_tx(struct uart_port *port)
>  {
>  	unsigned int regval;
> +	struct cdns_uart *cdns_uart = port->private_data;
>  
> -	regval = readl(port->membase + CDNS_UART_CR);
> -	regval |= CDNS_UART_CR_TX_DIS;
> -	/* Disable the transmitter */
> -	writel(regval, port->membase + CDNS_UART_CR);
> +	if ((cdns_uart->port->rs485.flags & SER_RS485_ENABLED) &&
> +	    !timer_pending(&cdns_uart->stop_tx_timer) &&
> +	    cdns_uart->rs485_tx_started) {
> +		mod_timer(&cdns_uart->stop_tx_timer, jiffies +
> +			  msecs_to_jiffies(cdns_uart->port->rs485.delay_rts_after_send));
> +	} else {
> +		regval = readl(port->membase + CDNS_UART_CR);
> +		regval |= CDNS_UART_CR_TX_DIS;
> +		/* Disable the transmitter */
> +		writel(regval, port->membase + CDNS_UART_CR);
> +	}
>  }
>  
>  /**
> @@ -829,6 +971,12 @@ static int cdns_uart_startup(struct uart_port *port)
>  		(CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST))
>  		cpu_relax();
>  
> +	if (cdns_uart->port->rs485.flags & SER_RS485_ENABLED) {
> +		timer_setup(&cdns_uart->stop_tx_timer,
> +			    cdns_rs485_stop_tx_callback, 0);
> +		cdns_rs485_rx_setup(cdns_uart);
> +	}
> +
>  	/*
>  	 * Clear the RX disable bit and then set the RX enable bit to enable
>  	 * the receiver.
> @@ -888,6 +1036,7 @@ static void cdns_uart_shutdown(struct uart_port *port)
>  {
>  	int status;
>  	unsigned long flags;
> +	struct cdns_uart *cdns_uart = port->private_data;
>  
>  	uart_port_lock_irqsave(port, &flags);
>  
> @@ -903,6 +1052,11 @@ static void cdns_uart_shutdown(struct uart_port *port)
>  	uart_port_unlock_irqrestore(port, flags);
>  
>  	free_irq(port->irq, port);
> +
> +	if (cdns_uart->port->rs485.flags & SER_RS485_ENABLED) {
> +		del_timer_sync(&cdns_uart->timer);
> +		del_timer_sync(&cdns_uart->stop_tx_timer);
> +	}
>  }
>  
>  /**
> @@ -1032,7 +1186,7 @@ static void cdns_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
>  	mode_reg &= ~CDNS_UART_MR_CHMODE_MASK;
>  
>  	if (mctrl & TIOCM_RTS)
> -		val |= CDNS_UART_MODEMCR_RTS;
> +		cdns_rts_gpio_enable(cdns_uart_data, 1);
>  	if (mctrl & TIOCM_DTR)
>  		val |= CDNS_UART_MODEMCR_DTR;
>  	if (mctrl & TIOCM_LOOP)
> @@ -1455,6 +1609,31 @@ MODULE_DEVICE_TABLE(of, cdns_uart_of_match);
>  /* Temporary variable for storing number of instances */
>  static int instances;
>  
> +/**
> + * cdns_rs485_config - Called when an application calls TIOCSRS485 ioctl.
> + * @port: Pointer to the uart_port structure
> + * @termios: Pointer to the ktermios structure
> + * @rs485: Pointer to the serial_rs485 structure
> + *
> + * Return: 0
> + */
> +static int cdns_rs485_config(struct uart_port *port, struct ktermios *termios,
> +			     struct serial_rs485 *rs485)
> +{
> +	u32 val;
> +
> +	if (rs485->flags & SER_RS485_ENABLED) {
> +		dev_dbg(port->dev, "Setting UART to RS485\n");
> +		/* Make sure auto RTS is disabled */
> +		val = readl(port->membase + CDNS_UART_MODEMCR);
> +		val &= ~CDNS_UART_MODEMCR_FCM;
> +		writel(val, port->membase + CDNS_UART_MODEMCR);
> +		/* Disable transmitter and make Rx setup*/

Missing space.

> +		cdns_uart_stop_tx(port);
> +	}

So you provide no way to disable RS485 after it once gets enable?
Arguably that might not usually be a very useful thing to do in practice 
but API-wise this function is expected to be able to also turn RS485 off.

I didn't do a full review but having to add a timer smells like you could
reuse the existing em485 framework for timing, please check.


-- 
 i.


WARNING: multiple messages have this Message-ID (diff)
From: "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>
To: Manikanta Guntupalli <manikanta.guntupalli@amd.com>
Cc: git@amd.com, michal.simek@amd.com,
	 Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	robh+dt@kernel.org,  krzysztof.kozlowski+dt@linaro.org,
	conor+dt@kernel.org,  linux-serial <linux-serial@vger.kernel.org>,
	devicetree@vger.kernel.org,  LKML <linux-kernel@vger.kernel.org>,
	Jiri Slaby <jirislaby@kernel.org>,
	 linux-arm-kernel@lists.infradead.org,
	radhey.shyam.pandey@amd.com,  srinivas.goud@amd.com,
	shubhrajyoti.datta@amd.com, manion05gk@gmail.com
Subject: Re: [PATCH V5 3/3] tty: serial: uartps: Add rs485 support to uartps driver
Date: Wed, 13 Dec 2023 15:57:41 +0200 (EET)	[thread overview]
Message-ID: <7919791e-f52f-eb35-ead-deea90cbe8@linux.intel.com> (raw)
In-Reply-To: <20231213130023.606486-4-manikanta.guntupalli@amd.com>

On Wed, 13 Dec 2023, Manikanta Guntupalli wrote:

> Add rs485 support to uartps driver. Use either rts-gpios or RTS
> to control RS485 phy as driver or a receiver.
> 
> Signed-off-by: Manikanta Guntupalli <manikanta.guntupalli@amd.com>
> ---
> Changes for V2:
> Modify optional gpio name to xlnx,phy-ctrl-gpios.
> Update commit description.
> Add support for RTS, delay_rts_before_send and delay_rts_after_send in RS485 mode.
> 
> Changes for V3:
> Modify optional gpio name to rts-gpios.
> Update commit description.
> Move cdns_uart_tx_empty function to avoid prototype statement.
> Remove assignment of struct serial_rs485 to port->rs485 as
> serial core performs that.
> Switch to native RTS in non GPIO case.
> Handle rs485 during stop tx.
> Remove explicit calls to configure gpio direction and value,
> as devm_gpiod_get_optional performs that by using GPIOD_OUT_LOW argument.
> Update implementation to support configuration of GPIO/RTS value
> based on user configuration of SER_RS485_RTS_ON_SEND and
> SER_RS485_RTS_AFTER_SEND. Move implementation to start_tx from handle_tx.
> 
> Changes for V4:
> Create separate patch for cdns_uart_tx_empty relocation.
> Call cdns_rs485_rx_setup() before uart_add_one_port() in probe.
> Update gpio descriptor name to gpiod_rts.
> Instead of cdns_rs485_config_gpio_rts_high() and
> cdns_rs485_config_gpio_rts_low() functions for RTS/GPIO value
> configuration implement cdns_rts_gpio_enable().
> Disable auto rts and call cdns_uart_stop_tx() from cdns_rs485_config.
> Use timer instead of mdelay for delay_rts_before_send and delay_rts_after_send.
> Update cdns_uart_set_mctrl to support GPIO/RTS.
> 
> Changes for V5:
> None.
> ---
>  drivers/tty/serial/xilinx_uartps.c | 214 +++++++++++++++++++++++++++--
>  1 file changed, 205 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c
> index aafcc2179e0e..3e1045896812 100644
> --- a/drivers/tty/serial/xilinx_uartps.c
> +++ b/drivers/tty/serial/xilinx_uartps.c
> @@ -22,7 +22,9 @@
>  #include <linux/of.h>
>  #include <linux/module.h>
>  #include <linux/pm_runtime.h>
> -#include <linux/iopoll.h>
> +#include <linux/gpio.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/delay.h>
>  
>  #define CDNS_UART_TTY_NAME	"ttyPS"
>  #define CDNS_UART_NAME		"xuartps"
> @@ -193,6 +195,10 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255");
>   * @clk_rate_change_nb:	Notifier block for clock changes
>   * @quirks:		Flags for RXBS support.
>   * @cts_override:	Modem control state override
> + * @gpiod_rts:		Pointer to the gpio descriptor
> + * @rs485_tx_started:	RS485 tx state
> + * @timer:		Timer for tx and rx
> + * @stop_tx_timer:	Timer for stop tx
>   */
>  struct cdns_uart {
>  	struct uart_port	*port;
> @@ -203,10 +209,22 @@ struct cdns_uart {
>  	struct notifier_block	clk_rate_change_nb;
>  	u32			quirks;
>  	bool cts_override;
> +	struct gpio_desc	*gpiod_rts;
> +	bool			rs485_tx_started;
> +	struct timer_list	timer;
> +	struct timer_list	stop_tx_timer;
>  };
>  struct cdns_platform_data {
>  	u32 quirks;
>  };
> +
> +struct serial_rs485 cdns_rs485_supported = {
> +	.flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND |
> +		 SER_RS485_RTS_AFTER_SEND,
> +	.delay_rts_before_send = 1,
> +	.delay_rts_after_send = 1,
> +};
> +
>  #define to_cdns_uart(_nb) container_of(_nb, struct cdns_uart, \
>  		clk_rate_change_nb)
>  
> @@ -305,6 +323,55 @@ static void cdns_uart_handle_rx(void *dev_id, unsigned int isrstatus)
>  	tty_flip_buffer_push(&port->state->port);
>  }
>  
> +/**
> + * cdns_rts_gpio_enable - Configure RTS/GPIO to high/low
> + * @cdns_uart: Handle to the cdns_uart
> + * @enable: Value to be set to RTS/GPIO
> + */
> +static void cdns_rts_gpio_enable(struct cdns_uart *cdns_uart, bool enable)
> +{
> +	u32 val;
> +
> +	if (cdns_uart->gpiod_rts) {
> +		gpiod_set_value(cdns_uart->gpiod_rts, enable);
> +	} else {
> +		val = readl(cdns_uart->port->membase + CDNS_UART_MODEMCR);
> +		if (enable)
> +			val &= ~CDNS_UART_MODEMCR_RTS;
> +		else
> +			val |= CDNS_UART_MODEMCR_RTS;
> +		writel(val, cdns_uart->port->membase + CDNS_UART_MODEMCR);
> +	}
> +}
> +
> +/**
> + * cdns_rs485_tx_setup - Tx setup specific to rs485
> + * @cdns_uart: Handle to the cdns_uart
> + */
> +static void cdns_rs485_tx_setup(struct cdns_uart *cdns_uart)
> +{
> +	if (cdns_uart->port->rs485.flags & SER_RS485_RTS_ON_SEND)
> +		cdns_rts_gpio_enable(cdns_uart, 1);
> +	else
> +		cdns_rts_gpio_enable(cdns_uart, 0);
> +
> +	cdns_uart->rs485_tx_started = true;
> +}
> +
> +/**
> + * cdns_rs485_rx_setup - Rx setup specific to rs485
> + * @cdns_uart: Handle to the cdns_uart
> + */
> +static void cdns_rs485_rx_setup(struct cdns_uart *cdns_uart)
> +{
> +	if (cdns_uart->port->rs485.flags & SER_RS485_RTS_AFTER_SEND)
> +		cdns_rts_gpio_enable(cdns_uart, 1);
> +	else
> +		cdns_rts_gpio_enable(cdns_uart, 0);
> +
> +	cdns_uart->rs485_tx_started = false;
> +}
> +
>  /**
>   * cdns_uart_tx_empty -  Check whether TX is empty
>   * @port: Handle to the uart port structure
> @@ -579,6 +646,42 @@ static int cdns_uart_clk_notifier_cb(struct notifier_block *nb,
>  }
>  #endif
>  
> +/**
> + * cdns_rs485_rx_callback - Timer rx callback handler for rs485.
> + * @t: Handle to the timer list structure
> + */
> +static void cdns_rs485_rx_callback(struct timer_list *t)
> +{
> +	struct cdns_uart *cdns_uart = from_timer(cdns_uart, t, timer);
> +
> +	/*
> +	 * Default Rx should be setup, because Rx signaling path
> +	 * need to enable to receive data.
> +	 */
> +	cdns_rs485_rx_setup(cdns_uart);
> +}
> +
> +/**
> + * cdns_rs485_tx_callback - Timer tx callback handler for rs485.
> + * @t: Handle to the timer list structure
> + */
> +static void cdns_rs485_tx_callback(struct timer_list *t)
> +{
> +	struct cdns_uart *cdns_uart = from_timer(cdns_uart, t, timer);
> +
> +	cdns_uart_handle_tx(cdns_uart->port);
> +
> +	/* Enable the TX Empty interrupt */
> +	writel(CDNS_UART_IXR_TXEMPTY, cdns_uart->port->membase + CDNS_UART_IER);
> +
> +	if (uart_circ_empty(&cdns_uart->port->state->xmit) ||
> +	    uart_tx_stopped(cdns_uart->port)) {
> +		timer_setup(&cdns_uart->timer, cdns_rs485_rx_callback, 0);
> +		mod_timer(&cdns_uart->timer, jiffies +
> +			  msecs_to_jiffies(cdns_uart->port->rs485.delay_rts_after_send));
> +	}
> +}
> +
>  /**
>   * cdns_uart_start_tx -  Start transmitting bytes
>   * @port: Handle to the uart port structure
> @@ -586,6 +689,7 @@ static int cdns_uart_clk_notifier_cb(struct notifier_block *nb,
>  static void cdns_uart_start_tx(struct uart_port *port)
>  {
>  	unsigned int status;
> +	struct cdns_uart *cdns_uart = port->private_data;
>  
>  	if (uart_tx_stopped(port))
>  		return;
> @@ -604,10 +708,40 @@ static void cdns_uart_start_tx(struct uart_port *port)
>  
>  	writel(CDNS_UART_IXR_TXEMPTY, port->membase + CDNS_UART_ISR);
>  
> -	cdns_uart_handle_tx(port);
> +	if (cdns_uart->port->rs485.flags & SER_RS485_ENABLED) {
> +		if (!cdns_uart->rs485_tx_started) {
> +			timer_setup(&cdns_uart->timer,
> +				    cdns_rs485_tx_callback, 0);
> +			cdns_rs485_tx_setup(cdns_uart);
> +			mod_timer(&cdns_uart->timer, jiffies +
> +				  msecs_to_jiffies(port->rs485.delay_rts_before_send));
> +		} else {
> +			if (!timer_pending(&cdns_uart->timer))
> +				mod_timer(&cdns_uart->timer, jiffies);
> +		}
> +	} else {
> +		cdns_uart_handle_tx(port);
>  
> -	/* Enable the TX Empty interrupt */
> -	writel(CDNS_UART_IXR_TXEMPTY, port->membase + CDNS_UART_IER);
> +		/* Enable the TX Empty interrupt */
> +		writel(CDNS_UART_IXR_TXEMPTY, port->membase + CDNS_UART_IER);
> +	}
> +}
> +
> +/**
> + * cdns_rs485_stop_tx_callback - Timer stop tx callback handler for rs485.
> + * @t: Handle to the timer list structure
> + */
> +static void cdns_rs485_stop_tx_callback(struct timer_list *t)
> +{
> +	unsigned int regval;
> +	struct cdns_uart *cdns_uart = from_timer(cdns_uart, t, stop_tx_timer);
> +
> +	cdns_rs485_rx_setup(cdns_uart);
> +
> +	regval = readl(cdns_uart->port->membase + CDNS_UART_CR);
> +	regval |= CDNS_UART_CR_TX_DIS;
> +	/* Disable the transmitter */
> +	writel(regval, cdns_uart->port->membase + CDNS_UART_CR);
>  }
>  
>  /**
> @@ -617,11 +751,19 @@ static void cdns_uart_start_tx(struct uart_port *port)
>  static void cdns_uart_stop_tx(struct uart_port *port)
>  {
>  	unsigned int regval;
> +	struct cdns_uart *cdns_uart = port->private_data;
>  
> -	regval = readl(port->membase + CDNS_UART_CR);
> -	regval |= CDNS_UART_CR_TX_DIS;
> -	/* Disable the transmitter */
> -	writel(regval, port->membase + CDNS_UART_CR);
> +	if ((cdns_uart->port->rs485.flags & SER_RS485_ENABLED) &&
> +	    !timer_pending(&cdns_uart->stop_tx_timer) &&
> +	    cdns_uart->rs485_tx_started) {
> +		mod_timer(&cdns_uart->stop_tx_timer, jiffies +
> +			  msecs_to_jiffies(cdns_uart->port->rs485.delay_rts_after_send));
> +	} else {
> +		regval = readl(port->membase + CDNS_UART_CR);
> +		regval |= CDNS_UART_CR_TX_DIS;
> +		/* Disable the transmitter */
> +		writel(regval, port->membase + CDNS_UART_CR);
> +	}
>  }
>  
>  /**
> @@ -829,6 +971,12 @@ static int cdns_uart_startup(struct uart_port *port)
>  		(CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST))
>  		cpu_relax();
>  
> +	if (cdns_uart->port->rs485.flags & SER_RS485_ENABLED) {
> +		timer_setup(&cdns_uart->stop_tx_timer,
> +			    cdns_rs485_stop_tx_callback, 0);
> +		cdns_rs485_rx_setup(cdns_uart);
> +	}
> +
>  	/*
>  	 * Clear the RX disable bit and then set the RX enable bit to enable
>  	 * the receiver.
> @@ -888,6 +1036,7 @@ static void cdns_uart_shutdown(struct uart_port *port)
>  {
>  	int status;
>  	unsigned long flags;
> +	struct cdns_uart *cdns_uart = port->private_data;
>  
>  	uart_port_lock_irqsave(port, &flags);
>  
> @@ -903,6 +1052,11 @@ static void cdns_uart_shutdown(struct uart_port *port)
>  	uart_port_unlock_irqrestore(port, flags);
>  
>  	free_irq(port->irq, port);
> +
> +	if (cdns_uart->port->rs485.flags & SER_RS485_ENABLED) {
> +		del_timer_sync(&cdns_uart->timer);
> +		del_timer_sync(&cdns_uart->stop_tx_timer);
> +	}
>  }
>  
>  /**
> @@ -1032,7 +1186,7 @@ static void cdns_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
>  	mode_reg &= ~CDNS_UART_MR_CHMODE_MASK;
>  
>  	if (mctrl & TIOCM_RTS)
> -		val |= CDNS_UART_MODEMCR_RTS;
> +		cdns_rts_gpio_enable(cdns_uart_data, 1);
>  	if (mctrl & TIOCM_DTR)
>  		val |= CDNS_UART_MODEMCR_DTR;
>  	if (mctrl & TIOCM_LOOP)
> @@ -1455,6 +1609,31 @@ MODULE_DEVICE_TABLE(of, cdns_uart_of_match);
>  /* Temporary variable for storing number of instances */
>  static int instances;
>  
> +/**
> + * cdns_rs485_config - Called when an application calls TIOCSRS485 ioctl.
> + * @port: Pointer to the uart_port structure
> + * @termios: Pointer to the ktermios structure
> + * @rs485: Pointer to the serial_rs485 structure
> + *
> + * Return: 0
> + */
> +static int cdns_rs485_config(struct uart_port *port, struct ktermios *termios,
> +			     struct serial_rs485 *rs485)
> +{
> +	u32 val;
> +
> +	if (rs485->flags & SER_RS485_ENABLED) {
> +		dev_dbg(port->dev, "Setting UART to RS485\n");
> +		/* Make sure auto RTS is disabled */
> +		val = readl(port->membase + CDNS_UART_MODEMCR);
> +		val &= ~CDNS_UART_MODEMCR_FCM;
> +		writel(val, port->membase + CDNS_UART_MODEMCR);
> +		/* Disable transmitter and make Rx setup*/

Missing space.

> +		cdns_uart_stop_tx(port);
> +	}

So you provide no way to disable RS485 after it once gets enable?
Arguably that might not usually be a very useful thing to do in practice 
but API-wise this function is expected to be able to also turn RS485 off.

I didn't do a full review but having to add a timer smells like you could
reuse the existing em485 framework for timing, please check.


-- 
 i.


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

  reply	other threads:[~2023-12-13 13:57 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-12-13 13:00 [PATCH V5 0/3] Add rs485 support to uartps driver Manikanta Guntupalli
2023-12-13 13:00 ` Manikanta Guntupalli
2023-12-13 13:00 ` [PATCH V5 1/3] dt-bindings: Add reference to rs485.yaml Manikanta Guntupalli
2023-12-13 13:00   ` Manikanta Guntupalli
2023-12-13 16:02   ` Conor Dooley
2023-12-13 16:02     ` Conor Dooley
2023-12-13 13:00 ` [PATCH V5 2/3] tty: serial: uartps: Relocate cdns_uart_tx_empty to facilitate rs485 Manikanta Guntupalli
2023-12-13 13:00   ` Manikanta Guntupalli
2023-12-13 13:00 ` [PATCH V5 3/3] tty: serial: uartps: Add rs485 support to uartps driver Manikanta Guntupalli
2023-12-13 13:00   ` Manikanta Guntupalli
2023-12-13 13:57   ` Ilpo Järvinen [this message]
2023-12-13 13:57     ` Ilpo Järvinen
2023-12-14  9:00   ` Lino Sanfilippo
2023-12-14  9:00     ` Lino Sanfilippo

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=7919791e-f52f-eb35-ead-deea90cbe8@linux.intel.com \
    --to=ilpo.jarvinen@linux.intel.com \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=git@amd.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=jirislaby@kernel.org \
    --cc=krzysztof.kozlowski+dt@linaro.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-serial@vger.kernel.org \
    --cc=manikanta.guntupalli@amd.com \
    --cc=manion05gk@gmail.com \
    --cc=michal.simek@amd.com \
    --cc=radhey.shyam.pandey@amd.com \
    --cc=robh+dt@kernel.org \
    --cc=shubhrajyoti.datta@amd.com \
    --cc=srinivas.goud@amd.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.