All of lore.kernel.org
 help / color / mirror / Atom feed
From: Greg KH <gregkh@linuxfoundation.org>
To: Lino Sanfilippo <LinoSanfilippo@gmx.de>
Cc: linux@armlinux.org.uk, jirislaby@kernel.org,
	linux-serial@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH v2] serial: amba-pl011: add RS485 support
Date: Thu, 24 Jun 2021 14:55:48 +0200	[thread overview]
Message-ID: <YNSA1H0cFKiPUn6N@kroah.com> (raw)
In-Reply-To: <20210618145153.1906-1-LinoSanfilippo@gmx.de>

On Fri, Jun 18, 2021 at 04:51:53PM +0200, Lino Sanfilippo wrote:
> Add basic support for RS485: Provide a callback to configure RS485
> settings. Handle the RS485 specific part in the functions
> pl011_rs485_tx_start() and pl011_rs485_tx_stop() which extend the generic
> start/stop callbacks.
> Beside via IOCTL from userspace RS485 can be enabled by means of the
> device tree property "rs485-enabled-at-boot-time".
> 
> Signed-off-by: Lino Sanfilippo <LinoSanfilippo@gmx.de>
> ---
> This patch applies against Gregs tty-testing branch and was tested on a
> Raspberry Pi CM3.
> 
> Changes in V2:
> - clamp RTS delays to 100ms as suggested by Jiri Slaby
> - instead of counting bits "by hand" use the new function tty_get_frame_size()
>   (also suggested by Jiri)
> - use the term RS485 consistently in the commit message
> - remove one blank line
> 
> 
>  drivers/tty/serial/amba-pl011.c | 152 +++++++++++++++++++++++++++++++-
>  1 file changed, 150 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
> index e14f3378b8a0..92c6b08d56fb 100644
> --- a/drivers/tty/serial/amba-pl011.c
> +++ b/drivers/tty/serial/amba-pl011.c
> @@ -265,6 +265,8 @@ struct uart_amba_port {
>  	unsigned int		old_cr;		/* state during shutdown */
>  	unsigned int		fixed_baud;	/* vendor-set fixed baud rate */
>  	char			type[12];
> +	bool			rs485_tx_started;
> +	unsigned int		rs485_tx_drain_interval; /* usecs */
>  #ifdef CONFIG_DMA_ENGINE
>  	/* DMA stuff */
>  	bool			using_tx_dma;
> @@ -275,6 +277,8 @@ struct uart_amba_port {
>  #endif
>  };
>  
> +static unsigned int pl011_tx_empty(struct uart_port *port);
> +
>  static unsigned int pl011_reg_to_offset(const struct uart_amba_port *uap,
>  	unsigned int reg)
>  {
> @@ -1282,6 +1286,34 @@ static inline bool pl011_dma_rx_running(struct uart_amba_port *uap)
>  #define pl011_dma_flush_buffer	NULL
>  #endif
>  
> +static int pl011_rs485_tx_stop(struct uart_amba_port *uap)
> +{
> +	struct uart_port *port = &uap->port;
> +	u32 cr;
> +
> +	/* Wait until hardware tx queue is empty */
> +	while (!pl011_tx_empty(port))
> +		udelay(uap->rs485_tx_drain_interval);

No way out if the hardware doesn't ever empty?  Shouldn't you have an
"upper bound" on this loop somehow?


> +
> +	if (port->rs485.delay_rts_after_send)
> +		mdelay(port->rs485.delay_rts_after_send);
> +
> +	cr = pl011_read(uap, REG_CR);
> +
> +	if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND)
> +		cr &= ~UART011_CR_RTS;
> +	else
> +		cr |= UART011_CR_RTS;

Blank line here please.

> +	/* Disable the transmitter and reenable the transceiver */
> +	cr &= ~UART011_CR_TXE;
> +	cr |= UART011_CR_RXE;
> +	pl011_write(cr, uap, REG_CR);
> +
> +	uap->rs485_tx_started = false;
> +
> +	return 0;

Why does this function return a value if it can not fail and you do not
check the return value of it?

> +}
> +
>  static void pl011_stop_tx(struct uart_port *port)
>  {
>  	struct uart_amba_port *uap =
> @@ -1290,6 +1322,9 @@ static void pl011_stop_tx(struct uart_port *port)
>  	uap->im &= ~UART011_TXIM;
>  	pl011_write(uap->im, uap, REG_IMSC);
>  	pl011_dma_tx_stop(uap);
> +
> +	if ((port->rs485.flags & SER_RS485_ENABLED) && uap->rs485_tx_started)
> +		pl011_rs485_tx_stop(uap);

So, no check :(


>  }
>  
>  static bool pl011_tx_chars(struct uart_amba_port *uap, bool from_irq);
> @@ -1380,6 +1415,31 @@ static bool pl011_tx_char(struct uart_amba_port *uap, unsigned char c,
>  	return true;
>  }
>  
> +static void pl011_rs485_tx_start(struct uart_amba_port *uap)
> +{
> +	struct uart_port *port = &uap->port;
> +	u32 cr;
> +
> +	/* Enable transmitter */
> +	cr = pl011_read(uap, REG_CR);
> +	cr |= UART011_CR_TXE;

Blank line please.

> +	/* Disable receiver if half-duplex */
> +	if (!(port->rs485.flags & SER_RS485_RX_DURING_TX))
> +		cr &= ~UART011_CR_RXE;
> +
> +	if (port->rs485.flags & SER_RS485_RTS_ON_SEND)
> +		cr &= ~UART011_CR_RTS;
> +	else
> +		cr |= UART011_CR_RTS;
> +
> +	pl011_write(cr, uap, REG_CR);
> +
> +	if (port->rs485.delay_rts_before_send)
> +		mdelay(port->rs485.delay_rts_before_send);
> +
> +	uap->rs485_tx_started = true;
> +}
> +
>  /* Returns true if tx interrupts have to be (kept) enabled  */
>  static bool pl011_tx_chars(struct uart_amba_port *uap, bool from_irq)
>  {
> @@ -1397,6 +1457,10 @@ static bool pl011_tx_chars(struct uart_amba_port *uap, bool from_irq)
>  		return false;
>  	}
>  
> +	if ((uap->port.rs485.flags & SER_RS485_ENABLED) &&
> +	    !uap->rs485_tx_started)
> +		pl011_rs485_tx_start(uap);
> +
>  	/* If we are using DMA mode, try to send some characters. */
>  	if (pl011_dma_tx_irq(uap))
>  		return true;
> @@ -1542,6 +1606,9 @@ static void pl011_set_mctrl(struct uart_port *port, unsigned int mctrl)
>  	    container_of(port, struct uart_amba_port, port);
>  	unsigned int cr;
>  
> +	if (port->rs485.flags & SER_RS485_ENABLED)
> +		mctrl &= ~TIOCM_RTS;
> +
>  	cr = pl011_read(uap, REG_CR);
>  
>  #define	TIOCMBIT(tiocmbit, uartbit)		\
> @@ -1763,7 +1830,17 @@ static int pl011_startup(struct uart_port *port)
>  
>  	/* restore RTS and DTR */
>  	cr = uap->old_cr & (UART011_CR_RTS | UART011_CR_DTR);
> -	cr |= UART01x_CR_UARTEN | UART011_CR_RXE | UART011_CR_TXE;
> +	cr |= UART01x_CR_UARTEN | UART011_CR_RXE;
> +
> +	if (port->rs485.flags & SER_RS485_ENABLED) {
> +		if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND)
> +			cr &= ~UART011_CR_RTS;
> +		else
> +			cr |= UART011_CR_RTS;
> +	} else {
> +		cr |= UART011_CR_TXE;
> +	}
> +
>  	pl011_write(cr, uap, REG_CR);
>  
>  	spin_unlock_irq(&uap->port.lock);
> @@ -1864,6 +1941,9 @@ static void pl011_shutdown(struct uart_port *port)
>  
>  	pl011_dma_shutdown(uap);
>  
> +	if ((port->rs485.flags & SER_RS485_ENABLED) && uap->rs485_tx_started)
> +		pl011_rs485_tx_stop(uap);
> +
>  	free_irq(uap->port.irq, uap);
>  
>  	pl011_disable_uart(uap);
> @@ -1941,6 +2021,7 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios,
>  	unsigned int lcr_h, old_cr;
>  	unsigned long flags;
>  	unsigned int baud, quot, clkdiv;
> +	unsigned int bits;
>  
>  	if (uap->vendor->oversampling)
>  		clkdiv = 8;
> @@ -1991,18 +2072,29 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios,
>  	if (uap->fifosize > 1)
>  		lcr_h |= UART01x_LCRH_FEN;
>  
> +	bits = tty_get_frame_size(termios->c_cflag);
> +
>  	spin_lock_irqsave(&port->lock, flags);
>  
>  	/*
>  	 * Update the per-port timeout.
>  	 */
>  	uart_update_timeout(port, termios->c_cflag, baud);

Blank line

> +	/*
> +	 * Calculate the approximated time it takes to transmit one character
> +	 * with the given baud rate. We use this as the poll interval when we
> +	 * wait for the tx queue to empty.
> +	 */
> +	uap->rs485_tx_drain_interval = (bits * 1000 * 1000) / baud;
>  
>  	pl011_setup_status_masks(port, termios);
>  
>  	if (UART_ENABLE_MS(port, termios->c_cflag))
>  		pl011_enable_ms(port);
>  
> +	if (port->rs485.flags & SER_RS485_ENABLED)
> +		termios->c_cflag &= ~CRTSCTS;
> +
>  	/* first, disable everything */
>  	old_cr = pl011_read(uap, REG_CR);
>  	pl011_write(0, uap, REG_CR);
> @@ -2124,6 +2216,40 @@ static int pl011_verify_port(struct uart_port *port, struct serial_struct *ser)
>  	return ret;
>  }
>  
> +static int pl011_rs485_config(struct uart_port *port,
> +			      struct serial_rs485 *rs485)
> +{
> +	struct uart_amba_port *uap =
> +		container_of(port, struct uart_amba_port, port);
> +
> +	/* pick sane settings if the user hasn't */
> +	if (!!(rs485->flags & SER_RS485_RTS_ON_SEND) ==

Why the !! in an if statement?

> +	    !!(rs485->flags & SER_RS485_RTS_AFTER_SEND)) {

Same here, why?

> +		rs485->flags |= SER_RS485_RTS_ON_SEND;
> +		rs485->flags &= ~SER_RS485_RTS_AFTER_SEND;
> +	}
> +	/* clamp the delays to [0, 100ms] */
> +	rs485->delay_rts_before_send = min(rs485->delay_rts_before_send, 100U);
> +	rs485->delay_rts_after_send = min(rs485->delay_rts_after_send, 100U);
> +	memset(rs485->padding, 0, sizeof(rs485->padding));
> +
> +	if (port->rs485.flags & SER_RS485_ENABLED)
> +		pl011_rs485_tx_stop(uap);
> +
> +	/* Set new configuration */
> +	port->rs485 = *rs485;

Blank line please.

> +	/* Make sure auto RTS is disabled */
> +	if (port->rs485.flags & SER_RS485_ENABLED) {
> +		u32 cr = pl011_read(uap, REG_CR);
> +
> +		cr &= ~UART011_CR_RTSEN;
> +		pl011_write(cr, uap, REG_CR);
> +		port->status &= ~UPSTAT_AUTORTS;
> +	}
> +
> +	return 0;
> +}
> +

thanks,

greg k-h

  reply	other threads:[~2021-06-24 12:55 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-06-18 14:51 [PATCH v2] serial: amba-pl011: add RS485 support Lino Sanfilippo
2021-06-24 12:55 ` Greg KH [this message]
2021-06-25  0:15   ` Lino Sanfilippo
2021-06-25  6:01     ` Jiri Slaby
2021-06-25 11:21       ` Lino Sanfilippo
2021-06-26  3:41         ` Jiri Slaby
2021-06-29  9:46           ` 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=YNSA1H0cFKiPUn6N@kroah.com \
    --to=gregkh@linuxfoundation.org \
    --cc=LinoSanfilippo@gmx.de \
    --cc=jirislaby@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-serial@vger.kernel.org \
    --cc=linux@armlinux.org.uk \
    /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.