dmaengine: dw-edma: Join read/write channels into a single device

There is no point in splitting read/write channels.  First of all, eDMA
read and write channels belong to one physical controller. Secondly,
channel differentiation can be done by filtering and dma_get_slave_caps().
Finally, having these channels handled separately needlessly complicates
the code and causes this debugfs warning:

  debugfs: Directory '1f052000.pcie' with parent 'dmaengine' already present!

Join the read/write channels into a single DMA device.  Client drivers can
choose the correct channel via the DMA slave direction setting. The default
value is overridden by the dw_edma_device_caps() callback in accordance
with the channel type.

Link: https://lore.kernel.org/r/20230113171409.30470-18-Sergey.Semin@baikalelectronics.ru
Tested-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Acked-by: Vinod Koul <vkoul@kernel.org>
This commit is contained in:
Serge Semin 2023-01-13 20:13:59 +03:00 committed by Bjorn Helgaas
parent d015216853
commit 3883d64449
2 changed files with 61 additions and 60 deletions

View file

@ -208,6 +208,24 @@ static void dw_edma_start_transfer(struct dw_edma_chan *chan)
desc->chunks_alloc--;
}
static void dw_edma_device_caps(struct dma_chan *dchan,
struct dma_slave_caps *caps)
{
struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
if (chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) {
if (chan->dir == EDMA_DIR_READ)
caps->directions = BIT(DMA_DEV_TO_MEM);
else
caps->directions = BIT(DMA_MEM_TO_DEV);
} else {
if (chan->dir == EDMA_DIR_WRITE)
caps->directions = BIT(DMA_DEV_TO_MEM);
else
caps->directions = BIT(DMA_MEM_TO_DEV);
}
}
static int dw_edma_device_config(struct dma_chan *dchan,
struct dma_slave_config *config)
{
@ -717,8 +735,7 @@ static void dw_edma_free_chan_resources(struct dma_chan *dchan)
}
}
static int dw_edma_channel_setup(struct dw_edma *dw, bool write,
u32 wr_alloc, u32 rd_alloc)
static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc)
{
struct dw_edma_chip *chip = dw->chip;
struct dw_edma_region *dt_region;
@ -726,27 +743,15 @@ static int dw_edma_channel_setup(struct dw_edma *dw, bool write,
struct dw_edma_chan *chan;
struct dw_edma_irq *irq;
struct dma_device *dma;
u32 alloc, off_alloc;
u32 i, j, cnt;
int err = 0;
u32 i, ch_cnt;
u32 pos;
if (write) {
i = 0;
cnt = dw->wr_ch_cnt;
dma = &dw->wr_edma;
alloc = wr_alloc;
off_alloc = 0;
} else {
i = dw->wr_ch_cnt;
cnt = dw->rd_ch_cnt;
dma = &dw->rd_edma;
alloc = rd_alloc;
off_alloc = wr_alloc;
}
ch_cnt = dw->wr_ch_cnt + dw->rd_ch_cnt;
dma = &dw->dma;
INIT_LIST_HEAD(&dma->channels);
for (j = 0; (alloc || dw->nr_irqs == 1) && j < cnt; j++, i++) {
for (i = 0; i < ch_cnt; i++) {
chan = &dw->chan[i];
dt_region = devm_kzalloc(dev, sizeof(*dt_region), GFP_KERNEL);
@ -756,52 +761,62 @@ static int dw_edma_channel_setup(struct dw_edma *dw, bool write,
chan->vc.chan.private = dt_region;
chan->dw = dw;
chan->id = j;
chan->dir = write ? EDMA_DIR_WRITE : EDMA_DIR_READ;
if (i < dw->wr_ch_cnt) {
chan->id = i;
chan->dir = EDMA_DIR_WRITE;
} else {
chan->id = i - dw->wr_ch_cnt;
chan->dir = EDMA_DIR_READ;
}
chan->configured = false;
chan->request = EDMA_REQ_NONE;
chan->status = EDMA_ST_IDLE;
if (write)
chan->ll_max = (chip->ll_region_wr[j].sz / EDMA_LL_SZ);
if (chan->dir == EDMA_DIR_WRITE)
chan->ll_max = (chip->ll_region_wr[chan->id].sz / EDMA_LL_SZ);
else
chan->ll_max = (chip->ll_region_rd[j].sz / EDMA_LL_SZ);
chan->ll_max = (chip->ll_region_rd[chan->id].sz / EDMA_LL_SZ);
chan->ll_max -= 1;
dev_vdbg(dev, "L. List:\tChannel %s[%u] max_cnt=%u\n",
write ? "write" : "read", j, chan->ll_max);
chan->dir == EDMA_DIR_WRITE ? "write" : "read",
chan->id, chan->ll_max);
if (dw->nr_irqs == 1)
pos = 0;
else if (chan->dir == EDMA_DIR_WRITE)
pos = chan->id % wr_alloc;
else
pos = off_alloc + (j % alloc);
pos = wr_alloc + chan->id % rd_alloc;
irq = &dw->irq[pos];
if (write)
irq->wr_mask |= BIT(j);
if (chan->dir == EDMA_DIR_WRITE)
irq->wr_mask |= BIT(chan->id);
else
irq->rd_mask |= BIT(j);
irq->rd_mask |= BIT(chan->id);
irq->dw = dw;
memcpy(&chan->msi, &irq->msi, sizeof(chan->msi));
dev_vdbg(dev, "MSI:\t\tChannel %s[%u] addr=0x%.8x%.8x, data=0x%.8x\n",
write ? "write" : "read", j,
chan->dir == EDMA_DIR_WRITE ? "write" : "read", chan->id,
chan->msi.address_hi, chan->msi.address_lo,
chan->msi.data);
chan->vc.desc_free = vchan_free_desc;
vchan_init(&chan->vc, dma);
if (write) {
dt_region->paddr = chip->dt_region_wr[j].paddr;
dt_region->vaddr = chip->dt_region_wr[j].vaddr;
dt_region->sz = chip->dt_region_wr[j].sz;
if (chan->dir == EDMA_DIR_WRITE) {
dt_region->paddr = chip->dt_region_wr[chan->id].paddr;
dt_region->vaddr = chip->dt_region_wr[chan->id].vaddr;
dt_region->sz = chip->dt_region_wr[chan->id].sz;
} else {
dt_region->paddr = chip->dt_region_rd[j].paddr;
dt_region->vaddr = chip->dt_region_rd[j].vaddr;
dt_region->sz = chip->dt_region_rd[j].sz;
dt_region->paddr = chip->dt_region_rd[chan->id].paddr;
dt_region->vaddr = chip->dt_region_rd[chan->id].vaddr;
dt_region->sz = chip->dt_region_rd[chan->id].sz;
}
dw_edma_v0_core_device_config(chan);
@ -813,7 +828,7 @@ static int dw_edma_channel_setup(struct dw_edma *dw, bool write,
dma_cap_set(DMA_CYCLIC, dma->cap_mask);
dma_cap_set(DMA_PRIVATE, dma->cap_mask);
dma_cap_set(DMA_INTERLEAVE, dma->cap_mask);
dma->directions = BIT(write ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV);
dma->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
dma->src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
dma->dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
dma->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
@ -822,6 +837,7 @@ static int dw_edma_channel_setup(struct dw_edma *dw, bool write,
dma->dev = chip->dev;
dma->device_alloc_chan_resources = dw_edma_alloc_chan_resources;
dma->device_free_chan_resources = dw_edma_free_chan_resources;
dma->device_caps = dw_edma_device_caps;
dma->device_config = dw_edma_device_config;
dma->device_pause = dw_edma_device_pause;
dma->device_resume = dw_edma_device_resume;
@ -835,9 +851,7 @@ static int dw_edma_channel_setup(struct dw_edma *dw, bool write,
dma_set_max_seg_size(dma->dev, U32_MAX);
/* Register DMA device */
err = dma_async_device_register(dma);
return err;
return dma_async_device_register(dma);
}
static inline void dw_edma_dec_irq_alloc(int *nr_irqs, u32 *alloc, u16 cnt)
@ -982,13 +996,8 @@ int dw_edma_probe(struct dw_edma_chip *chip)
if (err)
return err;
/* Setup write channels */
err = dw_edma_channel_setup(dw, true, wr_alloc, rd_alloc);
if (err)
goto err_irq_free;
/* Setup read channels */
err = dw_edma_channel_setup(dw, false, wr_alloc, rd_alloc);
/* Setup write/read channels */
err = dw_edma_channel_setup(dw, wr_alloc, rd_alloc);
if (err)
goto err_irq_free;
@ -1022,15 +1031,8 @@ int dw_edma_remove(struct dw_edma_chip *chip)
free_irq(chip->ops->irq_vector(dev, i), &dw->irq[i]);
/* Deregister eDMA device */
dma_async_device_unregister(&dw->wr_edma);
list_for_each_entry_safe(chan, _chan, &dw->wr_edma.channels,
vc.chan.device_node) {
tasklet_kill(&chan->vc.task);
list_del(&chan->vc.chan.device_node);
}
dma_async_device_unregister(&dw->rd_edma);
list_for_each_entry_safe(chan, _chan, &dw->rd_edma.channels,
dma_async_device_unregister(&dw->dma);
list_for_each_entry_safe(chan, _chan, &dw->dma.channels,
vc.chan.device_node) {
tasklet_kill(&chan->vc.task);
list_del(&chan->vc.chan.device_node);

View file

@ -98,10 +98,9 @@ struct dw_edma_irq {
struct dw_edma {
char name[20];
struct dma_device wr_edma;
u16 wr_ch_cnt;
struct dma_device dma;
struct dma_device rd_edma;
u16 wr_ch_cnt;
u16 rd_ch_cnt;
struct dw_edma_irq *irq;