diff options
| author | Mark Brown <broonie@kernel.org> | 2026-07-23 15:40:27 +0100 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-07-23 15:40:27 +0100 |
| commit | 719fd2fe250a20940099aa08ff0bc6a72fe6aac8 (patch) | |
| tree | 4556bf119cf6d3c6c342c58e6e143ad2006f7e4c /drivers | |
| parent | ed9b79b61e9248ec87c34c0d2f7f2109e7f3a651 (diff) | |
| parent | 0613e7934ee233d726af8d8c89f251a1c4df738d (diff) | |
| download | linux-next-719fd2fe250a20940099aa08ff0bc6a72fe6aac8.tar.gz linux-next-719fd2fe250a20940099aa08ff0bc6a72fe6aac8.zip | |
Merge branch 'next' of https://git.kernel.org/pub/scm/linux/kernel/git/vkoul/dmaengine.git
Diffstat (limited to 'drivers')
23 files changed, 1157 insertions, 736 deletions
diff --git a/drivers/crypto/atmel-aes.c b/drivers/crypto/atmel-aes.c index f79d7c456546..30acfd937d54 100644 --- a/drivers/crypto/atmel-aes.c +++ b/drivers/crypto/atmel-aes.c @@ -794,7 +794,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; @@ -819,12 +818,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; diff --git a/drivers/dma/arm-dma350.c b/drivers/dma/arm-dma350.c index 84220fa83029..09403aca8bb0 100644 --- a/drivers/dma/arm-dma350.c +++ b/drivers/dma/arm-dma350.c @@ -13,6 +13,11 @@ #include "dmaengine.h" #include "virt-dma.h" +#define DMANSECCTRL 0x200 + +#define NSEC_CTRL 0x0c +#define INTREN_ANYCHINTR_EN BIT(0) + #define DMAINFO 0x0f00 #define DMA_BUILDCFG0 0xb0 @@ -582,6 +587,10 @@ static int d350_probe(struct platform_device *pdev) dmac->dma.device_issue_pending = d350_issue_pending; INIT_LIST_HEAD(&dmac->dma.channels); + reg = readl_relaxed(base + DMANSECCTRL + NSEC_CTRL); + writel_relaxed(reg | INTREN_ANYCHINTR_EN, + base + DMANSECCTRL + NSEC_CTRL); + /* Would be nice to have per-channel caps for this... */ memset = true; for (int i = 0; i < nchan; i++) { diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 9049171df857..6ffd8bd82154 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -814,10 +814,15 @@ static const struct dma_slave_map *dma_filter_match(struct dma_device *device, */ struct dma_chan *dma_request_chan(struct device *dev, const char *name) { - struct fwnode_handle *fwnode = dev_fwnode(dev); + struct fwnode_handle *fwnode; struct dma_device *d, *_d; struct dma_chan *chan = NULL; + if (WARN_ON(!dev || !name)) + return ERR_PTR(-EINVAL); + + fwnode = dev_fwnode(dev); + if (is_of_node(fwnode)) chan = of_dma_request_slave_channel(to_of_node(fwnode), name); else if (is_acpi_device_node(fwnode)) @@ -1100,6 +1105,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 diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c index bcefaff03b5c..eebed2474210 100644 --- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c +++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c @@ -1126,7 +1126,7 @@ static void axi_chan_block_xfer_complete(struct axi_dma_chan *chan) hw_desc = &desc->hw_desc[i]; if (hw_desc->llp == llp) { axi_chan_irq_clear(chan, hw_desc->lli->status_lo); - hw_desc->lli->ctl_hi |= CH_CTL_H_LLI_VALID; + hw_desc->lli->ctl_hi |= cpu_to_le32(CH_CTL_H_LLI_VALID); desc->completed_blocks = i; if (((hw_desc->len * (i + 1)) % desc->period_len) == 0) diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c index 89a4c498a17b..1f893dc54c79 100644 --- a/drivers/dma/dw-edma/dw-edma-core.c +++ b/drivers/dma/dw-edma/dw-edma-core.c @@ -7,6 +7,7 @@ */ #include <linux/module.h> +#include <linux/delay.h> #include <linux/device.h> #include <linux/kernel.h> #include <linux/dmaengine.h> @@ -29,6 +30,11 @@ struct dw_edma_desc *vd2dw_edma_desc(struct virt_dma_desc *vd) return container_of(vd, struct dw_edma_desc, vd); } +enum dw_edma_irq_event { + DW_EDMA_IRQ_DONE = BIT(0), + DW_EDMA_IRQ_ABORT = BIT(1), +}; + static inline u64 dw_edma_get_pci_address(struct dw_edma_chan *chan, phys_addr_t cpu_addr) { @@ -40,141 +46,63 @@ u64 dw_edma_get_pci_address(struct dw_edma_chan *chan, phys_addr_t cpu_addr) return cpu_addr; } -static struct dw_edma_burst *dw_edma_alloc_burst(struct dw_edma_chunk *chunk) -{ - struct dw_edma_burst *burst; - - burst = kzalloc_obj(*burst, GFP_NOWAIT); - if (unlikely(!burst)) - return NULL; - - INIT_LIST_HEAD(&burst->list); - if (chunk->burst) { - /* Create and add new element into the linked list */ - chunk->bursts_alloc++; - list_add_tail(&burst->list, &chunk->burst->list); - } else { - /* List head */ - chunk->bursts_alloc = 0; - chunk->burst = burst; - } - - return burst; -} - -static struct dw_edma_chunk *dw_edma_alloc_chunk(struct dw_edma_desc *desc) -{ - struct dw_edma_chip *chip = desc->chan->dw->chip; - struct dw_edma_chan *chan = desc->chan; - struct dw_edma_chunk *chunk; - - chunk = kzalloc_obj(*chunk, GFP_NOWAIT); - if (unlikely(!chunk)) - return NULL; - - INIT_LIST_HEAD(&chunk->list); - 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); - if (chan->dir == EDMA_DIR_WRITE) { - chunk->ll_region.paddr = chip->ll_region_wr[chan->id].paddr; - chunk->ll_region.vaddr = chip->ll_region_wr[chan->id].vaddr; - } else { - chunk->ll_region.paddr = chip->ll_region_rd[chan->id].paddr; - chunk->ll_region.vaddr = chip->ll_region_rd[chan->id].vaddr; - } - - if (desc->chunk) { - /* Create and add new element into the linked list */ - if (!dw_edma_alloc_burst(chunk)) { - kfree(chunk); - return NULL; - } - desc->chunks_alloc++; - list_add_tail(&chunk->list, &desc->chunk->list); - } else { - /* List head */ - chunk->burst = NULL; - desc->chunks_alloc = 0; - desc->chunk = chunk; - } - - 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; - if (!dw_edma_alloc_chunk(desc)) { - kfree(desc); - return NULL; - } + desc->nburst = nburst; + desc->cb = true; return desc; } -static void dw_edma_free_burst(struct dw_edma_chunk *chunk) +static void vchan_free_desc(struct virt_dma_desc *vdesc) { - struct dw_edma_burst *child, *_next; - - /* Remove all the list elements */ - list_for_each_entry_safe(child, _next, &chunk->burst->list, list) { - list_del(&child->list); - kfree(child); - chunk->bursts_alloc--; - } - - /* Remove the list head */ - kfree(child); - chunk->burst = NULL; + kfree(vd2dw_edma_desc(vdesc)); } -static void dw_edma_free_chunk(struct dw_edma_desc *desc) +static void dw_edma_core_start(struct dw_edma_desc *desc, bool first) { - struct dw_edma_chunk *child, *_next; + struct dw_edma_chan *chan = desc->chan; + size_t i = 0; - if (!desc->chunk) + if (chan->non_ll) { + chan->dw->core->non_ll_start(chan, &desc->burst[desc->start_burst]); + desc->done_burst = desc->start_burst; + desc->start_burst += 1; return; + } - /* Remove all the list elements */ - list_for_each_entry_safe(child, _next, &desc->chunk->list, list) { - dw_edma_free_burst(child); - list_del(&child->list); - kfree(child); - desc->chunks_alloc--; + for (i = 0; i + desc->start_burst < desc->nburst; i++) { + u32 idx = i + desc->start_burst; + + 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); } - /* Remove the list head */ - kfree(child); - desc->chunk = NULL; -} + desc->done_burst = desc->start_burst; + desc->start_burst += i; -static void dw_edma_free_desc(struct dw_edma_desc *desc) -{ - dw_edma_free_chunk(desc); - kfree(desc); -} + dw_edma_core_ll_link(chan, i, desc->cb, chan->ll_region.paddr); -static void vchan_free_desc(struct virt_dma_desc *vdesc) -{ - dw_edma_free_desc(vd2dw_edma_desc(vdesc)); + if (first) + dw_edma_core_ch_enable(chan); + + dw_edma_core_ch_doorbell(chan); } static int dw_edma_start_transfer(struct dw_edma_chan *chan) { - struct dw_edma *dw = chan->dw; - struct dw_edma_chunk *child; struct dw_edma_desc *desc; struct virt_dma_desc *vd; @@ -186,21 +114,42 @@ 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(dw, child, !desc->xfer_sz); - desc->xfer_sz += child->ll_region.sz; - dw_edma_free_burst(child); - list_del(&child->list); - kfree(child); - desc->chunks_alloc--; + desc->cb = !desc->cb; return 1; } +static void dw_edma_terminate_vdesc(struct virt_dma_desc *vd) +{ + list_del(&vd->node); + dma_cookie_complete(&vd->tx); + vchan_terminate_vdesc(vd); +} + +static void dw_edma_terminate_vdesc_list(struct list_head *head) +{ + struct virt_dma_desc *vd, *_vd; + + list_for_each_entry_safe(vd, _vd, head, node) + dw_edma_terminate_vdesc(vd); +} + +/* Must be called with vc.lock held. */ +static void dw_edma_terminate_all_descs(struct dw_edma_chan *chan) +{ + /* + * This order must not be reversed. Cookies are assigned when + * descriptors are submitted, so desc_issued contains older cookies + * than desc_submitted. Completing desc_submitted first could move + * chan->vc.chan.completed_cookie backwards when desc_issued is + * terminated afterwards. + */ + dw_edma_terminate_vdesc_list(&chan->vc.desc_issued); + dw_edma_terminate_vdesc_list(&chan->vc.desc_submitted); +} + static void dw_edma_device_caps(struct dma_chan *dchan, struct dma_slave_caps *caps) { @@ -219,6 +168,15 @@ static void dw_edma_device_caps(struct dma_chan *dchan, } } +static enum dw_edma_ch_irq_mode +dw_edma_get_default_irq_mode(struct dw_edma_chan *chan) +{ + struct dw_edma_chip *chip = chan->dw->chip; + + return chip->flags & DW_EDMA_CHIP_LOCAL ? DW_EDMA_CH_IRQ_LOCAL : + DW_EDMA_CH_IRQ_REMOTE; +} + static int dw_edma_device_config(struct dma_chan *dchan, struct dma_slave_config *config) { @@ -267,11 +225,27 @@ 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); int err = 0; + guard(spinlock_irqsave)(&chan->vc.lock); + if (!chan->configured) err = -EPERM; else if (chan->status != EDMA_ST_BUSY) @@ -289,6 +263,8 @@ static int dw_edma_device_resume(struct dma_chan *dchan) struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan); int err = 0; + guard(spinlock_irqsave)(&chan->vc.lock); + if (!chan->configured) { err = -EPERM; } else if (chan->status != EDMA_ST_PAUSE) { @@ -297,7 +273,8 @@ static int dw_edma_device_resume(struct dma_chan *dchan) err = -EPERM; } else { chan->status = EDMA_ST_BUSY; - dw_edma_start_transfer(chan); + if (!dw_edma_start_transfer(chan)) + chan->status = EDMA_ST_IDLE; } return err; @@ -308,25 +285,29 @@ static int dw_edma_device_terminate_all(struct dma_chan *dchan) struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan); int err = 0; + guard(spinlock_irqsave)(&chan->vc.lock); + if (!chan->configured) { - /* Do nothing */ + dw_edma_terminate_all_descs(chan); } else if (chan->status == EDMA_ST_PAUSE) { + dw_edma_terminate_all_descs(chan); chan->status = EDMA_ST_IDLE; - chan->configured = false; } else if (chan->status == EDMA_ST_IDLE) { - chan->configured = false; + dw_edma_terminate_all_descs(chan); } else if (dw_edma_core_ch_status(chan) == DMA_COMPLETE) { /* * The channel is in a false BUSY state, probably didn't * receive or lost an interrupt */ + dw_edma_terminate_all_descs(chan); chan->status = EDMA_ST_IDLE; - chan->configured = false; } else if (chan->request > EDMA_REQ_PAUSE) { err = -EPERM; } else { chan->request = EDMA_REQ_STOP; } + if (chan->status == EDMA_ST_IDLE) + chan->request = EDMA_REQ_NONE; return err; } @@ -336,11 +317,9 @@ static void dw_edma_device_issue_pending(struct dma_chan *dchan) struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan); unsigned long flags; - if (!chan->configured) - return; - spin_lock_irqsave(&chan->vc.lock, flags); - if (vchan_issue_pending(&chan->vc) && chan->request == EDMA_REQ_NONE && + if (chan->configured && vchan_issue_pending(&chan->vc) && + chan->request == EDMA_REQ_NONE && chan->status == EDMA_ST_IDLE) { chan->status = EDMA_ST_BUSY; dw_edma_start_transfer(chan); @@ -373,8 +352,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); @@ -385,19 +366,18 @@ ret_residue: } 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; struct scatterlist *sg = NULL; - struct dw_edma_chunk *chunk; 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; - int i; + size_t cnt = 0; + u32 i; if (!chan->configured) return NULL; @@ -453,27 +433,12 @@ 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; - - chunk = dw_edma_alloc_chunk(desc); - if (unlikely(!chunk)) - goto err_alloc; - if (xfer->type == EDMA_XFER_INTERLEAVED) { 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) @@ -491,19 +456,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 (chunk->bursts_alloc == bursts_max) { - chunk = dw_edma_alloc_chunk(desc); - if (unlikely(!chunk)) - goto err_alloc; - } - - burst = dw_edma_alloc_burst(chunk); - if (unlikely(!burst)) - goto err_alloc; + burst = desc->burst + i; if (xfer->type == EDMA_XFER_CYCLIC) burst->sz = xfer->xfer.cyclic.len; @@ -512,8 +473,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->ll_region.sz += burst->sz; desc->alloc_sz += burst->sz; + burst->xfer_sz = desc->alloc_sz; if (dir == DMA_DEV_TO_MEM) { burst->sar = src_addr; @@ -568,19 +529,14 @@ 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 * -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 +547,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 +569,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 +585,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, @@ -640,8 +599,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; @@ -655,35 +620,41 @@ static void dw_edma_done_interrupt(struct dw_edma_chan *chan) unsigned long flags; spin_lock_irqsave(&chan->vc.lock, flags); + if (chan->status == EDMA_ST_PAUSE) { + spin_unlock_irqrestore(&chan->vc.lock, flags); + return; + } + vd = vchan_next_desc(&chan->vc); if (vd) { switch (chan->request) { case EDMA_REQ_NONE: + case EDMA_REQ_PAUSE: 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); vchan_cookie_complete(vd); } + if (chan->request == EDMA_REQ_PAUSE) { + chan->request = EDMA_REQ_NONE; + chan->status = EDMA_ST_PAUSE; + break; + } + /* Continue transferring if there are remaining chunks or issued requests. */ chan->status = dw_edma_start_transfer(chan) ? EDMA_ST_BUSY : EDMA_ST_IDLE; break; case EDMA_REQ_STOP: - list_del(&vd->node); - vchan_cookie_complete(vd); + dw_edma_terminate_all_descs(chan); chan->request = EDMA_REQ_NONE; chan->status = EDMA_ST_IDLE; break; - case EDMA_REQ_PAUSE: - chan->request = EDMA_REQ_NONE; - chan->status = EDMA_ST_PAUSE; - break; - default: break; } @@ -698,14 +669,49 @@ static void dw_edma_abort_interrupt(struct dw_edma_chan *chan) spin_lock_irqsave(&chan->vc.lock, flags); vd = vchan_next_desc(&chan->vc); - if (vd) { + if (vd && chan->request == EDMA_REQ_STOP) { + dw_edma_terminate_all_descs(chan); + } else if (vd) { dw_hdma_set_callback_result(vd, DMA_TRANS_ABORTED); list_del(&vd->node); vchan_cookie_complete(vd); } - spin_unlock_irqrestore(&chan->vc.lock, flags); chan->request = EDMA_REQ_NONE; chan->status = EDMA_ST_IDLE; + spin_unlock_irqrestore(&chan->vc.lock, flags); +} + +static void dw_edma_irq_work(struct work_struct *work) +{ + struct dw_edma_chan *chan = container_of(work, struct dw_edma_chan, + irq_work); + unsigned int events; + + do { + events = atomic_xchg(&chan->irq_pending, 0); + + if (events & DW_EDMA_IRQ_DONE) + dw_edma_done_interrupt(chan); + if (events & DW_EDMA_IRQ_ABORT) + dw_edma_abort_interrupt(chan); + } while (atomic_read(&chan->irq_pending)); +} + +static void dw_edma_queue_irq_work(struct dw_edma_chan *chan, + enum dw_edma_irq_event event) +{ + atomic_or(event, &chan->irq_pending); + queue_work(chan->dw->wq, &chan->irq_work); +} + +static void dw_edma_done_interrupt_deferred(struct dw_edma_chan *chan) +{ + dw_edma_queue_irq_work(chan, DW_EDMA_IRQ_DONE); +} + +static void dw_edma_abort_interrupt_deferred(struct dw_edma_chan *chan) +{ + dw_edma_queue_irq_work(chan, DW_EDMA_IRQ_ABORT); } static void dw_edma_emul_irq_ack(struct irq_data *d) @@ -739,6 +745,9 @@ static int dw_edma_emul_irq_alloc(struct dw_edma *dw) chip->db_irq = 0; chip->db_offset = ~0; + if (chip->flags & DW_EDMA_CHIP_PARTIAL) + return 0; + /* * Only meaningful when the core provides the deassert sequence * for interrupt emulation. @@ -802,8 +811,8 @@ static inline irqreturn_t dw_edma_interrupt_write_inner(int irq, void *data) struct dw_edma_irq *dw_irq = data; return dw_edma_core_handle_int(dw_irq, EDMA_DIR_WRITE, - dw_edma_done_interrupt, - dw_edma_abort_interrupt); + dw_edma_done_interrupt_deferred, + dw_edma_abort_interrupt_deferred); } static inline irqreturn_t dw_edma_interrupt_read_inner(int irq, void *data) @@ -811,8 +820,8 @@ static inline irqreturn_t dw_edma_interrupt_read_inner(int irq, void *data) struct dw_edma_irq *dw_irq = data; return dw_edma_core_handle_int(dw_irq, EDMA_DIR_READ, - dw_edma_done_interrupt, - dw_edma_abort_interrupt); + dw_edma_done_interrupt_deferred, + dw_edma_abort_interrupt_deferred); } static inline irqreturn_t dw_edma_interrupt_write(int irq, void *data) @@ -856,21 +865,51 @@ static int dw_edma_alloc_chan_resources(struct dma_chan *dchan) return 0; } -static void dw_edma_free_chan_resources(struct dma_chan *dchan) +static void dw_edma_wait_termination(struct dma_chan *dchan) { + struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan); unsigned long timeout = jiffies + msecs_to_jiffies(5000); - int ret; + bool stopping; + /* + * A STOP may be deferred to a later interrupt while the channel is still + * running. Wait until that handler completes the termination. + */ while (time_before(jiffies, timeout)) { - ret = dw_edma_device_terminate_all(dchan); - if (!ret) - break; + scoped_guard(spinlock_irqsave, &chan->vc.lock) + stopping = chan->request == EDMA_REQ_STOP; - if (time_after_eq(jiffies, timeout)) + if (!stopping) return; - cpu_relax(); + fsleep(1000); } + + dev_warn(chan->dw->chip->dev, + "timeout waiting for channel termination\n"); +} + +static void dw_edma_device_synchronize(struct dma_chan *dchan) +{ + struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan); + + dw_edma_wait_termination(dchan); + cancel_work_sync(&chan->irq_work); + atomic_set(&chan->irq_pending, 0); + vchan_synchronize(&chan->vc); +} + +static void dw_edma_free_chan_resources(struct dma_chan *dchan) +{ + struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan); + + dw_edma_device_terminate_all(dchan); + dw_edma_device_synchronize(dchan); + + scoped_guard(spinlock_irqsave, &chan->vc.lock) + chan->configured = false; + + vchan_free_chan_resources(&chan->vc); } static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc) @@ -892,6 +931,7 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc) chan = &dw->chan[i]; chan->dw = dw; + chan->func_no = chip->func_no; if (i < dw->wr_ch_cnt) { chan->id = i; @@ -904,12 +944,16 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc) chan->configured = false; chan->request = EDMA_REQ_NONE; chan->status = EDMA_ST_IDLE; + chan->irq_mode = dw_edma_get_default_irq_mode(chan); + INIT_WORK(&chan->irq_work, dw_edma_irq_work); + atomic_set(&chan->irq_pending, 0); if (chan->dir == EDMA_DIR_WRITE) - chan->ll_max = (chip->ll_region_wr[chan->id].sz / EDMA_LL_SZ); + chan->ll_region = chip->ll_region_wr[chan->id]; else - chan->ll_max = (chip->ll_region_rd[chan->id].sz / EDMA_LL_SZ); - chan->ll_max -= 1; + chan->ll_region = chip->ll_region_rd[chan->id]; + + chan->ll_max = chan->ll_region.sz / EDMA_LL_SZ - 1; dev_vdbg(dev, "L. List:\tChannel %s[%u] max_cnt=%u\n", str_write_read(chan->dir == EDMA_DIR_WRITE), @@ -925,11 +969,10 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc) irq = &dw->irq[pos]; if (chan->dir == EDMA_DIR_WRITE) - irq->wr_mask |= BIT(chan->id); + bitmap_set(irq->wr_mask, chan->id, 1); else - irq->rd_mask |= BIT(chan->id); + bitmap_set(irq->rd_mask, chan->id, 1); - 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", @@ -968,9 +1011,10 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc) dma->device_pause = dw_edma_device_pause; dma->device_resume = dw_edma_device_resume; dma->device_terminate_all = dw_edma_device_terminate_all; + dma->device_synchronize = dw_edma_device_synchronize; 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; @@ -1010,6 +1054,7 @@ static int dw_edma_irq_request(struct dw_edma *dw, if (chip->nr_irqs == 1) { /* Common IRQ shared among all channels */ irq = chip->ops->irq_vector(dev, 0); + dw->irq[0].dw = dw; err = request_irq(irq, dw_edma_interrupt_common, IRQF_SHARED, dw->name, &dw->irq[0]); if (err) { @@ -1032,6 +1077,7 @@ static int dw_edma_irq_request(struct dw_edma *dw, for (i = 0; i < (*wr_alloc + *rd_alloc); i++) { irq = chip->ops->irq_vector(dev, i); + dw->irq[i].dw = dw; err = request_irq(irq, i < *wr_alloc ? dw_edma_interrupt_write : @@ -1062,12 +1108,37 @@ err_irq_free: return err; } +static int dw_edma_check_partial(struct dw_edma_chip *chip, + u16 hw_wr_ch_cnt, u16 hw_rd_ch_cnt) +{ + if (!(chip->flags & DW_EDMA_CHIP_PARTIAL)) + return 0; + + if (chip->mf != EDMA_MF_EDMA_UNROLL && + chip->mf != EDMA_MF_HDMA_COMPAT) + return 0; + + /* + * Direction-wide registers are shared by all channels in that + * direction, so a direction must have a single owner. + */ + if ((chip->ll_wr_cnt && chip->ll_wr_cnt != hw_wr_ch_cnt) || + (chip->ll_rd_cnt && chip->ll_rd_cnt != hw_rd_ch_cnt)) + return -EOPNOTSUPP; + + return 0; +} + int dw_edma_probe(struct dw_edma_chip *chip) { struct device *dev; struct dw_edma *dw; + u16 hw_wr_ch_cnt; + u16 hw_rd_ch_cnt; u32 wr_alloc = 0; u32 rd_alloc = 0; + u16 max_wr_cnt; + u16 max_rd_cnt; int i, err; if (!chip) @@ -1077,26 +1148,50 @@ int dw_edma_probe(struct dw_edma_chip *chip) if (!dev || !chip->ops) return -EINVAL; + if (chip->flags & DW_EDMA_CHIP_PARTIAL) { + switch (chip->mf) { + case EDMA_MF_EDMA_UNROLL: + case EDMA_MF_HDMA_COMPAT: + case EDMA_MF_HDMA_NATIVE: + break; + default: + return -EOPNOTSUPP; + } + } + dw = devm_kzalloc(dev, sizeof(*dw), GFP_KERNEL); if (!dw) return -ENOMEM; dw->chip = chip; - if (dw->chip->mf == EDMA_MF_HDMA_NATIVE) + if (dw->chip->mf == EDMA_MF_HDMA_NATIVE) { dw_hdma_v0_core_register(dw); - else + max_wr_cnt = HDMA_MAX_WR_CH; + max_rd_cnt = HDMA_MAX_RD_CH; + } else { dw_edma_v0_core_register(dw); + max_wr_cnt = EDMA_MAX_WR_CH; + max_rd_cnt = EDMA_MAX_RD_CH; + } raw_spin_lock_init(&dw->lock); - dw->wr_ch_cnt = min_t(u16, chip->ll_wr_cnt, - dw_edma_core_ch_count(dw, EDMA_DIR_WRITE)); - dw->wr_ch_cnt = min_t(u16, dw->wr_ch_cnt, EDMA_MAX_WR_CH); + /* + * chip->ll_*_cnt describes the channels exposed by this instance. Keep + * the usable hardware counts separate for partial ownership checks. + */ + hw_wr_ch_cnt = min(dw_edma_core_ch_count(dw, EDMA_DIR_WRITE), + max_wr_cnt); + hw_rd_ch_cnt = min(dw_edma_core_ch_count(dw, EDMA_DIR_READ), + max_rd_cnt); + + err = dw_edma_check_partial(chip, hw_wr_ch_cnt, hw_rd_ch_cnt); + if (err) + return err; - dw->rd_ch_cnt = min_t(u16, chip->ll_rd_cnt, - dw_edma_core_ch_count(dw, EDMA_DIR_READ)); - dw->rd_ch_cnt = min_t(u16, dw->rd_ch_cnt, EDMA_MAX_RD_CH); + dw->wr_ch_cnt = min(chip->ll_wr_cnt, hw_wr_ch_cnt); + dw->rd_ch_cnt = min(chip->ll_rd_cnt, hw_rd_ch_cnt); if (!dw->wr_ch_cnt && !dw->rd_ch_cnt) return -EINVAL; @@ -1113,13 +1208,34 @@ int dw_edma_probe(struct dw_edma_chip *chip) snprintf(dw->name, sizeof(dw->name), "dw-edma-core:%s", dev_name(chip->dev)); - /* Disable eDMA, only to establish the ideal initial conditions */ - dw_edma_core_off(dw); + if (chip->flags & DW_EDMA_CHIP_PARTIAL) { + /* + * Do not reset the shared controller, but drain stale state + * from resources represented by this instance. + */ + err = dw_edma_core_quiesce(dw); + if (err) + return err; + } else { + /* Disable eDMA only when this instance owns the controller. */ + dw_edma_core_off(dw); + } + + /* + * Deferred IRQ works are queued from the hard IRQ handlers, so the + * workqueue must exist before any IRQ is requested. + */ + dw->wq = alloc_workqueue("dw-edma:%s", WQ_UNBOUND | WQ_HIGHPRI, 0, + dev_name(chip->dev)); + if (!dw->wq) + return -ENOMEM; /* Request IRQs */ err = dw_edma_irq_request(dw, &wr_alloc, &rd_alloc); - if (err) + if (err) { + destroy_workqueue(dw->wq); return err; + } /* Allocate a dedicated virtual IRQ for interrupt-emulation doorbells */ err = dw_edma_emul_irq_alloc(dw); @@ -1142,6 +1258,7 @@ err_irq_free: for (i = (dw->nr_irqs - 1); i >= 0; i--) free_irq(chip->ops->irq_vector(dev, i), &dw->irq[i]); dw_edma_emul_irq_free(dw); + destroy_workqueue(dw->wq); return err; } @@ -1152,20 +1269,27 @@ int dw_edma_remove(struct dw_edma_chip *chip) struct dw_edma_chan *chan, *_chan; struct device *dev = chip->dev; struct dw_edma *dw = chip->dw; - int i; + int i, err = 0; /* Skip removal if no private data found */ if (!dw) return -ENODEV; - /* Disable eDMA */ - dw_edma_core_off(dw); + if (chip->flags & DW_EDMA_CHIP_PARTIAL) + err = dw_edma_core_quiesce(dw); + else + dw_edma_core_off(dw); /* Free irqs */ for (i = (dw->nr_irqs - 1); i >= 0; i--) free_irq(chip->ops->irq_vector(dev, i), &dw->irq[i]); dw_edma_emul_irq_free(dw); + for (i = 0; i < dw->wr_ch_cnt + dw->rd_ch_cnt; i++) + cancel_work_sync(&dw->chan[i].irq_work); + + destroy_workqueue(dw->wq); + /* Deregister eDMA device */ dma_async_device_unregister(&dw->dma); list_for_each_entry_safe(chan, _chan, &dw->dma.channels, @@ -1174,7 +1298,7 @@ int dw_edma_remove(struct dw_edma_chip *chip) list_del(&chan->vc.chan.device_node); } - return 0; + return err; } EXPORT_SYMBOL_GPL(dw_edma_remove); diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h index 6474cacf7195..f6a5ad317567 100644 --- a/drivers/dma/dw-edma/dw-edma-core.h +++ b/drivers/dma/dw-edma/dw-edma-core.h @@ -9,8 +9,10 @@ #ifndef _DW_EDMA_CORE_H #define _DW_EDMA_CORE_H +#include <linux/atomic.h> #include <linux/msi.h> #include <linux/dma/edma.h> +#include <linux/workqueue.h> #include "../virt-dma.h" @@ -43,32 +45,24 @@ struct dw_edma_chan; struct dw_edma_chunk; struct dw_edma_burst { - struct list_head list; u64 sar; u64 dar; u32 sz; -}; - -struct dw_edma_chunk { - struct list_head list; - struct dw_edma_chan *chan; - struct dw_edma_burst *burst; - - u32 bursts_alloc; - - u8 cb; - struct dw_edma_region ll_region; /* Linked list */ + /* precalulate summary of previous burst total size */ + u32 xfer_sz; }; struct dw_edma_desc { struct virt_dma_desc vd; struct dw_edma_chan *chan; - struct dw_edma_chunk *chunk; - - 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 { @@ -76,24 +70,32 @@ struct dw_edma_chan { struct dw_edma *dw; int id; enum dw_edma_dir dir; + u8 func_no; u32 ll_max; + struct dw_edma_region ll_region; /* Linked list */ struct msi_msg msi; + enum dw_edma_ch_irq_mode irq_mode; + enum dw_edma_request request; enum dw_edma_status status; u8 configured; struct dma_slave_config config; bool non_ll; + + struct work_struct irq_work; + atomic_t irq_pending; }; struct dw_edma_irq { struct msi_msg msi; - u32 wr_mask; - u32 rd_mask; struct dw_edma *dw; + + DECLARE_BITMAP(wr_mask, HDMA_MAX_WR_CH); + DECLARE_BITMAP(rd_mask, HDMA_MAX_RD_CH); }; struct dw_edma { @@ -109,6 +111,12 @@ struct dw_edma { struct dw_edma_chan *chan; + /* + * WQ_HIGHPRI keeps completion processing responsive under heavy load; + * WQ_UNBOUND lets different channels run on different CPUs. + */ + struct workqueue_struct *wq; + raw_spinlock_t lock; /* Protect v0 shared registers */ struct dw_edma_chip *chip; @@ -120,11 +128,18 @@ typedef void (*dw_edma_handler_t)(struct dw_edma_chan *); struct dw_edma_core_ops { void (*off)(struct dw_edma *dw); + int (*quiesce)(struct dw_edma *dw); + int (*ch_quiesce)(struct dw_edma_chan *chan); u16 (*ch_count)(struct dw_edma *dw, enum dw_edma_dir dir); enum dma_status (*ch_status)(struct dw_edma_chan *chan); irqreturn_t (*handle_int)(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir, dw_edma_handler_t done, dw_edma_handler_t abort); - void (*start)(struct dw_edma_chunk *chunk, bool first); + void (*non_ll_start)(struct dw_edma_chan *chan, struct dw_edma_burst *child); + void (*ll_data)(struct dw_edma_chan *chan, struct dw_edma_burst *burst, + u32 idx, bool cb, bool irq); + 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); @@ -166,6 +181,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) { @@ -173,6 +196,18 @@ void dw_edma_core_off(struct dw_edma *dw) } static inline +int dw_edma_core_quiesce(struct dw_edma *dw) +{ + return dw->core->quiesce(dw); +} + +static inline +int dw_edma_core_ch_quiesce(struct dw_edma_chan *chan) +{ + return chan->dw->core->ch_quiesce(chan); +} + +static inline u16 dw_edma_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir) { return dw->core->ch_count(dw, dir); @@ -192,15 +227,32 @@ dw_edma_core_handle_int(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir, } static inline -void dw_edma_core_start(struct dw_edma *dw, struct dw_edma_chunk *chunk, bool first) +void dw_edma_core_ch_config(struct dw_edma_chan *chan) { - dw->core->start(chunk, first); + chan->dw->core->ch_config(chan); } -static inline -void dw_edma_core_ch_config(struct dw_edma_chan *chan) +static inline void +dw_edma_core_ll_data(struct dw_edma_chan *chan, struct dw_edma_burst *burst, + u32 idx, bool cb, bool irq) { - chan->dw->core->ch_config(chan); + chan->dw->core->ll_data(chan, burst, idx, cb, irq); +} + +static inline void +dw_edma_core_ll_link(struct dw_edma_chan *chan, u32 idx, bool cb, u64 addr) +{ + chan->dw->core->ll_link(chan, idx, cb, addr); +} + +static inline void dw_edma_core_ch_doorbell(struct dw_edma_chan *chan) +{ + chan->dw->core->ch_doorbell(chan); +} + +static inline void dw_edma_core_ch_enable(struct dw_edma_chan *chan) +{ + chan->dw->core->ch_enable(chan); } static inline @@ -224,4 +276,15 @@ dw_edma_core_db_offset(struct dw_edma *dw) return dw->core->db_offset(dw); } +static inline bool +dw_edma_core_ch_ignore_irq(struct dw_edma_chan *chan) +{ + struct dw_edma *dw = chan->dw; + + if (dw->chip->flags & DW_EDMA_CHIP_LOCAL) + return chan->irq_mode == DW_EDMA_CH_IRQ_REMOTE; + else + return chan->irq_mode == DW_EDMA_CH_IRQ_LOCAL; +} + #endif /* _DW_EDMA_CORE_H */ diff --git a/drivers/dma/dw-edma/dw-edma-pcie.c b/drivers/dma/dw-edma/dw-edma-pcie.c index 791c46e8ae4c..9f237ba916de 100644 --- a/drivers/dma/dw-edma/dw-edma-pcie.c +++ b/drivers/dma/dw-edma/dw-edma-pcie.c @@ -55,6 +55,8 @@ struct dw_edma_block { enum pci_barno bar; off_t off; + u64 paddr; + bool paddr_valid; size_t sz; }; @@ -62,19 +64,36 @@ struct dw_edma_pcie_data { /* eDMA registers location */ struct dw_edma_block rg; /* eDMA memory linked list location */ - struct dw_edma_block ll_wr[EDMA_MAX_WR_CH]; - struct dw_edma_block ll_rd[EDMA_MAX_RD_CH]; + struct dw_edma_block ll_wr[HDMA_MAX_WR_CH]; + struct dw_edma_block ll_rd[HDMA_MAX_RD_CH]; /* eDMA memory data location */ - struct dw_edma_block dt_wr[EDMA_MAX_WR_CH]; - struct dw_edma_block dt_rd[EDMA_MAX_RD_CH]; + struct dw_edma_block dt_wr[HDMA_MAX_WR_CH]; + struct dw_edma_block dt_rd[HDMA_MAX_RD_CH]; /* Other */ enum dw_edma_map_format mf; u8 irqs; u16 wr_ch_cnt; u16 rd_ch_cnt; u64 devmem_phys_off; + bool cfg_non_ll; }; +struct dw_edma_pcie_match_data { + const struct dw_edma_pcie_data *data; + const struct dw_edma_plat_ops *plat_ops; + /* + * Mandatory callback. It may leave @pdata unchanged when the static + * template already describes the device. + */ + int (*parse_caps)(struct pci_dev *pdev, + struct dw_edma_pcie_data *pdata); + unsigned long flags; + u32 chip_flags; +}; + +#define DW_EDMA_PCIE_F_DEVMEM_PHYS_OFF BIT(0) +#define DW_EDMA_PCIE_F_REG_OFFSET BIT(1) + static const struct dw_edma_pcie_data snps_edda_data = { /* eDMA registers location */ .rg.bar = BAR_0, @@ -309,31 +328,88 @@ static void dw_edma_pcie_get_xilinx_dma_data(struct pci_dev *pdev, pdata->devmem_phys_off = off; } +static int +dw_edma_pcie_parse_synopsys_caps(struct pci_dev *pdev, + struct dw_edma_pcie_data *pdata) +{ + dw_edma_pcie_get_synopsys_dma_data(pdev, pdata); + + return 0; +} + +static int +dw_edma_pcie_parse_xilinx_caps(struct pci_dev *pdev, + struct dw_edma_pcie_data *pdata) +{ + dw_edma_pcie_get_xilinx_dma_data(pdev, pdata); + + /* + * There is no valid address found for the LL memory space on the + * device side. In the absence of LL base address use the non-LL mode or + * simple mode supported by the HDMA IP. + */ + if (pdata->devmem_phys_off == DW_PCIE_XILINX_MDB_INVALID_ADDR) { + pdata->cfg_non_ll = true; + return 0; + } + + /* + * Configure the channel LL and data blocks if number of channels + * enabled in VSEC capability are more than the channels configured in + * xilinx_mdb_data. + */ + dw_edma_set_chan_region_offset(pdata, BAR_2, 0, + DW_PCIE_XILINX_MDB_LL_OFF_GAP, + DW_PCIE_XILINX_MDB_LL_SIZE, + DW_PCIE_XILINX_MDB_DT_OFF_GAP, + DW_PCIE_XILINX_MDB_DT_SIZE); + + return 0; +} + static u64 dw_edma_get_phys_addr(struct pci_dev *pdev, + const struct dw_edma_pcie_match_data *match, struct dw_edma_pcie_data *pdata, enum pci_barno bar) { - if (pdev->vendor == PCI_VENDOR_ID_XILINX) + if (match->flags & DW_EDMA_PCIE_F_DEVMEM_PHYS_OFF) return pdata->devmem_phys_off; + return pci_bus_address(pdev, bar); } +static u64 dw_edma_get_block_addr(struct pci_dev *pdev, + const struct dw_edma_pcie_match_data *match, + struct dw_edma_pcie_data *pdata, + const struct dw_edma_block *block) +{ + if (block->paddr_valid) + return block->paddr; + + return dw_edma_get_phys_addr(pdev, match, pdata, block->bar) + + block->off; +} + static int dw_edma_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *pid) { - struct dw_edma_pcie_data *pdata = (void *)pid->driver_data; + const struct dw_edma_pcie_match_data *match = (void *)pid->driver_data; + const struct dw_edma_pcie_data *pdata; struct device *dev = &pdev->dev; struct dw_edma_chip *chip; int err, nr_irqs; int i, mask; - bool non_ll = false; + + if (!match) + return -ENODEV; + pdata = match->data; if (!pdata) return -ENODEV; - struct dw_edma_pcie_data *vsec_data __free(kfree) = - kmalloc_obj(*vsec_data); - if (!vsec_data) + struct dw_edma_pcie_data *dma_data __free(kfree) = + kmemdup(pdata, sizeof(*dma_data), GFP_KERNEL); + if (!dma_data) return -ENOMEM; /* Enable PCI device */ @@ -343,48 +419,25 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev, return err; } - memcpy(vsec_data, pdata, sizeof(struct dw_edma_pcie_data)); + /* Let device-specific discovery override the static template data. */ + if (!match->parse_caps || !match->plat_ops) + return -EINVAL; - /* - * Tries to find if exists a PCIe Vendor-Specific Extended Capability - * for the DMA, if one exists, then reconfigures it. - */ - dw_edma_pcie_get_synopsys_dma_data(pdev, vsec_data); - - if (pdev->vendor == PCI_VENDOR_ID_XILINX) { - dw_edma_pcie_get_xilinx_dma_data(pdev, vsec_data); - - /* - * There is no valid address found for the LL memory - * space on the device side. In the absence of LL base - * address use the non-LL mode or simple mode supported by - * the HDMA IP. - */ - if (vsec_data->devmem_phys_off == DW_PCIE_XILINX_MDB_INVALID_ADDR) - non_ll = true; - - /* - * Configure the channel LL and data blocks if number of - * channels enabled in VSEC capability are more than the - * channels configured in xilinx_mdb_data. - */ - if (!non_ll) - dw_edma_set_chan_region_offset(vsec_data, BAR_2, 0, - DW_PCIE_XILINX_MDB_LL_OFF_GAP, - DW_PCIE_XILINX_MDB_LL_SIZE, - DW_PCIE_XILINX_MDB_DT_OFF_GAP, - DW_PCIE_XILINX_MDB_DT_SIZE); - } + err = match->parse_caps(pdev, dma_data); + if (err) + return err; /* Mapping PCI BAR regions */ - mask = BIT(vsec_data->rg.bar); - for (i = 0; i < vsec_data->wr_ch_cnt; i++) { - mask |= BIT(vsec_data->ll_wr[i].bar); - mask |= BIT(vsec_data->dt_wr[i].bar); + mask = BIT(dma_data->rg.bar); + for (i = 0; i < dma_data->wr_ch_cnt; i++) { + mask |= BIT(dma_data->ll_wr[i].bar); + if (dma_data->dt_wr[i].sz) + mask |= BIT(dma_data->dt_wr[i].bar); } - for (i = 0; i < vsec_data->rd_ch_cnt; i++) { - mask |= BIT(vsec_data->ll_rd[i].bar); - mask |= BIT(vsec_data->dt_rd[i].bar); + for (i = 0; i < dma_data->rd_ch_cnt; i++) { + mask |= BIT(dma_data->ll_rd[i].bar); + if (dma_data->dt_rd[i].sz) + mask |= BIT(dma_data->dt_rd[i].bar); } err = pcim_iomap_regions(pdev, mask, pci_name(pdev)); if (err) { @@ -407,7 +460,7 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev, return -ENOMEM; /* IRQs allocation */ - nr_irqs = pci_alloc_irq_vectors(pdev, 1, vsec_data->irqs, + nr_irqs = pci_alloc_irq_vectors(pdev, 1, dma_data->irqs, PCI_IRQ_MSI | PCI_IRQ_MSIX); if (nr_irqs < 1) { pci_err(pdev, "fail to alloc IRQ vector (number of IRQs=%u)\n", @@ -418,69 +471,75 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev, /* Data structure initialization */ chip->dev = dev; - chip->mf = vsec_data->mf; + chip->mf = dma_data->mf; + chip->flags = match->chip_flags; + chip->func_no = PCI_FUNC(pdev->devfn); chip->nr_irqs = nr_irqs; - chip->ops = &dw_edma_pcie_plat_ops; - chip->cfg_non_ll = non_ll; + chip->ops = match->plat_ops; + chip->cfg_non_ll = dma_data->cfg_non_ll; - chip->ll_wr_cnt = vsec_data->wr_ch_cnt; - chip->ll_rd_cnt = vsec_data->rd_ch_cnt; + chip->ll_wr_cnt = dma_data->wr_ch_cnt; + chip->ll_rd_cnt = dma_data->rd_ch_cnt; - chip->reg_base = pcim_iomap_table(pdev)[vsec_data->rg.bar]; + chip->reg_base = pcim_iomap_table(pdev)[dma_data->rg.bar]; if (!chip->reg_base) return -ENOMEM; + if (match->flags & DW_EDMA_PCIE_F_REG_OFFSET) + chip->reg_base += dma_data->rg.off; - for (i = 0; i < chip->ll_wr_cnt && !non_ll; i++) { + for (i = 0; i < chip->ll_wr_cnt && !dma_data->cfg_non_ll; i++) { struct dw_edma_region *ll_region = &chip->ll_region_wr[i]; struct dw_edma_region *dt_region = &chip->dt_region_wr[i]; - struct dw_edma_block *ll_block = &vsec_data->ll_wr[i]; - struct dw_edma_block *dt_block = &vsec_data->dt_wr[i]; + struct dw_edma_block *ll_block = &dma_data->ll_wr[i]; + struct dw_edma_block *dt_block = &dma_data->dt_wr[i]; ll_region->vaddr.io = pcim_iomap_table(pdev)[ll_block->bar]; if (!ll_region->vaddr.io) return -ENOMEM; ll_region->vaddr.io += ll_block->off; - ll_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data, - ll_block->bar); - ll_region->paddr += ll_block->off; + ll_region->paddr = dw_edma_get_block_addr(pdev, match, dma_data, + ll_block); ll_region->sz = ll_block->sz; + if (!dt_block->sz) + continue; + dt_region->vaddr.io = pcim_iomap_table(pdev)[dt_block->bar]; if (!dt_region->vaddr.io) return -ENOMEM; dt_region->vaddr.io += dt_block->off; - dt_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data, - dt_block->bar); - dt_region->paddr += dt_block->off; + dt_region->paddr = dw_edma_get_block_addr(pdev, match, dma_data, + dt_block); dt_region->sz = dt_block->sz; } - for (i = 0; i < chip->ll_rd_cnt && !non_ll; i++) { + for (i = 0; i < chip->ll_rd_cnt && !dma_data->cfg_non_ll; i++) { struct dw_edma_region *ll_region = &chip->ll_region_rd[i]; struct dw_edma_region *dt_region = &chip->dt_region_rd[i]; - struct dw_edma_block *ll_block = &vsec_data->ll_rd[i]; - struct dw_edma_block *dt_block = &vsec_data->dt_rd[i]; + struct dw_edma_block *ll_block = &dma_data->ll_rd[i]; + struct dw_edma_block *dt_block = &dma_data->dt_rd[i]; ll_region->vaddr.io = pcim_iomap_table(pdev)[ll_block->bar]; if (!ll_region->vaddr.io) return -ENOMEM; ll_region->vaddr.io += ll_block->off; - ll_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data, - ll_block->bar); - ll_region->paddr += ll_block->off; + ll_region->paddr = dw_edma_get_block_addr(pdev, match, dma_data, + ll_block); ll_region->sz = ll_block->sz; + if (!dt_block->sz) + continue; + dt_region->vaddr.io = pcim_iomap_table(pdev)[dt_block->bar]; if (!dt_region->vaddr.io) return -ENOMEM; dt_region->vaddr.io += dt_block->off; - dt_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data, - dt_block->bar); - dt_region->paddr += dt_block->off; + dt_region->paddr = dw_edma_get_block_addr(pdev, match, dma_data, + dt_block); dt_region->sz = dt_block->sz; } @@ -497,32 +556,40 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev, pci_dbg(pdev, "Version:\tUnknown (0x%x)\n", chip->mf); pci_dbg(pdev, "Registers:\tBAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p)\n", - vsec_data->rg.bar, vsec_data->rg.off, vsec_data->rg.sz, + dma_data->rg.bar, dma_data->rg.off, dma_data->rg.sz, chip->reg_base); for (i = 0; i < chip->ll_wr_cnt; i++) { pci_dbg(pdev, "L. List:\tWRITE CH%.2u, BAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n", - i, vsec_data->ll_wr[i].bar, - vsec_data->ll_wr[i].off, chip->ll_region_wr[i].sz, + i, dma_data->ll_wr[i].bar, + dma_data->ll_wr[i].off, chip->ll_region_wr[i].sz, chip->ll_region_wr[i].vaddr.io, &chip->ll_region_wr[i].paddr); + if (!dma_data->dt_wr[i].sz) + continue; + pci_dbg(pdev, "Data:\tWRITE CH%.2u, BAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n", - i, vsec_data->dt_wr[i].bar, - vsec_data->dt_wr[i].off, chip->dt_region_wr[i].sz, - chip->dt_region_wr[i].vaddr.io, &chip->dt_region_wr[i].paddr); + i, dma_data->dt_wr[i].bar, + dma_data->dt_wr[i].off, chip->dt_region_wr[i].sz, + chip->dt_region_wr[i].vaddr.io, + &chip->dt_region_wr[i].paddr); } for (i = 0; i < chip->ll_rd_cnt; i++) { pci_dbg(pdev, "L. List:\tREAD CH%.2u, BAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n", - i, vsec_data->ll_rd[i].bar, - vsec_data->ll_rd[i].off, chip->ll_region_rd[i].sz, + i, dma_data->ll_rd[i].bar, + dma_data->ll_rd[i].off, chip->ll_region_rd[i].sz, chip->ll_region_rd[i].vaddr.io, &chip->ll_region_rd[i].paddr); + if (!dma_data->dt_rd[i].sz) + continue; + pci_dbg(pdev, "Data:\tREAD CH%.2u, BAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n", - i, vsec_data->dt_rd[i].bar, - vsec_data->dt_rd[i].off, chip->dt_region_rd[i].sz, - chip->dt_region_rd[i].vaddr.io, &chip->dt_region_rd[i].paddr); + i, dma_data->dt_rd[i].bar, + dma_data->dt_rd[i].off, chip->dt_region_rd[i].sz, + chip->dt_region_rd[i].vaddr.io, + &chip->dt_region_rd[i].paddr); } pci_dbg(pdev, "Nr. IRQs:\t%u\n", chip->nr_irqs); @@ -555,17 +622,34 @@ static void dw_edma_pcie_remove(struct pci_dev *pdev) err = dw_edma_remove(chip); if (err) pci_warn(pdev, "can't remove device properly: %d\n", err); - - /* Freeing IRQs */ - pci_free_irq_vectors(pdev); } +static const struct dw_edma_pcie_match_data snps_edda_match_data = { + .data = &snps_edda_data, + .plat_ops = &dw_edma_pcie_plat_ops, + .parse_caps = dw_edma_pcie_parse_synopsys_caps, +}; + +static const struct dw_edma_pcie_match_data xilinx_mdb_match_data = { + .data = &xilinx_mdb_data, + .plat_ops = &dw_edma_pcie_plat_ops, + .parse_caps = dw_edma_pcie_parse_xilinx_caps, + .flags = DW_EDMA_PCIE_F_DEVMEM_PHYS_OFF, +}; + +static const struct dw_edma_pcie_match_data xilinx_cpm6_dma_match_data = { + .data = &xilinx_cpm6_dma_data, + .plat_ops = &dw_edma_pcie_plat_ops, + .parse_caps = dw_edma_pcie_parse_xilinx_caps, + .flags = DW_EDMA_PCIE_F_DEVMEM_PHYS_OFF, +}; + static const struct pci_device_id dw_edma_pcie_id_table[] = { - { PCI_DEVICE_DATA(SYNOPSYS, EDDA, &snps_edda_data) }, + { PCI_DEVICE_DATA(SYNOPSYS, EDDA, &snps_edda_match_data) }, { PCI_VDEVICE(XILINX, PCI_DEVICE_ID_XILINX_B054), - (kernel_ulong_t)&xilinx_mdb_data }, + .driver_data = (kernel_ulong_t)&xilinx_mdb_match_data }, { PCI_VDEVICE(XILINX, PCI_DEVICE_ID_XILINX_B00F), - .driver_data = (kernel_ulong_t)&xilinx_cpm6_dma_data }, + .driver_data = (kernel_ulong_t)&xilinx_cpm6_dma_match_data }, { } }; MODULE_DEVICE_TABLE(pci, dw_edma_pcie_id_table); diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c index cfdd6463252e..abc1bd4c0be2 100644 --- a/drivers/dma/dw-edma/dw-edma-v0-core.c +++ b/drivers/dma/dw-edma/dw-edma-v0-core.c @@ -7,6 +7,7 @@ */ #include <linux/bitfield.h> +#include <linux/iopoll.h> #include <linux/irqreturn.h> #include <linux/io-64-nonatomic-lo-hi.h> @@ -25,6 +26,8 @@ enum dw_edma_control { DW_EDMA_V0_LLE = BIT(9), }; +#define EDMA_V0_FUNC_NUM_MASK GENMASK(16, 12) + static inline struct dw_edma_v0_regs __iomem *__dw_regs(struct dw_edma *dw) { return dw->chip->reg_base; @@ -159,7 +162,93 @@ static inline u32 readl_ch(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch, #define GET_CH_32(dw, dir, ch, name) \ readl_ch(dw, dir, ch, &(__dw_ch_regs(dw, dir, ch)->name)) +static u32 dw_edma_v0_func_num(struct dw_edma_chan *chan) +{ + return FIELD_PREP(EDMA_V0_FUNC_NUM_MASK, chan->func_no); +} + /* eDMA management callbacks */ +static void dw_edma_v0_core_ch_power(struct dw_edma *dw, + enum dw_edma_dir dir, u16 id, bool enable) +{ + u32 value = enable ? BIT(0) : 0; + + if (WARN_ON_ONCE(id >= EDMA_V0_MAX_NR_CH)) + return; + + switch (id) { + case 0: + SET_RW_COMPAT(dw, dir, ch0_pwr_en, value); + break; + case 1: + SET_RW_COMPAT(dw, dir, ch1_pwr_en, value); + break; + case 2: + SET_RW_COMPAT(dw, dir, ch2_pwr_en, value); + break; + case 3: + SET_RW_COMPAT(dw, dir, ch3_pwr_en, value); + break; + case 4: + SET_RW_COMPAT(dw, dir, ch4_pwr_en, value); + break; + case 5: + SET_RW_COMPAT(dw, dir, ch5_pwr_en, value); + break; + case 6: + SET_RW_COMPAT(dw, dir, ch6_pwr_en, value); + break; + case 7: + SET_RW_COMPAT(dw, dir, ch7_pwr_en, value); + break; + } +} + +static int dw_edma_v0_core_engine_disable(struct dw_edma *dw, + enum dw_edma_dir dir) +{ + u32 value; + int ret; + + SET_RW_32(dw, dir, engine_en, 0); + ret = read_poll_timeout(GET_RW_32, value, !(value & BIT(0)), 100, + 200000, false, dw, dir, engine_en); + if (ret) + dev_warn(dw->chip->dev, "%s engine did not stop within 200ms\n", + dir == EDMA_DIR_WRITE ? "write" : "read"); + + return ret; +} + +static int dw_edma_v0_core_dir_off(struct dw_edma *dw, enum dw_edma_dir dir) +{ + u16 count, id; + int ret = 0; + + scoped_guard(raw_spinlock_irqsave, &dw->lock) + SET_RW_32(dw, dir, int_mask, + EDMA_V0_DONE_INT_MASK | EDMA_V0_ABORT_INT_MASK); + + if (dw->chip->mf == EDMA_MF_HDMA_COMPAT) { + /* + * DWC PCIe Controller Databook 6.10a-lca06, "Legacy DMA + * and HDMA Software Compatibility": HDMA compatibility mode + * does not implement ENGINE_EN, but retains CHi_PWR_EN for + * per-channel enable and disable. + */ + count = dir == EDMA_DIR_WRITE ? dw->wr_ch_cnt : dw->rd_ch_cnt; + for (id = 0; id < count; id++) + dw_edma_v0_core_ch_power(dw, dir, id, false); + } else { + ret = dw_edma_v0_core_engine_disable(dw, dir); + } + + SET_RW_32(dw, dir, int_clear, + EDMA_V0_DONE_INT_MASK | EDMA_V0_ABORT_INT_MASK); + + return ret; +} + static void dw_edma_v0_core_off(struct dw_edma *dw) { SET_BOTH_32(dw, int_mask, @@ -169,6 +258,33 @@ static void dw_edma_v0_core_off(struct dw_edma *dw) SET_BOTH_32(dw, engine_en, 0); } +static int dw_edma_v0_core_quiesce(struct dw_edma *dw) +{ + int ret = 0; + int err; + + if (dw->wr_ch_cnt) + ret = dw_edma_v0_core_dir_off(dw, EDMA_DIR_WRITE); + if (dw->rd_ch_cnt) { + err = dw_edma_v0_core_dir_off(dw, EDMA_DIR_READ); + if (!ret) + ret = err; + } + + return ret; +} + +/* + * The unrolled eDMA and HDMA compatibility register maps share interrupt + * control per direction, so the whole direction is quiesced. Callers must + * own the direction entirely and prevent the peer from programming it after + * this point. Partial ownership mode validates direction granularity. + */ +static int dw_edma_v0_core_ch_quiesce(struct dw_edma_chan *chan) +{ + return dw_edma_v0_core_dir_off(chan->dw, chan->dir); +} + static u16 dw_edma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir) { u32 num_ch; @@ -218,18 +334,6 @@ static void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan) FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id))); } -static u32 dw_edma_v0_core_status_done_int(struct dw_edma *dw, enum dw_edma_dir dir) -{ - return FIELD_GET(EDMA_V0_DONE_INT_MASK, - GET_RW_32(dw, dir, int_status)); -} - -static u32 dw_edma_v0_core_status_abort_int(struct dw_edma *dw, enum dw_edma_dir dir) -{ - return FIELD_GET(EDMA_V0_ABORT_INT_MASK, - GET_RW_32(dw, dir, int_status)); -} - static irqreturn_t dw_edma_v0_core_handle_int(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir, dw_edma_handler_t done, dw_edma_handler_t abort) @@ -239,7 +343,8 @@ dw_edma_v0_core_handle_int(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir, irqreturn_t ret = IRQ_NONE; struct dw_edma_chan *chan; unsigned long off; - u32 mask; + unsigned long *mask; + u32 sts; if (dir == EDMA_DIR_WRITE) { total = dw->wr_ch_cnt; @@ -251,22 +356,38 @@ dw_edma_v0_core_handle_int(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir, mask = dw_irq->rd_mask; } - val = dw_edma_v0_core_status_done_int(dw, dir); - val &= mask; + /* + * DONE and ABORT status share one register, and on remote setups + * every read is a non-posted round trip across the PCIe link. Take + * one snapshot and derive both views from it. An abort raised + * after the snapshot is deferred, not lost: only bits observed in + * the snapshot are ever cleared below, so its status remains set and + * triggers another handler pass. + */ + sts = GET_RW_32(dw, dir, int_status); + + val = FIELD_GET(EDMA_V0_DONE_INT_MASK, sts); + val &= *mask; for_each_set_bit(pos, &val, total) { chan = &dw->chan[pos + off]; + if (unlikely(dw_edma_core_ch_ignore_irq(chan))) + continue; + dw_edma_v0_core_clear_done_int(chan); done(chan); ret = IRQ_HANDLED; } - val = dw_edma_v0_core_status_abort_int(dw, dir); - val &= mask; + val = FIELD_GET(EDMA_V0_ABORT_INT_MASK, sts); + val &= *mask; for_each_set_bit(pos, &val, total) { chan = &dw->chan[pos + off]; + if (unlikely(dw_edma_core_ch_ignore_irq(chan))) + continue; + dw_edma_v0_core_clear_abort_int(chan); abort(chan); @@ -276,77 +397,90 @@ dw_edma_v0_core_handle_int(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir, return ret; } -static void dw_edma_v0_write_ll_data(struct dw_edma_chunk *chunk, int i, +static void dw_edma_v0_write_ll_data(struct dw_edma_chan *chan, int i, u32 control, u32 size, u64 sar, u64 dar) { ptrdiff_t ofs = i * sizeof(struct dw_edma_v0_lli); - if (chunk->chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) { - struct dw_edma_v0_lli *lli = chunk->ll_region.vaddr.mem + ofs; + if (chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) { + struct dw_edma_v0_lli *lli = chan->ll_region.vaddr.mem + ofs; - lli->control = control; lli->transfer_size = size; lli->sar.reg = sar; lli->dar.reg = dar; + dma_wmb(); + lli->control = control; } else { - struct dw_edma_v0_lli __iomem *lli = chunk->ll_region.vaddr.io + ofs; + struct dw_edma_v0_lli __iomem *lli = chan->ll_region.vaddr.io + ofs; - writel(control, &lli->control); writel(size, &lli->transfer_size); writeq(sar, &lli->sar.reg); writeq(dar, &lli->dar.reg); + writel(control, &lli->control); } } -static void dw_edma_v0_write_ll_link(struct dw_edma_chunk *chunk, +static void dw_edma_v0_write_ll_link(struct dw_edma_chan *chan, int i, u32 control, u64 pointer) { ptrdiff_t ofs = i * sizeof(struct dw_edma_v0_lli); - if (chunk->chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) { - struct dw_edma_v0_llp *llp = chunk->ll_region.vaddr.mem + ofs; + if (chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) { + struct dw_edma_v0_llp *llp = chan->ll_region.vaddr.mem + ofs; - llp->control = control; llp->llp.reg = pointer; + dma_wmb(); + llp->control = control; } else { - struct dw_edma_v0_llp __iomem *llp = chunk->ll_region.vaddr.io + ofs; + struct dw_edma_v0_llp __iomem *llp = chan->ll_region.vaddr.io + ofs; - writel(control, &llp->control); writeq(pointer, &llp->llp.reg); + writel(control, &llp->control); } } -static void dw_edma_v0_core_write_chunk(struct dw_edma_chunk *chunk) +static void dw_edma_v0_core_ch_enable(struct dw_edma_chan *chan) { - struct dw_edma_burst *child; - struct dw_edma_chan *chan = chunk->chan; - u32 control = 0, i = 0; - int j; - - if (chunk->cb) - control = DW_EDMA_V0_CB; - - j = chunk->bursts_alloc; - list_for_each_entry(child, &chunk->burst->list, list) { - j--; - if (!j) { - control |= DW_EDMA_V0_LIE; - if (!(chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL)) - control |= DW_EDMA_V0_RIE; - } - - dw_edma_v0_write_ll_data(chunk, i++, control, child->sz, - child->sar, child->dar); - } - - control = DW_EDMA_V0_LLP | DW_EDMA_V0_TCB; - if (!chunk->cb) - control |= DW_EDMA_V0_CB; + struct dw_edma *dw = chan->dw; + unsigned long flags; + u32 tmp; - dw_edma_v0_write_ll_link(chunk, i, control, chunk->ll_region.paddr); + /* Enable engine */ + SET_RW_32(dw, chan->dir, engine_en, BIT(0)); + if (dw->chip->mf == EDMA_MF_HDMA_COMPAT) + dw_edma_v0_core_ch_power(dw, chan->dir, chan->id, true); + /* Interrupt mask/unmask - done, abort */ + raw_spin_lock_irqsave(&dw->lock, flags); + + tmp = GET_RW_32(dw, chan->dir, int_mask); + if (chan->irq_mode == DW_EDMA_CH_IRQ_REMOTE) { + tmp |= FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id)); + tmp |= FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id)); + } else { + tmp &= ~FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id)); + tmp &= ~FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id)); + } + SET_RW_32(dw, chan->dir, int_mask, tmp); + /* Linked list error */ + tmp = GET_RW_32(dw, chan->dir, linked_list_err_en); + tmp |= FIELD_PREP(EDMA_V0_LINKED_LIST_ERR_MASK, BIT(chan->id)); + SET_RW_32(dw, chan->dir, linked_list_err_en, tmp); + + raw_spin_unlock_irqrestore(&dw->lock, flags); + + /* Channel control */ + SET_CH_32(dw, chan->dir, chan->id, ch_control1, + DW_EDMA_V0_CCS | DW_EDMA_V0_LLE | + dw_edma_v0_func_num(chan)); + /* Linked list */ + /* llp is not aligned on 64bit -> keep 32bit accesses */ + SET_CH_32(dw, chan->dir, chan->id, llp.lsb, + lower_32_bits(chan->ll_region.paddr)); + SET_CH_32(dw, chan->dir, chan->id, llp.msb, + upper_32_bits(chan->ll_region.paddr)); } -static void dw_edma_v0_sync_ll_data(struct dw_edma_chunk *chunk) +static void dw_edma_v0_sync_ll_data(struct dw_edma_chan *chan) { /* * In case of remote eDMA engine setup, the DW PCIe RP/EP internal @@ -356,88 +490,8 @@ static void dw_edma_v0_sync_ll_data(struct dw_edma_chunk *chunk) * LL memory in a hope that the MRd TLP will return only after the * last MWr TLP is completed */ - if (!(chunk->chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL)) - readl(chunk->ll_region.vaddr.io); -} - -static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first) -{ - struct dw_edma_chan *chan = chunk->chan; - struct dw_edma *dw = chan->dw; - unsigned long flags; - u32 tmp; - - dw_edma_v0_core_write_chunk(chunk); - - if (first) { - /* Enable engine */ - SET_RW_32(dw, chan->dir, engine_en, BIT(0)); - if (dw->chip->mf == EDMA_MF_HDMA_COMPAT) { - switch (chan->id) { - case 0: - SET_RW_COMPAT(dw, chan->dir, ch0_pwr_en, - BIT(0)); - break; - case 1: - SET_RW_COMPAT(dw, chan->dir, ch1_pwr_en, - BIT(0)); - break; - case 2: - SET_RW_COMPAT(dw, chan->dir, ch2_pwr_en, - BIT(0)); - break; - case 3: - SET_RW_COMPAT(dw, chan->dir, ch3_pwr_en, - BIT(0)); - break; - case 4: - SET_RW_COMPAT(dw, chan->dir, ch4_pwr_en, - BIT(0)); - break; - case 5: - SET_RW_COMPAT(dw, chan->dir, ch5_pwr_en, - BIT(0)); - break; - case 6: - SET_RW_COMPAT(dw, chan->dir, ch6_pwr_en, - BIT(0)); - break; - case 7: - SET_RW_COMPAT(dw, chan->dir, ch7_pwr_en, - BIT(0)); - break; - } - } - /* Interrupt unmask - done, abort */ - raw_spin_lock_irqsave(&dw->lock, flags); - - tmp = GET_RW_32(dw, chan->dir, int_mask); - tmp &= ~FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id)); - tmp &= ~FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id)); - SET_RW_32(dw, chan->dir, int_mask, tmp); - /* Linked list error */ - tmp = GET_RW_32(dw, chan->dir, linked_list_err_en); - tmp |= FIELD_PREP(EDMA_V0_LINKED_LIST_ERR_MASK, BIT(chan->id)); - SET_RW_32(dw, chan->dir, linked_list_err_en, tmp); - - raw_spin_unlock_irqrestore(&dw->lock, flags); - - /* Channel control */ - SET_CH_32(dw, chan->dir, chan->id, ch_control1, - (DW_EDMA_V0_CCS | DW_EDMA_V0_LLE)); - /* Linked list */ - /* llp is not aligned on 64bit -> keep 32bit accesses */ - SET_CH_32(dw, chan->dir, chan->id, llp.lsb, - lower_32_bits(chunk->ll_region.paddr)); - SET_CH_32(dw, chan->dir, chan->id, llp.msb, - upper_32_bits(chunk->ll_region.paddr)); - } - - dw_edma_v0_sync_ll_data(chunk); - - /* Doorbell */ - SET_RW_32(dw, chan->dir, doorbell, - FIELD_PREP(EDMA_V0_DOORBELL_CH_MASK, chan->id)); + if (!(chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL)) + readl(chan->ll_region.vaddr.io); } static void dw_edma_v0_core_ch_config(struct dw_edma_chan *chan) @@ -509,6 +563,59 @@ static void dw_edma_v0_core_ch_config(struct dw_edma_chan *chan) } } +static void +dw_edma_v0_core_ll_data(struct dw_edma_chan *chan, struct dw_edma_burst *burst, + u32 idx, bool cb, bool irq) +{ + u32 control = 0; + + if (cb) + control |= DW_EDMA_V0_CB; + + if (irq) { + control |= DW_EDMA_V0_LIE; + + /* + * A local instance never issues transfers on a remote-routed + * channel: on CHIP_LOCAL instances, REMOTE routing denotes a + * channel handed over to the remote side, which programs the + * linked list through its own instance. The remote-only + * recipe (LIE|RIE with the local interrupt masked) is thus + * applied by the instance that owns the transfer, and the + * LIE-only write below never executes for a remote-routed + * channel. + */ + if (!(chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) && + chan->irq_mode == DW_EDMA_CH_IRQ_REMOTE) + control |= DW_EDMA_V0_RIE; + } + + dw_edma_v0_write_ll_data(chan, idx, control, burst->sz, burst->sar, + burst->dar); +} + +static void +dw_edma_v0_core_ll_link(struct dw_edma_chan *chan, u32 idx, bool cb, u64 addr) +{ + u32 control = DW_EDMA_V0_LLP | DW_EDMA_V0_TCB; + + if (!cb) + control |= DW_EDMA_V0_CB; + + dw_edma_v0_write_ll_link(chan, idx, control, addr); +} + +static void dw_edma_v0_core_ch_doorbell(struct dw_edma_chan *chan) +{ + struct dw_edma *dw = chan->dw; + + dw_edma_v0_sync_ll_data(chan); + + /* Doorbell */ + SET_RW_32(dw, chan->dir, doorbell, + FIELD_PREP(EDMA_V0_DOORBELL_CH_MASK, chan->id)); +} + /* eDMA debugfs callbacks */ static void dw_edma_v0_core_debugfs_on(struct dw_edma *dw) { @@ -536,10 +643,15 @@ static resource_size_t dw_edma_v0_core_db_offset(struct dw_edma *dw) static const struct dw_edma_core_ops dw_edma_v0_core = { .off = dw_edma_v0_core_off, + .quiesce = dw_edma_v0_core_quiesce, + .ch_quiesce = dw_edma_v0_core_ch_quiesce, .ch_count = dw_edma_v0_core_ch_count, .ch_status = dw_edma_v0_core_ch_status, .handle_int = dw_edma_v0_core_handle_int, - .start = dw_edma_v0_core_start, + .ll_data = dw_edma_v0_core_ll_data, + .ll_link = dw_edma_v0_core_ll_link, + .ch_doorbell = dw_edma_v0_core_ch_doorbell, + .ch_enable = dw_edma_v0_core_ch_enable, .ch_config = dw_edma_v0_core_ch_config, .debugfs_on = dw_edma_v0_core_debugfs_on, .ack_emulated_irq = dw_edma_v0_core_ack_emulated_irq, diff --git a/drivers/dma/dw-edma/dw-hdma-v0-core.c b/drivers/dma/dw-edma/dw-hdma-v0-core.c index 632abb8b481c..36ee72efcd31 100644 --- a/drivers/dma/dw-edma/dw-hdma-v0-core.c +++ b/drivers/dma/dw-edma/dw-hdma-v0-core.c @@ -49,20 +49,82 @@ __dw_ch_regs(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch) writel(value, &(__dw_ch_regs(dw, EDMA_DIR_READ, ch)->name)); \ } while (0) +static u32 dw_hdma_v0_core_int_setup(struct dw_edma_chan *chan, u32 val) +{ + val &= ~(HDMA_V0_LOCAL_ABORT_INT_EN | HDMA_V0_REMOTE_ABORT_INT_EN | + HDMA_V0_LOCAL_STOP_INT_EN | HDMA_V0_REMOTE_STOP_INT_EN | + HDMA_V0_ABORT_INT_MASK | HDMA_V0_STOP_INT_MASK); + + /* + * HDMA_INT_STATUS.STOP and .ABORT are latched only when LSIE and + * LAIE are enabled. A remote handler needs those status bits to + * identify the source of the IMWr, so keep local generation enabled + * and mask the local interrupt pins instead. + */ + val |= HDMA_V0_LOCAL_ABORT_INT_EN | HDMA_V0_LOCAL_STOP_INT_EN; + + if (chan->irq_mode == DW_EDMA_CH_IRQ_REMOTE) + val |= HDMA_V0_REMOTE_ABORT_INT_EN | + HDMA_V0_REMOTE_STOP_INT_EN | + HDMA_V0_ABORT_INT_MASK | HDMA_V0_STOP_INT_MASK; + + return val; +} + /* HDMA management callbacks */ +static void dw_hdma_v0_core_ch_off(struct dw_edma *dw, enum dw_edma_dir dir, + u16 id) +{ + SET_CH_32(dw, dir, id, int_setup, + HDMA_V0_STOP_INT_MASK | HDMA_V0_ABORT_INT_MASK); + SET_CH_32(dw, dir, id, ch_en, 0); + SET_CH_32(dw, dir, id, int_clear, + HDMA_V0_STOP_INT_MASK | HDMA_V0_ABORT_INT_MASK); +} + static void dw_hdma_v0_core_off(struct dw_edma *dw) { int id; + enum dw_edma_dir dir; + + dir = EDMA_DIR_WRITE; + for (id = 0; id < dw->wr_ch_cnt; id++) { + SET_CH_32(dw, dir, id, int_setup, + HDMA_V0_STOP_INT_MASK | HDMA_V0_ABORT_INT_MASK); + SET_CH_32(dw, dir, id, int_clear, + HDMA_V0_STOP_INT_MASK | HDMA_V0_ABORT_INT_MASK); + SET_CH_32(dw, dir, id, ch_en, 0); + } - for (id = 0; id < HDMA_V0_MAX_NR_CH; id++) { - SET_BOTH_CH_32(dw, id, int_setup, - HDMA_V0_STOP_INT_MASK | HDMA_V0_ABORT_INT_MASK); - SET_BOTH_CH_32(dw, id, int_clear, - HDMA_V0_STOP_INT_MASK | HDMA_V0_ABORT_INT_MASK); - SET_BOTH_CH_32(dw, id, ch_en, 0); + dir = EDMA_DIR_READ; + for (id = 0; id < dw->rd_ch_cnt; id++) { + SET_CH_32(dw, dir, id, int_setup, + HDMA_V0_STOP_INT_MASK | HDMA_V0_ABORT_INT_MASK); + SET_CH_32(dw, dir, id, int_clear, + HDMA_V0_STOP_INT_MASK | HDMA_V0_ABORT_INT_MASK); + SET_CH_32(dw, dir, id, ch_en, 0); } } +static int dw_hdma_v0_core_quiesce(struct dw_edma *dw) +{ + int id; + + for (id = 0; id < dw->wr_ch_cnt; id++) + dw_hdma_v0_core_ch_off(dw, EDMA_DIR_WRITE, id); + for (id = 0; id < dw->rd_ch_cnt; id++) + dw_hdma_v0_core_ch_off(dw, EDMA_DIR_READ, id); + + return 0; +} + +static int dw_hdma_v0_core_ch_quiesce(struct dw_edma_chan *chan) +{ + dw_hdma_v0_core_ch_off(chan->dw, chan->dir, chan->id); + + return 0; +} + static u16 dw_hdma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir) { /* @@ -79,7 +141,7 @@ static enum dma_status dw_hdma_v0_core_ch_status(struct dw_edma_chan *chan) u32 tmp; tmp = FIELD_GET(HDMA_V0_CH_STATUS_MASK, - GET_CH_32(dw, chan->id, chan->dir, ch_stat)); + GET_CH_32(dw, chan->dir, chan->id, ch_stat)); if (tmp == 1) return DMA_IN_PROGRESS; @@ -118,7 +180,7 @@ dw_hdma_v0_core_handle_int(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir, unsigned long total, pos, val; irqreturn_t ret = IRQ_NONE; struct dw_edma_chan *chan; - unsigned long off, mask; + unsigned long off, *mask; if (dir == EDMA_DIR_WRITE) { total = dw->wr_ch_cnt; @@ -130,8 +192,10 @@ dw_hdma_v0_core_handle_int(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir, mask = dw_irq->rd_mask; } - for_each_set_bit(pos, &mask, total) { + for_each_set_bit(pos, mask, total) { chan = &dw->chan[pos + off]; + if (unlikely(dw_edma_core_ch_ignore_irq(chan))) + continue; val = dw_hdma_v0_core_status_int(chan); if (FIELD_GET(HDMA_V0_STOP_INT_MASK, val)) { @@ -152,66 +216,73 @@ dw_hdma_v0_core_handle_int(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir, return ret; } -static void dw_hdma_v0_write_ll_data(struct dw_edma_chunk *chunk, int i, +static void dw_hdma_v0_write_ll_data(struct dw_edma_chan *chan, int i, u32 control, u32 size, u64 sar, u64 dar) { ptrdiff_t ofs = i * sizeof(struct dw_hdma_v0_lli); - if (chunk->chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) { - struct dw_hdma_v0_lli *lli = chunk->ll_region.vaddr.mem + ofs; + if (chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) { + struct dw_hdma_v0_lli *lli = chan->ll_region.vaddr.mem + ofs; - lli->control = control; lli->transfer_size = size; lli->sar.reg = sar; lli->dar.reg = dar; + dma_wmb(); + lli->control = control; } else { - struct dw_hdma_v0_lli __iomem *lli = chunk->ll_region.vaddr.io + ofs; + struct dw_hdma_v0_lli __iomem *lli = chan->ll_region.vaddr.io + ofs; - writel(control, &lli->control); writel(size, &lli->transfer_size); writeq(sar, &lli->sar.reg); writeq(dar, &lli->dar.reg); + writel(control, &lli->control); } } -static void dw_hdma_v0_write_ll_link(struct dw_edma_chunk *chunk, +static void dw_hdma_v0_write_ll_link(struct dw_edma_chan *chan, int i, u32 control, u64 pointer) { ptrdiff_t ofs = i * sizeof(struct dw_hdma_v0_lli); - if (chunk->chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) { - struct dw_hdma_v0_llp *llp = chunk->ll_region.vaddr.mem + ofs; + if (chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) { + struct dw_hdma_v0_llp *llp = chan->ll_region.vaddr.mem + ofs; - llp->control = control; llp->llp.reg = pointer; + dma_wmb(); + llp->control = control; } else { - struct dw_hdma_v0_llp __iomem *llp = chunk->ll_region.vaddr.io + ofs; + struct dw_hdma_v0_llp __iomem *llp = chan->ll_region.vaddr.io + ofs; - writel(control, &llp->control); writeq(pointer, &llp->llp.reg); + writel(control, &llp->control); } } -static void dw_hdma_v0_core_write_chunk(struct dw_edma_chunk *chunk) +static void dw_hdma_v0_core_ch_enable(struct dw_edma_chan *chan) { - struct dw_edma_burst *child; - u32 control = 0, i = 0; - - if (chunk->cb) - control = DW_HDMA_V0_CB; - - list_for_each_entry(child, &chunk->burst->list, list) - dw_hdma_v0_write_ll_data(chunk, i++, control, child->sz, - child->sar, child->dar); - - control = DW_HDMA_V0_LLP | DW_HDMA_V0_TCB; - if (!chunk->cb) - control |= DW_HDMA_V0_CB; + struct dw_edma *dw = chan->dw; + u32 tmp; - dw_hdma_v0_write_ll_link(chunk, i, control, chunk->ll_region.paddr); + /* Enable engine */ + SET_CH_32(dw, chan->dir, chan->id, ch_en, BIT(0)); + /* Interrupt unmask - stop, abort */ + tmp = GET_CH_32(dw, chan->dir, chan->id, int_setup); + tmp = dw_hdma_v0_core_int_setup(chan, tmp); + SET_CH_32(dw, chan->dir, chan->id, int_setup, tmp); + /* Channel control */ + SET_CH_32(dw, chan->dir, chan->id, control1, HDMA_V0_LINKLIST_EN); + /* Linked list */ + /* llp is not aligned on 64bit -> keep 32bit accesses */ + SET_CH_32(dw, chan->dir, chan->id, llp.lsb, + lower_32_bits(chan->ll_region.paddr)); + SET_CH_32(dw, chan->dir, chan->id, llp.msb, + upper_32_bits(chan->ll_region.paddr)); + /* Set consumer cycle */ + SET_CH_32(dw, chan->dir, chan->id, cycle_sync, + HDMA_V0_CONSUMER_CYCLE_STAT | HDMA_V0_CONSUMER_CYCLE_BIT); } -static void dw_hdma_v0_sync_ll_data(struct dw_edma_chunk *chunk) +static void dw_hdma_v0_sync_ll_data(struct dw_edma_chan *chan) { /* * In case of remote HDMA engine setup, the DW PCIe RP/EP internal @@ -221,60 +292,16 @@ static void dw_hdma_v0_sync_ll_data(struct dw_edma_chunk *chunk) * LL memory in a hope that the MRd TLP will return only after the * last MWr TLP is completed */ - if (!(chunk->chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL)) - readl(chunk->ll_region.vaddr.io); + if (!(chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL)) + readl(chan->ll_region.vaddr.io); } -static void dw_hdma_v0_core_ll_start(struct dw_edma_chunk *chunk, bool first) +static void dw_hdma_v0_core_non_ll_start(struct dw_edma_chan *chan, + struct dw_edma_burst *child) { - struct dw_edma_chan *chan = chunk->chan; struct dw_edma *dw = chan->dw; - u32 tmp; - - dw_hdma_v0_core_write_chunk(chunk); - - if (first) { - /* Enable engine */ - SET_CH_32(dw, chan->dir, chan->id, ch_en, BIT(0)); - /* Interrupt unmask - stop, abort */ - tmp = GET_CH_32(dw, chan->dir, chan->id, int_setup); - tmp &= ~(HDMA_V0_STOP_INT_MASK | HDMA_V0_ABORT_INT_MASK); - /* Interrupt enable - stop, abort */ - tmp |= HDMA_V0_LOCAL_STOP_INT_EN | HDMA_V0_LOCAL_ABORT_INT_EN; - if (!(dw->chip->flags & DW_EDMA_CHIP_LOCAL)) - tmp |= HDMA_V0_REMOTE_STOP_INT_EN | HDMA_V0_REMOTE_ABORT_INT_EN; - SET_CH_32(dw, chan->dir, chan->id, int_setup, tmp); - /* Channel control */ - SET_CH_32(dw, chan->dir, chan->id, control1, HDMA_V0_LINKLIST_EN); - /* Linked list */ - /* llp is not aligned on 64bit -> keep 32bit accesses */ - SET_CH_32(dw, chan->dir, chan->id, llp.lsb, - lower_32_bits(chunk->ll_region.paddr)); - SET_CH_32(dw, chan->dir, chan->id, llp.msb, - upper_32_bits(chunk->ll_region.paddr)); - /* Set consumer cycle */ - SET_CH_32(dw, chan->dir, chan->id, cycle_sync, - HDMA_V0_CONSUMER_CYCLE_STAT | HDMA_V0_CONSUMER_CYCLE_BIT); - } - - dw_hdma_v0_sync_ll_data(chunk); - - /* Doorbell */ - SET_CH_32(dw, chan->dir, chan->id, doorbell, HDMA_V0_DOORBELL_START); -} - -static void dw_hdma_v0_core_non_ll_start(struct dw_edma_chunk *chunk) -{ - struct dw_edma_chan *chan = chunk->chan; - struct dw_edma *dw = chan->dw; - struct dw_edma_burst *child; u32 val; - child = list_first_entry_or_null(&chunk->burst->list, - struct dw_edma_burst, list); - if (!child) - return; - SET_CH_32(dw, chan->dir, chan->id, ch_en, HDMA_V0_CH_EN); /* Source address */ @@ -293,17 +320,8 @@ static void dw_hdma_v0_core_non_ll_start(struct dw_edma_chunk *chunk) SET_CH_32(dw, chan->dir, chan->id, transfer_size, child->sz); /* Interrupt setup */ - val = GET_CH_32(dw, chan->dir, chan->id, int_setup) | - HDMA_V0_STOP_INT_MASK | - HDMA_V0_ABORT_INT_MASK | - HDMA_V0_LOCAL_STOP_INT_EN | - HDMA_V0_LOCAL_ABORT_INT_EN; - - if (!(dw->chip->flags & DW_EDMA_CHIP_LOCAL)) { - val |= HDMA_V0_REMOTE_STOP_INT_EN | - HDMA_V0_REMOTE_ABORT_INT_EN; - } - + val = GET_CH_32(dw, chan->dir, chan->id, int_setup); + val = dw_hdma_v0_core_int_setup(chan, val); SET_CH_32(dw, chan->dir, chan->id, int_setup, val); /* Channel control setup */ @@ -315,16 +333,6 @@ static void dw_hdma_v0_core_non_ll_start(struct dw_edma_chunk *chunk) HDMA_V0_DOORBELL_START); } -static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool first) -{ - struct dw_edma_chan *chan = chunk->chan; - - if (chan->non_ll) - dw_hdma_v0_core_non_ll_start(chunk); - else - dw_hdma_v0_core_ll_start(chunk, first); -} - static void dw_hdma_v0_core_ch_config(struct dw_edma_chan *chan) { struct dw_edma *dw = chan->dw; @@ -337,6 +345,43 @@ static void dw_hdma_v0_core_ch_config(struct dw_edma_chan *chan) SET_CH_32(dw, chan->dir, chan->id, msi_abort.msb, chan->msi.address_hi); /* config MSI data */ SET_CH_32(dw, chan->dir, chan->id, msi_msgdata, chan->msi.data); + /* Configure the requester function number used by outbound TLPs. */ + SET_CH_32(dw, chan->dir, chan->id, func_num, + FIELD_PREP(HDMA_V0_FUNC_NUM_PF_MASK, chan->func_no)); +} + +static void +dw_hdma_v0_core_ll_data(struct dw_edma_chan *chan, struct dw_edma_burst *burst, + u32 idx, bool cb, bool irq) +{ + u32 control = 0; + + if (cb) + control |= DW_HDMA_V0_CB; + + dw_hdma_v0_write_ll_data(chan, idx, control, burst->sz, burst->sar, + burst->dar); +} + +static void +dw_hdma_v0_core_ll_link(struct dw_edma_chan *chan, u32 idx, bool cb, u64 addr) +{ + u32 control = DW_HDMA_V0_LLP | DW_HDMA_V0_TCB; + + if (!cb) + control |= DW_HDMA_V0_CB; + + dw_hdma_v0_write_ll_link(chan, idx, control, addr); +} + +static void dw_hdma_v0_core_ch_doorbell(struct dw_edma_chan *chan) +{ + struct dw_edma *dw = chan->dw; + + dw_hdma_v0_sync_ll_data(chan); + + /* Doorbell */ + SET_CH_32(dw, chan->dir, chan->id, doorbell, HDMA_V0_DOORBELL_START); } /* HDMA debugfs callbacks */ @@ -353,10 +398,16 @@ static resource_size_t dw_hdma_v0_core_db_offset(struct dw_edma *dw) static const struct dw_edma_core_ops dw_hdma_v0_core = { .off = dw_hdma_v0_core_off, + .quiesce = dw_hdma_v0_core_quiesce, + .ch_quiesce = dw_hdma_v0_core_ch_quiesce, .ch_count = dw_hdma_v0_core_ch_count, .ch_status = dw_hdma_v0_core_ch_status, .handle_int = dw_hdma_v0_core_handle_int, - .start = dw_hdma_v0_core_start, + .non_ll_start = dw_hdma_v0_core_non_ll_start, + .ll_data = dw_hdma_v0_core_ll_data, + .ll_link = dw_hdma_v0_core_ll_link, + .ch_doorbell = dw_hdma_v0_core_ch_doorbell, + .ch_enable = dw_hdma_v0_core_ch_enable, .ch_config = dw_hdma_v0_core_ch_config, .debugfs_on = dw_hdma_v0_core_debugfs_on, .db_offset = dw_hdma_v0_core_db_offset, diff --git a/drivers/dma/dw-edma/dw-hdma-v0-regs.h b/drivers/dma/dw-edma/dw-hdma-v0-regs.h index 7759ba9b4850..2124c162a62f 100644 --- a/drivers/dma/dw-edma/dw-hdma-v0-regs.h +++ b/drivers/dma/dw-edma/dw-hdma-v0-regs.h @@ -11,7 +11,7 @@ #include <linux/dmaengine.h> -#define HDMA_V0_MAX_NR_CH 8 +#define HDMA_V0_MAX_NR_CH 64 #define HDMA_V0_CH_EN BIT(0) #define HDMA_V0_LOCAL_ABORT_INT_EN BIT(6) #define HDMA_V0_REMOTE_ABORT_INT_EN BIT(5) @@ -24,6 +24,7 @@ #define HDMA_V0_CONSUMER_CYCLE_BIT BIT(0) #define HDMA_V0_DOORBELL_START BIT(0) #define HDMA_V0_CH_STATUS_MASK GENMASK(1, 0) +#define HDMA_V0_FUNC_NUM_PF_MASK GENMASK(7, 0) struct dw_hdma_v0_ch_regs { u32 ch_en; /* 0x0000 */ diff --git a/drivers/dma/fsl-edma-main.c b/drivers/dma/fsl-edma-main.c index 36155ab1602a..d9fb717b5b53 100644 --- a/drivers/dma/fsl-edma-main.c +++ b/drivers/dma/fsl-edma-main.c @@ -414,6 +414,8 @@ static int fsl_edma3_irq_init(struct platform_device *pdev, struct fsl_edma_engi errirq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s-err", dev_name(&pdev->dev)); + if (!errirq_name) + return -ENOMEM; ret = devm_request_irq(&pdev->dev, fsl_edma->errirq, fsl_edma3_err_handler_shared, 0, errirq_name, fsl_edma); diff --git a/drivers/dma/fsl_raid.c b/drivers/dma/fsl_raid.c index 99945845d8b5..2d86f61105e5 100644 --- a/drivers/dma/fsl_raid.c +++ b/drivers/dma/fsl_raid.c @@ -242,9 +242,9 @@ static void fill_cfd_frame(struct fsl_re_cmpnd_frame *cf, u8 index, u32 efrl = length & FSL_RE_CF_LENGTH_MASK; efrl |= final << FSL_RE_CF_FINAL_SHIFT; - cf[index].efrl32 = efrl; - cf[index].addr_high = upper_32_bits(addr); - cf[index].addr_low = lower_32_bits(addr); + cf[index].efrl32 = cpu_to_be32(efrl); + cf[index].addr_high = cpu_to_be32(upper_32_bits(addr)); + cf[index].addr_low = cpu_to_be32(lower_32_bits(addr)); } static struct fsl_re_desc *fsl_re_init_desc(struct fsl_re_chan *re_chan, @@ -256,9 +256,10 @@ static struct fsl_re_desc *fsl_re_init_desc(struct fsl_re_chan *re_chan, dma_async_tx_descriptor_init(&desc->async_tx, &re_chan->chan); INIT_LIST_HEAD(&desc->node); - desc->hwdesc.fmt32 = FSL_RE_FRAME_FORMAT << FSL_RE_HWDESC_FMT_SHIFT; - desc->hwdesc.lbea32 = upper_32_bits(paddr); - desc->hwdesc.addr_low = lower_32_bits(paddr); + desc->hwdesc.fmt32 = cpu_to_be32(FSL_RE_FRAME_FORMAT << + FSL_RE_HWDESC_FMT_SHIFT); + desc->hwdesc.lbea32 = cpu_to_be32(upper_32_bits(paddr)); + desc->hwdesc.addr_low = cpu_to_be32(lower_32_bits(paddr)); desc->cf_addr = cf; desc->cf_paddr = paddr; @@ -374,11 +375,11 @@ static struct dma_async_tx_descriptor *fsl_re_prep_dma_genq( for (i = 2, j = 0; j < save_src_cnt; i++, j++) fill_cfd_frame(cf, i, len, src[j], 0); + /* Fill the last frame and mark it final */ if (cont_q) - fill_cfd_frame(cf, i++, len, dest, 0); - - /* Setting the final bit in the last source buffer frame in CFD */ - cf[i - 1].efrl32 |= 1 << FSL_RE_CF_FINAL_SHIFT; + fill_cfd_frame(cf, i, len, dest, 1); + else + fill_cfd_frame(cf, i - 1, len, src[j - 1], 1); return &desc->async_tx; } @@ -504,16 +505,16 @@ static struct dma_async_tx_descriptor *fsl_re_prep_dma_pq( p[save_src_cnt + 2] = 1; fill_cfd_frame(cf, i++, len, dest[0], 0); fill_cfd_frame(cf, i++, len, dest[1], 0); - fill_cfd_frame(cf, i++, len, dest[1], 0); + fill_cfd_frame(cf, i++, len, dest[1], 1); } else { dev_err(re_chan->dev, "PQ tx continuation error!\n"); return NULL; } + } else { + /* Mark the last source buffer frame final */ + fill_cfd_frame(cf, i - 1, len, src[j - 1], 1); } - /* Setting the final bit in the last source buffer frame in CFD */ - cf[i - 1].efrl32 |= 1 << FSL_RE_CF_FINAL_SHIFT; - return &desc->async_tx; } @@ -656,8 +657,7 @@ static int fsl_re_chan_probe(struct platform_device *ofdev, goto err_free; } - chan->jrregs = (struct fsl_re_chan_cfg *)((u8 *)re_priv->re_regs + - off + ptr); + chan->jrregs = re_priv->base + off + ptr; /* read irq property from dts */ chan->irq = irq_of_parse_and_map(np, 0); @@ -745,38 +745,36 @@ err_free: /* Probe function for RAID Engine */ static int fsl_re_probe(struct platform_device *ofdev) { + struct fsl_re_ctrl __iomem *re_regs; struct fsl_re_drv_private *re_priv; struct device_node *child; u32 off; u8 ridx = 0; struct dma_device *dma_dev; - struct resource *res; int rc; struct device *dev = &ofdev->dev; + /* IOMAP the entire RAID Engine region */ + re_regs = devm_platform_ioremap_resource(ofdev, 0); + if (IS_ERR(re_regs)) + return PTR_ERR(re_regs); + re_priv = devm_kzalloc(dev, sizeof(*re_priv), GFP_KERNEL); if (!re_priv) return -ENOMEM; - res = platform_get_resource(ofdev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; - - /* IOMAP the entire RAID Engine region */ - re_priv->re_regs = devm_ioremap(dev, res->start, resource_size(res)); - if (!re_priv->re_regs) - return -EBUSY; + re_priv->base = re_regs; /* Program the RE mode */ - out_be32(&re_priv->re_regs->global_config, FSL_RE_NON_DPAA_MODE); + out_be32(&re_regs->global_config, FSL_RE_NON_DPAA_MODE); /* Program Galois Field polynomial */ - out_be32(&re_priv->re_regs->galois_field_config, FSL_RE_GFM_POLY); + out_be32(&re_regs->galois_field_config, FSL_RE_GFM_POLY); dev_info(dev, "version %x, mode %x, gfp %x\n", - in_be32(&re_priv->re_regs->re_version_id), - in_be32(&re_priv->re_regs->global_config), - in_be32(&re_priv->re_regs->galois_field_config)); + in_be32(&re_regs->re_version_id), + in_be32(&re_regs->global_config), + in_be32(&re_regs->galois_field_config)); dma_dev = &re_priv->dma_dev; dma_dev->dev = dev; diff --git a/drivers/dma/fsl_raid.h b/drivers/dma/fsl_raid.h index 69d743c04973..adbfede330a7 100644 --- a/drivers/dma/fsl_raid.h +++ b/drivers/dma/fsl_raid.h @@ -256,7 +256,7 @@ struct fsl_re_hw_desc { struct fsl_re_drv_private { u8 total_chans; struct dma_device dma_dev; - struct fsl_re_ctrl *re_regs; + void __iomem *base; struct fsl_re_chan *re_jrs[FSL_RE_MAX_CHANS]; struct dma_pool *cf_desc_pool; struct dma_pool *hw_desc_pool; @@ -273,7 +273,7 @@ struct fsl_re_chan { struct device *dev; struct fsl_re_drv_private *re_dev; struct dma_chan chan; - struct fsl_re_chan_cfg *jrregs; + struct fsl_re_chan_cfg __iomem *jrregs; int irq; struct tasklet_struct irqtask; u32 alloc_count; diff --git a/drivers/dma/hisi_dma.c b/drivers/dma/hisi_dma.c index 28bf818f9aa6..c751a2e49e6d 100644 --- a/drivers/dma/hisi_dma.c +++ b/drivers/dma/hisi_dma.c @@ -983,7 +983,7 @@ static int hisi_dma_probe(struct pci_dev *pdev, const struct pci_device_id *id) hdma_dev = devm_kzalloc(dev, struct_size(hdma_dev, chan, chan_num), GFP_KERNEL); if (!hdma_dev) - return -EINVAL; + return -ENOMEM; hdma_dev->base = pcim_iomap_table(pdev)[PCI_BAR_2]; hdma_dev->pdev = pdev; diff --git a/drivers/dma/mediatek/mtk-uart-apdma.c b/drivers/dma/mediatek/mtk-uart-apdma.c index c269d84d7bd2..f74e9a328588 100644 --- a/drivers/dma/mediatek/mtk-uart-apdma.c +++ b/drivers/dma/mediatek/mtk-uart-apdma.c @@ -531,7 +531,7 @@ static int mtk_uart_apdma_probe(struct platform_device *pdev) for (i = 0; i < mtkd->dma_requests; i++) { c = devm_kzalloc(mtkd->ddev.dev, sizeof(*c), GFP_KERNEL); if (!c) { - rc = -ENODEV; + rc = -ENOMEM; goto err_no_dma; } diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 25ba84b18704..6214d9000db8 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -502,6 +502,7 @@ struct pl330_dmac { struct dma_pl330_chan *peripherals; /* keep at end */ int quirks; + struct dentry *dbgfs; struct reset_control *rstc; struct reset_control *rstc_ocp; }; @@ -2952,14 +2953,24 @@ DEFINE_SHOW_ATTRIBUTE(pl330_debugfs); static inline void init_pl330_debugfs(struct pl330_dmac *pl330) { - debugfs_create_file(dev_name(pl330->ddma.dev), - S_IFREG | 0444, NULL, pl330, - &pl330_debugfs_fops); + pl330->dbgfs = debugfs_create_file(dev_name(pl330->ddma.dev), + S_IFREG | 0444, NULL, pl330, + &pl330_debugfs_fops); +} + +static inline void deinit_pl330_debugfs(struct pl330_dmac *pl330) +{ + debugfs_remove(pl330->dbgfs); + pl330->dbgfs = NULL; } #else static inline void init_pl330_debugfs(struct pl330_dmac *pl330) { } + +static inline void deinit_pl330_debugfs(struct pl330_dmac *pl330) +{ +} #endif /* @@ -3204,6 +3215,8 @@ static void pl330_remove(struct amba_device *adev) struct dma_pl330_chan *pch, *_p; int i, irq; + deinit_pl330_debugfs(pl330); + pm_runtime_get_noresume(pl330->ddma.dev); if (adev->dev.of_node) diff --git a/drivers/dma/tegra210-adma.c b/drivers/dma/tegra210-adma.c index ceaee1e33e68..21a381d022cf 100644 --- a/drivers/dma/tegra210-adma.c +++ b/drivers/dma/tegra210-adma.c @@ -1087,15 +1087,9 @@ static int tegra_adma_probe(struct platform_device *pdev) } } else { /* If no 'page' property found, then reg DT binding would be legacy */ - res_base = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res_base) { - tdma->base_addr = devm_ioremap_resource(&pdev->dev, res_base); - if (IS_ERR(tdma->base_addr)) - return PTR_ERR(tdma->base_addr); - } else { - return dev_err_probe(&pdev->dev, -ENODEV, - "failed to get memory resource\n"); - } + tdma->base_addr = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(tdma->base_addr)) + return PTR_ERR(tdma->base_addr); tdma->ch_base_addr = tdma->base_addr + cdata->ch_base_offset; } diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c index 1cf158eb7bdb..fb21e0df5ab7 100644 --- a/drivers/dma/ti/k3-udma.c +++ b/drivers/dma/ti/k3-udma.c @@ -3408,7 +3408,7 @@ static int udma_set_metadata_len(struct dma_async_tx_descriptor *desc, return 0; } -static struct dma_descriptor_metadata_ops metadata_ops = { +static const struct dma_descriptor_metadata_ops metadata_ops = { .attach = udma_attach_metadata, .get_ptr = udma_get_metadata_ptr, .set_len = udma_set_metadata_len, diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c index 404235c17353..bef2b031dba1 100644 --- a/drivers/dma/xilinx/xilinx_dma.c +++ b/drivers/dma/xilinx/xilinx_dma.c @@ -165,8 +165,10 @@ #define XILINX_DMA_FLUSH_MM2S 2 #define XILINX_DMA_FLUSH_BOTH 1 -/* Delay loop counter to prevent hardware failure */ -#define XILINX_DMA_LOOP_COUNT 1000000 +/* Timeout for polling various registers */ +#define XILINX_DMA_POLL_TIMEOUT_US 1000000 +/* Delay between polls (avoid a delay of 0 to prevent CPU stalls) */ +#define XILINX_DMA_POLL_DELAY_US 10 /* AXI DMA Specific Registers/Offsets */ #define XILINX_DMA_REG_SRCDSTADDR 0x18 @@ -653,7 +655,7 @@ static void *xilinx_dma_get_metadata_ptr(struct dma_async_tx_descriptor *tx, return seg->hw.app; } -static struct dma_descriptor_metadata_ops xilinx_dma_metadata_ops = { +static const struct dma_descriptor_metadata_ops xilinx_dma_metadata_ops = { .get_ptr = xilinx_dma_get_metadata_ptr, }; @@ -1324,8 +1326,9 @@ static int xilinx_dma_stop_transfer(struct xilinx_dma_chan *chan) /* Wait for the hardware to halt */ return xilinx_dma_poll_timeout(chan, XILINX_DMA_REG_DMASR, val, - val & XILINX_DMA_DMASR_HALTED, 0, - XILINX_DMA_LOOP_COUNT); + val & XILINX_DMA_DMASR_HALTED, + XILINX_DMA_POLL_DELAY_US, + XILINX_DMA_POLL_TIMEOUT_US); } /** @@ -1339,8 +1342,9 @@ static int xilinx_cdma_stop_transfer(struct xilinx_dma_chan *chan) u32 val; return xilinx_dma_poll_timeout(chan, XILINX_DMA_REG_DMASR, val, - val & XILINX_DMA_DMASR_IDLE, 0, - XILINX_DMA_LOOP_COUNT); + val & XILINX_DMA_DMASR_IDLE, + XILINX_DMA_POLL_DELAY_US, + XILINX_DMA_POLL_TIMEOUT_US); } /** @@ -1356,8 +1360,9 @@ static void xilinx_dma_start(struct xilinx_dma_chan *chan) /* Wait for the hardware to start */ err = xilinx_dma_poll_timeout(chan, XILINX_DMA_REG_DMASR, val, - !(val & XILINX_DMA_DMASR_HALTED), 0, - XILINX_DMA_LOOP_COUNT); + !(val & XILINX_DMA_DMASR_HALTED), + XILINX_DMA_POLL_DELAY_US, + XILINX_DMA_POLL_TIMEOUT_US); if (err) { dev_err(chan->dev, "Cannot start channel %p: %x\n", @@ -1580,7 +1585,14 @@ static void xilinx_dma_start_transfer(struct xilinx_dma_chan *chan) return; } - if (!chan->idle) + /* + * Direct (non-SG) mode has no descriptor queue: writing the BTT + * register launches a transfer immediately, so a new transfer must + * not be programmed while one is in flight. Keep such transfers + * serialized. SG mode supports chaining onto a running transfer via + * tail-pointer extension, so it is allowed to proceed when busy. + */ + if (!chan->has_sg && !chan->idle) return; head_desc = list_first_entry(&chan->pending_list, @@ -1596,10 +1608,9 @@ static void xilinx_dma_start_transfer(struct xilinx_dma_chan *chan) reg &= ~XILINX_DMA_CR_COALESCE_MAX; reg |= chan->desc_pendingcount << XILINX_DMA_CR_COALESCE_SHIFT; - dma_ctrl_write(chan, XILINX_DMA_REG_DMACR, reg); } - if (chan->has_sg) + if (chan->has_sg && list_empty(&chan->active_list)) xilinx_write(chan, XILINX_DMA_REG_CURDESC, head_desc->async_tx.phys); reg &= ~XILINX_DMA_CR_DELAY_MAX; @@ -1607,7 +1618,8 @@ static void xilinx_dma_start_transfer(struct xilinx_dma_chan *chan) reg |= XILINX_DMA_DMAXR_ALL_IRQ_MASK; dma_ctrl_write(chan, XILINX_DMA_REG_DMACR, reg); - xilinx_dma_start(chan); + if (chan->idle) + xilinx_dma_start(chan); if (chan->err) return; @@ -1660,9 +1672,6 @@ static void xilinx_mcdma_start_transfer(struct xilinx_dma_chan *chan) if (chan->err) return; - if (!chan->idle) - return; - if (list_empty(&chan->pending_list)) return; @@ -1685,8 +1694,9 @@ static void xilinx_mcdma_start_transfer(struct xilinx_dma_chan *chan) dma_ctrl_write(chan, XILINX_MCDMA_CHAN_CR_OFFSET(chan->tdest), reg); /* Program current descriptor */ - xilinx_write(chan, XILINX_MCDMA_CHAN_CDESC_OFFSET(chan->tdest), - head_desc->async_tx.phys); + if (chan->has_sg && list_empty(&chan->active_list)) + xilinx_write(chan, XILINX_MCDMA_CHAN_CDESC_OFFSET(chan->tdest), + head_desc->async_tx.phys); /* Program channel enable register */ reg = dma_ctrl_read(chan, XILINX_MCDMA_CHEN_OFFSET); @@ -1698,7 +1708,8 @@ static void xilinx_mcdma_start_transfer(struct xilinx_dma_chan *chan) reg |= XILINX_MCDMA_CR_RUNSTOP_MASK; dma_ctrl_write(chan, XILINX_MCDMA_CHAN_CR_OFFSET(chan->tdest), reg); - xilinx_dma_start(chan); + if (chan->idle) + xilinx_dma_start(chan); if (chan->err) return; @@ -1794,8 +1805,9 @@ static int xilinx_dma_reset(struct xilinx_dma_chan *chan) /* Wait for the hardware to finish reset */ err = xilinx_dma_poll_timeout(chan, XILINX_DMA_REG_DMACR, tmp, - !(tmp & XILINX_DMA_DMACR_RESET), 0, - XILINX_DMA_LOOP_COUNT); + !(tmp & XILINX_DMA_DMACR_RESET), + XILINX_DMA_POLL_DELAY_US, + XILINX_DMA_POLL_TIMEOUT_US); if (err) { dev_err(chan->dev, "reset timeout, cr %x, sr %x\n", @@ -1893,8 +1905,10 @@ static irqreturn_t xilinx_mcdma_irq_handler(int irq, void *data) if (status & XILINX_MCDMA_IRQ_IOC_MASK) { spin_lock(&chan->lock); xilinx_dma_complete_descriptor(chan); - chan->idle = true; - chan->start_transfer(chan); + if (list_empty(&chan->active_list)) { + chan->idle = true; + chan->start_transfer(chan); + } spin_unlock(&chan->lock); } @@ -1950,8 +1964,10 @@ static irqreturn_t xilinx_dma_irq_handler(int irq, void *data) XILINX_DMA_DMASR_DLY_CNT_IRQ)) { spin_lock(&chan->lock); xilinx_dma_complete_descriptor(chan); - chan->idle = true; - chan->start_transfer(chan); + if (list_empty(&chan->active_list)) { + chan->idle = true; + chan->start_transfer(chan); + } spin_unlock(&chan->lock); } diff --git a/drivers/dma/xilinx/zynqmp_dma.c b/drivers/dma/xilinx/zynqmp_dma.c index f6a812e49ddc..2b03584102ac 100644 --- a/drivers/dma/xilinx/zynqmp_dma.c +++ b/drivers/dma/xilinx/zynqmp_dma.c @@ -1159,8 +1159,6 @@ err_disable_pm: /** * zynqmp_dma_remove - Driver remove function * @pdev: Pointer to the platform_device structure - * - * Return: Always '0' */ static void zynqmp_dma_remove(struct platform_device *pdev) { @@ -1170,9 +1168,9 @@ static void zynqmp_dma_remove(struct platform_device *pdev) dma_async_device_unregister(&zdev->common); zynqmp_dma_chan_remove(zdev->chan); - if (pm_runtime_active(zdev->dev)) - zynqmp_dma_runtime_suspend(zdev->dev); pm_runtime_disable(zdev->dev); + if (!pm_runtime_status_suspended(zdev->dev)) + zynqmp_dma_runtime_suspend(zdev->dev); } static const struct of_device_id zynqmp_dma_of_match[] = { diff --git a/drivers/nvme/target/pci-epf.c b/drivers/nvme/target/pci-epf.c index 4e9db96ebfec..b1ba2d0bea6d 100644 --- a/drivers/nvme/target/pci-epf.c +++ b/drivers/nvme/target/pci-epf.c @@ -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 @@ out_dma_no_tx: 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; - - ret = dmaengine_slave_config(chan, &sconf); - if (ret) { - dev_err(dev, "Failed to configure DMA channel\n"); - goto unmap; - } + return ret; - 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; } diff --git a/drivers/pci/endpoint/functions/pci-epf-mhi.c b/drivers/pci/endpoint/functions/pci-epf-mhi.c index 7f5326925ed5..c3e3b58fb86c 100644 --- a/drivers/pci/endpoint/functions/pci-epf-mhi.c +++ b/drivers/pci/endpoint/functions/pci-epf-mhi.c @@ -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; diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index 4802d4f80f78..d4905aa8e4c0 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -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); |
