dmaengine: fsl-edma: dma map slave device address
authorLaurentiu Tudor <laurentiu.tudor@nxp.com>
Fri, 18 Jan 2019 10:06:23 +0000 (12:06 +0200)
committerVinod Koul <vkoul@kernel.org>
Mon, 4 Feb 2019 07:02:53 +0000 (12:32 +0530)
This mapping needs to be created in order for slave dma transfers
to work on systems with SMMU. The implementation mostly mimics the
one in pl330 dma driver, authored by Robin Murphy.

Signed-off-by: Laurentiu Tudor <laurentiu.tudor@nxp.com>
Suggested-by: Robin Murphy <robin.murphy@arm.com>
Tested-by: Angelo Dureghello <angelo@sysam.it>
Signed-off-by: Vinod Koul <vkoul@kernel.org>
drivers/dma/fsl-edma-common.c
drivers/dma/fsl-edma-common.h
drivers/dma/fsl-edma.c
drivers/dma/mcf-edma.c

index fe529100674f6002370b51867e49b86bea42477a..680b2a00a953221547383a082af38f413d063502 100644 (file)
@@ -6,6 +6,7 @@
 #include <linux/dmapool.h>
 #include <linux/module.h>
 #include <linux/slab.h>
+#include <linux/dma-mapping.h>
 
 #include "fsl-edma-common.h"
 
@@ -173,12 +174,62 @@ int fsl_edma_resume(struct dma_chan *chan)
 }
 EXPORT_SYMBOL_GPL(fsl_edma_resume);
 
+static void fsl_edma_unprep_slave_dma(struct fsl_edma_chan *fsl_chan)
+{
+       if (fsl_chan->dma_dir != DMA_NONE)
+               dma_unmap_resource(fsl_chan->vchan.chan.device->dev,
+                                  fsl_chan->dma_dev_addr,
+                                  fsl_chan->dma_dev_size,
+                                  fsl_chan->dma_dir, 0);
+       fsl_chan->dma_dir = DMA_NONE;
+}
+
+static bool fsl_edma_prep_slave_dma(struct fsl_edma_chan *fsl_chan,
+                                   enum dma_transfer_direction dir)
+{
+       struct device *dev = fsl_chan->vchan.chan.device->dev;
+       enum dma_data_direction dma_dir;
+       phys_addr_t addr = 0;
+       u32 size = 0;
+
+       switch (dir) {
+       case DMA_MEM_TO_DEV:
+               dma_dir = DMA_FROM_DEVICE;
+               addr = fsl_chan->cfg.dst_addr;
+               size = fsl_chan->cfg.dst_maxburst;
+               break;
+       case DMA_DEV_TO_MEM:
+               dma_dir = DMA_TO_DEVICE;
+               addr = fsl_chan->cfg.src_addr;
+               size = fsl_chan->cfg.src_maxburst;
+               break;
+       default:
+               dma_dir = DMA_NONE;
+               break;
+       }
+
+       /* Already mapped for this config? */
+       if (fsl_chan->dma_dir == dma_dir)
+               return true;
+
+       fsl_edma_unprep_slave_dma(fsl_chan);
+
+       fsl_chan->dma_dev_addr = dma_map_resource(dev, addr, size, dma_dir, 0);
+       if (dma_mapping_error(dev, fsl_chan->dma_dev_addr))
+               return false;
+       fsl_chan->dma_dev_size = size;
+       fsl_chan->dma_dir = dma_dir;
+
+       return true;
+}
+
 int fsl_edma_slave_config(struct dma_chan *chan,
                                 struct dma_slave_config *cfg)
 {
        struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
 
        memcpy(&fsl_chan->cfg, cfg, sizeof(*cfg));
+       fsl_edma_unprep_slave_dma(fsl_chan);
 
        return 0;
 }
