mbox series

[0/7] RZN1 UART DMA support

Message ID 20220310161650.289387-1-miquel.raynal@bootlin.com
Headers show
Series RZN1 UART DMA support | expand

Message

Miquel Raynal March 10, 2022, 4:16 p.m. UTC
Hello,

Support for the RZN1 DMA engine allows us adapt a little bit the 8250 DW
UART driver with to bring DMA support for this SoC.

This short series applies on top of the series bringing RZN1 DMA
support, currently on its v4, see [1]. Technically speaking, only the DT
patch needs to be applied after [1]. The other patches can come in at
any moment, because if no "dmas" property is provided in the DT, DMA
support will simply be ignored.

[1] https://lore.kernel.org/dmaengine/20220310155755.287294-1-miquel.raynal@bootlin.com/T/#mce6fec36e16dca560ab18935c273fcaf794a1cc4

Thanks,
Miquèl

Miquel Raynal (2):
  serial: 8250_dw: Provide the RZN1 CPR register value
  ARM: dts: r9a06g032: Fill the UART DMA properties

Phil Edworthy (5):
  serial: 8250_dma: Use ->tx_dma function pointer to start next DMA
  serial: 8250_dw: Move the per-device structure
  serial: 8250_dw: Use a fallback CPR value if not synthesized
  serial: 8250_dw: Add a dma_capable bit to the platform data
  serial: 8250_dw: Add support for RZ/N1 DMA

 arch/arm/boot/dts/r9a06g032.dtsi     |  15 ++++
 drivers/tty/serial/8250/8250_dma.c   |   4 +-
 drivers/tty/serial/8250/8250_dw.c    | 119 +++++++++++++++++++++++----
 drivers/tty/serial/8250/8250_dwlib.c |  17 +++-
 drivers/tty/serial/8250/8250_dwlib.h |  22 +++++
 5 files changed, 155 insertions(+), 22 deletions(-)

Comments

Miquel Raynal March 10, 2022, 7:01 p.m. UTC | #1
Hi Andy,

andriy.shevchenko@linux.intel.com wrote on Thu, 10 Mar 2022 20:02:41
+0200:

> On Thu, Mar 10, 2022 at 05:16:46PM +0100, Miquel Raynal wrote:
> > From: Phil Edworthy <phil.edworthy@renesas.com>
> > 
> > This UART controller can be synthesized without the CPR register.
> > In that case, let's use the platform information to provide a CPR value.  
> 
> ...
> 
> > +#include <linux/of_device.h>  
> 
> > +	const struct dw8250_platform_data *plat = of_device_get_match_data(up->port.dev);  
> 
> No. Please use device property APIs.

Are you suggesting the use of CPR DT property? If yes, what is the
point if there is one CPR per SoC? I would argue that DT description is
already quite complex and supporting one or another register is up to
the OS as long as we can map CPR registers to SoCs?

TBH Phil initially introduced a DT property which I turned into a pdata
entry because when I can avoid playing with the bindings, I generally
do so.

Thanks,
Miquèl
Miquel Raynal March 10, 2022, 7:27 p.m. UTC | #2
Hi Andy,

andriy.shevchenko@linux.intel.com wrote on Thu, 10 Mar 2022 20:25:52
+0200:

> On Thu, Mar 10, 2022 at 05:16:49PM +0100, Miquel Raynal wrote:
> > From: Phil Edworthy <phil.edworthy@renesas.com>
> > 
> > The Renesas RZ/N1 devices have a modified Synopsys DW UART. The
> > modifications are mostly related to the DMA handlnig, and so this patch
> > adds support for DMA.  
> 
> > The RZ/N1 UART must be used with the peripheral as the flow
> > controller.  
> 
> (1)
> 
> > This means the DMA length should also be programmed into
> > UART registers.  
> 
> (2)
> 
> Hmm... DMA controller vs. Peripheral flow control is about signalling on the HW
> level on who starts the transaction. This is programmed in the DMA controller
> device driver. Is it what you do in DesignWare DMA patch series?
> 
> Ah, I see now, you set fc here.
> 
> But still it's not clear how (2) and (1) are related.

