summaryrefslogtreecommitdiff
path: root/drivers/dma/dma-axi-dmac.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/dma/dma-axi-dmac.c')
-rw-r--r--drivers/dma/dma-axi-dmac.c118
1 files changed, 74 insertions, 44 deletions
diff --git a/drivers/dma/dma-axi-dmac.c b/drivers/dma/dma-axi-dmac.c
index 4e339c04fc1e..36943b0c6d60 100644
--- a/drivers/dma/dma-axi-dmac.c
+++ b/drivers/dma/dma-axi-dmac.c
@@ -620,6 +620,45 @@ static struct axi_dmac_sg *axi_dmac_fill_linear_sg(struct axi_dmac_chan *chan,
return sg;
}
+static struct dma_async_tx_descriptor *
+axi_dmac_prep_peripheral_dma_vec(struct dma_chan *c, const struct dma_vec *vecs,
+ size_t nb, enum dma_transfer_direction direction,
+ unsigned long flags)
+{
+ struct axi_dmac_chan *chan = to_axi_dmac_chan(c);
+ struct axi_dmac_desc *desc;
+ unsigned int num_sgs = 0;
+ struct axi_dmac_sg *dsg;
+ size_t i;
+
+ if (direction != chan->direction)
+ return NULL;
+
+ for (i = 0; i < nb; i++)
+ num_sgs += DIV_ROUND_UP(vecs[i].len, chan->max_length);
+
+ desc = axi_dmac_alloc_desc(chan, num_sgs);
+ if (!desc)
+ return NULL;
+
+ dsg = desc->sg;
+
+ for (i = 0; i < nb; i++) {
+ if (!axi_dmac_check_addr(chan, vecs[i].addr) ||
+ !axi_dmac_check_len(chan, vecs[i].len)) {
+ kfree(desc);
+ return NULL;
+ }
+
+ dsg = axi_dmac_fill_linear_sg(chan, direction, vecs[i].addr, 1,
+ vecs[i].len, dsg);
+ }
+
+ desc->cyclic = false;
+
+ return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
+}
+
static struct dma_async_tx_descriptor *axi_dmac_prep_slave_sg(
struct dma_chan *c, struct scatterlist *sgl,
unsigned int sg_len, enum dma_transfer_direction direction,
@@ -1002,6 +1041,16 @@ static int axi_dmac_detect_caps(struct axi_dmac *dmac, unsigned int version)
return 0;
}
+static void axi_dmac_tasklet_kill(void *task)
+{
+ tasklet_kill(task);
+}
+
+static void axi_dmac_free_dma_controller(void *of_node)
+{
+ of_dma_controller_free(of_node);
+}
+
static int axi_dmac_probe(struct platform_device *pdev)
{
struct dma_device *dma_dev;
@@ -1025,14 +1074,10 @@ static int axi_dmac_probe(struct platform_device *pdev)
if (IS_ERR(dmac->base))
return PTR_ERR(dmac->base);
- dmac->clk = devm_clk_get(&pdev->dev, NULL);
+ dmac->clk = devm_clk_get_enabled(&pdev->dev, NULL);
if (IS_ERR(dmac->clk))
return PTR_ERR(dmac->clk);
- ret = clk_prepare_enable(dmac->clk);
- if (ret < 0)
- return ret;
-
version = axi_dmac_read(dmac, ADI_AXI_REG_VERSION);
if (version >= ADI_AXI_PCORE_VER(4, 3, 'a'))
@@ -1041,7 +1086,7 @@ static int axi_dmac_probe(struct platform_device *pdev)
ret = axi_dmac_parse_dt(&pdev->dev, dmac);
if (ret < 0)
- goto err_clk_disable;
+ return ret;
INIT_LIST_HEAD(&dmac->chan.active_descs);
@@ -1055,6 +1100,7 @@ static int axi_dmac_probe(struct platform_device *pdev)
dma_dev->device_tx_status = dma_cookie_status;
dma_dev->device_issue_pending = axi_dmac_issue_pending;
dma_dev->device_prep_slave_sg = axi_dmac_prep_slave_sg;
+ dma_dev->device_prep_peripheral_dma_vec = axi_dmac_prep_peripheral_dma_vec;
dma_dev->device_prep_dma_cyclic = axi_dmac_prep_dma_cyclic;
dma_dev->device_prep_interleaved_dma = axi_dmac_prep_interleaved;
dma_dev->device_terminate_all = axi_dmac_terminate_all;
@@ -1072,7 +1118,7 @@ static int axi_dmac_probe(struct platform_device *pdev)
ret = axi_dmac_detect_caps(dmac, version);
if (ret)
- goto err_clk_disable;
+ return ret;
dma_dev->copy_align = (dmac->chan.address_align_mask + 1);
@@ -1088,57 +1134,42 @@ static int axi_dmac_probe(struct platform_device *pdev)
!AXI_DMAC_DST_COHERENT_GET(ret)) {
dev_err(dmac->dma_dev.dev,
"Coherent DMA not supported in hardware");
- ret = -EINVAL;
- goto err_clk_disable;
+ return -EINVAL;
}
}
- ret = dma_async_device_register(dma_dev);
+ ret = dmaenginem_async_device_register(dma_dev);
if (ret)
- goto err_clk_disable;
+ return ret;
+
+ /*
+ * Put the action in here so it get's done before unregistering the DMA
+ * device.
+ */
+ ret = devm_add_action_or_reset(&pdev->dev, axi_dmac_tasklet_kill,
+ &dmac->chan.vchan.task);
+ if (ret)
+ return ret;
ret = of_dma_controller_register(pdev->dev.of_node,
of_dma_xlate_by_chan_id, dma_dev);
if (ret)
- goto err_unregister_device;
+ return ret;
- ret = request_irq(dmac->irq, axi_dmac_interrupt_handler, IRQF_SHARED,
- dev_name(&pdev->dev), dmac);
+ ret = devm_add_action_or_reset(&pdev->dev, axi_dmac_free_dma_controller,
+ pdev->dev.of_node);
if (ret)
- goto err_unregister_of;
+ return ret;
- platform_set_drvdata(pdev, dmac);
+ ret = devm_request_irq(&pdev->dev, dmac->irq, axi_dmac_interrupt_handler,
+ IRQF_SHARED, dev_name(&pdev->dev), dmac);
+ if (ret)
+ return ret;
regmap = devm_regmap_init_mmio(&pdev->dev, dmac->base,
&axi_dmac_regmap_config);
- if (IS_ERR(regmap)) {
- ret = PTR_ERR(regmap);
- goto err_free_irq;
- }
-
- return 0;
-
-err_free_irq:
- free_irq(dmac->irq, dmac);
-err_unregister_of:
- of_dma_controller_free(pdev->dev.of_node);
-err_unregister_device:
- dma_async_device_unregister(&dmac->dma_dev);
-err_clk_disable:
- clk_disable_unprepare(dmac->clk);
-
- return ret;
-}
-
-static void axi_dmac_remove(struct platform_device *pdev)
-{
- struct axi_dmac *dmac = platform_get_drvdata(pdev);
- of_dma_controller_free(pdev->dev.of_node);
- free_irq(dmac->irq, dmac);
- tasklet_kill(&dmac->chan.vchan.task);
- dma_async_device_unregister(&dmac->dma_dev);
- clk_disable_unprepare(dmac->clk);
+ return PTR_ERR_OR_ZERO(regmap);
}
static const struct of_device_id axi_dmac_of_match_table[] = {
@@ -1153,7 +1184,6 @@ static struct platform_driver axi_dmac_driver = {
.of_match_table = axi_dmac_of_match_table,
},
.probe = axi_dmac_probe,
- .remove_new = axi_dmac_remove,
};
module_platform_driver(axi_dmac_driver);