@@ -376,6 +427,9 @@ struct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic(
        if (!is_slave_direction(direction))
                return NULL;
 
+       if (!fsl_edma_prep_slave_dma(fsl_chan, direction))
+               return NULL;
+
        sg_len = buf_len / period_len;
        fsl_desc = fsl_edma_alloc_desc(fsl_chan, sg_len);
        if (!fsl_desc)
@@ -407,11 +461,11 @@ struct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic(
 
                if (direction == DMA_MEM_TO_DEV) {
                        src_addr = dma_buf_next;
-                       dst_addr = fsl_chan->cfg.dst_addr;
+                       dst_addr = fsl_chan->dma_dev_addr;
                        soff = fsl_chan->cfg.dst_addr_width;
                        doff = 0;
                } else {
-                       src_addr = fsl_chan->cfg.src_addr;
+                       src_addr = fsl_chan->dma_dev_addr;
                        dst_addr = dma_buf_next;
                        soff = 0;
                        doff = fsl_chan->cfg.src_addr_width;
@@ -442,6 +496,9 @@ struct dma_async_tx_descriptor *fsl_edma_prep_slave_sg(
        if (!is_slave_direction(direction))
                return NULL;
 
+       if (!fsl_edma_prep_slave_dma(fsl_chan, direction))
+               return NULL;
+
        fsl_desc = fsl_edma_alloc_desc(fsl_chan, sg_len);
        if (!fsl_desc)
                return NULL;
@@ -466,11 +523,11 @@ struct dma_async_tx_descriptor *fsl_edma_prep_slave_sg(
 
                if (direction == DMA_MEM_TO_DEV) {
                        src_addr = sg_dma_address(sg);
-                       dst_addr = fsl_chan->cfg.dst_addr;
+                       dst_addr = fsl_chan->dma_dev_addr;
                        soff = fsl_chan->cfg.dst_addr_width;
                        doff = 0;
                } else {
-                       src_addr = fsl_chan->cfg.src_addr;
+                       src_addr = fsl_chan->dma_dev_addr;
                        dst_addr = sg_dma_address(sg);
                        soff = 0;
                        doff = fsl_chan->cfg.src_addr_width;
@@ -553,6 +610,7 @@ void fsl_edma_free_chan_resources(struct dma_chan *chan)
        fsl_edma_chan_mux(fsl_chan, 0, false);
        fsl_chan->edesc = NULL;
        vchan_get_all_descriptors(&fsl_chan->vchan, &head);
+       fsl_edma_unprep_slave_dma(fsl_chan);
        spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
 
        vchan_dma_desc_free_list(&fsl_chan->vchan, &head);
index 8917e8865959a09626bd046addef6b65dfd725fa..b435d8e1e3a1954eb0fd46cb94f4ac18338429f1 100644 (file)
@@ -6,6 +6,7 @@
 #ifndef _FSL_EDMA_COMMON_H_
 #define _FSL_EDMA_COMMON_H_
 
+#include <linux/dma-direction.h>
 #include "virt-dma.h"
 
 #define EDMA_CR_EDBG           BIT(1)
@@ -120,6 +121,9 @@ struct fsl_edma_chan {
        struct dma_slave_config         cfg;
        u32                             attr;
        struct dma_pool                 *tcd_pool;
+       dma_addr_t                      dma_dev_addr;
+       u32                             dma_dev_size;
+       enum dma_data_direction         dma_dir;
 };
 
 struct fsl_edma_desc {
index 34d70112fcc9d028378457743ea574423d3716bc..75e8a7ba3a225b6df86c1a80a2e8e5304fbb6fd1 100644 (file)
@@ -254,6 +254,7 @@ static int fsl_edma_probe(struct platform_device *pdev)
                fsl_chan->pm_state = RUNNING;
                fsl_chan->slave_id = 0;
                fsl_chan->idle = true;
+               fsl_chan->dma_dir = DMA_NONE;
                fsl_chan->vchan.desc_free = fsl_edma_free_desc;
                vchan_init(&fsl_chan->vchan, &fsl_edma->dma_dev);
 
index 5de1b07eddff4912a7506e1e5666878bd57bbb52..7de54b2fafdb5467a2678d8f198b947d0b51876d 100644 (file)
@@ -214,6 +214,7 @@ static int mcf_edma_probe(struct platform_device *pdev)
                mcf_chan->edma = mcf_edma;
                mcf_chan->slave_id = i;
                mcf_chan->idle = true;
+               mcf_chan->dma_dir = DMA_NONE;
                mcf_chan->vchan.desc_free = fsl_edma_free_desc;
                vchan_init(&mcf_chan->vchan, &mcf_edma->dma_dev);
                iowrite32(0x0, &regs->tcd[i].csr);