Both come from the system manual:
(1) table 11.45 "Flow Control Combinations" states that using UART with
DMA requires setting the DMA in the peripheral flow controller mode
regardless of the direction.
(2) chapter 11.6.1.3 "Basic Interface Definitions" explains that the
burst size in the above case must be configured in the peripheral's
register DEST/SRC_BURST_SIZE.

> > Aside from this, there are some points to note about DMA burst sizes.
> > First, DMA must not remove all of the data from the rx FIFO. Otherwise,
> > we do not get a 'character timeout' interrupt, and so do not know that
> > we should push data up the serial stack. Therefore, we have the rx
> > threshold for generating an interrupt set to half the FIFO depth (this
> > is the default for 16550A), and set the DMA burst size when reading the
> > FIFO to a quarter of the FIFO depth.
> > 
> > Second, when transmitting data using DMA, the burst size must be limited
> > to 1 byte to handle then case when transmitting just 1 byte. Otherwise
> > the DMA doesn't complete the burst, and nothing happens.  
> 
> ...
> 
> > +/* Offsets for the Renesas RZ/N1 DesignWare specific registers */
> > +/* DMA Software Ack */
> > +#define RZN1_UART_DMASA			0xa8  
> 
> Is it specific to Renesas? IIRC it's Synopsys DesignWare register, makes
> sense to use appropriate prefix or no prefix.

I have no idea, I can use a more common prefix.

> 
> ...
> 
> > +#define RZN1_UART_xDMACR_1_WORD_BURST	0
> > +#define RZN1_UART_xDMACR_4_WORD_BURST	BIT(1)
> > +#define RZN1_UART_xDMACR_8_WORD_BURST	(BIT(1) | BIT(2))  
> 
> This looks like incorrect use of BIT() macro.
> Please, use plain decimal integers. Something like
> 
> 	1	(0 << 1)
> 	4	(1 << 1)
> 	8	(3 << 1)
> 
> If I'm mistaken, describe the meaning of each bit there.

Matter of taste, I believe, whatever.

> 
> ...
> 
> > +static void rzn1_8250_handle_irq(struct uart_port *port, unsigned int iir)
> > +{
> > +	struct uart_8250_port *up = up_to_u8250p(port);
> > +	struct uart_8250_dma *dma = up->dma;
> > +	unsigned char status;  
> 
> > +	if (up->dma && dma->rx_running) {  
> 
> With
> 
> 	if (!)up->dma && dma->rx_running))
> 		return;
> 
> maybe easier to read the rest.
> 
> > +		status = port->serial_in(port, UART_LSR);
> > +		if (status & (UART_LSR_DR | UART_LSR_BI)) {
> > +			/* Stop the DMA transfer */
> > +			writel(0, port->membase + RZN1_UART_RDMACR);
> > +			writel(1, port->membase + RZN1_UART_DMASA);
> > +		}
> > +	}
> > +}  
> 
> ...
> 
> > +	if (d->is_rzn1 && ((iir & 0x3f) == UART_IIR_RX_TIMEOUT))
> > +		rzn1_8250_handle_irq(p, iir);  
> 
> A few years ago it was a discussion about broken timeout on some platforms
> with Synopsys DesignWare UART + DMA. Can it be that this is actually required
> for all of them that uses same combination of IPs?

I am not sure because I went through the fix for this issue and for me
there are two different things

