Merge branch 'topic/config_prep_api' into next

This commit is contained in:
Vinod Koul 2026-07-02 20:55:47 +05:30
commit 678a0f8580
8 changed files with 208 additions and 96 deletions

View File

@ -80,6 +80,10 @@ The details of these operations are:
- slave_sg: DMA a list of scatter gather buffers from/to a peripheral
- config_sg: Similar with slave_sg, just pass down dma_slave_config
struct to avoid calling dmaengine_slave_config() every time adjusting the
burst length or the FIFO address is needed.
- peripheral_dma_vec: DMA an array of scatter gather buffers from/to a
peripheral. Similar to slave_sg, but uses an array of dma_vec
structures instead of a scatterlist.
@ -106,6 +110,11 @@ The details of these operations are:
unsigned int sg_len, enum dma_data_direction direction,
unsigned long flags);
struct dma_async_tx_descriptor *dmaengine_prep_config_sg(
struct dma_chan *chan, struct scatterlist *sgl,
unsigned int sg_len, enum dma_transfer_direction dir,
unsigned long flags, struct dma_slave_config *config);
struct dma_async_tx_descriptor *dmaengine_prep_peripheral_dma_vec(
struct dma_chan *chan, const struct dma_vec *vecs,
size_t nents, enum dma_data_direction direction,

View File

@ -795,7 +795,6 @@ static int atmel_aes_dma_transfer_start(struct atmel_aes_dev *dd,
struct dma_slave_config config;
dma_async_tx_callback callback;
struct atmel_aes_dma *dma;
int err;
memset(&config, 0, sizeof(config));
config.src_addr_width = addr_width;
@ -820,12 +819,9 @@ static int atmel_aes_dma_transfer_start(struct atmel_aes_dev *dd,
return -EINVAL;
}
err = dmaengine_slave_config(dma->chan, &config);
if (err)
return err;
desc = dmaengine_prep_slave_sg(dma->chan, dma->sg, dma->sg_len, dir,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
desc = dmaengine_prep_config_sg(dma->chan, dma->sg, dma->sg_len, dir,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK,
&config);
if (!desc)
return -ENOMEM;

View File

@ -1100,6 +1100,8 @@ static int __dma_async_device_channel_register(struct dma_device *device,
chan->dev->device.parent = device->dev;
chan->dev->chan = chan;
chan->dev->dev_id = device->dev_id;
spin_lock_init(&chan->lock);
if (!name)
dev_set_name(&chan->dev->device, "dma%dchan%d", device->dev_id, chan->chan_id);
else

View File

@ -267,6 +267,20 @@ static int dw_edma_device_config(struct dma_chan *dchan,
return 0;
}
static struct dma_slave_config *
dw_edma_device_get_config(struct dma_chan *dchan,
struct dma_slave_config *config)
{
struct dw_edma_chan *chan;
if (config)
return config;
chan = dchan2dw_edma_chan(dchan);
return &chan->config;
}
static int dw_edma_device_pause(struct dma_chan *dchan)
{
struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
@ -385,7 +399,8 @@ dw_edma_device_tx_status(struct dma_chan *dchan, dma_cookie_t cookie,
}
static struct dma_async_tx_descriptor *
dw_edma_device_transfer(struct dw_edma_transfer *xfer)
dw_edma_device_transfer(struct dw_edma_transfer *xfer,
struct dma_slave_config *config)
{
struct dw_edma_chan *chan = dchan2dw_edma_chan(xfer->dchan);
enum dma_transfer_direction dir = xfer->direction;
@ -472,8 +487,8 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer)
src_addr = xfer->xfer.il->src_start;
dst_addr = xfer->xfer.il->dst_start;
} else {
src_addr = chan->config.src_addr;
dst_addr = chan->config.dst_addr;
src_addr = config->src_addr;
dst_addr = config->dst_addr;
}
if (dir == DMA_DEV_TO_MEM)
@ -577,10 +592,11 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer)
}
static struct dma_async_tx_descriptor *
dw_edma_device_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl,
unsigned int len,
enum dma_transfer_direction direction,
unsigned long flags, void *context)
dw_edma_device_prep_config_sg(struct dma_chan *dchan, struct scatterlist *sgl,
unsigned int len,
enum dma_transfer_direction direction,
unsigned long flags,
struct dma_slave_config *config)
{
struct dw_edma_transfer xfer;
@ -591,7 +607,10 @@ dw_edma_device_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl,
xfer.flags = flags;
xfer.type = EDMA_XFER_SCATTER_GATHER;
return dw_edma_device_transfer(&xfer);
if (config && dw_edma_device_config(dchan, config))
return NULL;
return dw_edma_device_transfer(&xfer, dw_edma_device_get_config(dchan, config));
}
static struct dma_async_tx_descriptor *
@ -610,7 +629,7 @@ dw_edma_device_prep_dma_cyclic(struct dma_chan *dchan, dma_addr_t paddr,
xfer.flags = flags;
xfer.type = EDMA_XFER_CYCLIC;
return dw_edma_device_transfer(&xfer);
return dw_edma_device_transfer(&xfer, dw_edma_device_get_config(dchan, NULL));
}
static struct dma_async_tx_descriptor *
@ -626,7 +645,7 @@ dw_edma_device_prep_interleaved_dma(struct dma_chan *dchan,
xfer.flags = flags;
xfer.type = EDMA_XFER_INTERLEAVED;
return dw_edma_device_transfer(&xfer);
return dw_edma_device_transfer(&xfer, dw_edma_device_get_config(dchan, NULL));
}
static void dw_hdma_set_callback_result(struct virt_dma_desc *vd,
@ -970,7 +989,7 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc)
dma->device_terminate_all = dw_edma_device_terminate_all;
dma->device_issue_pending = dw_edma_device_issue_pending;
dma->device_tx_status = dw_edma_device_tx_status;
dma->device_prep_slave_sg = dw_edma_device_prep_slave_sg;
dma->device_prep_config_sg = dw_edma_device_prep_config_sg;
dma->device_prep_dma_cyclic = dw_edma_device_prep_dma_cyclic;
dma->device_prep_interleaved_dma = dw_edma_device_prep_interleaved_dma;

View File

@ -210,9 +210,7 @@ struct nvmet_pci_epf {
bool dma_enabled;
struct dma_chan *dma_tx_chan;
struct mutex dma_tx_lock;
struct dma_chan *dma_rx_chan;
struct mutex dma_rx_lock;
struct mutex mmio_lock;
@ -295,9 +293,6 @@ static void nvmet_pci_epf_init_dma(struct nvmet_pci_epf *nvme_epf)
struct dma_chan *chan;
dma_cap_mask_t mask;
mutex_init(&nvme_epf->dma_rx_lock);
mutex_init(&nvme_epf->dma_tx_lock);
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
@ -336,8 +331,6 @@ static void nvmet_pci_epf_init_dma(struct nvmet_pci_epf *nvme_epf)
nvme_epf->dma_rx_chan = NULL;
out_dma_no_rx:
mutex_destroy(&nvme_epf->dma_rx_lock);
mutex_destroy(&nvme_epf->dma_tx_lock);
nvme_epf->dma_enabled = false;
dev_info(&epf->dev, "DMA not supported, falling back to MMIO\n");
@ -352,8 +345,6 @@ static void nvmet_pci_epf_deinit_dma(struct nvmet_pci_epf *nvme_epf)
nvme_epf->dma_tx_chan = NULL;
dma_release_channel(nvme_epf->dma_rx_chan);
nvme_epf->dma_rx_chan = NULL;
mutex_destroy(&nvme_epf->dma_rx_lock);
mutex_destroy(&nvme_epf->dma_tx_lock);
nvme_epf->dma_enabled = false;
}
@ -368,18 +359,15 @@ static int nvmet_pci_epf_dma_transfer(struct nvmet_pci_epf *nvme_epf,
struct dma_chan *chan;
dma_cookie_t cookie;
dma_addr_t dma_addr;
struct mutex *lock;
int ret;
switch (dir) {
case DMA_FROM_DEVICE:
lock = &nvme_epf->dma_rx_lock;
chan = nvme_epf->dma_rx_chan;
sconf.direction = DMA_DEV_TO_MEM;
sconf.src_addr = seg->pci_addr;
break;
case DMA_TO_DEVICE:
lock = &nvme_epf->dma_tx_lock;
chan = nvme_epf->dma_tx_chan;
sconf.direction = DMA_MEM_TO_DEV;
sconf.dst_addr = seg->pci_addr;
@ -388,22 +376,15 @@ static int nvmet_pci_epf_dma_transfer(struct nvmet_pci_epf *nvme_epf,
return -EINVAL;
}
mutex_lock(lock);
dma_dev = dmaengine_get_dma_device(chan);
dma_addr = dma_map_single(dma_dev, seg->buf, seg->length, dir);
ret = dma_mapping_error(dma_dev, dma_addr);
if (ret)
goto unlock;
return ret;
ret = dmaengine_slave_config(chan, &sconf);
if (ret) {
dev_err(dev, "Failed to configure DMA channel\n");
goto unmap;
}
desc = dmaengine_prep_slave_single(chan, dma_addr, seg->length,
sconf.direction, DMA_CTRL_ACK);
desc = dmaengine_prep_config_single_safe(chan, dma_addr, seg->length,
sconf.direction,
DMA_CTRL_ACK, &sconf);
if (!desc) {
dev_err(dev, "Failed to prepare DMA\n");
ret = -EIO;
@ -420,16 +401,12 @@ static int nvmet_pci_epf_dma_transfer(struct nvmet_pci_epf *nvme_epf,
if (dma_sync_wait(chan, cookie) != DMA_COMPLETE) {
dev_err(dev, "DMA transfer failed\n");
ret = -EIO;
dmaengine_terminate_sync(chan);
}
dmaengine_terminate_sync(chan);
unmap:
dma_unmap_single(dma_dev, dma_addr, seg->length, dir);
unlock:
mutex_unlock(lock);
return ret;
}

View File

@ -328,12 +328,6 @@ static int pci_epf_mhi_edma_read(struct mhi_ep_cntrl *mhi_cntrl,
config.direction = DMA_DEV_TO_MEM;
config.src_addr = buf_info->host_addr;
ret = dmaengine_slave_config(chan, &config);
if (ret) {
dev_err(dev, "Failed to configure DMA channel\n");
goto err_unlock;
}
dst_addr = dma_map_single(dma_dev, buf_info->dev_addr, buf_info->size,
DMA_FROM_DEVICE);
ret = dma_mapping_error(dma_dev, dst_addr);
@ -342,9 +336,10 @@ static int pci_epf_mhi_edma_read(struct mhi_ep_cntrl *mhi_cntrl,
goto err_unlock;
}
desc = dmaengine_prep_slave_single(chan, dst_addr, buf_info->size,
DMA_DEV_TO_MEM,
DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
desc = dmaengine_prep_config_single(chan, dst_addr, buf_info->size,
DMA_DEV_TO_MEM,
DMA_CTRL_ACK | DMA_PREP_INTERRUPT,
&config);
if (!desc) {
dev_err(dev, "Failed to prepare DMA\n");
ret = -EIO;
@ -401,12 +396,6 @@ static int pci_epf_mhi_edma_write(struct mhi_ep_cntrl *mhi_cntrl,
config.direction = DMA_MEM_TO_DEV;
config.dst_addr = buf_info->host_addr;
ret = dmaengine_slave_config(chan, &config);
if (ret) {
dev_err(dev, "Failed to configure DMA channel\n");
goto err_unlock;
}
src_addr = dma_map_single(dma_dev, buf_info->dev_addr, buf_info->size,
DMA_TO_DEVICE);
ret = dma_mapping_error(dma_dev, src_addr);
@ -415,9 +404,10 @@ static int pci_epf_mhi_edma_write(struct mhi_ep_cntrl *mhi_cntrl,
goto err_unlock;
}
desc = dmaengine_prep_slave_single(chan, src_addr, buf_info->size,
DMA_MEM_TO_DEV,
DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
desc = dmaengine_prep_config_single(chan, src_addr, buf_info->size,
DMA_MEM_TO_DEV,
DMA_CTRL_ACK | DMA_PREP_INTERRUPT,
&config);
if (!desc) {
dev_err(dev, "Failed to prepare DMA\n");
ret = -EIO;
@ -506,12 +496,6 @@ static int pci_epf_mhi_edma_read_async(struct mhi_ep_cntrl *mhi_cntrl,
config.direction = DMA_DEV_TO_MEM;
config.src_addr = buf_info->host_addr;
ret = dmaengine_slave_config(chan, &config);
if (ret) {
dev_err(dev, "Failed to configure DMA channel\n");
goto err_unlock;
}
dst_addr = dma_map_single(dma_dev, buf_info->dev_addr, buf_info->size,
DMA_FROM_DEVICE);
ret = dma_mapping_error(dma_dev, dst_addr);
@ -520,9 +504,10 @@ static int pci_epf_mhi_edma_read_async(struct mhi_ep_cntrl *mhi_cntrl,
goto err_unlock;
}
desc = dmaengine_prep_slave_single(chan, dst_addr, buf_info->size,
DMA_DEV_TO_MEM,
DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
desc = dmaengine_prep_config_single(chan, dst_addr, buf_info->size,
DMA_DEV_TO_MEM,
DMA_CTRL_ACK | DMA_PREP_INTERRUPT,
&config);
if (!desc) {
dev_err(dev, "Failed to prepare DMA\n");
ret = -EIO;
@ -585,12 +570,6 @@ static int pci_epf_mhi_edma_write_async(struct mhi_ep_cntrl *mhi_cntrl,
config.direction = DMA_MEM_TO_DEV;
config.dst_addr = buf_info->host_addr;
ret = dmaengine_slave_config(chan, &config);
if (ret) {
dev_err(dev, "Failed to configure DMA channel\n");
goto err_unlock;
}
src_addr = dma_map_single(dma_dev, buf_info->dev_addr, buf_info->size,
DMA_TO_DEVICE);
ret = dma_mapping_error(dma_dev, src_addr);
@ -599,9 +578,10 @@ static int pci_epf_mhi_edma_write_async(struct mhi_ep_cntrl *mhi_cntrl,
goto err_unlock;
}
desc = dmaengine_prep_slave_single(chan, src_addr, buf_info->size,
DMA_MEM_TO_DEV,
DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
desc = dmaengine_prep_config_single(chan, src_addr, buf_info->size,
DMA_MEM_TO_DEV,
DMA_CTRL_ACK | DMA_PREP_INTERRUPT,
&config);
if (!desc) {
dev_err(dev, "Failed to prepare DMA\n");
ret = -EIO;

View File

@ -183,12 +183,8 @@ static int pci_epf_test_data_transfer(struct pci_epf_test *epf_test,
else
sconf.src_addr = dma_remote;
if (dmaengine_slave_config(chan, &sconf)) {
dev_err(dev, "DMA slave config fail\n");
return -EIO;
}
tx = dmaengine_prep_slave_single(chan, dma_local, len, dir,
flags);
tx = dmaengine_prep_config_single(chan, dma_local, len,
dir, flags, &sconf);
} else {
tx = dmaengine_prep_dma_memcpy(chan, dma_dst, dma_src, len,
flags);

View File

@ -322,6 +322,8 @@ struct dma_router {
* @slave: ptr to the device using this channel
* @cookie: last cookie value returned to client
* @completed_cookie: last completed cookie for this channel
* @lock: protect between config and prepare transfer when driver have not
* implemented callback device_prep_config_sg().
* @chan_id: channel ID for sysfs
* @dev: class device for sysfs
* @name: backlink name for sysfs
@ -341,6 +343,12 @@ struct dma_chan {
dma_cookie_t cookie;
dma_cookie_t completed_cookie;
/*
* protect between config and prepare transfer because *_prep() may be
* called from complete callback, which is in GFP_NOSLEEP context.
*/
spinlock_t lock;
/* sysfs */
int chan_id;
struct dma_chan_dev *dev;
@ -835,6 +843,7 @@ struct dma_filter {
* where the address and size of each segment is located in one entry of
* the dma_vec array.
* @device_prep_slave_sg: prepares a slave dma operation
* @device_prep_config_sg: prepares a slave DMA operation with dma_slave_config
* @device_prep_dma_cyclic: prepare a cyclic dma operation suitable for audio.
* The function takes a buffer of size buf_len. The callback function will
* be called after period_len bytes have been transferred.
@ -934,6 +943,10 @@ struct dma_device {
struct dma_chan *chan, struct scatterlist *sgl,
unsigned int sg_len, enum dma_transfer_direction direction,
unsigned long flags, void *context);
struct dma_async_tx_descriptor *(*device_prep_config_sg)(
struct dma_chan *chan, struct scatterlist *sgl,
unsigned int sg_len, enum dma_transfer_direction direction,
unsigned long flags, struct dma_slave_config *config);
struct dma_async_tx_descriptor *(*device_prep_dma_cyclic)(
struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
size_t period_len, enum dma_transfer_direction direction,
@ -974,22 +987,44 @@ static inline bool is_slave_direction(enum dma_transfer_direction direction)
(direction == DMA_DEV_TO_DEV);
}
static inline struct dma_async_tx_descriptor *dmaengine_prep_slave_single(
struct dma_chan *chan, dma_addr_t buf, size_t len,
enum dma_transfer_direction dir, unsigned long flags)
static inline struct dma_async_tx_descriptor *
dmaengine_prep_config_single(struct dma_chan *chan, dma_addr_t buf, size_t len,
enum dma_transfer_direction dir,
unsigned long flags,
struct dma_slave_config *config)
{
struct scatterlist sg;
if (!chan || !chan->device)
return NULL;
sg_init_table(&sg, 1);
sg_dma_address(&sg) = buf;
sg_dma_len(&sg) = len;
if (!chan || !chan->device || !chan->device->device_prep_slave_sg)
if (chan->device->device_prep_config_sg)
return chan->device->device_prep_config_sg(chan, &sg, 1, dir,
flags, config);
if (config)
if (dmaengine_slave_config(chan, config))
return NULL;
if (!chan->device->device_prep_slave_sg)
return NULL;
return chan->device->device_prep_slave_sg(chan, &sg, 1,
dir, flags, NULL);
}
static inline struct dma_async_tx_descriptor *
dmaengine_prep_slave_single(struct dma_chan *chan, dma_addr_t buf, size_t len,
enum dma_transfer_direction dir,
unsigned long flags)
{
return dmaengine_prep_config_single(chan, buf, len, dir, flags, NULL);
}
/**
* dmaengine_prep_peripheral_dma_vec() - Prepare a DMA scatter-gather descriptor
* @chan: The channel to be used for this descriptor
@ -1010,17 +1045,115 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_peripheral_dma_vec(
dir, flags);
}
static inline struct dma_async_tx_descriptor *dmaengine_prep_slave_sg(
struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len,
enum dma_transfer_direction dir, unsigned long flags)
static inline struct dma_async_tx_descriptor *
dmaengine_prep_config_sg(struct dma_chan *chan, struct scatterlist *sgl,
unsigned int sg_len, enum dma_transfer_direction dir,
unsigned long flags, struct dma_slave_config *config)
{
if (!chan || !chan->device || !chan->device->device_prep_slave_sg)
if (!chan || !chan->device)
return NULL;
if (chan->device->device_prep_config_sg)
return chan->device->device_prep_config_sg(chan, sgl, sg_len,
dir, flags, config);
if (config)
if (dmaengine_slave_config(chan, config))
return NULL;
if (!chan->device->device_prep_slave_sg)
return NULL;
return chan->device->device_prep_slave_sg(chan, sgl, sg_len,
dir, flags, NULL);
}
static inline struct dma_async_tx_descriptor *
dmaengine_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
unsigned int sg_len, enum dma_transfer_direction dir,
unsigned long flags)
{
return dmaengine_prep_config_sg(chan, sgl, sg_len, dir, flags, NULL);
}
/**
* dmaengine_prep_config_sg_safe - prepare a scatter-gather DMA transfer
* with atomic slave configuration update
* @chan: DMA channel
* @sgl: scatterlist for the transfer
* @sg_len: number of entries in @sgl
* @dir: DMA transfer direction
* @flags: transfer preparation flags
* @config: DMA slave configuration for this transfer
*
* Prepare a DMA scatter-gather transfer together with a corresponding slave
* configuration update in a re-entrant and race-safe manner.
*
* DMA engine drivers may implement the optional
* device_prep_config_sg() callback to perform both the slave configuration
* and descriptor preparation atomically. In this case, the operation is
* fully handled by the DMA engine driver.
*
* If the DMA engine driver does not implement device_prep_config_sg(), falls
* back to calling dmaengine_slave_config() followed by dmaengine_prep_slave_sg().
* The fallback path is protected by a per-channel spinlock to ensure that
* concurrent callers cannot interleave configuration and descriptor preparation
* on the same DMA channel.
*
* Return: Pointer to a prepared DMA async transaction descriptor on success,
* or %NULL if the transfer could not be prepared.
*/
static inline struct dma_async_tx_descriptor *
dmaengine_prep_config_sg_safe(struct dma_chan *chan, struct scatterlist *sgl,
unsigned int sg_len,
enum dma_transfer_direction dir,
unsigned long flags,
struct dma_slave_config *config)
{
struct dma_async_tx_descriptor *tx;
unsigned long spinlock_flags;
if (!chan || !chan->device)
return NULL;
if (!chan->device->device_prep_config_sg)
spin_lock_irqsave(&chan->lock, spinlock_flags);
tx = dmaengine_prep_config_sg(chan, sgl, sg_len, dir, flags, config);
if (!chan->device->device_prep_config_sg)
spin_unlock_irqrestore(&chan->lock, spinlock_flags);
return tx;
}
/**
* dmaengine_prep_config_single_safe - prepare a single-buffer DMA transfer
* with atomic slave configuration update
* @chan: DMA channel
* @buf: DMA buffer address
* @len: length of the transfer in bytes
* @dir: DMA transfer direction
* @flags: transfer preparation flags
* @config: DMA slave configuration for this transfer
*
* Detail see dmaengine_prep_config_sg_safe().
*/
static inline struct dma_async_tx_descriptor *
dmaengine_prep_config_single_safe(struct dma_chan *chan, dma_addr_t buf,
size_t len, enum dma_transfer_direction dir,
unsigned long flags,
struct dma_slave_config *config)
{
struct scatterlist sg;
sg_init_table(&sg, 1);
sg_dma_address(&sg) = buf;
sg_dma_len(&sg) = len;
return dmaengine_prep_config_sg_safe(chan, &sg, 1, dir, flags, config);
}
#ifdef CONFIG_RAPIDIO_DMA_ENGINE
struct rio_dma_ext;
static inline struct dma_async_tx_descriptor *dmaengine_prep_rio_sg(