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

Eugen.Hristev at microchip.com Eugen.Hristev at microchip.com
Fri Oct 16 02:52:57 EDT 2020


On 23.09.2020 10:08, Tudor Ambarus - M18064 wrote:
> 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.

Hi Tudor,

This would be difficult to do as atxdmac is allocated just a few lines 
before.
Also, actually the get_match_data should not fail normally. If this 
would fail it would mean that the driver is probed to a wrong driver 
compatible... which should not happen.

Thanks for reviewing. I am sending v2.

Eugen

> 
> 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