[PATCH 5/7] dmaengine: at_xdmac: add support for sama7g5 based at_xdmac

Tudor.Ambarus at microchip.com Tudor.Ambarus at microchip.com
Wed Sep 23 03:08:03 EDT 2020


On 9/14/20 5:09 PM, Eugen Hristev wrote:
> SAMA7G5 SoC uses a slightly different variant of the AT_XDMAC.
> Added support by a new compatible and a layout struct that copes
> to the specific version considering the compatible string.
> Only the differences in register map are present in the layout struct.
> I reworked the register access for this part that has the differences.
> Also the Source/Destination Interface bits are no longer valid for this
> variant of the XDMAC. Thus, the layout also has a bool for specifying
> whether these bits are required or not.
> 
> Signed-off-by: Eugen Hristev <eugen.hristev at microchip.com>
> ---
>  drivers/dma/at_xdmac.c      | 99 ++++++++++++++++++++++++++++++-------
>  drivers/dma/at_xdmac_regs.h |  9 ----
>  2 files changed, 82 insertions(+), 26 deletions(-)
> 
> diff --git a/drivers/dma/at_xdmac.c b/drivers/dma/at_xdmac.c
> index 81bb90206092..874484a4e79f 100644
> --- a/drivers/dma/at_xdmac.c
> +++ b/drivers/dma/at_xdmac.c
> @@ -38,6 +38,27 @@ enum atc_status {
>  	AT_XDMAC_CHAN_IS_PAUSED,
>  };
>  
> +struct at_xdmac_layout {
> +	/* Global Channel Read Suspend Register */
> +	u8				grs;
> +	/* Global Write Suspend Register */
> +	u8				gws;
> +	/* Global Channel Read Write Suspend Register */
> +	u8				grws;
> +	/* Global Channel Read Write Resume Register */
> +	u8				grwr;
> +	/* Global Channel Software Request Register */
> +	u8				gswr;
> +	/* Global channel Software Request Status Register */
> +	u8				gsws;
> +	/* Global Channel Software Flush Request Register */
> +	u8				gswf;
> +	/* Channel reg base */
> +	u8				chan_cc_reg_base;
> +	/* Source/Destination Interface must be specified or not */
> +	bool				sdif;
> +};
> +
>  /* ----- Channels ----- */
>  struct at_xdmac_chan {
>  	struct dma_chan			chan;
> @@ -71,6 +92,7 @@ struct at_xdmac {
>  	struct clk		*clk;
>  	u32			save_gim;
>  	struct dma_pool		*at_xdmac_desc_pool;
> +	const struct at_xdmac_layout	*layout;
>  	struct at_xdmac_chan	chan[];
>  };
>  
> @@ -103,9 +125,33 @@ struct at_xdmac_desc {
>  	struct list_head		xfer_node;
>  } __aligned(sizeof(u64));
>  
> +static struct at_xdmac_layout at_xdmac_sama5d4_layout = {

static const struct

> +	.grs = 0x28,
> +	.gws = 0x2C,
> +	.grws = 0x30,
> +	.grwr = 0x34,
> +	.gswr = 0x38,
> +	.gsws = 0x3C,
> +	.gswf = 0x40,
> +	.chan_cc_reg_base = 0x50,
> +	.sdif = true,
> +};
> +
> +static struct at_xdmac_layout at_xdmac_sama7g5_layout = {

static const struct

> +	.grs = 0x30,
> +	.gws = 0x38,
> +	.grws = 0x40,
> +	.grwr = 0x44,
> +	.gswr = 0x48,
> +	.gsws = 0x4C,
> +	.gswf = 0x50,
> +	.chan_cc_reg_base = 0x60,

one may find these plain offsets as too raw and probably prefer some defines
for them, but I too think that the members of the struct are self-explanatory,
so I'm ok either way.

> +	.sdif = false,
> +};
> +
>  static inline void __iomem *at_xdmac_chan_reg_base(struct at_xdmac *atxdmac, unsigned int chan_nb)
>  {
> -	return atxdmac->regs + (AT_XDMAC_CHAN_REG_BASE + chan_nb * 0x40);
> +	return atxdmac->regs + (atxdmac->layout->chan_cc_reg_base + chan_nb * 0x40);
>  }
>  
>  #define at_xdmac_read(atxdmac, reg) readl_relaxed((atxdmac)->regs + (reg))
> @@ -204,8 +250,10 @@ static void at_xdmac_start_xfer(struct at_xdmac_chan *atchan,
>  	first->active_xfer = true;
>  
>  	/* Tell xdmac where to get the first descriptor. */
> -	reg = AT_XDMAC_CNDA_NDA(first->tx_dma_desc.phys)
> -	      | AT_XDMAC_CNDA_NDAIF(atchan->memif);
> +	reg = AT_XDMAC_CNDA_NDA(first->tx_dma_desc.phys);
> +	if (atxdmac->layout->sdif)
> +		reg |= AT_XDMAC_CNDA_NDAIF(atchan->memif);
> +
>  	at_xdmac_chan_write(atchan, AT_XDMAC_CNDA, reg);
>  
>  	/*
> @@ -400,6 +448,7 @@ static int at_xdmac_compute_chan_conf(struct dma_chan *chan,
>  				      enum dma_transfer_direction direction)
>  {
>  	struct at_xdmac_chan	*atchan = to_at_xdmac_chan(chan);
> +	struct at_xdmac		*atxdmac = to_at_xdmac(atchan->chan.device);
>  	int			csize, dwidth;
>  
>  	if (direction == DMA_DEV_TO_MEM) {
> @@ -407,12 +456,14 @@ static int at_xdmac_compute_chan_conf(struct dma_chan *chan,
>  			AT91_XDMAC_DT_PERID(atchan->perid)
>  			| AT_XDMAC_CC_DAM_INCREMENTED_AM
>  			| AT_XDMAC_CC_SAM_FIXED_AM
> -			| AT_XDMAC_CC_DIF(atchan->memif)
> -			| AT_XDMAC_CC_SIF(atchan->perif)
>  			| AT_XDMAC_CC_SWREQ_HWR_CONNECTED
>  			| AT_XDMAC_CC_DSYNC_PER2MEM
>  			| AT_XDMAC_CC_MBSIZE_SIXTEEN
>  			| AT_XDMAC_CC_TYPE_PER_TRAN;
> +		if (atxdmac->layout->sdif)
> +			atchan->cfg |= AT_XDMAC_CC_DIF(atchan->memif)
> +				| AT_XDMAC_CC_SIF(atchan->perif);

very odd for me to see the "|" operator on the next line. I find it hard to
read and somehow exhausting. I would put it on the first line even if throughout
the driver it's on the next line.

> +
>  		csize = ffs(atchan->sconfig.src_maxburst) - 1;
>  		if (csize < 0) {
>  			dev_err(chan2dev(chan), "invalid src maxburst value\n");
> @@ -430,12 +481,14 @@ static int at_xdmac_compute_chan_conf(struct dma_chan *chan,
>  			AT91_XDMAC_DT_PERID(atchan->perid)
>  			| AT_XDMAC_CC_DAM_FIXED_AM
>  			| AT_XDMAC_CC_SAM_INCREMENTED_AM
> -			| AT_XDMAC_CC_DIF(atchan->perif)
> -			| AT_XDMAC_CC_SIF(atchan->memif)
>  			| AT_XDMAC_CC_SWREQ_HWR_CONNECTED
>  			| AT_XDMAC_CC_DSYNC_MEM2PER
>  			| AT_XDMAC_CC_MBSIZE_SIXTEEN
>  			| AT_XDMAC_CC_TYPE_PER_TRAN;
> +		if (atxdmac->layout->sdif)
> +			atchan->cfg |=  AT_XDMAC_CC_DIF(atchan->perif)
> +				| AT_XDMAC_CC_SIF(atchan->memif);
> +
>  		csize = ffs(atchan->sconfig.dst_maxburst) - 1;
>  		if (csize < 0) {
>  			dev_err(chan2dev(chan), "invalid src maxburst value\n");
> @@ -711,6 +764,7 @@ at_xdmac_interleaved_queue_desc(struct dma_chan *chan,
>  				struct data_chunk *chunk)
>  {
>  	struct at_xdmac_desc	*desc;
> +	struct at_xdmac		*atxdmac = to_at_xdmac(atchan->chan.device);
>  	u32			dwidth;
>  	unsigned long		flags;
>  	size_t			ublen;
> @@ -727,10 +781,10 @@ at_xdmac_interleaved_queue_desc(struct dma_chan *chan,
>  	 * flag status.
>  	 */
>  	u32			chan_cc = AT_XDMAC_CC_PERID(0x7f)
> -					| AT_XDMAC_CC_DIF(0)
> -					| AT_XDMAC_CC_SIF(0)
>  					| AT_XDMAC_CC_MBSIZE_SIXTEEN
>  					| AT_XDMAC_CC_TYPE_MEM_TRAN;
> +	if (atxdmac->layout->sdif)
> +		chan_cc |= AT_XDMAC_CC_DIF(0) | AT_XDMAC_CC_SIF(0);

there is a comment above chan_cc init that explains that we have to use
interface 0 for both source and destination, so maybe we can get rid of
this explicit OR with zero and update the comment for sama7g5 case.

>  
>  	dwidth = at_xdmac_align_width(chan, src | dst | chunk->size);
>  	if (chunk->size >= (AT_XDMAC_MBR_UBC_UBLEN_MAX << dwidth)) {
> @@ -893,6 +947,7 @@ at_xdmac_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
>  			 size_t len, unsigned long flags)
>  {
>  	struct at_xdmac_chan	*atchan = to_at_xdmac_chan(chan);
> +	struct at_xdmac		*atxdmac = to_at_xdmac(atchan->chan.device);
>  	struct at_xdmac_desc	*first = NULL, *prev = NULL;
>  	size_t			remaining_size = len, xfer_size = 0, ublen;
>  	dma_addr_t		src_addr = src, dst_addr = dest;
> @@ -911,12 +966,13 @@ at_xdmac_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
>  	u32			chan_cc = AT_XDMAC_CC_PERID(0x7f)
>  					| AT_XDMAC_CC_DAM_INCREMENTED_AM
>  					| AT_XDMAC_CC_SAM_INCREMENTED_AM
> -					| AT_XDMAC_CC_DIF(0)
> -					| AT_XDMAC_CC_SIF(0)
>  					| AT_XDMAC_CC_MBSIZE_SIXTEEN
>  					| AT_XDMAC_CC_TYPE_MEM_TRAN;
>  	unsigned long		irqflags;
>  
> +	if (atxdmac->layout->sdif)
> +		chan_cc |= AT_XDMAC_CC_DIF(0) | AT_XDMAC_CC_SIF(0);

same here

> +
>  	dev_dbg(chan2dev(chan), "%s: src=%pad, dest=%pad, len=%zd, flags=0x%lx\n",
>  		__func__, &src, &dest, len, flags);
>  
> @@ -999,6 +1055,7 @@ static struct at_xdmac_desc *at_xdmac_memset_create_desc(struct dma_chan *chan,
>  							 int value)
>  {
>  	struct at_xdmac_desc	*desc;
> +	struct at_xdmac		*atxdmac = to_at_xdmac(atchan->chan.device);
>  	unsigned long		flags;
>  	size_t			ublen;
>  	u32			dwidth;
> @@ -1017,11 +1074,11 @@ static struct at_xdmac_desc *at_xdmac_memset_create_desc(struct dma_chan *chan,
>  	u32			chan_cc = AT_XDMAC_CC_PERID(0x7f)
>  					| AT_XDMAC_CC_DAM_UBS_AM
>  					| AT_XDMAC_CC_SAM_INCREMENTED_AM
> -					| AT_XDMAC_CC_DIF(0)
> -					| AT_XDMAC_CC_SIF(0)
>  					| AT_XDMAC_CC_MBSIZE_SIXTEEN
>  					| AT_XDMAC_CC_MEMSET_HW_MODE
>  					| AT_XDMAC_CC_TYPE_MEM_TRAN;
> +	if (atxdmac->layout->sdif)
> +		chan_cc |= AT_XDMAC_CC_DIF(0) | AT_XDMAC_CC_SIF(0);

same here

>  
>  	dwidth = at_xdmac_align_width(chan, dst_addr);
>  
> @@ -1297,7 +1354,7 @@ at_xdmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
>  	mask = AT_XDMAC_CC_TYPE | AT_XDMAC_CC_DSYNC;
>  	value = AT_XDMAC_CC_TYPE_PER_TRAN | AT_XDMAC_CC_DSYNC_PER2MEM;
>  	if ((desc->lld.mbr_cfg & mask) == value) {
> -		at_xdmac_write(atxdmac, AT_XDMAC_GSWF, atchan->mask);
> +		at_xdmac_write(atxdmac, atxdmac->layout->gswf, atchan->mask);
>  		while (!(at_xdmac_chan_read(atchan, AT_XDMAC_CIS) & AT_XDMAC_CIS_FIS))
>  			cpu_relax();
>  	}
> @@ -1355,7 +1412,7 @@ at_xdmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
>  	 * FIFO flush ensures that data are really written.
>  	 */
>  	if ((desc->lld.mbr_cfg & mask) == value) {
> -		at_xdmac_write(atxdmac, AT_XDMAC_GSWF, atchan->mask);
> +		at_xdmac_write(atxdmac, atxdmac->layout->gswf, atchan->mask);
>  		while (!(at_xdmac_chan_read(atchan, AT_XDMAC_CIS) & AT_XDMAC_CIS_FIS))
>  			cpu_relax();
>  	}
> @@ -1620,7 +1677,7 @@ static int at_xdmac_device_pause(struct dma_chan *chan)
>  		return 0;
>  
>  	spin_lock_irqsave(&atchan->lock, flags);
> -	at_xdmac_write(atxdmac, AT_XDMAC_GRWS, atchan->mask);
> +	at_xdmac_write(atxdmac, atxdmac->layout->grws, atchan->mask);
>  	while (at_xdmac_chan_read(atchan, AT_XDMAC_CC)
>  	       & (AT_XDMAC_CC_WRIP | AT_XDMAC_CC_RDIP))
>  		cpu_relax();
> @@ -1643,7 +1700,7 @@ static int at_xdmac_device_resume(struct dma_chan *chan)
>  		return 0;
>  	}
>  
> -	at_xdmac_write(atxdmac, AT_XDMAC_GRWR, atchan->mask);
> +	at_xdmac_write(atxdmac, atxdmac->layout->grwr, atchan->mask);
>  	clear_bit(AT_XDMAC_CHAN_IS_PAUSED, &atchan->status);
>  	spin_unlock_irqrestore(&atchan->lock, flags);
>  
> @@ -1845,6 +1902,10 @@ static int at_xdmac_probe(struct platform_device *pdev)
>  	atxdmac->regs = base;
>  	atxdmac->irq = irq;
>  
> +	atxdmac->layout = of_device_get_match_data(&pdev->dev);
> +	if (!atxdmac->layout)
> +		return -ENODEV;

I would get the data upper in the function, after getting irq. If data is
not provided, you would spare some ops that will be done gratuitously.

With these addressed one may add:
Reviewed-by: Tudor Ambarus <tudor.ambarus at microchip.com>
> +
>  	atxdmac->clk = devm_clk_get(&pdev->dev, "dma_clk");
>  	if (IS_ERR(atxdmac->clk)) {
>  		dev_err(&pdev->dev, "can't get dma_clk\n");
> @@ -1988,6 +2049,10 @@ static const struct dev_pm_ops atmel_xdmac_dev_pm_ops = {
>  static const struct of_device_id atmel_xdmac_dt_ids[] = {
>  	{
>  		.compatible = "atmel,sama5d4-dma",
> +		.data = &at_xdmac_sama5d4_layout,
> +	}, {
> +		.compatible = "microchip,sama7g5-dma",
> +		.data = &at_xdmac_sama7g5_layout,
>  	}, {
>  		/* sentinel */
>  	}
> diff --git a/drivers/dma/at_xdmac_regs.h b/drivers/dma/at_xdmac_regs.h
> index 3f7dda4c5743..7b4b4e24de70 100644
> --- a/drivers/dma/at_xdmac_regs.h
> +++ b/drivers/dma/at_xdmac_regs.h
> @@ -22,13 +22,6 @@
>  #define AT_XDMAC_GE		0x1C	/* Global Channel Enable Register */
>  #define AT_XDMAC_GD		0x20	/* Global Channel Disable Register */
>  #define AT_XDMAC_GS		0x24	/* Global Channel Status Register */
> -#define AT_XDMAC_GRS		0x28	/* Global Channel Read Suspend Register */
> -#define AT_XDMAC_GWS		0x2C	/* Global Write Suspend Register */
> -#define AT_XDMAC_GRWS		0x30	/* Global Channel Read Write Suspend Register */
> -#define AT_XDMAC_GRWR		0x34	/* Global Channel Read Write Resume Register */
> -#define AT_XDMAC_GSWR		0x38	/* Global Channel Software Request Register */
> -#define AT_XDMAC_GSWS		0x3C	/* Global channel Software Request Status Register */
> -#define AT_XDMAC_GSWF		0x40	/* Global Channel Software Flush Request Register */
>  #define AT_XDMAC_VERSION	0xFFC	/* XDMAC Version Register */
>  
>  /* Channel relative registers offsets */
> @@ -134,8 +127,6 @@
>  #define AT_XDMAC_CSUS		0x30	/* Channel Source Microblock Stride */
>  #define AT_XDMAC_CDUS		0x34	/* Channel Destination Microblock Stride */
>  
> -#define AT_XDMAC_CHAN_REG_BASE	0x50	/* Channel registers base address */
> -
>  /* Microblock control members */
>  #define AT_XDMAC_MBR_UBC_UBLEN_MAX	0xFFFFFFUL	/* Maximum Microblock Length */
>  #define AT_XDMAC_MBR_UBC_NDE		(0x1 << 24)	/* Next Descriptor Enable */
> 



More information about the linux-arm-kernel mailing list