]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - drivers/dma/pl330.c
Merge tag 'dmaengine-4.13-rc1' of git://git.infradead.org/users/vkoul/slave-dma
[mirror_ubuntu-artful-kernel.git] / drivers / dma / pl330.c
index e90a7a0d760af6d031fa465208e88f2dd6f056b4..b19ee04567b57732a6519bd8fa18ace3a1c201ef 100644 (file)
@@ -443,7 +443,10 @@ struct dma_pl330_chan {
        /* For D-to-M and M-to-D channels */
        int burst_sz; /* the peripheral fifo width */
        int burst_len; /* the number of burst */
-       dma_addr_t fifo_addr;
+       phys_addr_t fifo_addr;
+       /* DMA-mapped view of the FIFO; may differ if an IOMMU is present */
+       dma_addr_t fifo_dma;
+       enum dma_data_direction dir;
 
        /* for cyclic capability */
        bool cyclic;
@@ -538,11 +541,6 @@ struct _xfer_spec {
        struct dma_pl330_desc *desc;
 };
 
-static inline bool _queue_empty(struct pl330_thread *thrd)
-{
-       return thrd->req[0].desc == NULL && thrd->req[1].desc == NULL;
-}
-
 static inline bool _queue_full(struct pl330_thread *thrd)
 {
        return thrd->req[0].desc != NULL && thrd->req[1].desc != NULL;
@@ -564,23 +562,6 @@ static inline u32 get_revision(u32 periph_id)
        return (periph_id >> PERIPH_REV_SHIFT) & PERIPH_REV_MASK;
 }
 
-static inline u32 _emit_ADDH(unsigned dry_run, u8 buf[],
-               enum pl330_dst da, u16 val)
-{
-       if (dry_run)
-               return SZ_DMAADDH;
-
-       buf[0] = CMD_DMAADDH;
-       buf[0] |= (da << 1);
-       buf[1] = val;
-       buf[2] = val >> 8;
-
-       PL330_DBGCMD_DUMP(SZ_DMAADDH, "\tDMAADDH %s %u\n",
-               da == 1 ? "DA" : "SA", val);
-
-       return SZ_DMAADDH;
-}
-
 static inline u32 _emit_END(unsigned dry_run, u8 buf[])
 {
        if (dry_run)
@@ -738,18 +719,6 @@ static inline u32 _emit_MOV(unsigned dry_run, u8 buf[],
        return SZ_DMAMOV;
 }
 
-static inline u32 _emit_NOP(unsigned dry_run, u8 buf[])
-{
-       if (dry_run)
-               return SZ_DMANOP;
-
-       buf[0] = CMD_DMANOP;
-
-       PL330_DBGCMD_DUMP(SZ_DMANOP, "\tDMANOP\n");
-
-       return SZ_DMANOP;
-}
-
 static inline u32 _emit_RMB(unsigned dry_run, u8 buf[])
 {
        if (dry_run)
@@ -817,39 +786,6 @@ static inline u32 _emit_STP(unsigned dry_run, u8 buf[],
        return SZ_DMASTP;
 }
 
-static inline u32 _emit_STZ(unsigned dry_run, u8 buf[])
-{
-       if (dry_run)
-               return SZ_DMASTZ;
-
-       buf[0] = CMD_DMASTZ;
-
-       PL330_DBGCMD_DUMP(SZ_DMASTZ, "\tDMASTZ\n");
-
-       return SZ_DMASTZ;
-}
-
-static inline u32 _emit_WFE(unsigned dry_run, u8 buf[], u8 ev,
-               unsigned invalidate)
-{
-       if (dry_run)
-               return SZ_DMAWFE;
-
-       buf[0] = CMD_DMAWFE;
-
-       ev &= 0x1f;
-       ev <<= 3;
-       buf[1] = ev;
-
-       if (invalidate)
-               buf[1] |= (1 << 1);
-
-       PL330_DBGCMD_DUMP(SZ_DMAWFE, "\tDMAWFE %u%s\n",
-               ev >> 3, invalidate ? ", I" : "");
-
-       return SZ_DMAWFE;
-}
-
 static inline u32 _emit_WFP(unsigned dry_run, u8 buf[],
                enum pl330_cond cond, u8 peri)
 {
@@ -2120,11 +2056,60 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan)
        return 1;
 }
 
+/*
+ * We need the data direction between the DMAC (the dma-mapping "device") and
+ * the FIFO (the dmaengine "dev"), from the FIFO's point of view. Confusing!
+ */
+static enum dma_data_direction
+pl330_dma_slave_map_dir(enum dma_transfer_direction dir)
+{
+       switch (dir) {
+       case DMA_MEM_TO_DEV:
+               return DMA_FROM_DEVICE;
+       case DMA_DEV_TO_MEM:
+               return DMA_TO_DEVICE;
+       case DMA_DEV_TO_DEV:
+               return DMA_BIDIRECTIONAL;
+       default:
+               return DMA_NONE;
+       }
+}
+
+static void pl330_unprep_slave_fifo(struct dma_pl330_chan *pch)
+{
+       if (pch->dir != DMA_NONE)
+               dma_unmap_resource(pch->chan.device->dev, pch->fifo_dma,
+                                  1 << pch->burst_sz, pch->dir, 0);
+       pch->dir = DMA_NONE;
+}
+
+
+static bool pl330_prep_slave_fifo(struct dma_pl330_chan *pch,
+                                 enum dma_transfer_direction dir)
+{
+       struct device *dev = pch->chan.device->dev;
+       enum dma_data_direction dma_dir = pl330_dma_slave_map_dir(dir);
+
+       /* Already mapped for this config? */
+       if (pch->dir == dma_dir)
+               return true;
+
+       pl330_unprep_slave_fifo(pch);
+       pch->fifo_dma = dma_map_resource(dev, pch->fifo_addr,
+                                        1 << pch->burst_sz, dma_dir, 0);
+       if (dma_mapping_error(dev, pch->fifo_dma))
+               return false;
+
+       pch->dir = dma_dir;
+       return true;
+}
+
 static int pl330_config(struct dma_chan *chan,
                        struct dma_slave_config *slave_config)
 {
        struct dma_pl330_chan *pch = to_pchan(chan);
 
+       pl330_unprep_slave_fifo(pch);
        if (slave_config->direction == DMA_MEM_TO_DEV) {
                if (slave_config->dst_addr)
                        pch->fifo_addr = slave_config->dst_addr;
@@ -2235,6 +2220,7 @@ static void pl330_free_chan_resources(struct dma_chan *chan)
        spin_unlock_irqrestore(&pl330->lock, flags);
        pm_runtime_mark_last_busy(pch->dmac->ddma.dev);
        pm_runtime_put_autosuspend(pch->dmac->ddma.dev);
+       pl330_unprep_slave_fifo(pch);
 }
 
 static int pl330_get_current_xferred_count(struct dma_pl330_chan *pch,
@@ -2564,6 +2550,9 @@ static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic(
                return NULL;
        }
 
+       if (!pl330_prep_slave_fifo(pch, direction))
+               return NULL;
+
        for (i = 0; i < len / period_len; i++) {
                desc = pl330_get_desc(pch);
                if (!desc) {
@@ -2593,12 +2582,12 @@ static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic(
                        desc->rqcfg.src_inc = 1;
                        desc->rqcfg.dst_inc = 0;
                        src = dma_addr;
-                       dst = pch->fifo_addr;
+                       dst = pch->fifo_dma;
                        break;
                case DMA_DEV_TO_MEM:
                        desc->rqcfg.src_inc = 0;
                        desc->rqcfg.dst_inc = 1;
-                       src = pch->fifo_addr;
+                       src = pch->fifo_dma;
                        dst = dma_addr;
                        break;
                default:
@@ -2711,12 +2700,12 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
        struct dma_pl330_chan *pch = to_pchan(chan);
        struct scatterlist *sg;
        int i;
-       dma_addr_t addr;
 
        if (unlikely(!pch || !sgl || !sg_len))
                return NULL;
 
-       addr = pch->fifo_addr;
+       if (!pl330_prep_slave_fifo(pch, direction))
+               return NULL;
 
        first = NULL;
 
@@ -2742,13 +2731,13 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
                if (direction == DMA_MEM_TO_DEV) {
                        desc->rqcfg.src_inc = 1;
                        desc->rqcfg.dst_inc = 0;
-                       fill_px(&desc->px,
-                               addr, sg_dma_address(sg), sg_dma_len(sg));
+                       fill_px(&desc->px, pch->fifo_dma, sg_dma_address(sg),
+                               sg_dma_len(sg));
                } else {
                        desc->rqcfg.src_inc = 0;
                        desc->rqcfg.dst_inc = 1;
-                       fill_px(&desc->px,
-                               sg_dma_address(sg), addr, sg_dma_len(sg));
+                       fill_px(&desc->px, sg_dma_address(sg), pch->fifo_dma,
+                               sg_dma_len(sg));
                }
 
                desc->rqcfg.brst_size = pch->burst_sz;
@@ -2906,6 +2895,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
                pch->thread = NULL;
                pch->chan.device = pd;
                pch->dmac = pl330;
+               pch->dir = DMA_NONE;
 
                /* Add the channel to the DMAC list */
                list_add_tail(&pch->chan.device_node, &pd->channels);