From d29bf01941795891828bf671f74c3a4f6fc3517f Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 9 Apr 2012 22:53:21 +0200 Subject: dma/amba-pl08x: check for terminal count status only For some reason I can't figure out we're reading the PL080_INT_STATUS register instead of PL080_TC_STATUS when checking for the terminal count. The PL080_INT_STATUS is a logical OR between the error and terminal count status register and may not report what we want it to, especially if there is an error and a terminal count at the same time and the former is not lowered in time for the check in the TC register. Make sure we read what we're actually interested in. Cc: Russell King Cc: Viresh Kumar Cc: Alim Akhtar Signed-off-by: Linus Walleij Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/amba-pl08x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/dma') diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index c301a8ec31aa..08589c683e2b 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -1615,7 +1615,7 @@ static irqreturn_t pl08x_irq(int irq, void *dev) __func__, err); writel(err, pl08x->base + PL080_ERR_CLEAR); } - tc = readl(pl08x->base + PL080_INT_STATUS); + tc = readl(pl08x->base + PL080_TC_STATUS); if (tc) writel(tc, pl08x->base + PL080_TC_CLEAR); -- cgit v1.2.2 From affa115ed365d646ad1a8cc7d2d063b8181cce37 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 12 Apr 2012 09:01:49 +0200 Subject: dma/amba-pl08x: add support for the Nomadik variant The Nomadik PL080 variant has some extra protection bits that may be set, so we need to check these bits to see if the channels are actually available for the DMAengine to use. Cc: Russell King Cc: Alim Akhtar Cc: Alessandro Rubini Reviewed-by: Viresh Kumar Signed-off-by: Linus Walleij Signed-off-by: Vinod Koul --- drivers/dma/amba-pl08x.c | 44 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 7 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 08589c683e2b..629250e36d3b 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -95,10 +95,14 @@ static struct amba_driver pl08x_amba_driver; * struct vendor_data - vendor-specific config parameters for PL08x derivatives * @channels: the number of channels available in this variant * @dualmaster: whether this version supports dual AHB masters or not. + * @nomadik: whether the channels have Nomadik security extension bits + * that need to be checked for permission before use and some registers are + * missing */ struct vendor_data { u8 channels; bool dualmaster; + bool nomadik; }; /* @@ -385,7 +389,7 @@ pl08x_get_phy_channel(struct pl08x_driver_data *pl08x, spin_lock_irqsave(&ch->lock, flags); - if (!ch->serving) { + if (!ch->locked && !ch->serving) { ch->serving = virt_chan; ch->signal = -1; spin_unlock_irqrestore(&ch->lock, flags); @@ -1483,6 +1487,9 @@ bool pl08x_filter_id(struct dma_chan *chan, void *chan_id) */ static void pl08x_ensure_on(struct pl08x_driver_data *pl08x) { + /* The Nomadik variant does not have the config register */ + if (pl08x->vd->nomadik) + return; writel(PL080_CONFIG_ENABLE, pl08x->base + PL080_CONFIG); } @@ -1772,8 +1779,10 @@ static int pl08x_debugfs_show(struct seq_file *s, void *data) spin_lock_irqsave(&ch->lock, flags); virt_chan = ch->serving; - seq_printf(s, "%d\t\t%s\n", - ch->id, virt_chan ? virt_chan->name : "(none)"); + seq_printf(s, "%d\t\t%s%s\n", + ch->id, + virt_chan ? virt_chan->name : "(none)", + ch->locked ? " LOCKED" : ""); spin_unlock_irqrestore(&ch->lock, flags); } @@ -1917,7 +1926,7 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id) } /* Initialize physical channels */ - pl08x->phy_chans = kmalloc((vd->channels * sizeof(*pl08x->phy_chans)), + pl08x->phy_chans = kzalloc((vd->channels * sizeof(*pl08x->phy_chans)), GFP_KERNEL); if (!pl08x->phy_chans) { dev_err(&adev->dev, "%s failed to allocate " @@ -1932,8 +1941,23 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id) ch->id = i; ch->base = pl08x->base + PL080_Cx_BASE(i); spin_lock_init(&ch->lock); - ch->serving = NULL; ch->signal = -1; + + /* + * Nomadik variants can have channels that are locked + * down for the secure world only. Lock up these channels + * by perpetually serving a dummy virtual channel. + */ + if (vd->nomadik) { + u32 val; + + val = readl(ch->base + PL080_CH_CONFIG); + if (val & (PL080N_CONFIG_ITPROT | PL080N_CONFIG_SECPROT)) { + dev_info(&adev->dev, "physical channel %d reserved for secure access only\n", i); + ch->locked = true; + } + } + dev_dbg(&adev->dev, "physical channel %d is %s\n", i, pl08x_phy_channel_busy(ch) ? "BUSY" : "FREE"); } @@ -2016,6 +2040,12 @@ static struct vendor_data vendor_pl080 = { .dualmaster = true, }; +static struct vendor_data vendor_nomadik = { + .channels = 8, + .dualmaster = true, + .nomadik = true, +}; + static struct vendor_data vendor_pl081 = { .channels = 2, .dualmaster = false, @@ -2036,9 +2066,9 @@ static struct amba_id pl08x_ids[] = { }, /* Nomadik 8815 PL080 variant */ { - .id = 0x00280880, + .id = 0x00280080, .mask = 0x00ffffff, - .data = &vendor_pl080, + .data = &vendor_nomadik, }, { 0, 0 }, }; -- cgit v1.2.2 From 3075528d3dd1ac8b729fccf2cbc3119057088223 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 17 Apr 2012 17:10:07 +0530 Subject: dmaengine: dw_dmac: Add clk_{un}prepare() support clk_{un}prepare is mandatory for platforms using common clock framework. Since this driver is used by SPEAr platform, which supports common clock framework, add clk_{un}prepare() support for it. Signed-off-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 7439079f5eed..300d976b6661 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1429,7 +1429,7 @@ static int __init dw_probe(struct platform_device *pdev) err = PTR_ERR(dw->clk); goto err_clk; } - clk_enable(dw->clk); + clk_prepare_enable(dw->clk); /* force dma off, just in case */ dw_dma_off(dw); @@ -1510,7 +1510,7 @@ static int __init dw_probe(struct platform_device *pdev) return 0; err_irq: - clk_disable(dw->clk); + clk_disable_unprepare(dw->clk); clk_put(dw->clk); err_clk: iounmap(dw->regs); @@ -1540,7 +1540,7 @@ static int __exit dw_remove(struct platform_device *pdev) channel_clear_bit(dw, CH_EN, dwc->mask); } - clk_disable(dw->clk); + clk_disable_unprepare(dw->clk); clk_put(dw->clk); iounmap(dw->regs); @@ -1559,7 +1559,7 @@ static void dw_shutdown(struct platform_device *pdev) struct dw_dma *dw = platform_get_drvdata(pdev); dw_dma_off(platform_get_drvdata(pdev)); - clk_disable(dw->clk); + clk_disable_unprepare(dw->clk); } static int dw_suspend_noirq(struct device *dev) @@ -1568,7 +1568,7 @@ static int dw_suspend_noirq(struct device *dev) struct dw_dma *dw = platform_get_drvdata(pdev); dw_dma_off(platform_get_drvdata(pdev)); - clk_disable(dw->clk); + clk_disable_unprepare(dw->clk); return 0; } @@ -1578,7 +1578,7 @@ static int dw_resume_noirq(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct dw_dma *dw = platform_get_drvdata(pdev); - clk_enable(dw->clk); + clk_prepare_enable(dw->clk); dma_writel(dw, CFG, DW_CFG_DMA_EN); return 0; } -- cgit v1.2.2 From d3f797d93e593aa891f5b04a404b4ab45fd0e66a Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 20 Apr 2012 20:15:34 +0530 Subject: dmaengine: dw_dma: add Device Tree probing capability SPEAr platforms now support DT and so must convert all drivers to support DT. This patch adds DT probing support for Synopsys DMA controller and updates its documentation too. Signed-off-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/dma') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 300d976b6661..13b92983b241 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -1592,12 +1593,21 @@ static const struct dev_pm_ops dw_dev_pm_ops = { .poweroff_noirq = dw_suspend_noirq, }; +#ifdef CONFIG_OF +static const struct of_device_id dw_dma_id_table[] = { + { .compatible = "snps,dma-spear1340" }, + {} +}; +MODULE_DEVICE_TABLE(of, dw_dma_id_table); +#endif + static struct platform_driver dw_driver = { .remove = __exit_p(dw_remove), .shutdown = dw_shutdown, .driver = { .name = "dw_dmac", .pm = &dw_dev_pm_ops, + .of_match_table = of_match_ptr(dw_dma_id_table), }, }; -- cgit v1.2.2 From abd9ccc84c35cf1e296335a7b655bba40c92386c Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Sat, 28 Apr 2012 18:15:42 +0800 Subject: dma: imx-sdma: keep the callbacks invoked in the tasklet The current code keeps the callbacks invoked from interrupt context, this does not conform to the Documentation/dmaengine.txt. So add tasklet support to fix this issue. Signed-off-by: Huang Shijie Signed-off-by: Vinod Koul --- drivers/dma/imx-sdma.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index d3e38e28bb6b..5a457777f5c0 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -271,6 +271,7 @@ struct sdma_channel { enum dma_status status; unsigned int chn_count; unsigned int chn_real_count; + struct tasklet_struct tasklet; }; #define IMX_DMA_SG_LOOP BIT(0) @@ -534,8 +535,10 @@ static void mxc_sdma_handle_channel_normal(struct sdma_channel *sdmac) sdmac->desc.callback(sdmac->desc.callback_param); } -static void mxc_sdma_handle_channel(struct sdma_channel *sdmac) +static void sdma_tasklet(unsigned long data) { + struct sdma_channel *sdmac = (struct sdma_channel *) data; + complete(&sdmac->done); /* not interested in channel 0 interrupts */ @@ -560,7 +563,7 @@ static irqreturn_t sdma_int_handler(int irq, void *dev_id) int channel = fls(stat) - 1; struct sdma_channel *sdmac = &sdma->channel[channel]; - mxc_sdma_handle_channel(sdmac); + tasklet_schedule(&sdmac->tasklet); __clear_bit(channel, &stat); } @@ -1359,6 +1362,8 @@ static int __init sdma_probe(struct platform_device *pdev) dma_cookie_init(&sdmac->chan); sdmac->channel = i; + tasklet_init(&sdmac->tasklet, sdma_tasklet, + (unsigned long) sdmac); /* * Add the channel to the DMAC list. Do not add channel 0 though * because we need it internally in the SDMA driver. This also means -- cgit v1.2.2 From 12366ad91adb41a193f93b3b0f2829ea06df1773 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 3 May 2012 15:02:28 +0530 Subject: DMA: PL330: Remove duplicate header file inclusion Removes file which was included twice. Signed-off-by: Sachin Kamat Acked-by: Jassi Brar Signed-off-by: Vinod Koul --- drivers/dma/pl330.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/dma') diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index fa3fb21e60be..cbcc28e79be6 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.2 From cbb796ccd8c33c50249b876d9773dfa8e67d39cb Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 25 Apr 2012 20:50:51 +0200 Subject: dmaengine: Use sg_dma_address instead of sg_phys dmaengine drivers should always use sg_dma_address instead of sg_phys to get the addresses for the transfer from a sg element. To quote Russel King: sg_phys(sg) of course has nothing to do with DMA addresses. It's the physical address _to the CPU_ of the memory associated with the scatterlist entry. That may, or may not have the same value for the DMA engine, particularly if IOMMUs are involved. And if these drivers are used on ARM, they must be fixed, sooner rather than later. There's patches in the works which will mean we will end up with IOMMU support in the DMA mapping later, which means everything I've said above will become reality. The patch has been generated using the following coccinelle patch: @@ struct scatterlist *sg; @@ -sg_phys(sg) +sg_dma_address(sg) Signed-off-by: Lars-Peter Clausen Acked-by: Linus Walleij Signed-off-by: Vinod Koul --- drivers/dma/amba-pl08x.c | 4 ++-- drivers/dma/coh901318_lli.c | 4 ++-- drivers/dma/dw_dmac.c | 4 ++-- drivers/dma/intel_mid_dma.c | 2 +- drivers/dma/pch_dma.c | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index e7d5550266e0..003220a60bcb 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -1382,11 +1382,11 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg( dsg->len = sg_dma_len(sg); if (direction == DMA_MEM_TO_DEV) { - dsg->src_addr = sg_phys(sg); + dsg->src_addr = sg_dma_address(sg); dsg->dst_addr = slave_addr; } else { dsg->src_addr = slave_addr; - dsg->dst_addr = sg_phys(sg); + dsg->dst_addr = sg_dma_address(sg); } } diff --git a/drivers/dma/coh901318_lli.c b/drivers/dma/coh901318_lli.c index 6c0e2d4c6682..780e0429b38c 100644 --- a/drivers/dma/coh901318_lli.c +++ b/drivers/dma/coh901318_lli.c @@ -270,10 +270,10 @@ coh901318_lli_fill_sg(struct coh901318_pool *pool, if (dir == DMA_MEM_TO_DEV) /* increment source address */ - src = sg_phys(sg); + src = sg_dma_address(sg); else /* increment destination address */ - dst = sg_phys(sg); + dst = sg_dma_address(sg); bytes_to_transfer = sg_dma_len(sg); diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 13b92983b241..e23dc82d43ac 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -743,7 +743,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, struct dw_desc *desc; u32 len, dlen, mem; - mem = sg_phys(sg); + mem = sg_dma_address(sg); len = sg_dma_len(sg); if (!((mem | len) & 7)) @@ -810,7 +810,7 @@ slave_sg_todev_fill_desc: struct dw_desc *desc; u32 len, dlen, mem; - mem = sg_phys(sg); + mem = sg_dma_address(sg); len = sg_dma_len(sg); if (!((mem | len) & 7)) diff --git a/drivers/dma/intel_mid_dma.c b/drivers/dma/intel_mid_dma.c index c900ca7aaec4..d0ef5937fbf6 100644 --- a/drivers/dma/intel_mid_dma.c +++ b/drivers/dma/intel_mid_dma.c @@ -398,7 +398,7 @@ static int midc_lli_fill_sg(struct intel_mid_dma_chan *midc, desc->width, midc->dma->block_size); /*Populate SAR and DAR values*/ - sg_phy_addr = sg_phys(sg); + sg_phy_addr = sg_dma_address(sg); if (desc->dirn == DMA_MEM_TO_DEV) { lli_bloc_desc->sar = sg_phy_addr; lli_bloc_desc->dar = mids->dma_slave.dst_addr; diff --git a/drivers/dma/pch_dma.c b/drivers/dma/pch_dma.c index 65c0495a6d40..987ab5cd2617 100644 --- a/drivers/dma/pch_dma.c +++ b/drivers/dma/pch_dma.c @@ -621,7 +621,7 @@ static struct dma_async_tx_descriptor *pd_prep_slave_sg(struct dma_chan *chan, goto err_desc_get; desc->regs.dev_addr = reg; - desc->regs.mem_addr = sg_phys(sg); + desc->regs.mem_addr = sg_dma_address(sg); desc->regs.size = sg_dma_len(sg); desc->regs.next = DMA_DESC_FOLLOW_WITHOUT_IRQ; -- cgit v1.2.2 From fdaf9c4b22247a6cc6cda9459be3e52764c14d95 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 25 Apr 2012 20:50:52 +0200 Subject: dmaengine: Use dma_sg_len(sg) instead of sg->length sg->length may or may not contain the length of the dma region to transfer, depending on the architecture - dma_sg_len(sg) always will though. For the architectures which use the drivers modified by this patch it probably is the case that sg->length contains the dma transfer length. But to be consistent and future proof change them to use dma_sg_len. To quote Russel King: sg->length is meaningless to something performing DMA. In cases where sg_dma_len(sg) and sg->length are the same storage, then there's no problem. But scatterlists _can_ (and one some architectures) do split them - especially when you have an IOMMU which can allow you to combine a scatterlist into fewer entries. So, anything using sg->length for the size of a scatterlist's DMA transfer _after_ a call to dma_map_sg() is almost certainly buggy. The patch has been generated using the following coccinelle patch: @@ struct scatterlist *sg; expression X; @@ -sg[X].length +sg_dma_len(&sg[X]) @@ struct scatterlist *sg; @@ -sg->length +sg_dma_len(sg) Signed-off-by: Lars-Peter Clausen Acked-by: Linus Walleij Signed-off-by: Vinod Koul --- drivers/dma/amba-pl08x.c | 2 +- drivers/dma/coh901318.c | 2 +- drivers/dma/imx-dma.c | 12 ++++++------ drivers/dma/imx-sdma.c | 2 +- drivers/dma/intel_mid_dma.c | 6 +++--- drivers/dma/mxs-dma.c | 6 +++--- drivers/dma/ste_dma40.c | 2 +- 7 files changed, 16 insertions(+), 16 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 003220a60bcb..49ecbbb8932d 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -1328,7 +1328,7 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg( int ret, tmp; dev_dbg(&pl08x->adev->dev, "%s prepare transaction of %d bytes from %s\n", - __func__, sgl->length, plchan->name); + __func__, sg_dma_len(sgl), plchan->name); txd = pl08x_get_txd(plchan, flags); if (!txd) { diff --git a/drivers/dma/coh901318.c b/drivers/dma/coh901318.c index dc89455f5550..c0b650c70bbd 100644 --- a/drivers/dma/coh901318.c +++ b/drivers/dma/coh901318.c @@ -1040,7 +1040,7 @@ coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, if (!sgl) goto out; - if (sgl->length == 0) + if (sg_dma_len(sgl) == 0) goto out; spin_lock_irqsave(&cohc->lock, flg); diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index bb787d8e1529..fcfeb3cd8d31 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -227,7 +227,7 @@ static inline int imxdma_sg_next(struct imxdma_desc *d) struct scatterlist *sg = d->sg; unsigned long now; - now = min(d->len, sg->length); + now = min(d->len, sg_dma_len(sg)); if (d->len != IMX_DMA_LENGTH_LOOP) d->len -= now; @@ -763,16 +763,16 @@ static struct dma_async_tx_descriptor *imxdma_prep_slave_sg( desc = list_first_entry(&imxdmac->ld_free, struct imxdma_desc, node); for_each_sg(sgl, sg, sg_len, i) { - dma_length += sg->length; + dma_length += sg_dma_len(sg); } switch (imxdmac->word_size) { case DMA_SLAVE_BUSWIDTH_4_BYTES: - if (sgl->length & 3 || sgl->dma_address & 3) + if (sg_dma_len(sgl) & 3 || sgl->dma_address & 3) return NULL; break; case DMA_SLAVE_BUSWIDTH_2_BYTES: - if (sgl->length & 1 || sgl->dma_address & 1) + if (sg_dma_len(sgl) & 1 || sgl->dma_address & 1) return NULL; break; case DMA_SLAVE_BUSWIDTH_1_BYTE: @@ -831,13 +831,13 @@ static struct dma_async_tx_descriptor *imxdma_prep_dma_cyclic( imxdmac->sg_list[i].page_link = 0; imxdmac->sg_list[i].offset = 0; imxdmac->sg_list[i].dma_address = dma_addr; - imxdmac->sg_list[i].length = period_len; + sg_dma_len(&imxdmac->sg_list[i]) = period_len; dma_addr += period_len; } /* close the loop */ imxdmac->sg_list[periods].offset = 0; - imxdmac->sg_list[periods].length = 0; + sg_dma_len(&imxdmac->sg_list[periods]) = 0; imxdmac->sg_list[periods].page_link = ((unsigned long)imxdmac->sg_list | 0x01) & ~0x02; diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 5a457777f5c0..cd0619a897ff 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -941,7 +941,7 @@ static struct dma_async_tx_descriptor *sdma_prep_slave_sg( bd->buffer_addr = sg->dma_address; - count = sg->length; + count = sg_dma_len(sg); if (count > 0xffff) { dev_err(sdma->dev, "SDMA channel %d: maximum bytes for sg entry exceeded: %d > %d\n", diff --git a/drivers/dma/intel_mid_dma.c b/drivers/dma/intel_mid_dma.c index d0ef5937fbf6..222e907bfaaa 100644 --- a/drivers/dma/intel_mid_dma.c +++ b/drivers/dma/intel_mid_dma.c @@ -394,7 +394,7 @@ static int midc_lli_fill_sg(struct intel_mid_dma_chan *midc, } } /*Populate CTL_HI values*/ - ctl_hi.ctlx.block_ts = get_block_ts(sg->length, + ctl_hi.ctlx.block_ts = get_block_ts(sg_dma_len(sg), desc->width, midc->dma->block_size); /*Populate SAR and DAR values*/ @@ -747,7 +747,7 @@ static struct dma_async_tx_descriptor *intel_mid_dma_prep_slave_sg( txd = intel_mid_dma_prep_memcpy(chan, mids->dma_slave.dst_addr, mids->dma_slave.src_addr, - sgl->length, + sg_dma_len(sgl), flags); return txd; } else { @@ -759,7 +759,7 @@ static struct dma_async_tx_descriptor *intel_mid_dma_prep_slave_sg( pr_debug("MDMA: SG Length = %d, direction = %d, Flags = %#lx\n", sg_len, direction, flags); - txd = intel_mid_dma_prep_memcpy(chan, 0, 0, sgl->length, flags); + txd = intel_mid_dma_prep_memcpy(chan, 0, 0, sg_dma_len(sgl), flags); if (NULL == txd) { pr_err("MDMA: Prep memcpy failed\n"); return NULL; diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index 655d4ce6ed0d..3db3a48d3f01 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c @@ -415,9 +415,9 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg( ccw->bits |= BF_CCW(MXS_DMA_CMD_NO_XFER, COMMAND); } else { for_each_sg(sgl, sg, sg_len, i) { - if (sg->length > MAX_XFER_BYTES) { + if (sg_dma_len(sg) > MAX_XFER_BYTES) { dev_err(mxs_dma->dma_device.dev, "maximum bytes for sg entry exceeded: %d > %d\n", - sg->length, MAX_XFER_BYTES); + sg_dma_len(sg), MAX_XFER_BYTES); goto err_out; } @@ -425,7 +425,7 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg( ccw->next = mxs_chan->ccw_phys + sizeof(*ccw) * idx; ccw->bufaddr = sg->dma_address; - ccw->xfer_bytes = sg->length; + ccw->xfer_bytes = sg_dma_len(sg); ccw->bits = 0; ccw->bits |= CCW_CHAIN; diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 2ed1ac3513f3..000d309602b2 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -2362,7 +2362,7 @@ dma40_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t dma_addr, } sg[periods].offset = 0; - sg[periods].length = 0; + sg_dma_len(&sg[periods]) = 0; sg[periods].page_link = ((unsigned long)sg | 0x01) & ~0x02; -- cgit v1.2.2 From 2ccaef0520d18d0072153f090d4110b4075c332c Mon Sep 17 00:00:00 2001 From: Richard Zhao Date: Fri, 11 May 2012 15:14:27 +0800 Subject: dma: imx-sdma: make channel0 operations atomic device_prep_dma_cyclic may be call in audio trigger function which is atomic context, so we make it atomic too. - change channel0 lock to spinlock. - Use polling to wait for channel0 finish running. Signed-off-by: Richard Zhao Acked-by: Shawn Guo Signed-off-by: Vinod Koul --- drivers/dma/imx-sdma.c | 57 +++++++++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 26 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index cd0619a897ff..a472a29d8497 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include #include @@ -324,7 +324,7 @@ struct sdma_engine { dma_addr_t context_phys; struct dma_device dma_device; struct clk *clk; - struct mutex channel_0_lock; + spinlock_t channel_0_lock; struct sdma_script_start_addrs *script_addrs; }; @@ -402,19 +402,27 @@ static void sdma_enable_channel(struct sdma_engine *sdma, int channel) } /* - * sdma_run_channel - run a channel and wait till it's done + * sdma_run_channel0 - run a channel and wait till it's done */ -static int sdma_run_channel(struct sdma_channel *sdmac) +static int sdma_run_channel0(struct sdma_engine *sdma) { - struct sdma_engine *sdma = sdmac->sdma; - int channel = sdmac->channel; int ret; + unsigned long timeout = 500; - init_completion(&sdmac->done); + sdma_enable_channel(sdma, 0); - sdma_enable_channel(sdma, channel); + while (!(ret = readl_relaxed(sdma->regs + SDMA_H_INTR) & 1)) { + if (timeout-- <= 0) + break; + udelay(1); + } - ret = wait_for_completion_timeout(&sdmac->done, HZ); + if (ret) { + /* Clear the interrupt status */ + writel_relaxed(ret, sdma->regs + SDMA_H_INTR); + } else { + dev_err(sdma->dev, "Timeout waiting for CH0 ready\n"); + } return ret ? 0 : -ETIMEDOUT; } @@ -426,17 +434,17 @@ static int sdma_load_script(struct sdma_engine *sdma, void *buf, int size, void *buf_virt; dma_addr_t buf_phys; int ret; - - mutex_lock(&sdma->channel_0_lock); + unsigned long flags; buf_virt = dma_alloc_coherent(NULL, size, &buf_phys, GFP_KERNEL); if (!buf_virt) { - ret = -ENOMEM; - goto err_out; + return -ENOMEM; } + spin_lock_irqsave(&sdma->channel_0_lock, flags); + bd0->mode.command = C0_SETPM; bd0->mode.status = BD_DONE | BD_INTR | BD_WRAP | BD_EXTD; bd0->mode.count = size / 2; @@ -445,12 +453,11 @@ static int sdma_load_script(struct sdma_engine *sdma, void *buf, int size, memcpy(buf_virt, buf, size); - ret = sdma_run_channel(&sdma->channel[0]); + ret = sdma_run_channel0(sdma); - dma_free_coherent(NULL, size, buf_virt, buf_phys); + spin_unlock_irqrestore(&sdma->channel_0_lock, flags); -err_out: - mutex_unlock(&sdma->channel_0_lock); + dma_free_coherent(NULL, size, buf_virt, buf_phys); return ret; } @@ -541,10 +548,6 @@ static void sdma_tasklet(unsigned long data) complete(&sdmac->done); - /* not interested in channel 0 interrupts */ - if (sdmac->channel == 0) - return; - if (sdmac->flags & IMX_DMA_SG_LOOP) sdma_handle_channel_loop(sdmac); else @@ -557,6 +560,8 @@ static irqreturn_t sdma_int_handler(int irq, void *dev_id) unsigned long stat; stat = readl_relaxed(sdma->regs + SDMA_H_INTR); + /* not interested in channel 0 interrupts */ + stat &= ~1; writel_relaxed(stat, sdma->regs + SDMA_H_INTR); while (stat) { @@ -662,6 +667,7 @@ static int sdma_load_context(struct sdma_channel *sdmac) struct sdma_context_data *context = sdma->context; struct sdma_buffer_descriptor *bd0 = sdma->channel[0].bd; int ret; + unsigned long flags; if (sdmac->direction == DMA_DEV_TO_MEM) { load_address = sdmac->pc_from_device; @@ -679,7 +685,7 @@ static int sdma_load_context(struct sdma_channel *sdmac) dev_dbg(sdma->dev, "event_mask0 = 0x%08x\n", (u32)sdmac->event_mask[0]); dev_dbg(sdma->dev, "event_mask1 = 0x%08x\n", (u32)sdmac->event_mask[1]); - mutex_lock(&sdma->channel_0_lock); + spin_lock_irqsave(&sdma->channel_0_lock, flags); memset(context, 0, sizeof(*context)); context->channel_state.pc = load_address; @@ -698,10 +704,9 @@ static int sdma_load_context(struct sdma_channel *sdmac) bd0->mode.count = sizeof(*context) / 4; bd0->buffer_addr = sdma->context_phys; bd0->ext_buffer_addr = 2048 + (sizeof(*context) / 4) * channel; + ret = sdma_run_channel0(sdma); - ret = sdma_run_channel(&sdma->channel[0]); - - mutex_unlock(&sdma->channel_0_lock); + spin_unlock_irqrestore(&sdma->channel_0_lock, flags); return ret; } @@ -1300,7 +1305,7 @@ static int __init sdma_probe(struct platform_device *pdev) if (!sdma) return -ENOMEM; - mutex_init(&sdma->channel_0_lock); + spin_lock_init(&sdma->channel_0_lock); sdma->dev = &pdev->dev; -- cgit v1.2.2 From b409ebfb14a71b64e11b156dc82ede698480397e Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Thu, 10 May 2012 12:17:40 +0200 Subject: dmaengine: at_hdmac: remove ATC_DEFAULT_CTRLA constant Not needed constant that was set to 0. Signed-off-by: Nicolas Ferre Signed-off-by: Vinod Koul --- drivers/dma/at_hdmac.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index bf0d7e4e345b..c057309c8ae5 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -39,7 +39,6 @@ */ #define ATC_DEFAULT_CFG (ATC_FIFOCFG_HALFFIFO) -#define ATC_DEFAULT_CTRLA (0) #define ATC_DEFAULT_CTRLB (ATC_SIF(AT_DMA_MEM_IF) \ |ATC_DIF(AT_DMA_MEM_IF)) @@ -574,7 +573,6 @@ atc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, return NULL; } - ctrla = ATC_DEFAULT_CTRLA; ctrlb = ATC_DEFAULT_CTRLB | ATC_IEN | ATC_SRC_ADDR_MODE_INCR | ATC_DST_ADDR_MODE_INCR @@ -585,13 +583,13 @@ atc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, * of the most common optimization. */ if (!((src | dest | len) & 3)) { - ctrla |= ATC_SRC_WIDTH_WORD | ATC_DST_WIDTH_WORD; + ctrla = ATC_SRC_WIDTH_WORD | ATC_DST_WIDTH_WORD; src_width = dst_width = 2; } else if (!((src | dest | len) & 1)) { - ctrla |= ATC_SRC_WIDTH_HALFWORD | ATC_DST_WIDTH_HALFWORD; + ctrla = ATC_SRC_WIDTH_HALFWORD | ATC_DST_WIDTH_HALFWORD; src_width = dst_width = 1; } else { - ctrla |= ATC_SRC_WIDTH_BYTE | ATC_DST_WIDTH_BYTE; + ctrla = ATC_SRC_WIDTH_BYTE | ATC_DST_WIDTH_BYTE; src_width = dst_width = 0; } @@ -668,7 +666,7 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, return NULL; } - ctrla = ATC_DEFAULT_CTRLA | atslave->ctrla; + ctrla = atslave->ctrla; ctrlb = ATC_IEN; switch (direction) { @@ -801,7 +799,7 @@ atc_dma_cyclic_fill_desc(struct dma_chan *chan, struct at_desc *desc, u32 ctrla; /* prepare common CRTLA value */ - ctrla = ATC_DEFAULT_CTRLA | atslave->ctrla + ctrla = atslave->ctrla | ATC_DST_WIDTH(reg_width) | ATC_SRC_WIDTH(reg_width) | period_len >> reg_width; -- cgit v1.2.2 From 1dd1ea8eb46a71201943148cc0ed3182cd04e288 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Thu, 10 May 2012 12:17:41 +0200 Subject: dmaengine: at_hdmac: take maxburst from slave configuration The maxburst/chunk size was taken from the private slave DMA data structure. Use the common API provided by DMA_SLAVE_CONFIG to setup src/dst maxburst values. The ctrla field is not needed anymore in the slave private structure nor the header constants that were located in an architecture specific directory. The at91sam9g45_devices.c file that was using this platform data is also modified to remove this now useless data. Signed-off-by: Nicolas Ferre Signed-off-by: Vinod Koul --- drivers/dma/at_hdmac.c | 7 ++++--- drivers/dma/at_hdmac_regs.h | 21 ++++++++++++++++++++- 2 files changed, 24 insertions(+), 4 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index c057309c8ae5..7292aa87b2dd 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -666,7 +666,8 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, return NULL; } - ctrla = atslave->ctrla; + ctrla = ATC_SCSIZE(sconfig->src_maxburst) + | ATC_DCSIZE(sconfig->dst_maxburst); ctrlb = ATC_IEN; switch (direction) { @@ -794,12 +795,12 @@ atc_dma_cyclic_fill_desc(struct dma_chan *chan, struct at_desc *desc, enum dma_transfer_direction direction) { struct at_dma_chan *atchan = to_at_dma_chan(chan); - struct at_dma_slave *atslave = chan->private; struct dma_slave_config *sconfig = &atchan->dma_sconfig; u32 ctrla; /* prepare common CRTLA value */ - ctrla = atslave->ctrla + ctrla = ATC_SCSIZE(sconfig->src_maxburst) + | ATC_DCSIZE(sconfig->dst_maxburst) | ATC_DST_WIDTH(reg_width) | ATC_SRC_WIDTH(reg_width) | period_len >> reg_width; diff --git a/drivers/dma/at_hdmac_regs.h b/drivers/dma/at_hdmac_regs.h index 897a8bcaec90..8a6c8e8b2940 100644 --- a/drivers/dma/at_hdmac_regs.h +++ b/drivers/dma/at_hdmac_regs.h @@ -87,7 +87,26 @@ /* Bitfields in CTRLA */ #define ATC_BTSIZE_MAX 0xFFFFUL /* Maximum Buffer Transfer Size */ #define ATC_BTSIZE(x) (ATC_BTSIZE_MAX & (x)) /* Buffer Transfer Size */ -/* Chunck Tranfer size definitions are in at_hdmac.h */ +#define ATC_SCSIZE_MASK (0x7 << 16) /* Source Chunk Transfer Size */ +#define ATC_SCSIZE(x) (ATC_SCSIZE_MASK & ((x) << 16)) +#define ATC_SCSIZE_1 (0x0 << 16) +#define ATC_SCSIZE_4 (0x1 << 16) +#define ATC_SCSIZE_8 (0x2 << 16) +#define ATC_SCSIZE_16 (0x3 << 16) +#define ATC_SCSIZE_32 (0x4 << 16) +#define ATC_SCSIZE_64 (0x5 << 16) +#define ATC_SCSIZE_128 (0x6 << 16) +#define ATC_SCSIZE_256 (0x7 << 16) +#define ATC_DCSIZE_MASK (0x7 << 20) /* Destination Chunk Transfer Size */ +#define ATC_DCSIZE(x) (ATC_DCSIZE_MASK & ((x) << 20)) +#define ATC_DCSIZE_1 (0x0 << 20) +#define ATC_DCSIZE_4 (0x1 << 20) +#define ATC_DCSIZE_8 (0x2 << 20) +#define ATC_DCSIZE_16 (0x3 << 20) +#define ATC_DCSIZE_32 (0x4 << 20) +#define ATC_DCSIZE_64 (0x5 << 20) +#define ATC_DCSIZE_128 (0x6 << 20) +#define ATC_DCSIZE_256 (0x7 << 20) #define ATC_SRC_WIDTH_MASK (0x3 << 24) /* Source Single Transfer Size */ #define ATC_SRC_WIDTH(x) ((x) << 24) #define ATC_SRC_WIDTH_BYTE (0x0 << 24) -- cgit v1.2.2