diff options
author | Vinod Koul <vinod.koul@intel.com> | 2017-07-04 10:05:18 +0530 |
---|---|---|
committer | Vinod Koul <vinod.koul@intel.com> | 2017-07-04 10:05:18 +0530 |
commit | 2e76eba5e0f0e4535868ce41e0a8d28754d0db29 (patch) | |
tree | 9848577a26d20bc7301e744ee37b6e432b67c9b9 /drivers/dma | |
parent | 98cd085ebb20acd3cb6304cd5bf4ef805efbbb87 (diff) | |
parent | d43674ecc002b49926f216cb414cff2d230ca3fb (diff) | |
download | linux-stable-2e76eba5e0f0e4535868ce41e0a8d28754d0db29.tar.gz linux-stable-2e76eba5e0f0e4535868ce41e0a8d28754d0db29.tar.bz2 linux-stable-2e76eba5e0f0e4535868ce41e0a8d28754d0db29.zip |
Merge branch 'topic/pl330' into for-linus
Diffstat (limited to 'drivers/dma')
-rw-r--r-- | drivers/dma/pl330.c | 142 |
1 files changed, 66 insertions, 76 deletions
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 8b0da7fa520d..311ee107e5bf 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -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); |