> 
> ...
> 
> > +static u32 rzn1_get_dmacr_burst(int max_burst)
> > +{  
> 
> > +	u32 val = 0;  
> 
> Redundant assignment and variable itself. Use return statements directly.
> 
> > +	if (max_burst >= 8)
> > +		val = RZN1_UART_xDMACR_8_WORD_BURST;
> > +	else if (max_burst >= 4)
> > +		val = RZN1_UART_xDMACR_4_WORD_BURST;
> > +	else
> > +		val = RZN1_UART_xDMACR_1_WORD_BURST;
> > +
> > +	return val;
> > +}  
> 
> ...
> 
> > +static int rzn1_dw8250_tx_dma(struct uart_8250_port *p)
> > +{
> > +	struct uart_port		*up = &p->port;
> > +	struct uart_8250_dma		*dma = p->dma;
> > +	struct circ_buf			*xmit = &p->port.state->xmit;
> > +	int tx_size;
> > +	u32 val;
> > +
> > +	if (uart_tx_stopped(&p->port) || dma->tx_running ||
> > +	    uart_circ_empty(xmit))
> > +		return 0;
> > +
> > +	tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);  
> 
> > +	writel(0, up->membase + RZN1_UART_TDMACR);
> > +	val = rzn1_get_dmacr_burst(dma->txconf.dst_maxburst);
> > +	val |= tx_size << RZN1_UART_xDMACR_BLK_SZ_OFFSET;
> > +	val |= RZN1_UART_xDMACR_DMA_EN;
> > +	writel(val, up->membase + RZN1_UART_TDMACR);  
> 
> Can this be added as a callback to the serial8250_tx_dma()?
> Ditto for Rx counterpart.

Fair enough.

> 
> > +	return serial8250_tx_dma(p);
> > +}  
> 
> ...
> 
> > +	data->is_rzn1 = of_device_is_compatible(dev->of_node, "renesas,rzn1-uart");  
> 
> Device property API.

I'm not sure to get what you mean here again. The Information is in the
device tree, the compatible string already gives us what we need, why
would we need a device property? (or perhaps I misunderstand what
"device property API" means)
> 
> >  	/* Always ask for fixed clock rate from a property. */
> >  	device_property_read_u32(dev, "clock-frequency", &p->uartclk);  
> 


Thanks,
Miquèl
Geert Uytterhoeven March 11, 2022, 9:39 a.m. UTC | #3
Hi Miquel,

On Thu, Mar 10, 2022 at 5:17 PM Miquel Raynal <miquel.raynal@bootlin.com> wrote:
> From: Phil Edworthy <phil.edworthy@renesas.com>
>
> The Renesas RZ/N1 devices have a modified Synopsys DW UART. The
> modifications are mostly related to the DMA handlnig, and so this patch
> adds support for DMA.
>
> The RZ/N1 UART must be used with the peripheral as the flow
> controller. This means the DMA length should also be programmed into
> UART registers.
>
> Aside from this, there are some points to note about DMA burst sizes.
> First, DMA must not remove all of the data from the rx FIFO. Otherwise,
> we do not get a 'character timeout' interrupt, and so do not know that
> we should push data up the serial stack. Therefore, we have the rx
> threshold for generating an interrupt set to half the FIFO depth (this
> is the default for 16550A), and set the DMA burst size when reading the
> FIFO to a quarter of the FIFO depth.
>
> Second, when transmitting data using DMA, the burst size must be limited
> to 1 byte to handle then case when transmitting just 1 byte. Otherwise
> the DMA doesn't complete the burst, and nothing happens.
>
> Signed-off-by: Phil Edworthy <phil.edworthy@renesas.com>
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>

Thanks for your patch!

> --- a/drivers/tty/serial/8250/8250_dma.c
> +++ b/drivers/tty/serial/8250/8250_dma.c

> @@ -501,6 +589,8 @@ static int dw8250_probe(struct platform_device *pdev)
>                 data->msr_mask_off |= UART_MSR_TERI;
>         }
>
> +       data->is_rzn1 = of_device_is_compatible(dev->of_node, "renesas,rzn1-uart");

Explicit checks for compatible values are frowned upon if you have
a match table.
Please handle this through of_device.data, cfr. the various quirks.
Please rename "is_rzn1" to something that describes the feature.

