mirror of
https://github.com/torvalds/linux.git
synced 2024-11-14 16:12:02 +00:00
dmaengine fixes for v5.12
Couple of dmaengine driver fixes for: - race and descriptor issue for xilinx driver - fix interrupt handling, wq state & cleanup, field sizes for completion, msix permissions for idxd driver - rumtim pm fix for tegra driver - double free fix in dma_async_device_register -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEE+vs47OPLdNbVcHzyfBQHDyUjg0cFAmB3F20ACgkQfBQHDyUj g0d4GhAAw24ujgoKFdtV11h8eVyHwbsP45saRD8FVZ81oQ/3PI60rwnfuBNRnv4y KkWxQj0TsV1XezJ2t16Ovoq/9y8m/x5pWgP0LxwYj463P8PP40p31WZJ1qaAcSCp elau7TaoUSPW5HmD+Rv9IKpzVwLR9VLu0nqL22aJV62zouTJ0CCBFC/tJO+qrnmx vsa8kE3B85M+KzONUlmYstclgYEMaH2IROnrY0fhY68Txg2BENl9+mT69YaY6btb UABhKCUlfHYlXqCZNNsXlPo0E+n/jjVnTUceblCWWMwz3bPuYlc2KQLz49Nd3dWQ CRj2WQ9YWBXcPntUOicLMOFEUiAyUEC+bRssCfLofqKltRDXI5vyq6Wdo23L1Dz5 eXLqI9DgccX4uoDQNp72AIPR7WO2pJnWjeLf7+tiXQ+htyr/SLfaZm2jciI4DcvK NLczhwMo0aWb50XYPI1iyN+Rhohq/s724xHPx5JrlxJlacuXwHIqUs27GrMpdEQv 7Cpn4Hb0orPVjDeMQLFUcN8L7OmxC3M+vfTTl35ACI0EyAaeMW5Bm8SsKY8FjmFz f6OioKE7wDlfUnJqqQyYaZoeaW40ofGJoVSB6cs3XyJXH6PNYoVD+rKw5joS06Jt PbKkSJm3ZyV/m4Dd+GALRqntpHuYB4AaJbDI/blPzRzCJj4olJI= =GduB -----END PGP SIGNATURE----- Merge tag 'dmaengine-fix-5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/dmaengine Pull dmaengine fixes from Vinod Koul: "A couple of dmaengine driver fixes for: - race and descriptor issue for xilinx driver - fix interrupt handling, wq state & cleanup, field sizes for completion, msix permissions for idxd driver - runtime pm fix for tegra driver - double free fix in dma_async_device_register" * tag 'dmaengine-fix-5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/dmaengine: dmaengine: idxd: fix wq cleanup of WQCFG registers dmaengine: idxd: clear MSIX permission entry on shutdown dmaengine: plx_dma: add a missing put_device() on error path dmaengine: tegra20: Fix runtime PM imbalance on error dmaengine: Fix a double free in dma_async_device_register dmaengine: dw: Make it dependent to HAS_IOMEM dmaengine: idxd: fix wq size store permission state dmaengine: idxd: fix opcap sysfs attribute output dmaengine: idxd: fix delta_rec and crc size field for completion record dmaengine: idxd: Fix clobbering of SWERR overflow bit on writeback dmaengine: xilinx: dpdma: Fix race condition in done IRQ dmaengine: xilinx: dpdma: Fix descriptor issuing on video group
This commit is contained in:
commit
c17a3066b4
@ -1086,6 +1086,7 @@ static int __dma_async_device_channel_register(struct dma_device *device,
|
||||
kfree(chan->dev);
|
||||
err_free_local:
|
||||
free_percpu(chan->local);
|
||||
chan->local = NULL;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ config DW_DMAC_CORE
|
||||
|
||||
config DW_DMAC
|
||||
tristate "Synopsys DesignWare AHB DMA platform driver"
|
||||
depends on HAS_IOMEM
|
||||
select DW_DMAC_CORE
|
||||
help
|
||||
Support the Synopsys DesignWare AHB DMA controller. This
|
||||
@ -18,6 +19,7 @@ config DW_DMAC
|
||||
config DW_DMAC_PCI
|
||||
tristate "Synopsys DesignWare AHB DMA PCI driver"
|
||||
depends on PCI
|
||||
depends on HAS_IOMEM
|
||||
select DW_DMAC_CORE
|
||||
help
|
||||
Support the Synopsys DesignWare AHB DMA controller on the
|
||||
|
@ -282,6 +282,22 @@ void idxd_wq_drain(struct idxd_wq *wq)
|
||||
idxd_cmd_exec(idxd, IDXD_CMD_DRAIN_WQ, operand, NULL);
|
||||
}
|
||||
|
||||
void idxd_wq_reset(struct idxd_wq *wq)
|
||||
{
|
||||
struct idxd_device *idxd = wq->idxd;
|
||||
struct device *dev = &idxd->pdev->dev;
|
||||
u32 operand;
|
||||
|
||||
if (wq->state != IDXD_WQ_ENABLED) {
|
||||
dev_dbg(dev, "WQ %d in wrong state: %d\n", wq->id, wq->state);
|
||||
return;
|
||||
}
|
||||
|
||||
operand = BIT(wq->id % 16) | ((wq->id / 16) << 16);
|
||||
idxd_cmd_exec(idxd, IDXD_CMD_RESET_WQ, operand, NULL);
|
||||
wq->state = IDXD_WQ_DISABLED;
|
||||
}
|
||||
|
||||
int idxd_wq_map_portal(struct idxd_wq *wq)
|
||||
{
|
||||
struct idxd_device *idxd = wq->idxd;
|
||||
@ -363,8 +379,6 @@ int idxd_wq_disable_pasid(struct idxd_wq *wq)
|
||||
void idxd_wq_disable_cleanup(struct idxd_wq *wq)
|
||||
{
|
||||
struct idxd_device *idxd = wq->idxd;
|
||||
struct device *dev = &idxd->pdev->dev;
|
||||
int i, wq_offset;
|
||||
|
||||
lockdep_assert_held(&idxd->dev_lock);
|
||||
memset(wq->wqcfg, 0, idxd->wqcfg_size);
|
||||
@ -376,14 +390,6 @@ void idxd_wq_disable_cleanup(struct idxd_wq *wq)
|
||||
wq->ats_dis = 0;
|
||||
clear_bit(WQ_FLAG_DEDICATED, &wq->flags);
|
||||
memset(wq->name, 0, WQ_NAME_SIZE);
|
||||
|
||||
for (i = 0; i < WQCFG_STRIDES(idxd); i++) {
|
||||
wq_offset = WQCFG_OFFSET(idxd, wq->id, i);
|
||||
iowrite32(0, idxd->reg_base + wq_offset);
|
||||
dev_dbg(dev, "WQ[%d][%d][%#x]: %#x\n",
|
||||
wq->id, i, wq_offset,
|
||||
ioread32(idxd->reg_base + wq_offset));
|
||||
}
|
||||
}
|
||||
|
||||
/* Device control bits */
|
||||
@ -574,6 +580,36 @@ void idxd_device_drain_pasid(struct idxd_device *idxd, int pasid)
|
||||
}
|
||||
|
||||
/* Device configuration bits */
|
||||
void idxd_msix_perm_setup(struct idxd_device *idxd)
|
||||
{
|
||||
union msix_perm mperm;
|
||||
int i, msixcnt;
|
||||
|
||||
msixcnt = pci_msix_vec_count(idxd->pdev);
|
||||
if (msixcnt < 0)
|
||||
return;
|
||||
|
||||
mperm.bits = 0;
|
||||
mperm.pasid = idxd->pasid;
|
||||
mperm.pasid_en = device_pasid_enabled(idxd);
|
||||
for (i = 1; i < msixcnt; i++)
|
||||
iowrite32(mperm.bits, idxd->reg_base + idxd->msix_perm_offset + i * 8);
|
||||
}
|
||||
|
||||
void idxd_msix_perm_clear(struct idxd_device *idxd)
|
||||
{
|
||||
union msix_perm mperm;
|
||||
int i, msixcnt;
|
||||
|
||||
msixcnt = pci_msix_vec_count(idxd->pdev);
|
||||
if (msixcnt < 0)
|
||||
return;
|
||||
|
||||
mperm.bits = 0;
|
||||
for (i = 1; i < msixcnt; i++)
|
||||
iowrite32(mperm.bits, idxd->reg_base + idxd->msix_perm_offset + i * 8);
|
||||
}
|
||||
|
||||
static void idxd_group_config_write(struct idxd_group *group)
|
||||
{
|
||||
struct idxd_device *idxd = group->idxd;
|
||||
@ -642,7 +678,14 @@ static int idxd_wq_config_write(struct idxd_wq *wq)
|
||||
if (!wq->group)
|
||||
return 0;
|
||||
|
||||
memset(wq->wqcfg, 0, idxd->wqcfg_size);
|
||||
/*
|
||||
* Instead of memset the entire shadow copy of WQCFG, copy from the hardware after
|
||||
* wq reset. This will copy back the sticky values that are present on some devices.
|
||||
*/
|
||||
for (i = 0; i < WQCFG_STRIDES(idxd); i++) {
|
||||
wq_offset = WQCFG_OFFSET(idxd, wq->id, i);
|
||||
wq->wqcfg->bits[i] = ioread32(idxd->reg_base + wq_offset);
|
||||
}
|
||||
|
||||
/* byte 0-3 */
|
||||
wq->wqcfg->wq_size = wq->size;
|
||||
|
@ -316,6 +316,8 @@ void idxd_unregister_driver(void);
|
||||
struct bus_type *idxd_get_bus_type(struct idxd_device *idxd);
|
||||
|
||||
/* device interrupt control */
|
||||
void idxd_msix_perm_setup(struct idxd_device *idxd);
|
||||
void idxd_msix_perm_clear(struct idxd_device *idxd);
|
||||
irqreturn_t idxd_irq_handler(int vec, void *data);
|
||||
irqreturn_t idxd_misc_thread(int vec, void *data);
|
||||
irqreturn_t idxd_wq_thread(int irq, void *data);
|
||||
@ -341,6 +343,7 @@ void idxd_wq_free_resources(struct idxd_wq *wq);
|
||||
int idxd_wq_enable(struct idxd_wq *wq);
|
||||
int idxd_wq_disable(struct idxd_wq *wq);
|
||||
void idxd_wq_drain(struct idxd_wq *wq);
|
||||
void idxd_wq_reset(struct idxd_wq *wq);
|
||||
int idxd_wq_map_portal(struct idxd_wq *wq);
|
||||
void idxd_wq_unmap_portal(struct idxd_wq *wq);
|
||||
void idxd_wq_disable_cleanup(struct idxd_wq *wq);
|
||||
|
@ -65,7 +65,6 @@ static int idxd_setup_interrupts(struct idxd_device *idxd)
|
||||
struct idxd_irq_entry *irq_entry;
|
||||
int i, msixcnt;
|
||||
int rc = 0;
|
||||
union msix_perm mperm;
|
||||
|
||||
msixcnt = pci_msix_vec_count(pdev);
|
||||
if (msixcnt < 0) {
|
||||
@ -144,14 +143,7 @@ static int idxd_setup_interrupts(struct idxd_device *idxd)
|
||||
}
|
||||
|
||||
idxd_unmask_error_interrupts(idxd);
|
||||
|
||||
/* Setup MSIX permission table */
|
||||
mperm.bits = 0;
|
||||
mperm.pasid = idxd->pasid;
|
||||
mperm.pasid_en = device_pasid_enabled(idxd);
|
||||
for (i = 1; i < msixcnt; i++)
|
||||
iowrite32(mperm.bits, idxd->reg_base + idxd->msix_perm_offset + i * 8);
|
||||
|
||||
idxd_msix_perm_setup(idxd);
|
||||
return 0;
|
||||
|
||||
err_no_irq:
|
||||
@ -510,6 +502,7 @@ static void idxd_shutdown(struct pci_dev *pdev)
|
||||
idxd_flush_work_list(irq_entry);
|
||||
}
|
||||
|
||||
idxd_msix_perm_clear(idxd);
|
||||
destroy_workqueue(idxd->wq);
|
||||
}
|
||||
|
||||
|
@ -124,7 +124,9 @@ static int process_misc_interrupts(struct idxd_device *idxd, u32 cause)
|
||||
for (i = 0; i < 4; i++)
|
||||
idxd->sw_err.bits[i] = ioread64(idxd->reg_base +
|
||||
IDXD_SWERR_OFFSET + i * sizeof(u64));
|
||||
iowrite64(IDXD_SWERR_ACK, idxd->reg_base + IDXD_SWERR_OFFSET);
|
||||
|
||||
iowrite64(idxd->sw_err.bits[0] & IDXD_SWERR_ACK,
|
||||
idxd->reg_base + IDXD_SWERR_OFFSET);
|
||||
|
||||
if (idxd->sw_err.valid && idxd->sw_err.wq_idx_valid) {
|
||||
int id = idxd->sw_err.wq_idx;
|
||||
|
@ -275,7 +275,6 @@ static void disable_wq(struct idxd_wq *wq)
|
||||
{
|
||||
struct idxd_device *idxd = wq->idxd;
|
||||
struct device *dev = &idxd->pdev->dev;
|
||||
int rc;
|
||||
|
||||
mutex_lock(&wq->wq_lock);
|
||||
dev_dbg(dev, "%s removing WQ %s\n", __func__, dev_name(&wq->conf_dev));
|
||||
@ -296,17 +295,13 @@ static void disable_wq(struct idxd_wq *wq)
|
||||
idxd_wq_unmap_portal(wq);
|
||||
|
||||
idxd_wq_drain(wq);
|
||||
rc = idxd_wq_disable(wq);
|
||||
idxd_wq_reset(wq);
|
||||
|
||||
idxd_wq_free_resources(wq);
|
||||
wq->client_count = 0;
|
||||
mutex_unlock(&wq->wq_lock);
|
||||
|
||||
if (rc < 0)
|
||||
dev_warn(dev, "Failed to disable %s: %d\n",
|
||||
dev_name(&wq->conf_dev), rc);
|
||||
else
|
||||
dev_info(dev, "wq %s disabled\n", dev_name(&wq->conf_dev));
|
||||
dev_info(dev, "wq %s disabled\n", dev_name(&wq->conf_dev));
|
||||
}
|
||||
|
||||
static int idxd_config_bus_remove(struct device *dev)
|
||||
@ -989,7 +984,7 @@ static ssize_t wq_size_store(struct device *dev,
|
||||
if (!test_bit(IDXD_FLAG_CONFIGURABLE, &idxd->flags))
|
||||
return -EPERM;
|
||||
|
||||
if (wq->state != IDXD_WQ_DISABLED)
|
||||
if (idxd->state == IDXD_DEV_ENABLED)
|
||||
return -EPERM;
|
||||
|
||||
if (size + total_claimed_wq_size(idxd) - wq->size > idxd->max_wq_size)
|
||||
@ -1449,8 +1444,14 @@ static ssize_t op_cap_show(struct device *dev,
|
||||
{
|
||||
struct idxd_device *idxd =
|
||||
container_of(dev, struct idxd_device, conf_dev);
|
||||
int i, rc = 0;
|
||||
|
||||
return sprintf(buf, "%#llx\n", idxd->hw.opcap.bits[0]);
|
||||
for (i = 0; i < 4; i++)
|
||||
rc += sysfs_emit_at(buf, rc, "%#llx ", idxd->hw.opcap.bits[i]);
|
||||
|
||||
rc--;
|
||||
rc += sysfs_emit_at(buf, rc, "\n");
|
||||
return rc;
|
||||
}
|
||||
static DEVICE_ATTR_RO(op_cap);
|
||||
|
||||
|
@ -507,10 +507,8 @@ static int plx_dma_create(struct pci_dev *pdev)
|
||||
|
||||
rc = request_irq(pci_irq_vector(pdev, 0), plx_dma_isr, 0,
|
||||
KBUILD_MODNAME, plxdev);
|
||||
if (rc) {
|
||||
kfree(plxdev);
|
||||
return rc;
|
||||
}
|
||||
if (rc)
|
||||
goto free_plx;
|
||||
|
||||
spin_lock_init(&plxdev->ring_lock);
|
||||
tasklet_setup(&plxdev->desc_task, plx_dma_desc_task);
|
||||
@ -540,14 +538,20 @@ static int plx_dma_create(struct pci_dev *pdev)
|
||||
rc = dma_async_device_register(dma);
|
||||
if (rc) {
|
||||
pci_err(pdev, "Failed to register dma device: %d\n", rc);
|
||||
free_irq(pci_irq_vector(pdev, 0), plxdev);
|
||||
kfree(plxdev);
|
||||
return rc;
|
||||
goto put_device;
|
||||
}
|
||||
|
||||
pci_set_drvdata(pdev, plxdev);
|
||||
|
||||
return 0;
|
||||
|
||||
put_device:
|
||||
put_device(&pdev->dev);
|
||||
free_irq(pci_irq_vector(pdev, 0), plxdev);
|
||||
free_plx:
|
||||
kfree(plxdev);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int plx_dma_probe(struct pci_dev *pdev,
|
||||
|
@ -723,7 +723,7 @@ static void tegra_dma_issue_pending(struct dma_chan *dc)
|
||||
goto end;
|
||||
}
|
||||
if (!tdc->busy) {
|
||||
err = pm_runtime_get_sync(tdc->tdma->dev);
|
||||
err = pm_runtime_resume_and_get(tdc->tdma->dev);
|
||||
if (err < 0) {
|
||||
dev_err(tdc2dev(tdc), "Failed to enable DMA\n");
|
||||
goto end;
|
||||
@ -818,7 +818,7 @@ static void tegra_dma_synchronize(struct dma_chan *dc)
|
||||
struct tegra_dma_channel *tdc = to_tegra_dma_chan(dc);
|
||||
int err;
|
||||
|
||||
err = pm_runtime_get_sync(tdc->tdma->dev);
|
||||
err = pm_runtime_resume_and_get(tdc->tdma->dev);
|
||||
if (err < 0) {
|
||||
dev_err(tdc2dev(tdc), "Failed to synchronize DMA: %d\n", err);
|
||||
return;
|
||||
|
@ -839,6 +839,7 @@ static void xilinx_dpdma_chan_queue_transfer(struct xilinx_dpdma_chan *chan)
|
||||
struct xilinx_dpdma_tx_desc *desc;
|
||||
struct virt_dma_desc *vdesc;
|
||||
u32 reg, channels;
|
||||
bool first_frame;
|
||||
|
||||
lockdep_assert_held(&chan->lock);
|
||||
|
||||
@ -852,14 +853,6 @@ static void xilinx_dpdma_chan_queue_transfer(struct xilinx_dpdma_chan *chan)
|
||||
chan->running = true;
|
||||
}
|
||||
|
||||
if (chan->video_group)
|
||||
channels = xilinx_dpdma_chan_video_group_ready(chan);
|
||||
else
|
||||
channels = BIT(chan->id);
|
||||
|
||||
if (!channels)
|
||||
return;
|
||||
|
||||
vdesc = vchan_next_desc(&chan->vchan);
|
||||
if (!vdesc)
|
||||
return;
|
||||
@ -884,13 +877,26 @@ static void xilinx_dpdma_chan_queue_transfer(struct xilinx_dpdma_chan *chan)
|
||||
FIELD_PREP(XILINX_DPDMA_CH_DESC_START_ADDRE_MASK,
|
||||
upper_32_bits(sw_desc->dma_addr)));
|
||||
|
||||
if (chan->first_frame)
|
||||
first_frame = chan->first_frame;
|
||||
chan->first_frame = false;
|
||||
|
||||
if (chan->video_group) {
|
||||
channels = xilinx_dpdma_chan_video_group_ready(chan);
|
||||
/*
|
||||
* Trigger the transfer only when all channels in the group are
|
||||
* ready.
|
||||
*/
|
||||
if (!channels)
|
||||
return;
|
||||
} else {
|
||||
channels = BIT(chan->id);
|
||||
}
|
||||
|
||||
if (first_frame)
|
||||
reg = XILINX_DPDMA_GBL_TRIG_MASK(channels);
|
||||
else
|
||||
reg = XILINX_DPDMA_GBL_RETRIG_MASK(channels);
|
||||
|
||||
chan->first_frame = false;
|
||||
|
||||
dpdma_write(xdev->reg, XILINX_DPDMA_GBL, reg);
|
||||
}
|
||||
|
||||
@ -1042,13 +1048,14 @@ static int xilinx_dpdma_chan_stop(struct xilinx_dpdma_chan *chan)
|
||||
*/
|
||||
static void xilinx_dpdma_chan_done_irq(struct xilinx_dpdma_chan *chan)
|
||||
{
|
||||
struct xilinx_dpdma_tx_desc *active = chan->desc.active;
|
||||
struct xilinx_dpdma_tx_desc *active;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&chan->lock, flags);
|
||||
|
||||
xilinx_dpdma_debugfs_desc_done_irq(chan);
|
||||
|
||||
active = chan->desc.active;
|
||||
if (active)
|
||||
vchan_cyclic_callback(&active->vdesc);
|
||||
else
|
||||
|
@ -247,8 +247,8 @@ struct dsa_completion_record {
|
||||
uint32_t rsvd2:8;
|
||||
};
|
||||
|
||||
uint16_t delta_rec_size;
|
||||
uint16_t crc_val;
|
||||
uint32_t delta_rec_size;
|
||||
uint32_t crc_val;
|
||||
|
||||
/* DIF check & strip */
|
||||
struct {
|
||||
|
Loading…
Reference in New Issue
Block a user