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@xxxxxxxxxxxxx> >> --- >> 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@xxxxxxxxxxxxx> >> + >> 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 */ >> >