> +
>         /* Always ask for fixed clock rate from a property. */
>         device_property_read_u32(dev, "clock-frequency", &p->uartclk);

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds
Miquel Raynal March 11, 2022, 9:59 a.m. UTC | #4
Hi Geert,

geert@linux-m68k.org wrote on Fri, 11 Mar 2022 10:51:53 +0100:

> Hi Miquel,
> 
> CC esmil
> 

> > > --- a/drivers/tty/serial/8250/8250_dma.c
> > > +++ b/drivers/tty/serial/8250/8250_dma.c  
> >  
> > > @@ -501,6 +589,8 @@ static int dw8250_probe(struct platform_device *pdev)
> > >                 data->msr_mask_off |= UART_MSR_TERI;
> > >         }
> > >
> > > +       data->is_rzn1 = of_device_is_compatible(dev->of_node, "renesas,rzn1-uart");  
> >
> > Explicit checks for compatible values are frowned upon if you have
> > a match table.
> > Please handle this through of_device.data, cfr. the various quirks.  
> 
> Oops, these are not yet upstream, but present in my tree due to including
> support for StarLight, cfr.
> https://github.com/esmil/linux/commits/visionfive/drivers/tty/serial/8250/8250_dw.c

Oh thanks for pointing it! Too bad that these quirks were not
introduced inside a wider structure, I think it's always a must even if
there is only one parameter there. Anyway, I'll introduce a wider
specific structure and use it.

> But you do already have:
> 
> +       { .compatible = "renesas,rzn1-uart", .data = &rzn1_pdata },
> 
> since "[PATCH 4/7] serial: 8250_dw: Provide the RZN1 CPR register value".
> 
> > Please rename "is_rzn1" to something that describes the feature.
> >  
> > > +
> > >         /* Always ask for fixed clock rate from a property. */
> > >         device_property_read_u32(dev, "clock-frequency", &p->uartclk);  
> 
> Gr{oetje,eeting}s,
> 
>                         Geert


Thanks,
Miquèl
Andy Shevchenko March 11, 2022, 5:27 p.m. UTC | #5
On Fri, Mar 11, 2022 at 03:48:14PM +0100, Emil Renner Berthing wrote:
> ..rather than multiple calls to of_device_is_compatible().

> For reference this is the patch I wrote for the StarFive JH7100 tree.
> Feel free to use it or do something better as you see fit.

