mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 07:03:28 -04:00
dmaengine: dw-edma: Remove struct dw_edma_chunk
The current descriptor layout is:
struct dw_edma_desc *desc
└─ chunk list
└─ burst[]
Creating a DMA descriptor requires at least two kzalloc() calls because
each chunk is allocated as a linked-list node. Since the number of bursts
is already known when the descriptor is created, this linked-list layer is
unnecessary.
Move the burst array directly into struct dw_edma_desc and remove the
struct dw_edma_chunk layer entirely.
Use start_burst and done_burst to track the current bursts, which current
are in the DMA link list.
Tested-by: Koichiro Den <den@valinux.co.jp>
Tested-By: Devendra Verma <devendra.verma@amd.com>
Signed-off-by: Frank Li <Frank.Li@nxp.com>
Link: https://patch.msgid.link/20260713-edma_ll-v7-10-6fb7498c901e@nxp.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
This commit is contained in:
@@ -40,82 +40,54 @@ u64 dw_edma_get_pci_address(struct dw_edma_chan *chan, phys_addr_t cpu_addr)
|
||||
return cpu_addr;
|
||||
}
|
||||
|
||||
static struct dw_edma_chunk *dw_edma_alloc_chunk(struct dw_edma_desc *desc, u32 nburst)
|
||||
{
|
||||
struct dw_edma_chan *chan = desc->chan;
|
||||
struct dw_edma_chunk *chunk;
|
||||
|
||||
chunk = kzalloc_flex(*chunk, burst, nburst, GFP_NOWAIT);
|
||||
if (unlikely(!chunk))
|
||||
return NULL;
|
||||
|
||||
chunk->chan = chan;
|
||||
/* Toggling change bit (CB) in each chunk, this is a mechanism to
|
||||
* inform the eDMA HW block that this is a new linked list ready
|
||||
* to be consumed.
|
||||
* - Odd chunks originate CB equal to 0
|
||||
* - Even chunks originate CB equal to 1
|
||||
*/
|
||||
chunk->cb = !(desc->chunks_alloc % 2);
|
||||
|
||||
chunk->nburst = nburst;
|
||||
|
||||
list_add_tail(&chunk->list, &desc->chunk_list);
|
||||
desc->chunks_alloc++;
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
static struct dw_edma_desc *dw_edma_alloc_desc(struct dw_edma_chan *chan)
|
||||
static struct dw_edma_desc *
|
||||
dw_edma_alloc_desc(struct dw_edma_chan *chan, size_t nburst)
|
||||
{
|
||||
struct dw_edma_desc *desc;
|
||||
|
||||
desc = kzalloc_obj(*desc, GFP_NOWAIT);
|
||||
desc = kzalloc_flex(*desc, burst, nburst, GFP_NOWAIT);
|
||||
if (unlikely(!desc))
|
||||
return NULL;
|
||||
|
||||
desc->chan = chan;
|
||||
|
||||
INIT_LIST_HEAD(&desc->chunk_list);
|
||||
desc->nburst = nburst;
|
||||
desc->cb = true;
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
static void dw_edma_free_desc(struct dw_edma_desc *desc)
|
||||
{
|
||||
struct dw_edma_chunk *child, *_next;
|
||||
|
||||
/* Remove all the list elements */
|
||||
list_for_each_entry_safe(child, _next, &desc->chunk_list, list) {
|
||||
list_del(&child->list);
|
||||
kfree(child);
|
||||
desc->chunks_alloc--;
|
||||
}
|
||||
|
||||
kfree(desc);
|
||||
}
|
||||
|
||||
static void vchan_free_desc(struct virt_dma_desc *vdesc)
|
||||
{
|
||||
dw_edma_free_desc(vd2dw_edma_desc(vdesc));
|
||||
kfree(vd2dw_edma_desc(vdesc));
|
||||
}
|
||||
|
||||
static void dw_edma_core_start(struct dw_edma_chunk *chunk, bool first)
|
||||
static void dw_edma_core_start(struct dw_edma_desc *desc, bool first)
|
||||
{
|
||||
struct dw_edma_chan *chan = chunk->chan;
|
||||
u32 i = 0;
|
||||
struct dw_edma_chan *chan = desc->chan;
|
||||
size_t i = 0;
|
||||
|
||||
if (chan->non_ll) {
|
||||
if (chunk->nburst == 1)
|
||||
chan->dw->core->non_ll_start(chunk->chan, &chunk->burst[0]);
|
||||
chan->dw->core->non_ll_start(chan, &desc->burst[desc->start_burst]);
|
||||
desc->done_burst = desc->start_burst;
|
||||
desc->start_burst += 1;
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < chunk->nburst; i++)
|
||||
dw_edma_core_ll_data(chan, &chunk->burst[i], i, chunk->cb,
|
||||
i == chunk->nburst - 1);
|
||||
for (i = 0; i + desc->start_burst < desc->nburst; i++) {
|
||||
u32 idx = i + desc->start_burst;
|
||||
|
||||
dw_edma_core_ll_link(chan, i, chunk->cb, chan->ll_region.paddr);
|
||||
if (i == chan->ll_max)
|
||||
break;
|
||||
|
||||
dw_edma_core_ll_data(chan, &desc->burst[idx],
|
||||
i, desc->cb,
|
||||
idx == desc->nburst - 1 || i == chan->ll_max - 1);
|
||||
}
|
||||
|
||||
desc->done_burst = desc->start_burst;
|
||||
desc->start_burst += i;
|
||||
|
||||
dw_edma_core_ll_link(chan, i, desc->cb, chan->ll_region.paddr);
|
||||
|
||||
if (first)
|
||||
dw_edma_core_ch_enable(chan);
|
||||
@@ -125,7 +97,6 @@ static void dw_edma_core_start(struct dw_edma_chunk *chunk, bool first)
|
||||
|
||||
static int dw_edma_start_transfer(struct dw_edma_chan *chan)
|
||||
{
|
||||
struct dw_edma_chunk *child;
|
||||
struct dw_edma_desc *desc;
|
||||
struct virt_dma_desc *vd;
|
||||
|
||||
@@ -137,16 +108,9 @@ static int dw_edma_start_transfer(struct dw_edma_chan *chan)
|
||||
if (!desc)
|
||||
return 0;
|
||||
|
||||
child = list_first_entry_or_null(&desc->chunk_list,
|
||||
struct dw_edma_chunk, list);
|
||||
if (!child)
|
||||
return 0;
|
||||
dw_edma_core_start(desc, !desc->start_burst);
|
||||
|
||||
dw_edma_core_start(child, !desc->xfer_sz);
|
||||
desc->xfer_sz += child->xfer_sz;
|
||||
list_del(&child->list);
|
||||
kfree(child);
|
||||
desc->chunks_alloc--;
|
||||
desc->cb = !desc->cb;
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -337,8 +301,10 @@ dw_edma_device_tx_status(struct dma_chan *dchan, dma_cookie_t cookie,
|
||||
vd = vchan_find_desc(&chan->vc, cookie);
|
||||
if (vd) {
|
||||
desc = vd2dw_edma_desc(vd);
|
||||
if (desc)
|
||||
residue = desc->alloc_sz - desc->xfer_sz;
|
||||
|
||||
residue = desc->alloc_sz;
|
||||
if (desc && desc->done_burst)
|
||||
residue -= desc->burst[desc->done_burst - 1].xfer_sz;
|
||||
}
|
||||
spin_unlock_irqrestore(&chan->vc.lock, flags);
|
||||
|
||||
@@ -355,13 +321,11 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer,
|
||||
struct dw_edma_chan *chan = dchan2dw_edma_chan(xfer->dchan);
|
||||
enum dma_transfer_direction dir = xfer->direction;
|
||||
struct scatterlist *sg = NULL;
|
||||
struct dw_edma_chunk *chunk = NULL;
|
||||
struct dw_edma_burst *burst;
|
||||
struct dw_edma_desc *desc;
|
||||
u64 src_addr, dst_addr;
|
||||
size_t fsz = 0;
|
||||
u32 bursts_max;
|
||||
u32 cnt = 0;
|
||||
size_t cnt = 0;
|
||||
u32 i;
|
||||
|
||||
if (!chan->configured)
|
||||
@@ -418,17 +382,6 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* For non-LL mode, only a single burst can be handled
|
||||
* in a single chunk unlike LL mode where multiple bursts
|
||||
* can be configured in a single chunk.
|
||||
*/
|
||||
bursts_max = chan->non_ll ? 1 : chan->ll_max;
|
||||
|
||||
desc = dw_edma_alloc_desc(chan);
|
||||
if (unlikely(!desc))
|
||||
goto err_alloc;
|
||||
|
||||
if (xfer->type == EDMA_XFER_INTERLEAVED) {
|
||||
src_addr = xfer->xfer.il->src_start;
|
||||
dst_addr = xfer->xfer.il->dst_start;
|
||||
@@ -452,19 +405,15 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer,
|
||||
fsz = xfer->xfer.il->frame_size;
|
||||
}
|
||||
|
||||
desc = dw_edma_alloc_desc(chan, cnt);
|
||||
if (unlikely(!desc))
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < cnt; i++) {
|
||||
if (xfer->type == EDMA_XFER_SCATTER_GATHER && !sg)
|
||||
break;
|
||||
|
||||
if (!(i % bursts_max)) {
|
||||
u32 n = min(cnt - i, bursts_max);
|
||||
|
||||
chunk = dw_edma_alloc_chunk(desc, n);
|
||||
if (unlikely(!chunk))
|
||||
goto err_alloc;
|
||||
}
|
||||
|
||||
burst = chunk->burst + (i % bursts_max);
|
||||
burst = desc->burst + i;
|
||||
|
||||
if (xfer->type == EDMA_XFER_CYCLIC)
|
||||
burst->sz = xfer->xfer.cyclic.len;
|
||||
@@ -473,8 +422,8 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer,
|
||||
else if (xfer->type == EDMA_XFER_INTERLEAVED)
|
||||
burst->sz = xfer->xfer.il->sgl[i % fsz].size;
|
||||
|
||||
chunk->xfer_sz += burst->sz;
|
||||
desc->alloc_sz += burst->sz;
|
||||
burst->xfer_sz = desc->alloc_sz;
|
||||
|
||||
if (dir == DMA_DEV_TO_MEM) {
|
||||
burst->sar = src_addr;
|
||||
@@ -529,12 +478,6 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer,
|
||||
}
|
||||
|
||||
return vchan_tx_prep(&chan->vc, &desc->vd, xfer->flags);
|
||||
|
||||
err_alloc:
|
||||
if (desc)
|
||||
dw_edma_free_desc(desc);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct dma_async_tx_descriptor *
|
||||
@@ -605,8 +548,14 @@ static void dw_hdma_set_callback_result(struct virt_dma_desc *vd,
|
||||
return;
|
||||
|
||||
desc = vd2dw_edma_desc(vd);
|
||||
if (desc)
|
||||
residue = desc->alloc_sz - desc->xfer_sz;
|
||||
if (desc) {
|
||||
residue = desc->alloc_sz;
|
||||
|
||||
if (result == DMA_TRANS_NOERROR)
|
||||
residue -= desc->burst[desc->start_burst - 1].xfer_sz;
|
||||
else if (desc->done_burst)
|
||||
residue -= desc->burst[desc->done_burst - 1].xfer_sz;
|
||||
}
|
||||
|
||||
res = &vd->tx_result;
|
||||
res->result = result;
|
||||
@@ -625,7 +574,7 @@ static void dw_edma_done_interrupt(struct dw_edma_chan *chan)
|
||||
switch (chan->request) {
|
||||
case EDMA_REQ_NONE:
|
||||
desc = vd2dw_edma_desc(vd);
|
||||
if (!desc->chunks_alloc) {
|
||||
if (desc->start_burst >= desc->nburst) {
|
||||
dw_hdma_set_callback_result(vd,
|
||||
DMA_TRANS_NOERROR);
|
||||
list_del(&vd->node);
|
||||
|
||||
@@ -46,26 +46,21 @@ struct dw_edma_burst {
|
||||
u64 sar;
|
||||
u64 dar;
|
||||
u32 sz;
|
||||
};
|
||||
|
||||
struct dw_edma_chunk {
|
||||
struct list_head list;
|
||||
struct dw_edma_chan *chan;
|
||||
u8 cb;
|
||||
/* precalulate summary of previous burst total size */
|
||||
u32 xfer_sz;
|
||||
u32 nburst;
|
||||
struct dw_edma_burst burst[] __counted_by(nburst);
|
||||
};
|
||||
|
||||
struct dw_edma_desc {
|
||||
struct virt_dma_desc vd;
|
||||
struct dw_edma_chan *chan;
|
||||
struct list_head chunk_list;
|
||||
|
||||
u32 chunks_alloc;
|
||||
|
||||
u32 alloc_sz;
|
||||
u32 xfer_sz;
|
||||
|
||||
size_t done_burst;
|
||||
size_t start_burst;
|
||||
u8 cb;
|
||||
size_t nburst;
|
||||
struct dw_edma_burst burst[] __counted_by(nburst);
|
||||
};
|
||||
|
||||
struct dw_edma_chan {
|
||||
@@ -128,7 +123,6 @@ struct dw_edma_core_ops {
|
||||
void (*ll_link)(struct dw_edma_chan *chan, u32 idx, bool cb, u64 addr);
|
||||
void (*ch_doorbell)(struct dw_edma_chan *chan);
|
||||
void (*ch_enable)(struct dw_edma_chan *chan);
|
||||
|
||||
void (*ch_config)(struct dw_edma_chan *chan);
|
||||
void (*debugfs_on)(struct dw_edma *dw);
|
||||
void (*ack_emulated_irq)(struct dw_edma *dw);
|
||||
@@ -170,6 +164,14 @@ struct dw_edma_chan *dchan2dw_edma_chan(struct dma_chan *dchan)
|
||||
return vc2dw_edma_chan(to_virt_chan(dchan));
|
||||
}
|
||||
|
||||
static inline u64 dw_edma_core_get_ll_paddr(struct dw_edma_chan *chan)
|
||||
{
|
||||
if (chan->dir == EDMA_DIR_WRITE)
|
||||
return chan->dw->chip->ll_region_wr[chan->id].paddr;
|
||||
|
||||
return chan->dw->chip->ll_region_rd[chan->id].paddr;
|
||||
}
|
||||
|
||||
static inline
|
||||
void dw_edma_core_off(struct dw_edma *dw)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user