>  	if (np) {
> +		unsigned long quirks = (unsigned long)of_device_get_match_data(p->dev);

It can be done outside of the np check with device property APIs in use.
Also it needs to use (uintptr_t) for better coverage.

		unsigned long quirks = (uintptr_t)device_get_match_data(p->dev);

Or use data structure as driver_data.

		const struct ... *data = device_get_match_data(p->dev);
Miquel Raynal March 16, 2022, 2:40 p.m. UTC | #6
Hi Emil,

kernel@esmil.dk wrote on Fri, 11 Mar 2022 15:48:14 +0100:

> ..rather than multiple calls to of_device_is_compatible().
> 
> Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
> ---
> 
> Hi Miquel,
> 
> > > > > --- a/drivers/tty/serial/8250/8250_dma.c
> > > > > +++ b/drivers/tty/serial/8250/8250_dma.c =20  
> > > > =20  
> > > > > @@ -501,6 +589,8 @@ static int dw8250_probe(struct platform_device *p=  
> > dev)  
> > > > >                 data->msr_mask_off |=3D UART_MSR_TERI;
> > > > >         }
> > > > >
> > > > > +       data->is_rzn1 =3D of_device_is_compatible(dev->of_node, "rene=  
> > sas,rzn1-uart"); =20  
> > > >
> > > > Explicit checks for compatible values are frowned upon if you have
> > > > a match table.
> > > > Please handle this through of_device.data, cfr. the various quirks. =20  
> > >=20
> > > Oops, these are not yet upstream, but present in my tree due to including
> > > support for StarLight, cfr.
> > > https://github.com/esmil/linux/commits/visionfive/drivers/tty/serial/8250=  
> > /8250_dw.c
> > 
> > Oh thanks for pointing it! Too bad that these quirks were not
> > introduced inside a wider structure, I think it's always a must even if
> > there is only one parameter there. Anyway, I'll introduce a wider
> > specific structure and use it.  
> 
> For reference this is the patch I wrote for the StarFive JH7100 tree.
> Feel free to use it or do something better as you see fit.

Thanks for the pointers, I've fetched the three 8250_dw patches from
your tree directly, and will build on top of them!

Cheers,
Miquèl

> 
> /Emil
> 
>  drivers/tty/serial/8250/8250_dw.c | 18 ++++++++++++------
>  1 file changed, 12 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
> index 1769808031c5..f564a019a7be 100644
> --- a/drivers/tty/serial/8250/8250_dw.c
> +++ b/drivers/tty/serial/8250/8250_dw.c
> @@ -37,6 +37,11 @@
>  /* DesignWare specific register fields */
>  #define DW_UART_MCR_SIRE		BIT(6)
>  
> +/* Quirks */
> +#define DW_UART_QUIRK_OCTEON		BIT(0)
> +#define DW_UART_QUIRK_ARMADA_38X	BIT(1)
> +#define DW_UART_QUIRK_SKIP_SET_RATE	BIT(2)
> +
>  struct dw8250_data {
>  	struct dw8250_port_data	data;
>  
> @@ -389,6 +394,7 @@ static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data)
>  	struct device_node *np = p->dev->of_node;
>  
>  	if (np) {
> +		unsigned long quirks = (unsigned long)of_device_get_match_data(p->dev);
>  		int id;
>  
>  		/* get index of serial line, if found in DT aliases */
> @@ -396,7 +402,7 @@ static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data)
>  		if (id >= 0)
>  			p->line = id;
>  #ifdef CONFIG_64BIT
> -		if (of_device_is_compatible(np, "cavium,octeon-3860-uart")) {
> +		if (quirks & DW_UART_QUIRK_OCTEON) {
>  			p->serial_in = dw8250_serial_inq;
>  			p->serial_out = dw8250_serial_outq;
>  			p->flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_TYPE;
> @@ -412,9 +418,9 @@ static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data)
>  			p->serial_out = dw8250_serial_out32be;
>  		}
>  
> -		if (of_device_is_compatible(np, "marvell,armada-38x-uart"))
> +		if (quirks & DW_UART_QUIRK_ARMADA_38X)
>  			p->serial_out = dw8250_serial_out38x;
> -		if (of_device_is_compatible(np, "starfive,jh7100-uart"))
> +		if (quirks & DW_UART_QUIRK_SKIP_SET_RATE)
>  			p->set_termios = dw8250_do_set_termios;
>  
>  	} else if (acpi_dev_present("APMC0D08", NULL, -1)) {
> @@ -695,10 +701,10 @@ static const struct dev_pm_ops dw8250_pm_ops = {
>  
>  static const struct of_device_id dw8250_of_match[] = {
>  	{ .compatible = "snps,dw-apb-uart" },
> -	{ .compatible = "cavium,octeon-3860-uart" },
> -	{ .compatible = "marvell,armada-38x-uart" },
> +	{ .compatible = "cavium,octeon-3860-uart", .data = (void *)DW_UART_QUIRK_OCTEON },
> +	{ .compatible = "marvell,armada-38x-uart", .data = (void *)DW_UART_QUIRK_ARMADA_38X },
>  	{ .compatible = "renesas,rzn1-uart" },
> -	{ .compatible = "starfive,jh7100-uart" },
> +	{ .compatible = "starfive,jh7100-uart",    .data = (void *)DW_UART_QUIRK_SKIP_SET_RATE },
>  	{ /* Sentinel */ }
>  };
>  MODULE_DEVICE_TABLE(of, dw8250_of_match);


Thanks,
Miquèl