From 439aa0efd7a4a9ac225e94feee03824101c7d38e Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Mon, 9 Jan 2012 10:32:47 +0100 Subject: [PATCH 01/67] mmc: mxcmmc: add missing dma_async_issue_pending Signed-off-by: Sascha Hauer Signed-off-by: Vinod Koul --- drivers/mmc/host/mxcmmc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c index 4184b7946bbf..f35b6bad0476 100644 --- a/drivers/mmc/host/mxcmmc.c +++ b/drivers/mmc/host/mxcmmc.c @@ -267,6 +267,7 @@ static int mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data) wmb(); dmaengine_submit(host->desc); + dma_async_issue_pending(host->dma); return 0; } From c0fa6c8af7ab4f5d69d512a0d5fc3de512f337ae Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Mon, 9 Jan 2012 10:32:48 +0100 Subject: [PATCH 02/67] ASoC: i.MX: Add missing dma_async_issue_pending Signed-off-by: Sascha Hauer Acked-by: Mark Brown Signed-off-by: Vinod Koul --- sound/soc/imx/imx-pcm-dma-mx2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/imx/imx-pcm-dma-mx2.c b/sound/soc/imx/imx-pcm-dma-mx2.c index 5780c9b9d569..1acfd25e107d 100644 --- a/sound/soc/imx/imx-pcm-dma-mx2.c +++ b/sound/soc/imx/imx-pcm-dma-mx2.c @@ -206,6 +206,7 @@ static int snd_imx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: dmaengine_submit(iprtd->desc); + dma_async_issue_pending(iprtd->dma_chan); break; From 5b3168763f507fd46285b7310fc2d18dafe7f1c7 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Mon, 9 Jan 2012 10:32:49 +0100 Subject: [PATCH 03/67] dma: imx-dma: start transfer in issue_pending The DMA engine API requires that transfers are started in issue_pending instead of tx_submit. Fix this. Signed-off-by: Sascha Hauer [corrected change log to DMA engine API insteadof DMA API] Signed-off-by: Vinod Koul --- drivers/dma/imx-dma.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index e4383ee2c9ac..3296a7337f25 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -186,8 +186,6 @@ static dma_cookie_t imxdma_tx_submit(struct dma_async_tx_descriptor *tx) cookie = imxdma_assign_cookie(imxdmac); - imx_dma_enable(imxdmac->imxdma_channel); - spin_unlock_irq(&imxdmac->lock); return cookie; @@ -332,9 +330,10 @@ static struct dma_async_tx_descriptor *imxdma_prep_dma_cyclic( static void imxdma_issue_pending(struct dma_chan *chan) { - /* - * Nothing to do. We only have a single descriptor - */ + struct imxdma_channel *imxdmac = to_imxdma_chan(chan); + + if (imxdmac->status == DMA_IN_PROGRESS) + imx_dma_enable(imxdmac->imxdma_channel); } static int __init imxdma_probe(struct platform_device *pdev) From 2b4f130e05cb28a9794921aad5139615e94a7b02 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Mon, 9 Jan 2012 10:32:50 +0100 Subject: [PATCH 04/67] dma: imx-sdma: start transfer in issue_pending The DMA engine API requires that transfers are started in issue_pending instead of tx_submit. Fix this. Signed-off-by: Sascha Hauer [corrected change log to DMA engine API insteadof DMA API] Signed-off-by: Vinod Koul --- drivers/dma/imx-sdma.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index a8af379680c1..e58dbf638cc9 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -838,15 +838,12 @@ static dma_cookie_t sdma_tx_submit(struct dma_async_tx_descriptor *tx) { unsigned long flags; struct sdma_channel *sdmac = to_sdma_chan(tx->chan); - struct sdma_engine *sdma = sdmac->sdma; dma_cookie_t cookie; spin_lock_irqsave(&sdmac->lock, flags); cookie = sdma_assign_cookie(sdmac); - sdma_enable_channel(sdma, sdmac->channel); - spin_unlock_irqrestore(&sdmac->lock, flags); return cookie; @@ -1135,9 +1132,11 @@ static enum dma_status sdma_tx_status(struct dma_chan *chan, static void sdma_issue_pending(struct dma_chan *chan) { - /* - * Nothing to do. We only have a single descriptor - */ + struct sdma_channel *sdmac = to_sdma_chan(chan); + struct sdma_engine *sdma = sdmac->sdma; + + if (sdmac->status == DMA_IN_PROGRESS) + sdma_enable_channel(sdma, sdmac->channel); } #define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1 34 From 7bec78e0a82418021dc2e63dea4d2b749953086d Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 12 Jan 2012 10:55:06 +0100 Subject: [PATCH 05/67] drivers/dma/pl330.c: add missing iounmap Add missing iounmap in error handling code, in a case where the function already preforms iounmap on some other execution path. This patch additionally adds calls to clk_disable and clk_put. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression e; statement S,S1; int ret; @@ e = \(ioremap\|ioremap_nocache\)(...) ... when != iounmap(e) if (<+...e...+>) S ... when any when != iounmap(e) *if (...) { ... when != iounmap(e) return ...; } ... when any iounmap(e); // Signed-off-by: Julia Lawall Signed-off-by: Vinod Koul --- drivers/dma/pl330.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index b8ec03ee8e22..84ebea9bc53a 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -829,7 +829,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) if (IS_ERR(pdmac->clk)) { dev_err(&adev->dev, "Cannot get operation clock.\n"); ret = -EINVAL; - goto probe_err1; + goto probe_err2; } amba_set_drvdata(adev, pdmac); @@ -843,11 +843,11 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) ret = request_irq(irq, pl330_irq_handler, 0, dev_name(&adev->dev), pi); if (ret) - goto probe_err2; + goto probe_err3; ret = pl330_add(pi); if (ret) - goto probe_err3; + goto probe_err4; INIT_LIST_HEAD(&pdmac->desc_pool); spin_lock_init(&pdmac->pool_lock); @@ -904,7 +904,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) ret = dma_async_device_register(pd); if (ret) { dev_err(&adev->dev, "unable to register DMAC\n"); - goto probe_err4; + goto probe_err5; } dev_info(&adev->dev, @@ -917,10 +917,15 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) return 0; -probe_err4: +probe_err5: pl330_del(pi); -probe_err3: +probe_err4: free_irq(irq, pi); +probe_err3: +#ifndef CONFIG_PM_RUNTIME + clk_disable(pdmac->clk); +#endif + clk_put(pdmac->clk); probe_err2: iounmap(pi->base); probe_err1: From 880db3ff17a973bc0e9847299d1fd9cdf4568ee1 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 12 Jan 2012 22:49:29 +0100 Subject: [PATCH 06/67] drivers/dma/amba-pl08x.c: adjust double test Rewrite a duplicated test to test the correct value The semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression E; @@ ( * E || ... || E | * E && ... && E ) // Signed-off-by: Julia Lawall Signed-off-by: Vinod Koul --- drivers/dma/amba-pl08x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 8a281584458b..840c6c0dc533 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -649,7 +649,7 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x, } if ((bd.srcbus.addr % bd.srcbus.buswidth) || - (bd.srcbus.addr % bd.srcbus.buswidth)) { + (bd.dstbus.addr % bd.dstbus.buswidth)) { dev_err(&pl08x->adev->dev, "%s src & dst address must be aligned to src" " & dst width if peripheral is flow controller", From b9a591664a21a3d342b9e3b09b0aa2223ae7c469 Mon Sep 17 00:00:00 2001 From: Richard Zhao Date: Fri, 13 Jan 2012 11:09:56 +0800 Subject: [PATCH 07/67] dma/imx-sdma: let sdma_run_channel call sdma_enable_channel Let all enable channel code call sdma_enable_channel. Signed-off-by: Richard Zhao Acked-by: Shawn Guo Acked-by: Sascha Hauer Signed-off-by: Vinod Koul --- drivers/dma/imx-sdma.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index e58dbf638cc9..46e334d48e8d 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -394,6 +394,11 @@ static int sdma_config_ownership(struct sdma_channel *sdmac, return 0; } +static void sdma_enable_channel(struct sdma_engine *sdma, int channel) +{ + __raw_writel(1 << channel, sdma->regs + SDMA_H_START); +} + /* * sdma_run_channel - run a channel and wait till it's done */ @@ -405,7 +410,7 @@ static int sdma_run_channel(struct sdma_channel *sdmac) init_completion(&sdmac->done); - __raw_writel(1 << channel, sdma->regs + SDMA_H_START); + sdma_enable_channel(sdma, channel); ret = wait_for_completion_timeout(&sdmac->done, HZ); @@ -811,11 +816,6 @@ out: return ret; } -static void sdma_enable_channel(struct sdma_engine *sdma, int channel) -{ - __raw_writel(1 << channel, sdma->regs + SDMA_H_START); -} - static dma_cookie_t sdma_assign_cookie(struct sdma_channel *sdmac) { dma_cookie_t cookie = sdmac->chan.cookie; From c4b56857d17540e8085a04ea479b0239f4ee764c Mon Sep 17 00:00:00 2001 From: Richard Zhao Date: Fri, 13 Jan 2012 11:09:57 +0800 Subject: [PATCH 08/67] dma/imx-sdma: use readl_relaxed/writel_relaxed and use writel when necessary use readl_relaxed/writel_relaxed in most places, and use writel when enable channel, because it needs memory barrier. Signed-off-by: Richard Zhao Signed-off-by: Vinod Koul --- drivers/dma/imx-sdma.c | 44 +++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 46e334d48e8d..fd9ce77655bb 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -368,9 +368,9 @@ static int sdma_config_ownership(struct sdma_channel *sdmac, if (event_override && mcu_override && dsp_override) return -EINVAL; - evt = __raw_readl(sdma->regs + SDMA_H_EVTOVR); - mcu = __raw_readl(sdma->regs + SDMA_H_HOSTOVR); - dsp = __raw_readl(sdma->regs + SDMA_H_DSPOVR); + evt = readl_relaxed(sdma->regs + SDMA_H_EVTOVR); + mcu = readl_relaxed(sdma->regs + SDMA_H_HOSTOVR); + dsp = readl_relaxed(sdma->regs + SDMA_H_DSPOVR); if (dsp_override) dsp &= ~(1 << channel); @@ -387,16 +387,16 @@ static int sdma_config_ownership(struct sdma_channel *sdmac, else mcu |= (1 << channel); - __raw_writel(evt, sdma->regs + SDMA_H_EVTOVR); - __raw_writel(mcu, sdma->regs + SDMA_H_HOSTOVR); - __raw_writel(dsp, sdma->regs + SDMA_H_DSPOVR); + writel_relaxed(evt, sdma->regs + SDMA_H_EVTOVR); + writel_relaxed(mcu, sdma->regs + SDMA_H_HOSTOVR); + writel_relaxed(dsp, sdma->regs + SDMA_H_DSPOVR); return 0; } static void sdma_enable_channel(struct sdma_engine *sdma, int channel) { - __raw_writel(1 << channel, sdma->regs + SDMA_H_START); + writel(1 << channel, sdma->regs + SDMA_H_START); } /* @@ -460,9 +460,9 @@ static void sdma_event_enable(struct sdma_channel *sdmac, unsigned int event) u32 val; u32 chnenbl = chnenbl_ofs(sdma, event); - val = __raw_readl(sdma->regs + chnenbl); + val = readl_relaxed(sdma->regs + chnenbl); val |= (1 << channel); - __raw_writel(val, sdma->regs + chnenbl); + writel_relaxed(val, sdma->regs + chnenbl); } static void sdma_event_disable(struct sdma_channel *sdmac, unsigned int event) @@ -472,9 +472,9 @@ static void sdma_event_disable(struct sdma_channel *sdmac, unsigned int event) u32 chnenbl = chnenbl_ofs(sdma, event); u32 val; - val = __raw_readl(sdma->regs + chnenbl); + val = readl_relaxed(sdma->regs + chnenbl); val &= ~(1 << channel); - __raw_writel(val, sdma->regs + chnenbl); + writel_relaxed(val, sdma->regs + chnenbl); } static void sdma_handle_channel_loop(struct sdma_channel *sdmac) @@ -552,8 +552,8 @@ static irqreturn_t sdma_int_handler(int irq, void *dev_id) struct sdma_engine *sdma = dev_id; u32 stat; - stat = __raw_readl(sdma->regs + SDMA_H_INTR); - __raw_writel(stat, sdma->regs + SDMA_H_INTR); + stat = readl_relaxed(sdma->regs + SDMA_H_INTR); + writel_relaxed(stat, sdma->regs + SDMA_H_INTR); while (stat) { int channel = fls(stat) - 1; @@ -707,7 +707,7 @@ static void sdma_disable_channel(struct sdma_channel *sdmac) struct sdma_engine *sdma = sdmac->sdma; int channel = sdmac->channel; - __raw_writel(1 << channel, sdma->regs + SDMA_H_STATSTOP); + writel_relaxed(1 << channel, sdma->regs + SDMA_H_STATSTOP); sdmac->status = DMA_ERROR; } @@ -780,7 +780,7 @@ static int sdma_set_channel_priority(struct sdma_channel *sdmac, return -EINVAL; } - __raw_writel(priority, sdma->regs + SDMA_CHNPRI_0 + 4 * channel); + writel_relaxed(priority, sdma->regs + SDMA_CHNPRI_0 + 4 * channel); return 0; } @@ -1228,7 +1228,7 @@ static int __init sdma_init(struct sdma_engine *sdma) clk_enable(sdma->clk); /* Be sure SDMA has not started yet */ - __raw_writel(0, sdma->regs + SDMA_H_C0PTR); + writel_relaxed(0, sdma->regs + SDMA_H_C0PTR); sdma->channel_control = dma_alloc_coherent(NULL, MAX_DMA_CHANNELS * sizeof (struct sdma_channel_control) + @@ -1251,11 +1251,11 @@ static int __init sdma_init(struct sdma_engine *sdma) /* disable all channels */ for (i = 0; i < sdma->num_events; i++) - __raw_writel(0, sdma->regs + chnenbl_ofs(sdma, i)); + writel_relaxed(0, sdma->regs + chnenbl_ofs(sdma, i)); /* All channels have priority 0 */ for (i = 0; i < MAX_DMA_CHANNELS; i++) - __raw_writel(0, sdma->regs + SDMA_CHNPRI_0 + i * 4); + writel_relaxed(0, sdma->regs + SDMA_CHNPRI_0 + i * 4); ret = sdma_request_channel(&sdma->channel[0]); if (ret) @@ -1264,16 +1264,16 @@ static int __init sdma_init(struct sdma_engine *sdma) sdma_config_ownership(&sdma->channel[0], false, true, false); /* Set Command Channel (Channel Zero) */ - __raw_writel(0x4050, sdma->regs + SDMA_CHN0ADDR); + writel_relaxed(0x4050, sdma->regs + SDMA_CHN0ADDR); /* Set bits of CONFIG register but with static context switching */ /* FIXME: Check whether to set ACR bit depending on clock ratios */ - __raw_writel(0, sdma->regs + SDMA_H_CONFIG); + writel_relaxed(0, sdma->regs + SDMA_H_CONFIG); - __raw_writel(ccb_phys, sdma->regs + SDMA_H_C0PTR); + writel_relaxed(ccb_phys, sdma->regs + SDMA_H_C0PTR); /* Set bits of CONFIG register with given context switching mode */ - __raw_writel(SDMA_H_CONFIG_CSM, sdma->regs + SDMA_H_CONFIG); + writel_relaxed(SDMA_H_CONFIG_CSM, sdma->regs + SDMA_H_CONFIG); /* Initializes channel's priorities */ sdma_set_channel_priority(&sdma->channel[0], 7); From 3bb5e7caf9641b6a532a55cf3a3621ef814f57ee Mon Sep 17 00:00:00 2001 From: Richard Zhao Date: Fri, 13 Jan 2012 11:09:58 +0800 Subject: [PATCH 09/67] dma/imx-sdma: call sdma_set_channel_priority after sdma_request_channel sdma_request_channel sets the default priority. sdma_alloc_chan_resources should call sdma_set_channel_priority thereafter to over write it. Signed-off-by: Richard Zhao Acked-by: Shawn Guo Signed-off-by: Vinod Koul --- drivers/dma/imx-sdma.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index fd9ce77655bb..17a9d46bba15 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -873,11 +873,11 @@ static int sdma_alloc_chan_resources(struct dma_chan *chan) sdmac->peripheral_type = data->peripheral_type; sdmac->event_id0 = data->dma_request; - ret = sdma_set_channel_priority(sdmac, prio); + ret = sdma_request_channel(sdmac); if (ret) return ret; - ret = sdma_request_channel(sdmac); + ret = sdma_set_channel_priority(sdmac, prio); if (ret) return ret; From c2c744d3aa48f845626131176772bae2a3c9d5bc Mon Sep 17 00:00:00 2001 From: Richard Zhao Date: Fri, 13 Jan 2012 11:09:59 +0800 Subject: [PATCH 10/67] dma/imx-sdma: move clk_enable out of sdma_request_channel It makes clk_enable/disable pair more readable, and fix one bug: sdma_init calls sdma_request_channel, but seems don't know sdma_request_channel enabled the clock. Signed-off-by: Richard Zhao Acked-by: Shawn Guo [fixed typo in commit log] Signed-off-by: Vinod Koul --- drivers/dma/imx-sdma.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 17a9d46bba15..1e0e516b481f 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -802,8 +802,6 @@ static int sdma_request_channel(struct sdma_channel *sdmac) sdma->channel_control[channel].base_bd_ptr = sdmac->bd_phys; sdma->channel_control[channel].current_bd_ptr = sdmac->bd_phys; - clk_enable(sdma->clk); - sdma_set_channel_priority(sdmac, MXC_SDMA_DEFAULT_PRIORITY); init_completion(&sdmac->done); @@ -873,6 +871,9 @@ static int sdma_alloc_chan_resources(struct dma_chan *chan) sdmac->peripheral_type = data->peripheral_type; sdmac->event_id0 = data->dma_request; + + clk_enable(sdmac->sdma->clk); + ret = sdma_request_channel(sdmac); if (ret) return ret; From b78bd91f47b28ba1290a7eb95d8cf48a357e1b90 Mon Sep 17 00:00:00 2001 From: Richard Zhao Date: Fri, 13 Jan 2012 11:10:00 +0800 Subject: [PATCH 11/67] dma/imx-sdma: use num_events to validate event_id0 event number is not always 32. use num_events for checking instead. Signed-off-by: Richard Zhao Acked-by: Shawn Guo Signed-off-by: Vinod Koul --- drivers/dma/imx-sdma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 1e0e516b481f..5eb96b53e6da 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -723,7 +723,7 @@ static int sdma_config_channel(struct sdma_channel *sdmac) sdmac->per_addr = 0; if (sdmac->event_id0) { - if (sdmac->event_id0 > 32) + if (sdmac->event_id0 >= sdmac->sdma->num_events) return -EINVAL; sdma_event_enable(sdmac, sdmac->event_id0); } From 0bbc1413028e93629a2ecd5588cc427fa834404b Mon Sep 17 00:00:00 2001 From: Richard Zhao Date: Fri, 13 Jan 2012 11:10:01 +0800 Subject: [PATCH 12/67] dma/imx-sdma: convernt to use bit ops We don't need extra lock, so we use non-atomic bit ops to set/clear bits, merge event_mask0 and event_mask1 into an array, it helps use bit ops. It also fixs the issue: sdmac->event_mask0 = 1 << sdmac->event_id0; sdmac->event_mask1 = 1 << (sdmac->event_id0 - 32); It event_id0 < 32, it shifts negative number. If event_id0 >= 32, it shifts number >= sizeof(int). Both the cases behavior is undefined. Signed-off-by: Richard Zhao Signed-off-by: Vinod Koul --- drivers/dma/imx-sdma.c | 68 +++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 5eb96b53e6da..f380e79fd4d1 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -260,8 +261,8 @@ struct sdma_channel { unsigned int pc_from_device, pc_to_device; unsigned long flags; dma_addr_t per_address; - u32 event_mask0, event_mask1; - u32 watermark_level; + unsigned long event_mask[2]; + unsigned long watermark_level; u32 shp_addr, per_addr; struct dma_chan chan; spinlock_t lock; @@ -272,7 +273,7 @@ struct sdma_channel { unsigned int chn_real_count; }; -#define IMX_DMA_SG_LOOP (1 << 0) +#define IMX_DMA_SG_LOOP BIT(0) #define MAX_DMA_CHANNELS 32 #define MXC_SDMA_DEFAULT_PRIORITY 1 @@ -346,9 +347,9 @@ static const struct of_device_id sdma_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, sdma_dt_ids); -#define SDMA_H_CONFIG_DSPDMA (1 << 12) /* indicates if the DSPDMA is used */ -#define SDMA_H_CONFIG_RTD_PINS (1 << 11) /* indicates if Real-Time Debug pins are enabled */ -#define SDMA_H_CONFIG_ACR (1 << 4) /* indicates if AHB freq /core freq = 2 or 1 */ +#define SDMA_H_CONFIG_DSPDMA BIT(12) /* indicates if the DSPDMA is used */ +#define SDMA_H_CONFIG_RTD_PINS BIT(11) /* indicates if Real-Time Debug pins are enabled */ +#define SDMA_H_CONFIG_ACR BIT(4) /* indicates if AHB freq /core freq = 2 or 1 */ #define SDMA_H_CONFIG_CSM (3) /* indicates which context switch mode is selected*/ static inline u32 chnenbl_ofs(struct sdma_engine *sdma, unsigned int event) @@ -363,7 +364,7 @@ static int sdma_config_ownership(struct sdma_channel *sdmac, { struct sdma_engine *sdma = sdmac->sdma; int channel = sdmac->channel; - u32 evt, mcu, dsp; + unsigned long evt, mcu, dsp; if (event_override && mcu_override && dsp_override) return -EINVAL; @@ -373,19 +374,19 @@ static int sdma_config_ownership(struct sdma_channel *sdmac, dsp = readl_relaxed(sdma->regs + SDMA_H_DSPOVR); if (dsp_override) - dsp &= ~(1 << channel); + __clear_bit(channel, &dsp); else - dsp |= (1 << channel); + __set_bit(channel, &dsp); if (event_override) - evt &= ~(1 << channel); + __clear_bit(channel, &evt); else - evt |= (1 << channel); + __set_bit(channel, &evt); if (mcu_override) - mcu &= ~(1 << channel); + __clear_bit(channel, &mcu); else - mcu |= (1 << channel); + __set_bit(channel, &mcu); writel_relaxed(evt, sdma->regs + SDMA_H_EVTOVR); writel_relaxed(mcu, sdma->regs + SDMA_H_HOSTOVR); @@ -396,7 +397,7 @@ static int sdma_config_ownership(struct sdma_channel *sdmac, static void sdma_enable_channel(struct sdma_engine *sdma, int channel) { - writel(1 << channel, sdma->regs + SDMA_H_START); + writel(BIT(channel), sdma->regs + SDMA_H_START); } /* @@ -457,11 +458,11 @@ static void sdma_event_enable(struct sdma_channel *sdmac, unsigned int event) { struct sdma_engine *sdma = sdmac->sdma; int channel = sdmac->channel; - u32 val; + unsigned long val; u32 chnenbl = chnenbl_ofs(sdma, event); val = readl_relaxed(sdma->regs + chnenbl); - val |= (1 << channel); + __set_bit(channel, &val); writel_relaxed(val, sdma->regs + chnenbl); } @@ -470,10 +471,10 @@ static void sdma_event_disable(struct sdma_channel *sdmac, unsigned int event) struct sdma_engine *sdma = sdmac->sdma; int channel = sdmac->channel; u32 chnenbl = chnenbl_ofs(sdma, event); - u32 val; + unsigned long val; val = readl_relaxed(sdma->regs + chnenbl); - val &= ~(1 << channel); + __clear_bit(channel, &val); writel_relaxed(val, sdma->regs + chnenbl); } @@ -550,7 +551,7 @@ static void mxc_sdma_handle_channel(struct sdma_channel *sdmac) static irqreturn_t sdma_int_handler(int irq, void *dev_id) { struct sdma_engine *sdma = dev_id; - u32 stat; + unsigned long stat; stat = readl_relaxed(sdma->regs + SDMA_H_INTR); writel_relaxed(stat, sdma->regs + SDMA_H_INTR); @@ -561,7 +562,7 @@ static irqreturn_t sdma_int_handler(int irq, void *dev_id) mxc_sdma_handle_channel(sdmac); - stat &= ~(1 << channel); + __clear_bit(channel, &stat); } return IRQ_HANDLED; @@ -669,11 +670,11 @@ static int sdma_load_context(struct sdma_channel *sdmac) return load_address; dev_dbg(sdma->dev, "load_address = %d\n", load_address); - dev_dbg(sdma->dev, "wml = 0x%08x\n", sdmac->watermark_level); + dev_dbg(sdma->dev, "wml = 0x%08x\n", (u32)sdmac->watermark_level); dev_dbg(sdma->dev, "shp_addr = 0x%08x\n", sdmac->shp_addr); dev_dbg(sdma->dev, "per_addr = 0x%08x\n", sdmac->per_addr); - dev_dbg(sdma->dev, "event_mask0 = 0x%08x\n", sdmac->event_mask0); - dev_dbg(sdma->dev, "event_mask1 = 0x%08x\n", sdmac->event_mask1); + 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); @@ -683,8 +684,8 @@ static int sdma_load_context(struct sdma_channel *sdmac) /* Send by context the event mask,base address for peripheral * and watermark level */ - context->gReg[0] = sdmac->event_mask1; - context->gReg[1] = sdmac->event_mask0; + context->gReg[0] = sdmac->event_mask[1]; + context->gReg[1] = sdmac->event_mask[0]; context->gReg[2] = sdmac->per_addr; context->gReg[6] = sdmac->shp_addr; context->gReg[7] = sdmac->watermark_level; @@ -707,7 +708,7 @@ static void sdma_disable_channel(struct sdma_channel *sdmac) struct sdma_engine *sdma = sdmac->sdma; int channel = sdmac->channel; - writel_relaxed(1 << channel, sdma->regs + SDMA_H_STATSTOP); + writel_relaxed(BIT(channel), sdma->regs + SDMA_H_STATSTOP); sdmac->status = DMA_ERROR; } @@ -717,8 +718,8 @@ static int sdma_config_channel(struct sdma_channel *sdmac) sdma_disable_channel(sdmac); - sdmac->event_mask0 = 0; - sdmac->event_mask1 = 0; + sdmac->event_mask[0] = 0; + sdmac->event_mask[1] = 0; sdmac->shp_addr = 0; sdmac->per_addr = 0; @@ -746,15 +747,14 @@ static int sdma_config_channel(struct sdma_channel *sdmac) (sdmac->peripheral_type != IMX_DMATYPE_DSP)) { /* Handle multiple event channels differently */ if (sdmac->event_id1) { - sdmac->event_mask1 = 1 << (sdmac->event_id1 % 32); + sdmac->event_mask[1] = BIT(sdmac->event_id1 % 32); if (sdmac->event_id1 > 31) - sdmac->watermark_level |= 1 << 31; - sdmac->event_mask0 = 1 << (sdmac->event_id0 % 32); + __set_bit(31, &sdmac->watermark_level); + sdmac->event_mask[0] = BIT(sdmac->event_id0 % 32); if (sdmac->event_id0 > 31) - sdmac->watermark_level |= 1 << 30; + __set_bit(30, &sdmac->watermark_level); } else { - sdmac->event_mask0 = 1 << sdmac->event_id0; - sdmac->event_mask1 = 1 << (sdmac->event_id0 - 32); + __set_bit(sdmac->event_id0, sdmac->event_mask); } /* Watermark Level */ sdmac->watermark_level |= sdmac->watermark_level; From b63fd6ce71226e5e048e2035ba202b53f89a1825 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20R=C3=A9tornaz?= Date: Tue, 24 Jan 2012 14:22:01 +0100 Subject: [PATCH 13/67] i.MX SDMA: Fix burstsize settings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 6584cb88 (ARM i.MX dma: Fix burstsize settings) fixed the mxcmmc driver but forgot to fix the SDMA driver to handle the correct burtsize. This make the SD card access works again with DMA on i.MX31 boards. Signed-off-by: Philippe Rétornaz Tested-by: Sascha Hauer Acked-by: Sascha Hauer Signed-off-by: Vinod Koul --- drivers/dma/imx-sdma.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index f380e79fd4d1..bf736ad679ca 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -1100,11 +1100,13 @@ static int sdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, case DMA_SLAVE_CONFIG: if (dmaengine_cfg->direction == DMA_DEV_TO_MEM) { sdmac->per_address = dmaengine_cfg->src_addr; - sdmac->watermark_level = dmaengine_cfg->src_maxburst; + sdmac->watermark_level = dmaengine_cfg->src_maxburst * + dmaengine_cfg->src_addr_width; sdmac->word_size = dmaengine_cfg->src_addr_width; } else { sdmac->per_address = dmaengine_cfg->dst_addr; - sdmac->watermark_level = dmaengine_cfg->dst_maxburst; + sdmac->watermark_level = dmaengine_cfg->dst_maxburst * + dmaengine_cfg->dst_addr_width; sdmac->word_size = dmaengine_cfg->dst_addr_width; } sdmac->direction = dmaengine_cfg->direction; From 7f99a4216d68f6d75d5d544aa265d72f193bad7f Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 31 Jan 2012 12:48:59 +0200 Subject: [PATCH 14/67] dmaengine: intel_mid_dma: fix error status mask The error status mask (MASK_ERR) has the same format as the other masks (MASK_TFR, MASK_BLOCK etc) and must be cleared the same way. Signed-off-by: Adrian Hunter Signed-off-by: Vinod Koul --- drivers/dma/intel_mid_dma.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/dma/intel_mid_dma.c b/drivers/dma/intel_mid_dma.c index 74f70aadf9e4..512e2ab3febb 100644 --- a/drivers/dma/intel_mid_dma.c +++ b/drivers/dma/intel_mid_dma.c @@ -1056,7 +1056,8 @@ static irqreturn_t intel_mid_dma_interrupt(int irq, void *data) } err_status &= mid->intr_mask; if (err_status) { - iowrite32(MASK_INTR_REG(err_status), mid->dma_base + MASK_ERR); + iowrite32((err_status << INT_MASK_WE), + mid->dma_base + MASK_ERR); call_tasklet = 1; } if (call_tasklet) From 91c1c9e36d9450974c9f04cf961d0b45aafca507 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 31 Jan 2012 12:49:00 +0200 Subject: [PATCH 15/67] dmaengine: intel_mid_dma: move pm_runtime_put Move pm_runtime_put() to the end of intel_mid_dma_free_chan_resources() because there is no sense in allowing runtime suspend while the driver is still accessing the device. Signed-off-by: Adrian Hunter Signed-off-by: Vinod Koul --- drivers/dma/intel_mid_dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/intel_mid_dma.c b/drivers/dma/intel_mid_dma.c index 512e2ab3febb..923476d74a5d 100644 --- a/drivers/dma/intel_mid_dma.c +++ b/drivers/dma/intel_mid_dma.c @@ -832,7 +832,6 @@ static void intel_mid_dma_free_chan_resources(struct dma_chan *chan) /*trying to free ch in use!!!!!*/ pr_err("ERR_MDMA: trying to free ch in use\n"); } - pm_runtime_put(&mid->pdev->dev); spin_lock_bh(&midc->lock); midc->descs_allocated = 0; list_for_each_entry_safe(desc, _desc, &midc->active_list, desc_node) { @@ -853,6 +852,7 @@ static void intel_mid_dma_free_chan_resources(struct dma_chan *chan) /* Disable CH interrupts */ iowrite32(MASK_INTR_REG(midc->ch_id), mid->dma_base + MASK_BLOCK); iowrite32(MASK_INTR_REG(midc->ch_id), mid->dma_base + MASK_ERR); + pm_runtime_put(&mid->pdev->dev); } /** From 7414a1b8b2b99093d6a271a1220557e87dc1ae94 Mon Sep 17 00:00:00 2001 From: Rajeev KUMAR Date: Wed, 1 Feb 2012 16:12:17 +0530 Subject: [PATCH 16/67] dmaengine/dw_dmac: Hibernation support in dw_dmac The suspend and resume implementation is through dev_pm_ops in dmac. So in order to support hibernation, freeze, thaw, restore and poweroff features are required. Signed-off-by: Rajeev Kumar Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 9b592b02b5f4..3a3d7edcd553 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1562,6 +1562,10 @@ static int dw_resume_noirq(struct device *dev) static const struct dev_pm_ops dw_dev_pm_ops = { .suspend_noirq = dw_suspend_noirq, .resume_noirq = dw_resume_noirq, + .freeze_noirq = dw_suspend_noirq, + .thaw_noirq = dw_resume_noirq, + .restore_noirq = dw_resume_noirq, + .poweroff_noirq = dw_suspend_noirq, }; static struct platform_driver dw_driver = { From dcc043dc0c60046cf6b75ca04a462314cf64e2ba Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 1 Feb 2012 16:12:18 +0530 Subject: [PATCH 17/67] dmaengine: Add flow controller information to dma_slave_config Flow controller is programmable for few controllers and there are few intelligent peripherals like, Synopsys JPEG controller, that needs to be a flow controller of DMA transfers on dest side. For this, currently two drivers, pl08x and dw_dmac, support flow controller to be passed from platform to these drivers. Perhaps, this should be a part of struct dma_slave_config. This patch adds another field device_fc to this structure. User drivers must pass this as true if they want to be flow controller of certain transfers. Signed-off-by: Viresh Kumar Acked-by: Linus Walleij Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 679b349d9b66..7e640bf27d2d 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -25,6 +25,7 @@ #include #include #include +#include #include /** @@ -331,6 +332,9 @@ enum dma_slave_buswidth { * may or may not be applicable on memory sources. * @dst_maxburst: same as src_maxburst but for destination target * mutatis mutandis. + * @device_fc: Flow Controller Settings. Only valid for slave channels. Fill + * with 'true' if peripheral should be flow controller. Direction will be + * selected at Runtime. * * This struct is passed in as configuration data to a DMA engine * in order to set up a certain channel for DMA transport at runtime. @@ -357,6 +361,7 @@ struct dma_slave_config { enum dma_slave_buswidth dst_addr_width; u32 src_maxburst; u32 dst_maxburst; + bool device_fc; }; static inline const char *dma_chan_name(struct dma_chan *chan) From 258aea76f552cc755da92e7e823abbb85e021514 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 1 Feb 2012 16:12:19 +0530 Subject: [PATCH 18/67] dmaengine: Pass dma_slave_config .device_fc = NULL for all existing users .device_fc is added in struct dma_slave_config recently. All user drivers, which want DMA to be the flow controller must pass this field as false. As earlier driver don't look to use this feature, mark it false for now. Signed-off-by: Viresh Kumar Acked-by: Linus Walleij Signed-off-by: Vinod Koul --- drivers/mmc/host/mmci.c | 2 ++ drivers/mmc/host/mxcmmc.c | 2 ++ drivers/spi/spi-dw-mid.c | 3 +++ drivers/spi/spi-pl022.c | 2 ++ drivers/tty/serial/amba-pl011.c | 3 +++ drivers/usb/musb/ux500_dma.c | 1 + sound/soc/imx/imx-pcm-dma-mx2.c | 3 +++ 7 files changed, 16 insertions(+) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 0d955ffaf44e..a09c06ba046c 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -370,6 +371,7 @@ static int mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data, .dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, .src_maxburst = variant->fifohalfsize >> 2, /* # of words */ .dst_maxburst = variant->fifohalfsize >> 2, /* # of words */ + .device_fc = false, }; struct dma_chan *chan; struct dma_device *device; diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c index f35b6bad0476..68b69a91e5ef 100644 --- a/drivers/mmc/host/mxcmmc.c +++ b/drivers/mmc/host/mxcmmc.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -711,6 +712,7 @@ static int mxcmci_setup_dma(struct mmc_host *mmc) config->src_addr_width = 4; config->dst_maxburst = host->burstlen; config->src_maxburst = host->burstlen; + config->device_fc = false; return dmaengine_slave_config(host->dma, config); } diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index 8418eb036651..04d6c1b31383 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "spi-dw.h" @@ -136,6 +137,7 @@ static int mid_spi_dma_transfer(struct dw_spi *dws, int cs_change) txconf.dst_maxburst = LNW_DMA_MSIZE_16; txconf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; txconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + txconf.device_fc = false; txchan->device->device_control(txchan, DMA_SLAVE_CONFIG, (unsigned long) &txconf); @@ -158,6 +160,7 @@ static int mid_spi_dma_transfer(struct dw_spi *dws, int cs_change) rxconf.src_maxburst = LNW_DMA_MSIZE_16; rxconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; rxconf.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + rxconf.device_fc = false; rxchan->device->device_control(rxchan, DMA_SLAVE_CONFIG, (unsigned long) &rxconf); diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 2f9cb43a2398..1dc667f8a305 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -901,10 +901,12 @@ static int configure_dma(struct pl022 *pl022) struct dma_slave_config rx_conf = { .src_addr = SSP_DR(pl022->phybase), .direction = DMA_DEV_TO_MEM, + .device_fc = false, }; struct dma_slave_config tx_conf = { .dst_addr = SSP_DR(pl022->phybase), .direction = DMA_MEM_TO_DEV, + .device_fc = false, }; unsigned int pages; int ret; diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 9ae024025ff3..db7f88a1bdee 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include @@ -270,6 +271,7 @@ static void pl011_dma_probe_initcall(struct uart_amba_port *uap) .dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, .direction = DMA_MEM_TO_DEV, .dst_maxburst = uap->fifosize >> 1, + .device_fc = false, }; struct dma_chan *chan; dma_cap_mask_t mask; @@ -303,6 +305,7 @@ static void pl011_dma_probe_initcall(struct uart_amba_port *uap) .src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, .direction = DMA_DEV_TO_MEM, .src_maxburst = uap->fifosize >> 1, + .device_fc = false, }; chan = dma_request_channel(mask, plat->dma_filter, plat->dma_rx_param); diff --git a/drivers/usb/musb/ux500_dma.c b/drivers/usb/musb/ux500_dma.c index 97cb45916c43..2a36bf37d7aa 100644 --- a/drivers/usb/musb/ux500_dma.c +++ b/drivers/usb/musb/ux500_dma.c @@ -115,6 +115,7 @@ static bool ux500_configure_channel(struct dma_channel *channel, slave_conf.dst_addr = usb_fifo_addr; slave_conf.dst_addr_width = addr_width; slave_conf.dst_maxburst = 16; + slave_conf.device_fc = false; dma_chan->device->device_control(dma_chan, DMA_SLAVE_CONFIG, (unsigned long) &slave_conf); diff --git a/sound/soc/imx/imx-pcm-dma-mx2.c b/sound/soc/imx/imx-pcm-dma-mx2.c index 1acfd25e107d..52b0dedbf996 100644 --- a/sound/soc/imx/imx-pcm-dma-mx2.c +++ b/sound/soc/imx/imx-pcm-dma-mx2.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -108,6 +109,8 @@ static int imx_ssi_dma_alloc(struct snd_pcm_substream *substream, return 0; } + slave_config.device_fc = false; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { slave_config.direction = DMA_MEM_TO_DEV; slave_config.dst_addr = dma_params->dma_addr; From 8c9f7aa316f547f70d270a08d1212f958721c071 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 1 Feb 2012 16:12:20 +0530 Subject: [PATCH 19/67] dmaengine/amba-pl08x: Take flow controller info from DMA_SLAVE_CONFIG Flow controller information is passed now from DMA_SLAVE_CONFIG option. This patch makes changes in pl08x driver to use device_fc from it instead of platform data. Signed-off-by: Viresh Kumar Acked-by: Linus Walleij Signed-off-by: Vinod Koul --- drivers/dma/amba-pl08x.c | 4 +++- include/linux/amba/pl08x.h | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 840c6c0dc533..513184b4fdd1 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -1139,6 +1139,8 @@ static int dma_set_runtime_config(struct dma_chan *chan, cctl |= burst << PL080_CONTROL_SB_SIZE_SHIFT; cctl |= burst << PL080_CONTROL_DB_SIZE_SHIFT; + plchan->device_fc = config->device_fc; + if (plchan->runtime_direction == DMA_DEV_TO_MEM) { plchan->src_addr = config->src_addr; plchan->src_cctl = pl08x_cctl(cctl) | PL080_CONTROL_DST_INCR | @@ -1370,7 +1372,7 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg( return NULL; } - if (plchan->cd->device_fc) + if (plchan->device_fc) tmp = (direction == DMA_MEM_TO_DEV) ? PL080_FLOW_MEM2PER_PER : PL080_FLOW_PER2MEM_PER; else diff --git a/include/linux/amba/pl08x.h b/include/linux/amba/pl08x.h index 033f6aa670de..2c58853ca423 100644 --- a/include/linux/amba/pl08x.h +++ b/include/linux/amba/pl08x.h @@ -47,9 +47,6 @@ enum { * @muxval: a number usually used to poke into some mux regiser to * mux in the signal to this channel * @cctl_opt: default options for the channel control register - * @device_fc: Flow Controller Settings for ccfg register. Only valid for slave - * channels. Fill with 'true' if peripheral should be flow controller. Direction - * will be selected at Runtime. * @addr: source/target address in physical memory for this DMA channel, * can be the address of a FIFO register for burst requests for example. * This can be left undefined if the PrimeCell API is used for configuring @@ -68,7 +65,6 @@ struct pl08x_channel_data { int max_signal; u32 muxval; u32 cctl; - bool device_fc; dma_addr_t addr; bool circular_buffer; bool single; @@ -183,6 +179,9 @@ enum pl08x_dma_chan_state { * @host: a pointer to the host (internal use) * @state: whether the channel is idle, paused, running etc * @slave: whether this channel is a device (slave) or for memcpy + * @device_fc: Flow Controller Settings for ccfg register. Only valid for slave + * channels. Fill with 'true' if peripheral should be flow controller. Direction + * will be selected at Runtime. * @waiting: a TX descriptor on this channel which is waiting for a physical * channel to become available */ @@ -205,6 +204,7 @@ struct pl08x_dma_chan { struct pl08x_driver_data *host; enum pl08x_dma_chan_state state; bool slave; + bool device_fc; struct pl08x_txd *waiting; }; From e8d9f875c9b02bb56ba3bb44834ae81ba0e8efbc Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 1 Feb 2012 16:12:21 +0530 Subject: [PATCH 20/67] dmaengine/dw_dmac: Don't use magic number for total number of channels Total number of channels is passed in pdata->nr_channels variable, thus we must not use magic number '7' for total number of channels. Signed-off-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 3a3d7edcd553..157739423734 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1432,7 +1432,7 @@ static int __init dw_probe(struct platform_device *pdev) /* 7 is highest priority & 0 is lowest. */ if (pdata->chan_priority == CHAN_PRIORITY_ASCENDING) - dwc->priority = 7 - i; + dwc->priority = pdata->nr_channels - i - 1; else dwc->priority = i; From 6c618c9de5f2b4b43c30c6203869620bec6ed929 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 1 Feb 2012 16:12:22 +0530 Subject: [PATCH 21/67] dmaengine/dw_dmac: Use dev_get_platdata() instead of accessing dev directly Use already defined function dev_get_platdata() instead of accessing pdev->dev.data. Signed-off-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 157739423734..f3aecb3c0343 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1369,7 +1369,7 @@ static int __init dw_probe(struct platform_device *pdev) int err; int i; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); if (!pdata || pdata->nr_channels > DW_DMA_MAX_NR_CHANNELS) return -EINVAL; From ff7b05f29fd4db810021d905e2dad95ab4fe2984 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 1 Feb 2012 16:12:23 +0530 Subject: [PATCH 22/67] dmaengine/dw_dmac: Don't handle block interrupts Block interrupts give interrupt on completion of every LLI, which is actually too much interrupts. This is just not required for current functioning of dw_dmac. So, just don't handle them at all. Signed-off-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 36 ++++++------------------------------ 1 file changed, 6 insertions(+), 30 deletions(-) diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index f3aecb3c0343..5d7b199208d9 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -192,7 +192,6 @@ static void dwc_initialize(struct dw_dma_chan *dwc) /* Enable interrupts */ channel_set_bit(dw, MASK.XFER, dwc->mask); - channel_set_bit(dw, MASK.BLOCK, dwc->mask); channel_set_bit(dw, MASK.ERROR, dwc->mask); dwc->initialized = true; @@ -329,12 +328,6 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) unsigned long flags; spin_lock_irqsave(&dwc->lock, flags); - /* - * Clear block interrupt flag before scanning so that we don't - * miss any, and read LLP before RAW_XFER to ensure it is - * valid if we decide to scan the list. - */ - dma_writel(dw, CLEAR.BLOCK, dwc->mask); llp = channel_readl(dwc, LLP); status_xfer = dma_readl(dw, RAW.XFER); @@ -470,17 +463,16 @@ EXPORT_SYMBOL(dw_dma_get_dst_addr); /* called with dwc->lock held and all DMAC interrupts disabled */ static void dwc_handle_cyclic(struct dw_dma *dw, struct dw_dma_chan *dwc, - u32 status_block, u32 status_err, u32 status_xfer) + u32 status_err, u32 status_xfer) { unsigned long flags; - if (status_block & dwc->mask) { + if (dwc->mask) { void (*callback)(void *param); void *callback_param; dev_vdbg(chan2dev(&dwc->chan), "new cyclic period llp 0x%08x\n", channel_readl(dwc, LLP)); - dma_writel(dw, CLEAR.BLOCK, dwc->mask); callback = dwc->cdesc->period_callback; callback_param = dwc->cdesc->period_callback_param; @@ -520,7 +512,6 @@ static void dwc_handle_cyclic(struct dw_dma *dw, struct dw_dma_chan *dwc, channel_writel(dwc, CTL_LO, 0); channel_writel(dwc, CTL_HI, 0); - dma_writel(dw, CLEAR.BLOCK, dwc->mask); dma_writel(dw, CLEAR.ERROR, dwc->mask); dma_writel(dw, CLEAR.XFER, dwc->mask); @@ -537,36 +528,29 @@ static void dw_dma_tasklet(unsigned long data) { struct dw_dma *dw = (struct dw_dma *)data; struct dw_dma_chan *dwc; - u32 status_block; u32 status_xfer; u32 status_err; int i; - status_block = dma_readl(dw, RAW.BLOCK); status_xfer = dma_readl(dw, RAW.XFER); status_err = dma_readl(dw, RAW.ERROR); - dev_vdbg(dw->dma.dev, "tasklet: status_block=%x status_err=%x\n", - status_block, status_err); + dev_vdbg(dw->dma.dev, "tasklet: status_err=%x\n", status_err); for (i = 0; i < dw->dma.chancnt; i++) { dwc = &dw->chan[i]; if (test_bit(DW_DMA_IS_CYCLIC, &dwc->flags)) - dwc_handle_cyclic(dw, dwc, status_block, status_err, - status_xfer); + dwc_handle_cyclic(dw, dwc, status_err, status_xfer); else if (status_err & (1 << i)) dwc_handle_error(dw, dwc); - else if ((status_block | status_xfer) & (1 << i)) + else if (status_xfer & (1 << i)) dwc_scan_descriptors(dw, dwc); } /* - * Re-enable interrupts. Block Complete interrupts are only - * enabled if the INT_EN bit in the descriptor is set. This - * will trigger a scan before the whole list is done. + * Re-enable interrupts. */ channel_set_bit(dw, MASK.XFER, dw->all_chan_mask); - channel_set_bit(dw, MASK.BLOCK, dw->all_chan_mask); channel_set_bit(dw, MASK.ERROR, dw->all_chan_mask); } @@ -583,7 +567,6 @@ static irqreturn_t dw_dma_interrupt(int irq, void *dev_id) * softirq handler. */ channel_clear_bit(dw, MASK.XFER, dw->all_chan_mask); - channel_clear_bit(dw, MASK.BLOCK, dw->all_chan_mask); channel_clear_bit(dw, MASK.ERROR, dw->all_chan_mask); status = dma_readl(dw, STATUS_INT); @@ -594,7 +577,6 @@ static irqreturn_t dw_dma_interrupt(int irq, void *dev_id) /* Try to recover */ channel_clear_bit(dw, MASK.XFER, (1 << 8) - 1); - channel_clear_bit(dw, MASK.BLOCK, (1 << 8) - 1); channel_clear_bit(dw, MASK.SRC_TRAN, (1 << 8) - 1); channel_clear_bit(dw, MASK.DST_TRAN, (1 << 8) - 1); channel_clear_bit(dw, MASK.ERROR, (1 << 8) - 1); @@ -1068,7 +1050,6 @@ static void dwc_free_chan_resources(struct dma_chan *chan) /* Disable interrupts */ channel_clear_bit(dw, MASK.XFER, dwc->mask); - channel_clear_bit(dw, MASK.BLOCK, dwc->mask); channel_clear_bit(dw, MASK.ERROR, dwc->mask); spin_unlock_irqrestore(&dwc->lock, flags); @@ -1120,7 +1101,6 @@ int dw_dma_cyclic_start(struct dma_chan *chan) return -EBUSY; } - dma_writel(dw, CLEAR.BLOCK, dwc->mask); dma_writel(dw, CLEAR.ERROR, dwc->mask); dma_writel(dw, CLEAR.XFER, dwc->mask); @@ -1322,7 +1302,6 @@ void dw_dma_cyclic_free(struct dma_chan *chan) while (dma_readl(dw, CH_EN) & dwc->mask) cpu_relax(); - dma_writel(dw, CLEAR.BLOCK, dwc->mask); dma_writel(dw, CLEAR.ERROR, dwc->mask); dma_writel(dw, CLEAR.XFER, dwc->mask); @@ -1347,7 +1326,6 @@ static void dw_dma_off(struct dw_dma *dw) dma_writel(dw, CFG, 0); channel_clear_bit(dw, MASK.XFER, dw->all_chan_mask); - channel_clear_bit(dw, MASK.BLOCK, dw->all_chan_mask); channel_clear_bit(dw, MASK.SRC_TRAN, dw->all_chan_mask); channel_clear_bit(dw, MASK.DST_TRAN, dw->all_chan_mask); channel_clear_bit(dw, MASK.ERROR, dw->all_chan_mask); @@ -1449,13 +1427,11 @@ static int __init dw_probe(struct platform_device *pdev) /* Clear/disable all interrupts on all channels. */ dma_writel(dw, CLEAR.XFER, dw->all_chan_mask); - dma_writel(dw, CLEAR.BLOCK, dw->all_chan_mask); dma_writel(dw, CLEAR.SRC_TRAN, dw->all_chan_mask); dma_writel(dw, CLEAR.DST_TRAN, dw->all_chan_mask); dma_writel(dw, CLEAR.ERROR, dw->all_chan_mask); channel_clear_bit(dw, MASK.XFER, dw->all_chan_mask); - channel_clear_bit(dw, MASK.BLOCK, dw->all_chan_mask); channel_clear_bit(dw, MASK.SRC_TRAN, dw->all_chan_mask); channel_clear_bit(dw, MASK.DST_TRAN, dw->all_chan_mask); channel_clear_bit(dw, MASK.ERROR, dw->all_chan_mask); From 6bc711f6bd9dd393e1f9bbae354906affcd02aa5 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 1 Feb 2012 16:12:25 +0530 Subject: [PATCH 23/67] dmaengine/dw_dmac: Add 64 bit access width support for slave xfers on mem side 64 bit transfers are possible on both sides in slave transfers (memory as well as peripherals). This patch adds support for it memory side 64 bit transfers. Signed-off-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 5d7b199208d9..9f4310f013f5 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -749,8 +749,14 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, mem = sg_phys(sg); len = sg_dma_len(sg); - mem_width = 2; - if (unlikely(mem & 3 || len & 3)) + + if (!((mem | len) & 7)) + mem_width = 3; + else if (!((mem | len) & 3)) + mem_width = 2; + else if (!((mem | len) & 1)) + mem_width = 1; + else mem_width = 0; slave_sg_todev_fill_desc: @@ -807,8 +813,14 @@ slave_sg_todev_fill_desc: mem = sg_phys(sg); len = sg_dma_len(sg); - mem_width = 2; - if (unlikely(mem & 3 || len & 3)) + + if (!((mem | len) & 7)) + mem_width = 3; + else if (!((mem | len) & 3)) + mem_width = 2; + else if (!((mem | len) & 1)) + mem_width = 1; + else mem_width = 0; slave_sg_fromdev_fill_desc: From 327e6970258618da810f72e86cf2a8b803927e14 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 1 Feb 2012 16:12:26 +0530 Subject: [PATCH 24/67] dmaengine/dw_dmac: Add support for DMA_SLAVE_CONFIG This patch adds support for DMA_SLAVE_CONFIG in dwc DMAC controller. Fields in struct dw_dma_slave for passing similar data are preserved in this patch untill all existing users are fixed. That will be handled later in this patchset. Signed-off-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 119 ++++++++++++++++++++++++++++--------- drivers/dma/dw_dmac_regs.h | 3 + 2 files changed, 93 insertions(+), 29 deletions(-) diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 9f4310f013f5..0e4b5c6a2f86 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -9,6 +9,7 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#include #include #include #include @@ -33,19 +34,23 @@ * which does not support descriptor writeback. */ -#define DWC_DEFAULT_CTLLO(private) ({ \ - struct dw_dma_slave *__slave = (private); \ - int dms = __slave ? __slave->dst_master : 0; \ - int sms = __slave ? __slave->src_master : 1; \ - u8 smsize = __slave ? __slave->src_msize : DW_DMA_MSIZE_16; \ - u8 dmsize = __slave ? __slave->dst_msize : DW_DMA_MSIZE_16; \ +#define DWC_DEFAULT_CTLLO(_chan) ({ \ + struct dw_dma_slave *__slave = (_chan->private); \ + struct dw_dma_chan *_dwc = to_dw_dma_chan(_chan); \ + struct dma_slave_config *_sconfig = &_dwc->dma_sconfig; \ + int _dms = __slave ? __slave->dst_master : 0; \ + int _sms = __slave ? __slave->src_master : 1; \ + u8 _smsize = __slave ? _sconfig->src_maxburst : \ + DW_DMA_MSIZE_16; \ + u8 _dmsize = __slave ? _sconfig->dst_maxburst : \ + DW_DMA_MSIZE_16; \ \ - (DWC_CTLL_DST_MSIZE(dmsize) \ - | DWC_CTLL_SRC_MSIZE(smsize) \ + (DWC_CTLL_DST_MSIZE(_dmsize) \ + | DWC_CTLL_SRC_MSIZE(_smsize) \ | DWC_CTLL_LLP_D_EN \ | DWC_CTLL_LLP_S_EN \ - | DWC_CTLL_DMS(dms) \ - | DWC_CTLL_SMS(sms)); \ + | DWC_CTLL_DMS(_dms) \ + | DWC_CTLL_SMS(_sms)); \ }) /* @@ -656,7 +661,7 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, else src_width = dst_width = 0; - ctllo = DWC_DEFAULT_CTLLO(chan->private) + ctllo = DWC_DEFAULT_CTLLO(chan) | DWC_CTLL_DST_WIDTH(dst_width) | DWC_CTLL_SRC_WIDTH(src_width) | DWC_CTLL_DST_INC @@ -717,6 +722,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, { struct dw_dma_chan *dwc = to_dw_dma_chan(chan); struct dw_dma_slave *dws = chan->private; + struct dma_slave_config *sconfig = &dwc->dma_sconfig; struct dw_desc *prev; struct dw_desc *first; u32 ctllo; @@ -732,17 +738,20 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, if (unlikely(!dws || !sg_len)) return NULL; - reg_width = dws->reg_width; prev = first = NULL; switch (direction) { case DMA_MEM_TO_DEV: - ctllo = (DWC_DEFAULT_CTLLO(chan->private) + reg_width = __fls(sconfig->dst_addr_width); + reg = sconfig->dst_addr; + ctllo = (DWC_DEFAULT_CTLLO(chan) | DWC_CTLL_DST_WIDTH(reg_width) | DWC_CTLL_DST_FIX - | DWC_CTLL_SRC_INC - | DWC_CTLL_FC(dws->fc)); - reg = dws->tx_reg; + | DWC_CTLL_SRC_INC); + + ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_M2P) : + DWC_CTLL_FC(DW_DMA_FC_D_M2P); + for_each_sg(sgl, sg, sg_len, i) { struct dw_desc *desc; u32 len, dlen, mem; @@ -800,13 +809,16 @@ slave_sg_todev_fill_desc: } break; case DMA_DEV_TO_MEM: - ctllo = (DWC_DEFAULT_CTLLO(chan->private) + reg_width = __fls(sconfig->src_addr_width); + reg = sconfig->src_addr; + ctllo = (DWC_DEFAULT_CTLLO(chan) | DWC_CTLL_SRC_WIDTH(reg_width) | DWC_CTLL_DST_INC - | DWC_CTLL_SRC_FIX - | DWC_CTLL_FC(dws->fc)); + | DWC_CTLL_SRC_FIX); + + ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_P2M) : + DWC_CTLL_FC(DW_DMA_FC_D_P2M); - reg = dws->rx_reg; for_each_sg(sgl, sg, sg_len, i) { struct dw_desc *desc; u32 len, dlen, mem; @@ -884,6 +896,39 @@ err_desc_get: return NULL; } +/* + * Fix sconfig's burst size according to dw_dmac. We need to convert them as: + * 1 -> 0, 4 -> 1, 8 -> 2, 16 -> 3. + * + * NOTE: burst size 2 is not supported by controller. + * + * This can be done by finding least significant bit set: n & (n - 1) + */ +static inline void convert_burst(u32 *maxburst) +{ + if (*maxburst > 1) + *maxburst = fls(*maxburst) - 2; + else + *maxburst = 0; +} + +static int +set_runtime_config(struct dma_chan *chan, struct dma_slave_config *sconfig) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + + /* Check if it is chan is configured for slave transfers */ + if (!chan->private) + return -EINVAL; + + memcpy(&dwc->dma_sconfig, sconfig, sizeof(*sconfig)); + + convert_burst(&dwc->dma_sconfig.src_maxburst); + convert_burst(&dwc->dma_sconfig.dst_maxburst); + + return 0; +} + static int dwc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long arg) { @@ -933,8 +978,11 @@ static int dwc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, /* Flush all pending and queued descriptors */ list_for_each_entry_safe(desc, _desc, &list, desc_node) dwc_descriptor_complete(dwc, desc, false); - } else + } else if (cmd == DMA_SLAVE_CONFIG) { + return set_runtime_config(chan, (struct dma_slave_config *)arg); + } else { return -ENXIO; + } return 0; } @@ -1167,11 +1215,11 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, enum dma_transfer_direction direction) { struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dma_slave_config *sconfig = &dwc->dma_sconfig; struct dw_cyclic_desc *cdesc; struct dw_cyclic_desc *retval = NULL; struct dw_desc *desc; struct dw_desc *last = NULL; - struct dw_dma_slave *dws = chan->private; unsigned long was_cyclic; unsigned int reg_width; unsigned int periods; @@ -1195,7 +1243,12 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, } retval = ERR_PTR(-EINVAL); - reg_width = dws->reg_width; + + if (direction == DMA_MEM_TO_DEV) + reg_width = __ffs(sconfig->dst_addr_width); + else + reg_width = __ffs(sconfig->src_addr_width); + periods = buf_len / period_len; /* Check for too big/unaligned periods and unaligned DMA buffer. */ @@ -1228,26 +1281,34 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, switch (direction) { case DMA_MEM_TO_DEV: - desc->lli.dar = dws->tx_reg; + desc->lli.dar = sconfig->dst_addr; desc->lli.sar = buf_addr + (period_len * i); - desc->lli.ctllo = (DWC_DEFAULT_CTLLO(chan->private) + desc->lli.ctllo = (DWC_DEFAULT_CTLLO(chan) | DWC_CTLL_DST_WIDTH(reg_width) | DWC_CTLL_SRC_WIDTH(reg_width) | DWC_CTLL_DST_FIX | DWC_CTLL_SRC_INC - | DWC_CTLL_FC(dws->fc) | DWC_CTLL_INT_EN); + + desc->lli.ctllo |= sconfig->device_fc ? + DWC_CTLL_FC(DW_DMA_FC_P_M2P) : + DWC_CTLL_FC(DW_DMA_FC_D_M2P); + break; case DMA_DEV_TO_MEM: desc->lli.dar = buf_addr + (period_len * i); - desc->lli.sar = dws->rx_reg; - desc->lli.ctllo = (DWC_DEFAULT_CTLLO(chan->private) + desc->lli.sar = sconfig->src_addr; + desc->lli.ctllo = (DWC_DEFAULT_CTLLO(chan) | DWC_CTLL_SRC_WIDTH(reg_width) | DWC_CTLL_DST_WIDTH(reg_width) | DWC_CTLL_DST_INC | DWC_CTLL_SRC_FIX - | DWC_CTLL_FC(dws->fc) | DWC_CTLL_INT_EN); + + desc->lli.ctllo |= sconfig->device_fc ? + DWC_CTLL_FC(DW_DMA_FC_P_P2M) : + DWC_CTLL_FC(DW_DMA_FC_D_P2M); + break; default: break; diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index 5eef6946a367..2005d301803d 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -153,6 +153,9 @@ struct dw_dma_chan { struct dw_cyclic_desc *cdesc; unsigned int descs_allocated; + + /* configuration passed via DMA_SLAVE_CONFIG */ + struct dma_slave_config dma_sconfig; }; static inline struct dw_dma_chan_regs __iomem * From e2b35f3dbfc080f15b72834d08f04f0269dbe9be Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 1 Feb 2012 16:12:27 +0530 Subject: [PATCH 25/67] dmaengine/dw_dmac: Fix dw_dmac user drivers to adapt to slave_config changes There are few existing user drivers of dw_dmac. They will break as soon as we remove unused fields from struct dw_dma_slave. This patch focuses to fix these user drivers to use dma_slave_config() routine. Signed-off-by: Viresh Kumar Signed-off-by: Vinod Koul --- arch/avr32/mach-at32ap/at32ap700x.c | 13 ------ .../mach-at32ap/include/mach/atmel-mci.h | 7 ---- drivers/mmc/host/atmel-mci.c | 24 +++++++---- sound/atmel/abdac.c | 18 ++++++-- sound/atmel/ac97c.c | 41 +++++++++++++++---- 5 files changed, 63 insertions(+), 40 deletions(-) diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c index 402a7bb72669..43c44e77d83c 100644 --- a/arch/avr32/mach-at32ap/at32ap700x.c +++ b/arch/avr32/mach-at32ap/at32ap700x.c @@ -1353,7 +1353,6 @@ at32_add_device_mci(unsigned int id, struct mci_platform_data *data) goto fail; slave->sdata.dma_dev = &dw_dmac0_device.dev; - slave->sdata.reg_width = DW_DMA_SLAVE_WIDTH_32BIT; slave->sdata.cfg_hi = (DWC_CFGH_SRC_PER(0) | DWC_CFGH_DST_PER(1)); slave->sdata.cfg_lo &= ~(DWC_CFGL_HS_DST_POL @@ -2048,27 +2047,19 @@ at32_add_device_ac97c(unsigned int id, struct ac97c_platform_data *data, /* Check if DMA slave interface for capture should be configured. */ if (flags & AC97C_CAPTURE) { rx_dws->dma_dev = &dw_dmac0_device.dev; - rx_dws->reg_width = DW_DMA_SLAVE_WIDTH_16BIT; rx_dws->cfg_hi = DWC_CFGH_SRC_PER(3); rx_dws->cfg_lo &= ~(DWC_CFGL_HS_DST_POL | DWC_CFGL_HS_SRC_POL); rx_dws->src_master = 0; rx_dws->dst_master = 1; - rx_dws->src_msize = DW_DMA_MSIZE_1; - rx_dws->dst_msize = DW_DMA_MSIZE_1; - rx_dws->fc = DW_DMA_FC_D_P2M; } /* Check if DMA slave interface for playback should be configured. */ if (flags & AC97C_PLAYBACK) { tx_dws->dma_dev = &dw_dmac0_device.dev; - tx_dws->reg_width = DW_DMA_SLAVE_WIDTH_16BIT; tx_dws->cfg_hi = DWC_CFGH_DST_PER(4); tx_dws->cfg_lo &= ~(DWC_CFGL_HS_DST_POL | DWC_CFGL_HS_SRC_POL); tx_dws->src_master = 0; tx_dws->dst_master = 1; - tx_dws->src_msize = DW_DMA_MSIZE_1; - tx_dws->dst_msize = DW_DMA_MSIZE_1; - tx_dws->fc = DW_DMA_FC_D_M2P; } if (platform_device_add_data(pdev, data, @@ -2138,14 +2129,10 @@ at32_add_device_abdac(unsigned int id, struct atmel_abdac_pdata *data) dws = &data->dws; dws->dma_dev = &dw_dmac0_device.dev; - dws->reg_width = DW_DMA_SLAVE_WIDTH_32BIT; dws->cfg_hi = DWC_CFGH_DST_PER(2); dws->cfg_lo &= ~(DWC_CFGL_HS_DST_POL | DWC_CFGL_HS_SRC_POL); dws->src_master = 0; dws->dst_master = 1; - dws->src_msize = DW_DMA_MSIZE_1; - dws->dst_msize = DW_DMA_MSIZE_1; - dws->fc = DW_DMA_FC_D_M2P; if (platform_device_add_data(pdev, data, sizeof(struct atmel_abdac_pdata))) diff --git a/arch/avr32/mach-at32ap/include/mach/atmel-mci.h b/arch/avr32/mach-at32ap/include/mach/atmel-mci.h index a9b38967f703..4bba58561d5c 100644 --- a/arch/avr32/mach-at32ap/include/mach/atmel-mci.h +++ b/arch/avr32/mach-at32ap/include/mach/atmel-mci.h @@ -14,11 +14,4 @@ struct mci_dma_data { #define slave_data_ptr(s) (&(s)->sdata) #define find_slave_dev(s) ((s)->sdata.dma_dev) -#define setup_dma_addr(s, t, r) do { \ - if (s) { \ - (s)->sdata.tx_reg = (t); \ - (s)->sdata.rx_reg = (r); \ - } \ -} while (0) - #endif /* __MACH_ATMEL_MCI_H */ diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index fcfe1eb5acc8..3ba865ddebc4 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -173,6 +174,7 @@ struct atmel_mci { struct atmel_mci_dma dma; struct dma_chan *data_chan; + struct dma_slave_config dma_conf; u32 cmd_status; u32 data_status; @@ -863,15 +865,16 @@ atmci_prepare_data_dma(struct atmel_mci *host, struct mmc_data *data) if (data->flags & MMC_DATA_READ) { direction = DMA_FROM_DEVICE; - slave_dirn = DMA_DEV_TO_MEM; + host->dma_conf.direction = slave_dirn = DMA_DEV_TO_MEM; } else { direction = DMA_TO_DEVICE; - slave_dirn = DMA_MEM_TO_DEV; + host->dma_conf.direction = slave_dirn = DMA_MEM_TO_DEV; } sglen = dma_map_sg(chan->device->dev, data->sg, data->sg_len, direction); + dmaengine_slave_config(chan, &host->dma_conf); desc = chan->device->device_prep_slave_sg(chan, data->sg, sglen, slave_dirn, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); @@ -1957,22 +1960,27 @@ static void atmci_configure_dma(struct atmel_mci *host) if (pdata && find_slave_dev(pdata->dma_slave)) { dma_cap_mask_t mask; - setup_dma_addr(pdata->dma_slave, - host->mapbase + ATMCI_TDR, - host->mapbase + ATMCI_RDR); - /* Try to grab a DMA channel */ dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); host->dma.chan = dma_request_channel(mask, atmci_filter, pdata->dma_slave); } - if (!host->dma.chan) + if (!host->dma.chan) { dev_notice(&host->pdev->dev, "DMA not available, using PIO\n"); - else + } else { dev_info(&host->pdev->dev, "Using %s for DMA transfers\n", dma_chan_name(host->dma.chan)); + + host->dma_conf.src_addr = host->mapbase + ATMCI_RDR; + host->dma_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + host->dma_conf.src_maxburst = 1; + host->dma_conf.dst_addr = host->mapbase + ATMCI_TDR; + host->dma_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + host->dma_conf.dst_maxburst = 1; + host->dma_conf.device_fc = false; + } } static inline unsigned int atmci_get_version(struct atmel_mci *host) diff --git a/sound/atmel/abdac.c b/sound/atmel/abdac.c index 4fa1dbd8ee83..f7c2bb08055d 100644 --- a/sound/atmel/abdac.c +++ b/sound/atmel/abdac.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -467,15 +468,24 @@ static int __devinit atmel_abdac_probe(struct platform_device *pdev) snd_card_set_dev(card, &pdev->dev); if (pdata->dws.dma_dev) { - struct dw_dma_slave *dws = &pdata->dws; dma_cap_mask_t mask; - dws->tx_reg = regs->start + DAC_DATA; - dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); - dac->dma.chan = dma_request_channel(mask, filter, dws); + dac->dma.chan = dma_request_channel(mask, filter, &pdata->dws); + if (dac->dma.chan) { + struct dma_slave_config dma_conf = { + .dst_addr = regs->start + DAC_DATA, + .dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .src_maxburst = 1, + .dst_maxburst = 1, + .direction = DMA_MEM_TO_DEV, + .device_fc = false, + }; + + dmaengine_slave_config(dac->dma.chan, &dma_conf); + } } if (!pdata->dws.dma_dev || !dac->dma.chan) { dev_dbg(&pdev->dev, "DMA not available\n"); diff --git a/sound/atmel/ac97c.c b/sound/atmel/ac97c.c index 61dade698358..115313ef54d6 100644 --- a/sound/atmel/ac97c.c +++ b/sound/atmel/ac97c.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -1014,16 +1015,28 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev) if (cpu_is_at32ap7000()) { if (pdata->rx_dws.dma_dev) { - struct dw_dma_slave *dws = &pdata->rx_dws; dma_cap_mask_t mask; - dws->rx_reg = regs->start + AC97C_CARHR + 2; - dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); chip->dma.rx_chan = dma_request_channel(mask, filter, - dws); + &pdata->rx_dws); + if (chip->dma.rx_chan) { + struct dma_slave_config dma_conf = { + .src_addr = regs->start + AC97C_CARHR + + 2, + .src_addr_width = + DMA_SLAVE_BUSWIDTH_2_BYTES, + .src_maxburst = 1, + .dst_maxburst = 1, + .direction = DMA_DEV_TO_MEM, + .device_fc = false, + }; + + dmaengine_slave_config(chip->dma.rx_chan, + &dma_conf); + } dev_info(&chip->pdev->dev, "using %s for DMA RX\n", dev_name(&chip->dma.rx_chan->dev->device)); @@ -1031,16 +1044,28 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev) } if (pdata->tx_dws.dma_dev) { - struct dw_dma_slave *dws = &pdata->tx_dws; dma_cap_mask_t mask; - dws->tx_reg = regs->start + AC97C_CATHR + 2; - dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); chip->dma.tx_chan = dma_request_channel(mask, filter, - dws); + &pdata->tx_dws); + if (chip->dma.tx_chan) { + struct dma_slave_config dma_conf = { + .dst_addr = regs->start + AC97C_CATHR + + 2, + .dst_addr_width = + DMA_SLAVE_BUSWIDTH_2_BYTES, + .src_maxburst = 1, + .dst_maxburst = 1, + .direction = DMA_MEM_TO_DEV, + .device_fc = false, + }; + + dmaengine_slave_config(chip->dma.tx_chan, + &dma_conf); + } dev_info(&chip->pdev->dev, "using %s for DMA TX\n", dev_name(&chip->dma.tx_chan->dev->device)); From a1c46016794fdd83b7993303915ec7f4a06682b7 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 1 Feb 2012 16:12:28 +0530 Subject: [PATCH 26/67] dmaengine/dw_dmac: Remove unused fields in struct dw_dma_slave Signed-off-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac_regs.h | 12 ++++++++++++ include/linux/dw_dmac.h | 38 -------------------------------------- 2 files changed, 12 insertions(+), 38 deletions(-) diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index 2005d301803d..eec0481a12f7 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -13,6 +13,18 @@ #define DW_DMA_MAX_NR_CHANNELS 8 +/* flow controller */ +enum dw_dma_fc { + DW_DMA_FC_D_M2M, + DW_DMA_FC_D_M2P, + DW_DMA_FC_D_P2M, + DW_DMA_FC_D_P2P, + DW_DMA_FC_P_P2M, + DW_DMA_FC_SP_P2P, + DW_DMA_FC_P_M2P, + DW_DMA_FC_DP_P2P, +}; + /* * Redefine this macro to handle differences between 32- and 64-bit * addressing, big vs. little endian, etc. diff --git a/include/linux/dw_dmac.h b/include/linux/dw_dmac.h index f2c64f92c4a0..2412e02d7c0f 100644 --- a/include/linux/dw_dmac.h +++ b/include/linux/dw_dmac.h @@ -31,18 +31,6 @@ struct dw_dma_platform_data { unsigned char chan_priority; }; -/** - * enum dw_dma_slave_width - DMA slave register access width. - * @DMA_SLAVE_WIDTH_8BIT: Do 8-bit slave register accesses - * @DMA_SLAVE_WIDTH_16BIT: Do 16-bit slave register accesses - * @DMA_SLAVE_WIDTH_32BIT: Do 32-bit slave register accesses - */ -enum dw_dma_slave_width { - DW_DMA_SLAVE_WIDTH_8BIT, - DW_DMA_SLAVE_WIDTH_16BIT, - DW_DMA_SLAVE_WIDTH_32BIT, -}; - /* bursts size */ enum dw_dma_msize { DW_DMA_MSIZE_1, @@ -55,47 +43,21 @@ enum dw_dma_msize { DW_DMA_MSIZE_256, }; -/* flow controller */ -enum dw_dma_fc { - DW_DMA_FC_D_M2M, - DW_DMA_FC_D_M2P, - DW_DMA_FC_D_P2M, - DW_DMA_FC_D_P2P, - DW_DMA_FC_P_P2M, - DW_DMA_FC_SP_P2P, - DW_DMA_FC_P_M2P, - DW_DMA_FC_DP_P2P, -}; - /** * struct dw_dma_slave - Controller-specific information about a slave * * @dma_dev: required DMA master device - * @tx_reg: physical address of data register used for - * memory-to-peripheral transfers - * @rx_reg: physical address of data register used for - * peripheral-to-memory transfers - * @reg_width: peripheral register width * @cfg_hi: Platform-specific initializer for the CFG_HI register * @cfg_lo: Platform-specific initializer for the CFG_LO register * @src_master: src master for transfers on allocated channel. * @dst_master: dest master for transfers on allocated channel. - * @src_msize: src burst size. - * @dst_msize: dest burst size. - * @fc: flow controller for DMA transfer */ struct dw_dma_slave { struct device *dma_dev; - dma_addr_t tx_reg; - dma_addr_t rx_reg; - enum dw_dma_slave_width reg_width; u32 cfg_hi; u32 cfg_lo; u8 src_master; u8 dst_master; - u8 src_msize; - u8 dst_msize; - u8 fc; }; /* Platform-configurable bits in CFG_HI */ From 865d9438eb1f7670d2e88849f059db551b320887 Mon Sep 17 00:00:00 2001 From: Danny Kukawka Date: Wed, 15 Feb 2012 20:20:26 +0100 Subject: [PATCH 27/67] drivers/dma: linux/module.h included twice drivers/dma/imx-dma.c and drivers/dma/imx-sdma.c included 'linux/module.h' twice, remove the duplicates. Signed-off-by: Danny Kukawka Signed-off-by: Vinod Koul --- drivers/dma/imx-dma.c | 1 - drivers/dma/imx-sdma.c | 1 - 2 files changed, 2 deletions(-) diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index 3296a7337f25..b51391adb289 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index bf736ad679ca..8220f5703c79 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -36,7 +36,6 @@ #include #include #include -#include #include #include From d8b53489d4c80490a70327fce6657816e33fafb3 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 21 Feb 2012 12:51:59 -0200 Subject: [PATCH 28/67] dma: dmaengine: Distinguish between 'dmaengine: failed to get' messages The message "dmaengine: failed to get" can come from two possible locations within dmaengine.c. In order to distinguish between them, replace "dmaengine" with __func__ string so that the source function of the error message can be easily identified. Signed-off-by: Fabio Estevam Signed-off-by: Vinod Koul --- drivers/dma/dmaengine.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index a6c6051ec858..767bcc31b365 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -510,8 +510,8 @@ struct dma_chan *__dma_request_channel(dma_cap_mask_t *mask, dma_filter_fn fn, v dma_chan_name(chan)); list_del_rcu(&device->global_node); } else if (err) - pr_debug("dmaengine: failed to get %s: (%d)\n", - dma_chan_name(chan), err); + pr_debug("%s: failed to get %s: (%d)\n", + __func__, dma_chan_name(chan), err); else break; if (--device->privatecnt == 0) @@ -564,8 +564,8 @@ void dmaengine_get(void) list_del_rcu(&device->global_node); break; } else if (err) - pr_err("dmaengine: failed to get %s: (%d)\n", - dma_chan_name(chan), err); + pr_err("%s: failed to get %s: (%d)\n", + __func__, dma_chan_name(chan), err); } } From 6c05f09155f40368c51ce00b8291401858e49bcb Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Tue, 28 Feb 2012 17:08:17 +0100 Subject: [PATCH 29/67] dmaengine: Add support for MEMCPY for imx-dma. MEMCPY transfers allow DMA copies from memory to memory. This patch has been tested with dmatest device driver. Signed-off-by: Javier Martin Acked-by: Sascha Hauer Signed-off-by: Vinod Koul --- drivers/dma/imx-dma.c | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index b51391adb289..9a1797873169 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -195,7 +195,8 @@ static int imxdma_alloc_chan_resources(struct dma_chan *chan) struct imxdma_channel *imxdmac = to_imxdma_chan(chan); struct imx_dma_data *data = chan->private; - imxdmac->dma_request = data->dma_request; + if (data != NULL) + imxdmac->dma_request = data->dma_request; dma_async_tx_descriptor_init(&imxdmac->desc, chan); imxdmac->desc.tx_submit = imxdma_tx_submit; @@ -327,6 +328,36 @@ static struct dma_async_tx_descriptor *imxdma_prep_dma_cyclic( return &imxdmac->desc; } +static struct dma_async_tx_descriptor *imxdma_prep_dma_memcpy( + struct dma_chan *chan, dma_addr_t dest, + dma_addr_t src, size_t len, unsigned long flags) +{ + struct imxdma_channel *imxdmac = to_imxdma_chan(chan); + struct imxdma_engine *imxdma = imxdmac->imxdma; + int ret; + + dev_dbg(imxdma->dev, "%s channel: %d src=0x%x dst=0x%x len=%d\n", + __func__, imxdmac->channel, src, dest, len); + + if (imxdmac->status == DMA_IN_PROGRESS) + return NULL; + imxdmac->status = DMA_IN_PROGRESS; + + ret = imx_dma_config_channel(imxdmac->imxdma_channel, + IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR, + IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR, + 0, 0); + if (ret) + return NULL; + + ret = imx_dma_setup_single(imxdmac->imxdma_channel, src, len, + dest, DMA_MODE_WRITE); + if (ret) + return NULL; + + return &imxdmac->desc; +} + static void imxdma_issue_pending(struct dma_chan *chan) { struct imxdma_channel *imxdmac = to_imxdma_chan(chan); @@ -348,6 +379,7 @@ static int __init imxdma_probe(struct platform_device *pdev) dma_cap_set(DMA_SLAVE, imxdma->dma_device.cap_mask); dma_cap_set(DMA_CYCLIC, imxdma->dma_device.cap_mask); + dma_cap_set(DMA_MEMCPY, imxdma->dma_device.cap_mask); /* Initialize channel parameters */ for (i = 0; i < MAX_DMA_CHANNELS; i++) { @@ -381,11 +413,13 @@ static int __init imxdma_probe(struct platform_device *pdev) imxdma->dma_device.device_tx_status = imxdma_tx_status; imxdma->dma_device.device_prep_slave_sg = imxdma_prep_slave_sg; imxdma->dma_device.device_prep_dma_cyclic = imxdma_prep_dma_cyclic; + imxdma->dma_device.device_prep_dma_memcpy = imxdma_prep_dma_memcpy; imxdma->dma_device.device_control = imxdma_control; imxdma->dma_device.device_issue_pending = imxdma_issue_pending; platform_set_drvdata(pdev, imxdma); + imxdma->dma_device.copy_align = 2; /* 2^2 = 4 bytes alignment */ imxdma->dma_device.dev->dma_parms = &imxdma->dma_parms; dma_set_max_seg_size(imxdma->dma_device.dev, 0xffffff); From 9e15db7ce949e9f2d8bb6ce32a74212a4f662370 Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Fri, 2 Mar 2012 09:28:47 +0100 Subject: [PATCH 30/67] dmaengine: Add support for multiple descriptors for imx-dma. dmaengine specifies the possibility that several descriptors can be queued for transfer. It also indicates that tasklets must be used for DMA callbacks. Acked-by: Sascha Hauer Signed-off-by: Javier Martin Signed-off-by: Vinod Koul --- drivers/dma/imx-dma.c | 332 ++++++++++++++++++++++++++++++++---------- 1 file changed, 258 insertions(+), 74 deletions(-) diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index 9a1797873169..85b3d3c21d91 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -5,6 +5,7 @@ * found on i.MX1/21/27 * * Copyright 2010 Sascha Hauer, Pengutronix + * Copyright 2012 Javier Martin, Vista Silicon * * The code contained herein is licensed under the GNU General Public * License. You may obtain a copy of the GNU General Public License @@ -13,6 +14,7 @@ * http://www.opensource.org/licenses/gpl-license.html * http://www.gnu.org/copyleft/gpl.html */ + #include #include #include @@ -29,19 +31,52 @@ #include #include +#define IMXDMA_MAX_CHAN_DESCRIPTORS 16 + +enum imxdma_prep_type { + IMXDMA_DESC_MEMCPY, + IMXDMA_DESC_INTERLEAVED, + IMXDMA_DESC_SLAVE_SG, + IMXDMA_DESC_CYCLIC, +}; + +struct imxdma_desc { + struct list_head node; + struct dma_async_tx_descriptor desc; + enum dma_status status; + dma_addr_t src; + dma_addr_t dest; + size_t len; + unsigned int dmamode; + enum imxdma_prep_type type; + /* For memcpy and interleaved */ + unsigned int config_port; + unsigned int config_mem; + /* For interleaved transfers */ + unsigned int x; + unsigned int y; + unsigned int w; + /* For slave sg and cyclic */ + struct scatterlist *sg; + unsigned int sgcount; +}; + struct imxdma_channel { struct imxdma_engine *imxdma; unsigned int channel; unsigned int imxdma_channel; + struct tasklet_struct dma_tasklet; + struct list_head ld_free; + struct list_head ld_queue; + struct list_head ld_active; + int descs_allocated; enum dma_slave_buswidth word_size; dma_addr_t per_address; u32 watermark_level; struct dma_chan chan; spinlock_t lock; - struct dma_async_tx_descriptor desc; dma_cookie_t last_completed; - enum dma_status status; int dma_request; struct scatterlist *sg_list; }; @@ -60,27 +95,31 @@ static struct imxdma_channel *to_imxdma_chan(struct dma_chan *chan) return container_of(chan, struct imxdma_channel, chan); } -static void imxdma_handle(struct imxdma_channel *imxdmac) +static inline bool imxdma_chan_is_doing_cyclic(struct imxdma_channel *imxdmac) { - if (imxdmac->desc.callback) - imxdmac->desc.callback(imxdmac->desc.callback_param); - imxdmac->last_completed = imxdmac->desc.cookie; + struct imxdma_desc *desc; + + if (!list_empty(&imxdmac->ld_active)) { + desc = list_first_entry(&imxdmac->ld_active, struct imxdma_desc, + node); + if (desc->type == IMXDMA_DESC_CYCLIC) + return true; + } + return false; } static void imxdma_irq_handler(int channel, void *data) { struct imxdma_channel *imxdmac = data; - imxdmac->status = DMA_SUCCESS; - imxdma_handle(imxdmac); + tasklet_schedule(&imxdmac->dma_tasklet); } static void imxdma_err_handler(int channel, void *data, int error) { struct imxdma_channel *imxdmac = data; - imxdmac->status = DMA_ERROR; - imxdma_handle(imxdmac); + tasklet_schedule(&imxdmac->dma_tasklet); } static void imxdma_progression(int channel, void *data, @@ -88,8 +127,88 @@ static void imxdma_progression(int channel, void *data, { struct imxdma_channel *imxdmac = data; - imxdmac->status = DMA_SUCCESS; - imxdma_handle(imxdmac); + tasklet_schedule(&imxdmac->dma_tasklet); +} + +static int imxdma_xfer_desc(struct imxdma_desc *d) +{ + struct imxdma_channel *imxdmac = to_imxdma_chan(d->desc.chan); + int ret; + + /* Configure and enable */ + switch (d->type) { + case IMXDMA_DESC_MEMCPY: + ret = imx_dma_config_channel(imxdmac->imxdma_channel, + d->config_port, d->config_mem, 0, 0); + if (ret < 0) + return ret; + ret = imx_dma_setup_single(imxdmac->imxdma_channel, d->src, + d->len, d->dest, d->dmamode); + if (ret < 0) + return ret; + break; + case IMXDMA_DESC_CYCLIC: + ret = imx_dma_setup_progression_handler(imxdmac->imxdma_channel, + imxdma_progression); + if (ret < 0) + return ret; + /* + * We fall through here since cyclic transfer is the same as + * slave_sg adding a progression handler and a specific sg + * configuration which is done in 'imxdma_prep_dma_cyclic'. + */ + case IMXDMA_DESC_SLAVE_SG: + if (d->dmamode == DMA_MODE_READ) + ret = imx_dma_setup_sg(imxdmac->imxdma_channel, d->sg, + d->sgcount, d->len, d->src, d->dmamode); + else + ret = imx_dma_setup_sg(imxdmac->imxdma_channel, d->sg, + d->sgcount, d->len, d->dest, d->dmamode); + if (ret < 0) + return ret; + break; + default: + return -EINVAL; + } + imx_dma_enable(imxdmac->imxdma_channel); + return 0; +} + +static void imxdma_tasklet(unsigned long data) +{ + struct imxdma_channel *imxdmac = (void *)data; + struct imxdma_engine *imxdma = imxdmac->imxdma; + struct imxdma_desc *desc; + + spin_lock(&imxdmac->lock); + + if (list_empty(&imxdmac->ld_active)) { + /* Someone might have called terminate all */ + goto out; + } + desc = list_first_entry(&imxdmac->ld_active, struct imxdma_desc, node); + + if (desc->desc.callback) + desc->desc.callback(desc->desc.callback_param); + + imxdmac->last_completed = desc->desc.cookie; + + /* If we are dealing with a cyclic descriptor keep it on ld_active */ + if (imxdma_chan_is_doing_cyclic(imxdmac)) + goto out; + + list_move_tail(imxdmac->ld_active.next, &imxdmac->ld_free); + + if (!list_empty(&imxdmac->ld_queue)) { + desc = list_first_entry(&imxdmac->ld_queue, struct imxdma_desc, + node); + list_move_tail(imxdmac->ld_queue.next, &imxdmac->ld_active); + if (imxdma_xfer_desc(desc) < 0) + dev_warn(imxdma->dev, "%s: channel: %d couldn't xfer desc\n", + __func__, imxdmac->channel); + } +out: + spin_unlock(&imxdmac->lock); } static int imxdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, @@ -98,12 +217,17 @@ static int imxdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, struct imxdma_channel *imxdmac = to_imxdma_chan(chan); struct dma_slave_config *dmaengine_cfg = (void *)arg; int ret; + unsigned long flags; unsigned int mode = 0; switch (cmd) { case DMA_TERMINATE_ALL: - imxdmac->status = DMA_ERROR; imx_dma_disable(imxdmac->imxdma_channel); + + spin_lock_irqsave(&imxdmac->lock, flags); + list_splice_tail_init(&imxdmac->ld_active, &imxdmac->ld_free); + list_splice_tail_init(&imxdmac->ld_queue, &imxdmac->ld_free); + spin_unlock_irqrestore(&imxdmac->lock, flags); return 0; case DMA_SLAVE_CONFIG: if (dmaengine_cfg->direction == DMA_DEV_TO_MEM) { @@ -154,11 +278,14 @@ static enum dma_status imxdma_tx_status(struct dma_chan *chan, struct imxdma_channel *imxdmac = to_imxdma_chan(chan); dma_cookie_t last_used; enum dma_status ret; + unsigned long flags; + spin_lock_irqsave(&imxdmac->lock, flags); last_used = chan->cookie; ret = dma_async_is_complete(cookie, imxdmac->last_completed, last_used); dma_set_tx_state(txstate, imxdmac->last_completed, last_used, 0); + spin_unlock_irqrestore(&imxdmac->lock, flags); return ret; } @@ -171,7 +298,6 @@ static dma_cookie_t imxdma_assign_cookie(struct imxdma_channel *imxdma) cookie = 1; imxdma->chan.cookie = cookie; - imxdma->desc.cookie = cookie; return cookie; } @@ -180,12 +306,15 @@ static dma_cookie_t imxdma_tx_submit(struct dma_async_tx_descriptor *tx) { struct imxdma_channel *imxdmac = to_imxdma_chan(tx->chan); dma_cookie_t cookie; + unsigned long flags; - spin_lock_irq(&imxdmac->lock); + spin_lock_irqsave(&imxdmac->lock, flags); + list_move_tail(imxdmac->ld_free.next, &imxdmac->ld_queue); cookie = imxdma_assign_cookie(imxdmac); + tx->cookie = cookie; - spin_unlock_irq(&imxdmac->lock); + spin_unlock_irqrestore(&imxdmac->lock, flags); return cookie; } @@ -198,21 +327,48 @@ static int imxdma_alloc_chan_resources(struct dma_chan *chan) if (data != NULL) imxdmac->dma_request = data->dma_request; - dma_async_tx_descriptor_init(&imxdmac->desc, chan); - imxdmac->desc.tx_submit = imxdma_tx_submit; - /* txd.flags will be overwritten in prep funcs */ - imxdmac->desc.flags = DMA_CTRL_ACK; + while (imxdmac->descs_allocated < IMXDMA_MAX_CHAN_DESCRIPTORS) { + struct imxdma_desc *desc; - imxdmac->status = DMA_SUCCESS; + desc = kzalloc(sizeof(*desc), GFP_KERNEL); + if (!desc) + break; + __memzero(&desc->desc, sizeof(struct dma_async_tx_descriptor)); + dma_async_tx_descriptor_init(&desc->desc, chan); + desc->desc.tx_submit = imxdma_tx_submit; + /* txd.flags will be overwritten in prep funcs */ + desc->desc.flags = DMA_CTRL_ACK; + desc->status = DMA_SUCCESS; - return 0; + list_add_tail(&desc->node, &imxdmac->ld_free); + imxdmac->descs_allocated++; + } + + if (!imxdmac->descs_allocated) + return -ENOMEM; + + return imxdmac->descs_allocated; } static void imxdma_free_chan_resources(struct dma_chan *chan) { struct imxdma_channel *imxdmac = to_imxdma_chan(chan); + struct imxdma_desc *desc, *_desc; + unsigned long flags; + + spin_lock_irqsave(&imxdmac->lock, flags); imx_dma_disable(imxdmac->imxdma_channel); + list_splice_tail_init(&imxdmac->ld_active, &imxdmac->ld_free); + list_splice_tail_init(&imxdmac->ld_queue, &imxdmac->ld_free); + + spin_unlock_irqrestore(&imxdmac->lock, flags); + + list_for_each_entry_safe(desc, _desc, &imxdmac->ld_free, node) { + kfree(desc); + imxdmac->descs_allocated--; + } + INIT_LIST_HEAD(&imxdmac->ld_free); if (imxdmac->sg_list) { kfree(imxdmac->sg_list); @@ -227,23 +383,19 @@ static struct dma_async_tx_descriptor *imxdma_prep_slave_sg( { struct imxdma_channel *imxdmac = to_imxdma_chan(chan); struct scatterlist *sg; - int i, ret, dma_length = 0; - unsigned int dmamode; + int i, dma_length = 0; + struct imxdma_desc *desc; - if (imxdmac->status == DMA_IN_PROGRESS) + if (list_empty(&imxdmac->ld_free) || + imxdma_chan_is_doing_cyclic(imxdmac)) return NULL; - imxdmac->status = DMA_IN_PROGRESS; + desc = list_first_entry(&imxdmac->ld_free, struct imxdma_desc, node); for_each_sg(sgl, sg, sg_len, i) { dma_length += sg->length; } - if (direction == DMA_DEV_TO_MEM) - dmamode = DMA_MODE_READ; - else - dmamode = DMA_MODE_WRITE; - switch (imxdmac->word_size) { case DMA_SLAVE_BUSWIDTH_4_BYTES: if (sgl->length & 3 || sgl->dma_address & 3) @@ -259,12 +411,21 @@ static struct dma_async_tx_descriptor *imxdma_prep_slave_sg( return NULL; } - ret = imx_dma_setup_sg(imxdmac->imxdma_channel, sgl, sg_len, - dma_length, imxdmac->per_address, dmamode); - if (ret) - return NULL; + desc->type = IMXDMA_DESC_SLAVE_SG; + desc->sg = sgl; + desc->sgcount = sg_len; + desc->len = dma_length; + if (direction == DMA_DEV_TO_MEM) { + desc->dmamode = DMA_MODE_READ; + desc->src = imxdmac->per_address; + } else { + desc->dmamode = DMA_MODE_WRITE; + desc->dest = imxdmac->per_address; + } + desc->desc.callback = NULL; + desc->desc.callback_param = NULL; - return &imxdmac->desc; + return &desc->desc; } static struct dma_async_tx_descriptor *imxdma_prep_dma_cyclic( @@ -273,23 +434,18 @@ static struct dma_async_tx_descriptor *imxdma_prep_dma_cyclic( { struct imxdma_channel *imxdmac = to_imxdma_chan(chan); struct imxdma_engine *imxdma = imxdmac->imxdma; - int i, ret; + struct imxdma_desc *desc; + int i; unsigned int periods = buf_len / period_len; - unsigned int dmamode; dev_dbg(imxdma->dev, "%s channel: %d buf_len=%d period_len=%d\n", __func__, imxdmac->channel, buf_len, period_len); - if (imxdmac->status == DMA_IN_PROGRESS) + if (list_empty(&imxdmac->ld_free) || + imxdma_chan_is_doing_cyclic(imxdmac)) return NULL; - imxdmac->status = DMA_IN_PROGRESS; - ret = imx_dma_setup_progression_handler(imxdmac->imxdma_channel, - imxdma_progression); - if (ret) { - dev_err(imxdma->dev, "Failed to setup the DMA handler\n"); - return NULL; - } + desc = list_first_entry(&imxdmac->ld_free, struct imxdma_desc, node); if (imxdmac->sg_list) kfree(imxdmac->sg_list); @@ -315,17 +471,21 @@ static struct dma_async_tx_descriptor *imxdma_prep_dma_cyclic( imxdmac->sg_list[periods].page_link = ((unsigned long)imxdmac->sg_list | 0x01) & ~0x02; - if (direction == DMA_DEV_TO_MEM) - dmamode = DMA_MODE_READ; - else - dmamode = DMA_MODE_WRITE; + desc->type = IMXDMA_DESC_CYCLIC; + desc->sg = imxdmac->sg_list; + desc->sgcount = periods; + desc->len = IMX_DMA_LENGTH_LOOP; + if (direction == DMA_DEV_TO_MEM) { + desc->dmamode = DMA_MODE_READ; + desc->src = imxdmac->per_address; + } else { + desc->dmamode = DMA_MODE_WRITE; + desc->dest = imxdmac->per_address; + } + desc->desc.callback = NULL; + desc->desc.callback_param = NULL; - ret = imx_dma_setup_sg(imxdmac->imxdma_channel, imxdmac->sg_list, periods, - IMX_DMA_LENGTH_LOOP, imxdmac->per_address, dmamode); - if (ret) - return NULL; - - return &imxdmac->desc; + return &desc->desc; } static struct dma_async_tx_descriptor *imxdma_prep_dma_memcpy( @@ -334,36 +494,53 @@ static struct dma_async_tx_descriptor *imxdma_prep_dma_memcpy( { struct imxdma_channel *imxdmac = to_imxdma_chan(chan); struct imxdma_engine *imxdma = imxdmac->imxdma; - int ret; + struct imxdma_desc *desc; dev_dbg(imxdma->dev, "%s channel: %d src=0x%x dst=0x%x len=%d\n", __func__, imxdmac->channel, src, dest, len); - if (imxdmac->status == DMA_IN_PROGRESS) - return NULL; - imxdmac->status = DMA_IN_PROGRESS; - - ret = imx_dma_config_channel(imxdmac->imxdma_channel, - IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR, - IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR, - 0, 0); - if (ret) + if (list_empty(&imxdmac->ld_free) || + imxdma_chan_is_doing_cyclic(imxdmac)) return NULL; - ret = imx_dma_setup_single(imxdmac->imxdma_channel, src, len, - dest, DMA_MODE_WRITE); - if (ret) - return NULL; + desc = list_first_entry(&imxdmac->ld_free, struct imxdma_desc, node); - return &imxdmac->desc; + desc->type = IMXDMA_DESC_MEMCPY; + desc->src = src; + desc->dest = dest; + desc->len = len; + desc->dmamode = DMA_MODE_WRITE; + desc->config_port = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR; + desc->config_mem = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR; + desc->desc.callback = NULL; + desc->desc.callback_param = NULL; + + return &desc->desc; } static void imxdma_issue_pending(struct dma_chan *chan) { struct imxdma_channel *imxdmac = to_imxdma_chan(chan); + struct imxdma_engine *imxdma = imxdmac->imxdma; + struct imxdma_desc *desc; + unsigned long flags; - if (imxdmac->status == DMA_IN_PROGRESS) - imx_dma_enable(imxdmac->imxdma_channel); + spin_lock_irqsave(&imxdmac->lock, flags); + if (list_empty(&imxdmac->ld_active) && + !list_empty(&imxdmac->ld_queue)) { + desc = list_first_entry(&imxdmac->ld_queue, + struct imxdma_desc, node); + + if (imxdma_xfer_desc(desc) < 0) { + dev_warn(imxdma->dev, + "%s: channel: %d couldn't issue DMA xfer\n", + __func__, imxdmac->channel); + } else { + list_move_tail(imxdmac->ld_queue.next, + &imxdmac->ld_active); + } + } + spin_unlock_irqrestore(&imxdmac->lock, flags); } static int __init imxdma_probe(struct platform_device *pdev) @@ -398,11 +575,18 @@ static int __init imxdma_probe(struct platform_device *pdev) imxdmac->imxdma = imxdma; spin_lock_init(&imxdmac->lock); + INIT_LIST_HEAD(&imxdmac->ld_queue); + INIT_LIST_HEAD(&imxdmac->ld_free); + INIT_LIST_HEAD(&imxdmac->ld_active); + + tasklet_init(&imxdmac->dma_tasklet, imxdma_tasklet, + (unsigned long)imxdmac); imxdmac->chan.device = &imxdma->dma_device; imxdmac->channel = i; /* Add the channel to the DMAC list */ - list_add_tail(&imxdmac->chan.device_node, &imxdma->dma_device.channels); + list_add_tail(&imxdmac->chan.device_node, + &imxdma->dma_device.channels); } imxdma->dev = &pdev->dev; From 6602b0ddf399d4438fdea17b4b4a65df5ec97bb8 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 29 Feb 2012 11:20:37 -0300 Subject: [PATCH 31/67] dma: imx-sdma: Let the driver be probed even if no firware is found On the device tree case the code bails out when the firmware name cannot be found or if the firmware fails to be requested. Fix this behaviour as the SDMA engine can still operate with the built-in ROM scripts. Signed-off-by: Fabio Estevam Acked-by: Sascha Hauer Signed-off-by: Vinod Koul --- drivers/dma/imx-sdma.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 8220f5703c79..05fa3a1d95a1 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -1396,15 +1396,12 @@ static int __init sdma_probe(struct platform_device *pdev) */ ret = of_property_read_string(np, "fsl,sdma-ram-script-name", &fw_name); - if (ret) { + if (ret) dev_err(&pdev->dev, "failed to get firmware name\n"); - goto err_init; - } - - ret = sdma_get_firmware(sdma, fw_name); - if (ret) { - dev_err(&pdev->dev, "failed to get firmware\n"); - goto err_init; + else { + ret = sdma_get_firmware(sdma, fw_name); + if (ret) + dev_err(&pdev->dev, "failed to get firmware\n"); } } From 6d0d7e2d554a2d1a39ee9397c3136df7a53ff348 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 29 Feb 2012 11:20:38 -0300 Subject: [PATCH 32/67] dma: imx-sdma: Print a message when firmare fails to be requested Print a message when firmare fails to be requested in the case of platform data being used. While at it, distinguish between the error messages of the device tree and platform data cases. Signed-off-by: Fabio Estevam Acked-by: Sascha Hauer Signed-off-by: Vinod Koul --- drivers/dma/imx-sdma.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 05fa3a1d95a1..c4958823518a 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -1387,7 +1387,9 @@ static int __init sdma_probe(struct platform_device *pdev) sdma_add_scripts(sdma, pdata->script_addrs); if (pdata) { - sdma_get_firmware(sdma, pdata->fw_name); + ret = sdma_get_firmware(sdma, pdata->fw_name); + if (ret) + dev_err(&pdev->dev, "failed to get firmware from platform data\n"); } else { /* * Because that device tree does not encode ROM script address, @@ -1401,7 +1403,7 @@ static int __init sdma_probe(struct platform_device *pdev) else { ret = sdma_get_firmware(sdma, fw_name); if (ret) - dev_err(&pdev->dev, "failed to get firmware\n"); + dev_err(&pdev->dev, "failed to get firmware from device tree\n"); } } From b7d861d9394534db94f0fb8f4b9d984f996d0528 Mon Sep 17 00:00:00 2001 From: Boojin Kim Date: Mon, 26 Dec 2011 18:49:52 +0900 Subject: [PATCH 33/67] DMA: PL330: Merge PL330 driver into drivers/dma/ Currently there were two part of DMAC PL330 driver for support old styled s3c-pl330 which has been merged into drivers/dma/pl330.c driver. Actually, there is no reason to separate them now. Basically this patch merges arch/arm/common/pl330.c into drivers/dma/pl330.c driver and removes useless exported symbol, externed function and so on. The newer pl330 driver tested on SMDKV310 and SMDK4212 boards Cc: Jassi Brar Cc: Russell King Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Boojin Kim Signed-off-by: Kukjin Kim Acked-by: Jassi Brar Signed-off-by: Vinod Koul --- arch/arm/common/Kconfig | 3 - arch/arm/common/Makefile | 1 - arch/arm/common/pl330.c | 1959 ----------------------- arch/arm/include/asm/hardware/pl330.h | 217 --- drivers/dma/Kconfig | 1 - drivers/dma/pl330.c | 2114 ++++++++++++++++++++++++- include/linux/amba/pl330.h | 1 - 7 files changed, 2113 insertions(+), 2183 deletions(-) delete mode 100644 arch/arm/common/pl330.c delete mode 100644 arch/arm/include/asm/hardware/pl330.h diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig index 81a933eb0903..a43c758ff0da 100644 --- a/arch/arm/common/Kconfig +++ b/arch/arm/common/Kconfig @@ -24,9 +24,6 @@ config ARM_VIC_NR config ICST bool -config PL330 - bool - config SA1111 bool select DMABOUNCE if !ARCH_PXA diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile index 6ea9b6f3607a..107d6d97c6e1 100644 --- a/arch/arm/common/Makefile +++ b/arch/arm/common/Makefile @@ -5,7 +5,6 @@ obj-$(CONFIG_ARM_GIC) += gic.o obj-$(CONFIG_ARM_VIC) += vic.o obj-$(CONFIG_ICST) += icst.o -obj-$(CONFIG_PL330) += pl330.o obj-$(CONFIG_SA1111) += sa1111.o obj-$(CONFIG_PCI_HOST_VIA82C505) += via82c505.o obj-$(CONFIG_DMABOUNCE) += dmabounce.o diff --git a/arch/arm/common/pl330.c b/arch/arm/common/pl330.c deleted file mode 100644 index d8e44a43047c..000000000000 --- a/arch/arm/common/pl330.c +++ /dev/null @@ -1,1959 +0,0 @@ -/* linux/arch/arm/common/pl330.c - * - * Copyright (C) 2010 Samsung Electronics Co Ltd. - * Jaswinder Singh - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -/* Register and Bit field Definitions */ -#define DS 0x0 -#define DS_ST_STOP 0x0 -#define DS_ST_EXEC 0x1 -#define DS_ST_CMISS 0x2 -#define DS_ST_UPDTPC 0x3 -#define DS_ST_WFE 0x4 -#define DS_ST_ATBRR 0x5 -#define DS_ST_QBUSY 0x6 -#define DS_ST_WFP 0x7 -#define DS_ST_KILL 0x8 -#define DS_ST_CMPLT 0x9 -#define DS_ST_FLTCMP 0xe -#define DS_ST_FAULT 0xf - -#define DPC 0x4 -#define INTEN 0x20 -#define ES 0x24 -#define INTSTATUS 0x28 -#define INTCLR 0x2c -#define FSM 0x30 -#define FSC 0x34 -#define FTM 0x38 - -#define _FTC 0x40 -#define FTC(n) (_FTC + (n)*0x4) - -#define _CS 0x100 -#define CS(n) (_CS + (n)*0x8) -#define CS_CNS (1 << 21) - -#define _CPC 0x104 -#define CPC(n) (_CPC + (n)*0x8) - -#define _SA 0x400 -#define SA(n) (_SA + (n)*0x20) - -#define _DA 0x404 -#define DA(n) (_DA + (n)*0x20) - -#define _CC 0x408 -#define CC(n) (_CC + (n)*0x20) - -#define CC_SRCINC (1 << 0) -#define CC_DSTINC (1 << 14) -#define CC_SRCPRI (1 << 8) -#define CC_DSTPRI (1 << 22) -#define CC_SRCNS (1 << 9) -#define CC_DSTNS (1 << 23) -#define CC_SRCIA (1 << 10) -#define CC_DSTIA (1 << 24) -#define CC_SRCBRSTLEN_SHFT 4 -#define CC_DSTBRSTLEN_SHFT 18 -#define CC_SRCBRSTSIZE_SHFT 1 -#define CC_DSTBRSTSIZE_SHFT 15 -#define CC_SRCCCTRL_SHFT 11 -#define CC_SRCCCTRL_MASK 0x7 -#define CC_DSTCCTRL_SHFT 25 -#define CC_DRCCCTRL_MASK 0x7 -#define CC_SWAP_SHFT 28 - -#define _LC0 0x40c -#define LC0(n) (_LC0 + (n)*0x20) - -#define _LC1 0x410 -#define LC1(n) (_LC1 + (n)*0x20) - -#define DBGSTATUS 0xd00 -#define DBG_BUSY (1 << 0) - -#define DBGCMD 0xd04 -#define DBGINST0 0xd08 -#define DBGINST1 0xd0c - -#define CR0 0xe00 -#define CR1 0xe04 -#define CR2 0xe08 -#define CR3 0xe0c -#define CR4 0xe10 -#define CRD 0xe14 - -#define PERIPH_ID 0xfe0 -#define PCELL_ID 0xff0 - -#define CR0_PERIPH_REQ_SET (1 << 0) -#define CR0_BOOT_EN_SET (1 << 1) -#define CR0_BOOT_MAN_NS (1 << 2) -#define CR0_NUM_CHANS_SHIFT 4 -#define CR0_NUM_CHANS_MASK 0x7 -#define CR0_NUM_PERIPH_SHIFT 12 -#define CR0_NUM_PERIPH_MASK 0x1f -#define CR0_NUM_EVENTS_SHIFT 17 -#define CR0_NUM_EVENTS_MASK 0x1f - -#define CR1_ICACHE_LEN_SHIFT 0 -#define CR1_ICACHE_LEN_MASK 0x7 -#define CR1_NUM_ICACHELINES_SHIFT 4 -#define CR1_NUM_ICACHELINES_MASK 0xf - -#define CRD_DATA_WIDTH_SHIFT 0 -#define CRD_DATA_WIDTH_MASK 0x7 -#define CRD_WR_CAP_SHIFT 4 -#define CRD_WR_CAP_MASK 0x7 -#define CRD_WR_Q_DEP_SHIFT 8 -#define CRD_WR_Q_DEP_MASK 0xf -#define CRD_RD_CAP_SHIFT 12 -#define CRD_RD_CAP_MASK 0x7 -#define CRD_RD_Q_DEP_SHIFT 16 -#define CRD_RD_Q_DEP_MASK 0xf -#define CRD_DATA_BUFF_SHIFT 20 -#define CRD_DATA_BUFF_MASK 0x3ff - -#define PART 0x330 -#define DESIGNER 0x41 -#define REVISION 0x0 -#define INTEG_CFG 0x0 -#define PERIPH_ID_VAL ((PART << 0) | (DESIGNER << 12)) - -#define PCELL_ID_VAL 0xb105f00d - -#define PL330_STATE_STOPPED (1 << 0) -#define PL330_STATE_EXECUTING (1 << 1) -#define PL330_STATE_WFE (1 << 2) -#define PL330_STATE_FAULTING (1 << 3) -#define PL330_STATE_COMPLETING (1 << 4) -#define PL330_STATE_WFP (1 << 5) -#define PL330_STATE_KILLING (1 << 6) -#define PL330_STATE_FAULT_COMPLETING (1 << 7) -#define PL330_STATE_CACHEMISS (1 << 8) -#define PL330_STATE_UPDTPC (1 << 9) -#define PL330_STATE_ATBARRIER (1 << 10) -#define PL330_STATE_QUEUEBUSY (1 << 11) -#define PL330_STATE_INVALID (1 << 15) - -#define PL330_STABLE_STATES (PL330_STATE_STOPPED | PL330_STATE_EXECUTING \ - | PL330_STATE_WFE | PL330_STATE_FAULTING) - -#define CMD_DMAADDH 0x54 -#define CMD_DMAEND 0x00 -#define CMD_DMAFLUSHP 0x35 -#define CMD_DMAGO 0xa0 -#define CMD_DMALD 0x04 -#define CMD_DMALDP 0x25 -#define CMD_DMALP 0x20 -#define CMD_DMALPEND 0x28 -#define CMD_DMAKILL 0x01 -#define CMD_DMAMOV 0xbc -#define CMD_DMANOP 0x18 -#define CMD_DMARMB 0x12 -#define CMD_DMASEV 0x34 -#define CMD_DMAST 0x08 -#define CMD_DMASTP 0x29 -#define CMD_DMASTZ 0x0c -#define CMD_DMAWFE 0x36 -#define CMD_DMAWFP 0x30 -#define CMD_DMAWMB 0x13 - -#define SZ_DMAADDH 3 -#define SZ_DMAEND 1 -#define SZ_DMAFLUSHP 2 -#define SZ_DMALD 1 -#define SZ_DMALDP 2 -#define SZ_DMALP 2 -#define SZ_DMALPEND 2 -#define SZ_DMAKILL 1 -#define SZ_DMAMOV 6 -#define SZ_DMANOP 1 -#define SZ_DMARMB 1 -#define SZ_DMASEV 2 -#define SZ_DMAST 1 -#define SZ_DMASTP 2 -#define SZ_DMASTZ 1 -#define SZ_DMAWFE 2 -#define SZ_DMAWFP 2 -#define SZ_DMAWMB 1 -#define SZ_DMAGO 6 - -#define BRST_LEN(ccr) ((((ccr) >> CC_SRCBRSTLEN_SHFT) & 0xf) + 1) -#define BRST_SIZE(ccr) (1 << (((ccr) >> CC_SRCBRSTSIZE_SHFT) & 0x7)) - -#define BYTE_TO_BURST(b, ccr) ((b) / BRST_SIZE(ccr) / BRST_LEN(ccr)) -#define BURST_TO_BYTE(c, ccr) ((c) * BRST_SIZE(ccr) * BRST_LEN(ccr)) - -/* - * With 256 bytes, we can do more than 2.5MB and 5MB xfers per req - * at 1byte/burst for P<->M and M<->M respectively. - * For typical scenario, at 1word/burst, 10MB and 20MB xfers per req - * should be enough for P<->M and M<->M respectively. - */ -#define MCODE_BUFF_PER_REQ 256 - -/* If the _pl330_req is available to the client */ -#define IS_FREE(req) (*((u8 *)((req)->mc_cpu)) == CMD_DMAEND) - -/* Use this _only_ to wait on transient states */ -#define UNTIL(t, s) while (!(_state(t) & (s))) cpu_relax(); - -#ifdef PL330_DEBUG_MCGEN -static unsigned cmd_line; -#define PL330_DBGCMD_DUMP(off, x...) do { \ - printk("%x:", cmd_line); \ - printk(x); \ - cmd_line += off; \ - } while (0) -#define PL330_DBGMC_START(addr) (cmd_line = addr) -#else -#define PL330_DBGCMD_DUMP(off, x...) do {} while (0) -#define PL330_DBGMC_START(addr) do {} while (0) -#endif - -struct _xfer_spec { - u32 ccr; - struct pl330_req *r; - struct pl330_xfer *x; -}; - -enum dmamov_dst { - SAR = 0, - CCR, - DAR, -}; - -enum pl330_dst { - SRC = 0, - DST, -}; - -enum pl330_cond { - SINGLE, - BURST, - ALWAYS, -}; - -struct _pl330_req { - u32 mc_bus; - void *mc_cpu; - /* Number of bytes taken to setup MC for the req */ - u32 mc_len; - struct pl330_req *r; - /* Hook to attach to DMAC's list of reqs with due callback */ - struct list_head rqd; -}; - -/* ToBeDone for tasklet */ -struct _pl330_tbd { - bool reset_dmac; - bool reset_mngr; - u8 reset_chan; -}; - -/* A DMAC Thread */ -struct pl330_thread { - u8 id; - int ev; - /* If the channel is not yet acquired by any client */ - bool free; - /* Parent DMAC */ - struct pl330_dmac *dmac; - /* Only two at a time */ - struct _pl330_req req[2]; - /* Index of the last enqueued request */ - unsigned lstenq; - /* Index of the last submitted request or -1 if the DMA is stopped */ - int req_running; -}; - -enum pl330_dmac_state { - UNINIT, - INIT, - DYING, -}; - -/* A DMAC */ -struct pl330_dmac { - spinlock_t lock; - /* Holds list of reqs with due callbacks */ - struct list_head req_done; - /* Pointer to platform specific stuff */ - struct pl330_info *pinfo; - /* Maximum possible events/irqs */ - int events[32]; - /* BUS address of MicroCode buffer */ - u32 mcode_bus; - /* CPU address of MicroCode buffer */ - void *mcode_cpu; - /* List of all Channel threads */ - struct pl330_thread *channels; - /* Pointer to the MANAGER thread */ - struct pl330_thread *manager; - /* To handle bad news in interrupt */ - struct tasklet_struct tasks; - struct _pl330_tbd dmac_tbd; - /* State of DMAC operation */ - enum pl330_dmac_state state; -}; - -static inline void _callback(struct pl330_req *r, enum pl330_op_err err) -{ - if (r && r->xfer_cb) - r->xfer_cb(r->token, err); -} - -static inline bool _queue_empty(struct pl330_thread *thrd) -{ - return (IS_FREE(&thrd->req[0]) && IS_FREE(&thrd->req[1])) - ? true : false; -} - -static inline bool _queue_full(struct pl330_thread *thrd) -{ - return (IS_FREE(&thrd->req[0]) || IS_FREE(&thrd->req[1])) - ? false : true; -} - -static inline bool is_manager(struct pl330_thread *thrd) -{ - struct pl330_dmac *pl330 = thrd->dmac; - - /* MANAGER is indexed at the end */ - if (thrd->id == pl330->pinfo->pcfg.num_chan) - return true; - else - return false; -} - -/* If manager of the thread is in Non-Secure mode */ -static inline bool _manager_ns(struct pl330_thread *thrd) -{ - struct pl330_dmac *pl330 = thrd->dmac; - - return (pl330->pinfo->pcfg.mode & DMAC_MODE_NS) ? true : false; -} - -static inline u32 get_id(struct pl330_info *pi, u32 off) -{ - void __iomem *regs = pi->base; - u32 id = 0; - - id |= (readb(regs + off + 0x0) << 0); - id |= (readb(regs + off + 0x4) << 8); - id |= (readb(regs + off + 0x8) << 16); - id |= (readb(regs + off + 0xc) << 24); - - return id; -} - -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); - *((u16 *)&buf[1]) = val; - - 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) - return SZ_DMAEND; - - buf[0] = CMD_DMAEND; - - PL330_DBGCMD_DUMP(SZ_DMAEND, "\tDMAEND\n"); - - return SZ_DMAEND; -} - -static inline u32 _emit_FLUSHP(unsigned dry_run, u8 buf[], u8 peri) -{ - if (dry_run) - return SZ_DMAFLUSHP; - - buf[0] = CMD_DMAFLUSHP; - - peri &= 0x1f; - peri <<= 3; - buf[1] = peri; - - PL330_DBGCMD_DUMP(SZ_DMAFLUSHP, "\tDMAFLUSHP %u\n", peri >> 3); - - return SZ_DMAFLUSHP; -} - -static inline u32 _emit_LD(unsigned dry_run, u8 buf[], enum pl330_cond cond) -{ - if (dry_run) - return SZ_DMALD; - - buf[0] = CMD_DMALD; - - if (cond == SINGLE) - buf[0] |= (0 << 1) | (1 << 0); - else if (cond == BURST) - buf[0] |= (1 << 1) | (1 << 0); - - PL330_DBGCMD_DUMP(SZ_DMALD, "\tDMALD%c\n", - cond == SINGLE ? 'S' : (cond == BURST ? 'B' : 'A')); - - return SZ_DMALD; -} - -static inline u32 _emit_LDP(unsigned dry_run, u8 buf[], - enum pl330_cond cond, u8 peri) -{ - if (dry_run) - return SZ_DMALDP; - - buf[0] = CMD_DMALDP; - - if (cond == BURST) - buf[0] |= (1 << 1); - - peri &= 0x1f; - peri <<= 3; - buf[1] = peri; - - PL330_DBGCMD_DUMP(SZ_DMALDP, "\tDMALDP%c %u\n", - cond == SINGLE ? 'S' : 'B', peri >> 3); - - return SZ_DMALDP; -} - -static inline u32 _emit_LP(unsigned dry_run, u8 buf[], - unsigned loop, u8 cnt) -{ - if (dry_run) - return SZ_DMALP; - - buf[0] = CMD_DMALP; - - if (loop) - buf[0] |= (1 << 1); - - cnt--; /* DMAC increments by 1 internally */ - buf[1] = cnt; - - PL330_DBGCMD_DUMP(SZ_DMALP, "\tDMALP_%c %u\n", loop ? '1' : '0', cnt); - - return SZ_DMALP; -} - -struct _arg_LPEND { - enum pl330_cond cond; - bool forever; - unsigned loop; - u8 bjump; -}; - -static inline u32 _emit_LPEND(unsigned dry_run, u8 buf[], - const struct _arg_LPEND *arg) -{ - enum pl330_cond cond = arg->cond; - bool forever = arg->forever; - unsigned loop = arg->loop; - u8 bjump = arg->bjump; - - if (dry_run) - return SZ_DMALPEND; - - buf[0] = CMD_DMALPEND; - - if (loop) - buf[0] |= (1 << 2); - - if (!forever) - buf[0] |= (1 << 4); - - if (cond == SINGLE) - buf[0] |= (0 << 1) | (1 << 0); - else if (cond == BURST) - buf[0] |= (1 << 1) | (1 << 0); - - buf[1] = bjump; - - PL330_DBGCMD_DUMP(SZ_DMALPEND, "\tDMALP%s%c_%c bjmpto_%x\n", - forever ? "FE" : "END", - cond == SINGLE ? 'S' : (cond == BURST ? 'B' : 'A'), - loop ? '1' : '0', - bjump); - - return SZ_DMALPEND; -} - -static inline u32 _emit_KILL(unsigned dry_run, u8 buf[]) -{ - if (dry_run) - return SZ_DMAKILL; - - buf[0] = CMD_DMAKILL; - - return SZ_DMAKILL; -} - -static inline u32 _emit_MOV(unsigned dry_run, u8 buf[], - enum dmamov_dst dst, u32 val) -{ - if (dry_run) - return SZ_DMAMOV; - - buf[0] = CMD_DMAMOV; - buf[1] = dst; - *((u32 *)&buf[2]) = val; - - PL330_DBGCMD_DUMP(SZ_DMAMOV, "\tDMAMOV %s 0x%x\n", - dst == SAR ? "SAR" : (dst == DAR ? "DAR" : "CCR"), val); - - 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) - return SZ_DMARMB; - - buf[0] = CMD_DMARMB; - - PL330_DBGCMD_DUMP(SZ_DMARMB, "\tDMARMB\n"); - - return SZ_DMARMB; -} - -static inline u32 _emit_SEV(unsigned dry_run, u8 buf[], u8 ev) -{ - if (dry_run) - return SZ_DMASEV; - - buf[0] = CMD_DMASEV; - - ev &= 0x1f; - ev <<= 3; - buf[1] = ev; - - PL330_DBGCMD_DUMP(SZ_DMASEV, "\tDMASEV %u\n", ev >> 3); - - return SZ_DMASEV; -} - -static inline u32 _emit_ST(unsigned dry_run, u8 buf[], enum pl330_cond cond) -{ - if (dry_run) - return SZ_DMAST; - - buf[0] = CMD_DMAST; - - if (cond == SINGLE) - buf[0] |= (0 << 1) | (1 << 0); - else if (cond == BURST) - buf[0] |= (1 << 1) | (1 << 0); - - PL330_DBGCMD_DUMP(SZ_DMAST, "\tDMAST%c\n", - cond == SINGLE ? 'S' : (cond == BURST ? 'B' : 'A')); - - return SZ_DMAST; -} - -static inline u32 _emit_STP(unsigned dry_run, u8 buf[], - enum pl330_cond cond, u8 peri) -{ - if (dry_run) - return SZ_DMASTP; - - buf[0] = CMD_DMASTP; - - if (cond == BURST) - buf[0] |= (1 << 1); - - peri &= 0x1f; - peri <<= 3; - buf[1] = peri; - - PL330_DBGCMD_DUMP(SZ_DMASTP, "\tDMASTP%c %u\n", - cond == SINGLE ? 'S' : 'B', peri >> 3); - - 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) -{ - if (dry_run) - return SZ_DMAWFP; - - buf[0] = CMD_DMAWFP; - - if (cond == SINGLE) - buf[0] |= (0 << 1) | (0 << 0); - else if (cond == BURST) - buf[0] |= (1 << 1) | (0 << 0); - else - buf[0] |= (0 << 1) | (1 << 0); - - peri &= 0x1f; - peri <<= 3; - buf[1] = peri; - - PL330_DBGCMD_DUMP(SZ_DMAWFP, "\tDMAWFP%c %u\n", - cond == SINGLE ? 'S' : (cond == BURST ? 'B' : 'P'), peri >> 3); - - return SZ_DMAWFP; -} - -static inline u32 _emit_WMB(unsigned dry_run, u8 buf[]) -{ - if (dry_run) - return SZ_DMAWMB; - - buf[0] = CMD_DMAWMB; - - PL330_DBGCMD_DUMP(SZ_DMAWMB, "\tDMAWMB\n"); - - return SZ_DMAWMB; -} - -struct _arg_GO { - u8 chan; - u32 addr; - unsigned ns; -}; - -static inline u32 _emit_GO(unsigned dry_run, u8 buf[], - const struct _arg_GO *arg) -{ - u8 chan = arg->chan; - u32 addr = arg->addr; - unsigned ns = arg->ns; - - if (dry_run) - return SZ_DMAGO; - - buf[0] = CMD_DMAGO; - buf[0] |= (ns << 1); - - buf[1] = chan & 0x7; - - *((u32 *)&buf[2]) = addr; - - return SZ_DMAGO; -} - -#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t) - -/* Returns Time-Out */ -static bool _until_dmac_idle(struct pl330_thread *thrd) -{ - void __iomem *regs = thrd->dmac->pinfo->base; - unsigned long loops = msecs_to_loops(5); - - do { - /* Until Manager is Idle */ - if (!(readl(regs + DBGSTATUS) & DBG_BUSY)) - break; - - cpu_relax(); - } while (--loops); - - if (!loops) - return true; - - return false; -} - -static inline void _execute_DBGINSN(struct pl330_thread *thrd, - u8 insn[], bool as_manager) -{ - void __iomem *regs = thrd->dmac->pinfo->base; - u32 val; - - val = (insn[0] << 16) | (insn[1] << 24); - if (!as_manager) { - val |= (1 << 0); - val |= (thrd->id << 8); /* Channel Number */ - } - writel(val, regs + DBGINST0); - - val = *((u32 *)&insn[2]); - writel(val, regs + DBGINST1); - - /* If timed out due to halted state-machine */ - if (_until_dmac_idle(thrd)) { - dev_err(thrd->dmac->pinfo->dev, "DMAC halted!\n"); - return; - } - - /* Get going */ - writel(0, regs + DBGCMD); -} - -/* - * Mark a _pl330_req as free. - * We do it by writing DMAEND as the first instruction - * because no valid request is going to have DMAEND as - * its first instruction to execute. - */ -static void mark_free(struct pl330_thread *thrd, int idx) -{ - struct _pl330_req *req = &thrd->req[idx]; - - _emit_END(0, req->mc_cpu); - req->mc_len = 0; - - thrd->req_running = -1; -} - -static inline u32 _state(struct pl330_thread *thrd) -{ - void __iomem *regs = thrd->dmac->pinfo->base; - u32 val; - - if (is_manager(thrd)) - val = readl(regs + DS) & 0xf; - else - val = readl(regs + CS(thrd->id)) & 0xf; - - switch (val) { - case DS_ST_STOP: - return PL330_STATE_STOPPED; - case DS_ST_EXEC: - return PL330_STATE_EXECUTING; - case DS_ST_CMISS: - return PL330_STATE_CACHEMISS; - case DS_ST_UPDTPC: - return PL330_STATE_UPDTPC; - case DS_ST_WFE: - return PL330_STATE_WFE; - case DS_ST_FAULT: - return PL330_STATE_FAULTING; - case DS_ST_ATBRR: - if (is_manager(thrd)) - return PL330_STATE_INVALID; - else - return PL330_STATE_ATBARRIER; - case DS_ST_QBUSY: - if (is_manager(thrd)) - return PL330_STATE_INVALID; - else - return PL330_STATE_QUEUEBUSY; - case DS_ST_WFP: - if (is_manager(thrd)) - return PL330_STATE_INVALID; - else - return PL330_STATE_WFP; - case DS_ST_KILL: - if (is_manager(thrd)) - return PL330_STATE_INVALID; - else - return PL330_STATE_KILLING; - case DS_ST_CMPLT: - if (is_manager(thrd)) - return PL330_STATE_INVALID; - else - return PL330_STATE_COMPLETING; - case DS_ST_FLTCMP: - if (is_manager(thrd)) - return PL330_STATE_INVALID; - else - return PL330_STATE_FAULT_COMPLETING; - default: - return PL330_STATE_INVALID; - } -} - -static void _stop(struct pl330_thread *thrd) -{ - void __iomem *regs = thrd->dmac->pinfo->base; - u8 insn[6] = {0, 0, 0, 0, 0, 0}; - - if (_state(thrd) == PL330_STATE_FAULT_COMPLETING) - UNTIL(thrd, PL330_STATE_FAULTING | PL330_STATE_KILLING); - - /* Return if nothing needs to be done */ - if (_state(thrd) == PL330_STATE_COMPLETING - || _state(thrd) == PL330_STATE_KILLING - || _state(thrd) == PL330_STATE_STOPPED) - return; - - _emit_KILL(0, insn); - - /* Stop generating interrupts for SEV */ - writel(readl(regs + INTEN) & ~(1 << thrd->ev), regs + INTEN); - - _execute_DBGINSN(thrd, insn, is_manager(thrd)); -} - -/* Start doing req 'idx' of thread 'thrd' */ -static bool _trigger(struct pl330_thread *thrd) -{ - void __iomem *regs = thrd->dmac->pinfo->base; - struct _pl330_req *req; - struct pl330_req *r; - struct _arg_GO go; - unsigned ns; - u8 insn[6] = {0, 0, 0, 0, 0, 0}; - int idx; - - /* Return if already ACTIVE */ - if (_state(thrd) != PL330_STATE_STOPPED) - return true; - - idx = 1 - thrd->lstenq; - if (!IS_FREE(&thrd->req[idx])) - req = &thrd->req[idx]; - else { - idx = thrd->lstenq; - if (!IS_FREE(&thrd->req[idx])) - req = &thrd->req[idx]; - else - req = NULL; - } - - /* Return if no request */ - if (!req || !req->r) - return true; - - r = req->r; - - if (r->cfg) - ns = r->cfg->nonsecure ? 1 : 0; - else if (readl(regs + CS(thrd->id)) & CS_CNS) - ns = 1; - else - ns = 0; - - /* See 'Abort Sources' point-4 at Page 2-25 */ - if (_manager_ns(thrd) && !ns) - dev_info(thrd->dmac->pinfo->dev, "%s:%d Recipe for ABORT!\n", - __func__, __LINE__); - - go.chan = thrd->id; - go.addr = req->mc_bus; - go.ns = ns; - _emit_GO(0, insn, &go); - - /* Set to generate interrupts for SEV */ - writel(readl(regs + INTEN) | (1 << thrd->ev), regs + INTEN); - - /* Only manager can execute GO */ - _execute_DBGINSN(thrd, insn, true); - - thrd->req_running = idx; - - return true; -} - -static bool _start(struct pl330_thread *thrd) -{ - switch (_state(thrd)) { - case PL330_STATE_FAULT_COMPLETING: - UNTIL(thrd, PL330_STATE_FAULTING | PL330_STATE_KILLING); - - if (_state(thrd) == PL330_STATE_KILLING) - UNTIL(thrd, PL330_STATE_STOPPED) - - case PL330_STATE_FAULTING: - _stop(thrd); - - case PL330_STATE_KILLING: - case PL330_STATE_COMPLETING: - UNTIL(thrd, PL330_STATE_STOPPED) - - case PL330_STATE_STOPPED: - return _trigger(thrd); - - case PL330_STATE_WFP: - case PL330_STATE_QUEUEBUSY: - case PL330_STATE_ATBARRIER: - case PL330_STATE_UPDTPC: - case PL330_STATE_CACHEMISS: - case PL330_STATE_EXECUTING: - return true; - - case PL330_STATE_WFE: /* For RESUME, nothing yet */ - default: - return false; - } -} - -static inline int _ldst_memtomem(unsigned dry_run, u8 buf[], - const struct _xfer_spec *pxs, int cyc) -{ - int off = 0; - - while (cyc--) { - off += _emit_LD(dry_run, &buf[off], ALWAYS); - off += _emit_RMB(dry_run, &buf[off]); - off += _emit_ST(dry_run, &buf[off], ALWAYS); - off += _emit_WMB(dry_run, &buf[off]); - } - - return off; -} - -static inline int _ldst_devtomem(unsigned dry_run, u8 buf[], - const struct _xfer_spec *pxs, int cyc) -{ - int off = 0; - - while (cyc--) { - off += _emit_WFP(dry_run, &buf[off], SINGLE, pxs->r->peri); - off += _emit_LDP(dry_run, &buf[off], SINGLE, pxs->r->peri); - off += _emit_ST(dry_run, &buf[off], ALWAYS); - off += _emit_FLUSHP(dry_run, &buf[off], pxs->r->peri); - } - - return off; -} - -static inline int _ldst_memtodev(unsigned dry_run, u8 buf[], - const struct _xfer_spec *pxs, int cyc) -{ - int off = 0; - - while (cyc--) { - off += _emit_WFP(dry_run, &buf[off], SINGLE, pxs->r->peri); - off += _emit_LD(dry_run, &buf[off], ALWAYS); - off += _emit_STP(dry_run, &buf[off], SINGLE, pxs->r->peri); - off += _emit_FLUSHP(dry_run, &buf[off], pxs->r->peri); - } - - return off; -} - -static int _bursts(unsigned dry_run, u8 buf[], - const struct _xfer_spec *pxs, int cyc) -{ - int off = 0; - - switch (pxs->r->rqtype) { - case MEMTODEV: - off += _ldst_memtodev(dry_run, &buf[off], pxs, cyc); - break; - case DEVTOMEM: - off += _ldst_devtomem(dry_run, &buf[off], pxs, cyc); - break; - case MEMTOMEM: - off += _ldst_memtomem(dry_run, &buf[off], pxs, cyc); - break; - default: - off += 0x40000000; /* Scare off the Client */ - break; - } - - return off; -} - -/* Returns bytes consumed and updates bursts */ -static inline int _loop(unsigned dry_run, u8 buf[], - unsigned long *bursts, const struct _xfer_spec *pxs) -{ - int cyc, cycmax, szlp, szlpend, szbrst, off; - unsigned lcnt0, lcnt1, ljmp0, ljmp1; - struct _arg_LPEND lpend; - - /* Max iterations possible in DMALP is 256 */ - if (*bursts >= 256*256) { - lcnt1 = 256; - lcnt0 = 256; - cyc = *bursts / lcnt1 / lcnt0; - } else if (*bursts > 256) { - lcnt1 = 256; - lcnt0 = *bursts / lcnt1; - cyc = 1; - } else { - lcnt1 = *bursts; - lcnt0 = 0; - cyc = 1; - } - - szlp = _emit_LP(1, buf, 0, 0); - szbrst = _bursts(1, buf, pxs, 1); - - lpend.cond = ALWAYS; - lpend.forever = false; - lpend.loop = 0; - lpend.bjump = 0; - szlpend = _emit_LPEND(1, buf, &lpend); - - if (lcnt0) { - szlp *= 2; - szlpend *= 2; - } - - /* - * Max bursts that we can unroll due to limit on the - * size of backward jump that can be encoded in DMALPEND - * which is 8-bits and hence 255 - */ - cycmax = (255 - (szlp + szlpend)) / szbrst; - - cyc = (cycmax < cyc) ? cycmax : cyc; - - off = 0; - - if (lcnt0) { - off += _emit_LP(dry_run, &buf[off], 0, lcnt0); - ljmp0 = off; - } - - off += _emit_LP(dry_run, &buf[off], 1, lcnt1); - ljmp1 = off; - - off += _bursts(dry_run, &buf[off], pxs, cyc); - - lpend.cond = ALWAYS; - lpend.forever = false; - lpend.loop = 1; - lpend.bjump = off - ljmp1; - off += _emit_LPEND(dry_run, &buf[off], &lpend); - - if (lcnt0) { - lpend.cond = ALWAYS; - lpend.forever = false; - lpend.loop = 0; - lpend.bjump = off - ljmp0; - off += _emit_LPEND(dry_run, &buf[off], &lpend); - } - - *bursts = lcnt1 * cyc; - if (lcnt0) - *bursts *= lcnt0; - - return off; -} - -static inline int _setup_loops(unsigned dry_run, u8 buf[], - const struct _xfer_spec *pxs) -{ - struct pl330_xfer *x = pxs->x; - u32 ccr = pxs->ccr; - unsigned long c, bursts = BYTE_TO_BURST(x->bytes, ccr); - int off = 0; - - while (bursts) { - c = bursts; - off += _loop(dry_run, &buf[off], &c, pxs); - bursts -= c; - } - - return off; -} - -static inline int _setup_xfer(unsigned dry_run, u8 buf[], - const struct _xfer_spec *pxs) -{ - struct pl330_xfer *x = pxs->x; - int off = 0; - - /* DMAMOV SAR, x->src_addr */ - off += _emit_MOV(dry_run, &buf[off], SAR, x->src_addr); - /* DMAMOV DAR, x->dst_addr */ - off += _emit_MOV(dry_run, &buf[off], DAR, x->dst_addr); - - /* Setup Loop(s) */ - off += _setup_loops(dry_run, &buf[off], pxs); - - return off; -} - -/* - * A req is a sequence of one or more xfer units. - * Returns the number of bytes taken to setup the MC for the req. - */ -static int _setup_req(unsigned dry_run, struct pl330_thread *thrd, - unsigned index, struct _xfer_spec *pxs) -{ - struct _pl330_req *req = &thrd->req[index]; - struct pl330_xfer *x; - u8 *buf = req->mc_cpu; - int off = 0; - - PL330_DBGMC_START(req->mc_bus); - - /* DMAMOV CCR, ccr */ - off += _emit_MOV(dry_run, &buf[off], CCR, pxs->ccr); - - x = pxs->r->x; - do { - /* Error if xfer length is not aligned at burst size */ - if (x->bytes % (BRST_SIZE(pxs->ccr) * BRST_LEN(pxs->ccr))) - return -EINVAL; - - pxs->x = x; - off += _setup_xfer(dry_run, &buf[off], pxs); - - x = x->next; - } while (x); - - /* DMASEV peripheral/event */ - off += _emit_SEV(dry_run, &buf[off], thrd->ev); - /* DMAEND */ - off += _emit_END(dry_run, &buf[off]); - - return off; -} - -static inline u32 _prepare_ccr(const struct pl330_reqcfg *rqc) -{ - u32 ccr = 0; - - if (rqc->src_inc) - ccr |= CC_SRCINC; - - if (rqc->dst_inc) - ccr |= CC_DSTINC; - - /* We set same protection levels for Src and DST for now */ - if (rqc->privileged) - ccr |= CC_SRCPRI | CC_DSTPRI; - if (rqc->nonsecure) - ccr |= CC_SRCNS | CC_DSTNS; - if (rqc->insnaccess) - ccr |= CC_SRCIA | CC_DSTIA; - - ccr |= (((rqc->brst_len - 1) & 0xf) << CC_SRCBRSTLEN_SHFT); - ccr |= (((rqc->brst_len - 1) & 0xf) << CC_DSTBRSTLEN_SHFT); - - ccr |= (rqc->brst_size << CC_SRCBRSTSIZE_SHFT); - ccr |= (rqc->brst_size << CC_DSTBRSTSIZE_SHFT); - - ccr |= (rqc->scctl << CC_SRCCCTRL_SHFT); - ccr |= (rqc->dcctl << CC_DSTCCTRL_SHFT); - - ccr |= (rqc->swap << CC_SWAP_SHFT); - - return ccr; -} - -static inline bool _is_valid(u32 ccr) -{ - enum pl330_dstcachectrl dcctl; - enum pl330_srccachectrl scctl; - - dcctl = (ccr >> CC_DSTCCTRL_SHFT) & CC_DRCCCTRL_MASK; - scctl = (ccr >> CC_SRCCCTRL_SHFT) & CC_SRCCCTRL_MASK; - - if (dcctl == DINVALID1 || dcctl == DINVALID2 - || scctl == SINVALID1 || scctl == SINVALID2) - return false; - else - return true; -} - -/* - * Submit a list of xfers after which the client wants notification. - * Client is not notified after each xfer unit, just once after all - * xfer units are done or some error occurs. - */ -int pl330_submit_req(void *ch_id, struct pl330_req *r) -{ - struct pl330_thread *thrd = ch_id; - struct pl330_dmac *pl330; - struct pl330_info *pi; - struct _xfer_spec xs; - unsigned long flags; - void __iomem *regs; - unsigned idx; - u32 ccr; - int ret = 0; - - /* No Req or Unacquired Channel or DMAC */ - if (!r || !thrd || thrd->free) - return -EINVAL; - - pl330 = thrd->dmac; - pi = pl330->pinfo; - regs = pi->base; - - if (pl330->state == DYING - || pl330->dmac_tbd.reset_chan & (1 << thrd->id)) { - dev_info(thrd->dmac->pinfo->dev, "%s:%d\n", - __func__, __LINE__); - return -EAGAIN; - } - - /* If request for non-existing peripheral */ - if (r->rqtype != MEMTOMEM && r->peri >= pi->pcfg.num_peri) { - dev_info(thrd->dmac->pinfo->dev, - "%s:%d Invalid peripheral(%u)!\n", - __func__, __LINE__, r->peri); - return -EINVAL; - } - - spin_lock_irqsave(&pl330->lock, flags); - - if (_queue_full(thrd)) { - ret = -EAGAIN; - goto xfer_exit; - } - - /* Prefer Secure Channel */ - if (!_manager_ns(thrd)) - r->cfg->nonsecure = 0; - else - r->cfg->nonsecure = 1; - - /* Use last settings, if not provided */ - if (r->cfg) - ccr = _prepare_ccr(r->cfg); - else - ccr = readl(regs + CC(thrd->id)); - - /* If this req doesn't have valid xfer settings */ - if (!_is_valid(ccr)) { - ret = -EINVAL; - dev_info(thrd->dmac->pinfo->dev, "%s:%d Invalid CCR(%x)!\n", - __func__, __LINE__, ccr); - goto xfer_exit; - } - - idx = IS_FREE(&thrd->req[0]) ? 0 : 1; - - xs.ccr = ccr; - xs.r = r; - - /* First dry run to check if req is acceptable */ - ret = _setup_req(1, thrd, idx, &xs); - if (ret < 0) - goto xfer_exit; - - if (ret > pi->mcbufsz / 2) { - dev_info(thrd->dmac->pinfo->dev, - "%s:%d Trying increasing mcbufsz\n", - __func__, __LINE__); - ret = -ENOMEM; - goto xfer_exit; - } - - /* Hook the request */ - thrd->lstenq = idx; - thrd->req[idx].mc_len = _setup_req(0, thrd, idx, &xs); - thrd->req[idx].r = r; - - ret = 0; - -xfer_exit: - spin_unlock_irqrestore(&pl330->lock, flags); - - return ret; -} -EXPORT_SYMBOL(pl330_submit_req); - -static void pl330_dotask(unsigned long data) -{ - struct pl330_dmac *pl330 = (struct pl330_dmac *) data; - struct pl330_info *pi = pl330->pinfo; - unsigned long flags; - int i; - - spin_lock_irqsave(&pl330->lock, flags); - - /* The DMAC itself gone nuts */ - if (pl330->dmac_tbd.reset_dmac) { - pl330->state = DYING; - /* Reset the manager too */ - pl330->dmac_tbd.reset_mngr = true; - /* Clear the reset flag */ - pl330->dmac_tbd.reset_dmac = false; - } - - if (pl330->dmac_tbd.reset_mngr) { - _stop(pl330->manager); - /* Reset all channels */ - pl330->dmac_tbd.reset_chan = (1 << pi->pcfg.num_chan) - 1; - /* Clear the reset flag */ - pl330->dmac_tbd.reset_mngr = false; - } - - for (i = 0; i < pi->pcfg.num_chan; i++) { - - if (pl330->dmac_tbd.reset_chan & (1 << i)) { - struct pl330_thread *thrd = &pl330->channels[i]; - void __iomem *regs = pi->base; - enum pl330_op_err err; - - _stop(thrd); - - if (readl(regs + FSC) & (1 << thrd->id)) - err = PL330_ERR_FAIL; - else - err = PL330_ERR_ABORT; - - spin_unlock_irqrestore(&pl330->lock, flags); - - _callback(thrd->req[1 - thrd->lstenq].r, err); - _callback(thrd->req[thrd->lstenq].r, err); - - spin_lock_irqsave(&pl330->lock, flags); - - thrd->req[0].r = NULL; - thrd->req[1].r = NULL; - mark_free(thrd, 0); - mark_free(thrd, 1); - - /* Clear the reset flag */ - pl330->dmac_tbd.reset_chan &= ~(1 << i); - } - } - - spin_unlock_irqrestore(&pl330->lock, flags); - - return; -} - -/* Returns 1 if state was updated, 0 otherwise */ -int pl330_update(const struct pl330_info *pi) -{ - struct _pl330_req *rqdone; - struct pl330_dmac *pl330; - unsigned long flags; - void __iomem *regs; - u32 val; - int id, ev, ret = 0; - - if (!pi || !pi->pl330_data) - return 0; - - regs = pi->base; - pl330 = pi->pl330_data; - - spin_lock_irqsave(&pl330->lock, flags); - - val = readl(regs + FSM) & 0x1; - if (val) - pl330->dmac_tbd.reset_mngr = true; - else - pl330->dmac_tbd.reset_mngr = false; - - val = readl(regs + FSC) & ((1 << pi->pcfg.num_chan) - 1); - pl330->dmac_tbd.reset_chan |= val; - if (val) { - int i = 0; - while (i < pi->pcfg.num_chan) { - if (val & (1 << i)) { - dev_info(pi->dev, - "Reset Channel-%d\t CS-%x FTC-%x\n", - i, readl(regs + CS(i)), - readl(regs + FTC(i))); - _stop(&pl330->channels[i]); - } - i++; - } - } - - /* Check which event happened i.e, thread notified */ - val = readl(regs + ES); - if (pi->pcfg.num_events < 32 - && val & ~((1 << pi->pcfg.num_events) - 1)) { - pl330->dmac_tbd.reset_dmac = true; - dev_err(pi->dev, "%s:%d Unexpected!\n", __func__, __LINE__); - ret = 1; - goto updt_exit; - } - - for (ev = 0; ev < pi->pcfg.num_events; ev++) { - if (val & (1 << ev)) { /* Event occurred */ - struct pl330_thread *thrd; - u32 inten = readl(regs + INTEN); - int active; - - /* Clear the event */ - if (inten & (1 << ev)) - writel(1 << ev, regs + INTCLR); - - ret = 1; - - id = pl330->events[ev]; - - thrd = &pl330->channels[id]; - - active = thrd->req_running; - if (active == -1) /* Aborted */ - continue; - - rqdone = &thrd->req[active]; - mark_free(thrd, active); - - /* Get going again ASAP */ - _start(thrd); - - /* For now, just make a list of callbacks to be done */ - list_add_tail(&rqdone->rqd, &pl330->req_done); - } - } - - /* Now that we are in no hurry, do the callbacks */ - while (!list_empty(&pl330->req_done)) { - struct pl330_req *r; - - rqdone = container_of(pl330->req_done.next, - struct _pl330_req, rqd); - - list_del_init(&rqdone->rqd); - - /* Detach the req */ - r = rqdone->r; - rqdone->r = NULL; - - spin_unlock_irqrestore(&pl330->lock, flags); - _callback(r, PL330_ERR_NONE); - spin_lock_irqsave(&pl330->lock, flags); - } - -updt_exit: - spin_unlock_irqrestore(&pl330->lock, flags); - - if (pl330->dmac_tbd.reset_dmac - || pl330->dmac_tbd.reset_mngr - || pl330->dmac_tbd.reset_chan) { - ret = 1; - tasklet_schedule(&pl330->tasks); - } - - return ret; -} -EXPORT_SYMBOL(pl330_update); - -int pl330_chan_ctrl(void *ch_id, enum pl330_chan_op op) -{ - struct pl330_thread *thrd = ch_id; - struct pl330_dmac *pl330; - unsigned long flags; - int ret = 0, active = thrd->req_running; - - if (!thrd || thrd->free || thrd->dmac->state == DYING) - return -EINVAL; - - pl330 = thrd->dmac; - - spin_lock_irqsave(&pl330->lock, flags); - - switch (op) { - case PL330_OP_FLUSH: - /* Make sure the channel is stopped */ - _stop(thrd); - - thrd->req[0].r = NULL; - thrd->req[1].r = NULL; - mark_free(thrd, 0); - mark_free(thrd, 1); - break; - - case PL330_OP_ABORT: - /* Make sure the channel is stopped */ - _stop(thrd); - - /* ABORT is only for the active req */ - if (active == -1) - break; - - thrd->req[active].r = NULL; - mark_free(thrd, active); - - /* Start the next */ - case PL330_OP_START: - if ((active == -1) && !_start(thrd)) - ret = -EIO; - break; - - default: - ret = -EINVAL; - } - - spin_unlock_irqrestore(&pl330->lock, flags); - return ret; -} -EXPORT_SYMBOL(pl330_chan_ctrl); - -int pl330_chan_status(void *ch_id, struct pl330_chanstatus *pstatus) -{ - struct pl330_thread *thrd = ch_id; - struct pl330_dmac *pl330; - struct pl330_info *pi; - void __iomem *regs; - int active; - u32 val; - - if (!pstatus || !thrd || thrd->free) - return -EINVAL; - - pl330 = thrd->dmac; - pi = pl330->pinfo; - regs = pi->base; - - /* The client should remove the DMAC and add again */ - if (pl330->state == DYING) - pstatus->dmac_halted = true; - else - pstatus->dmac_halted = false; - - val = readl(regs + FSC); - if (val & (1 << thrd->id)) - pstatus->faulting = true; - else - pstatus->faulting = false; - - active = thrd->req_running; - - if (active == -1) { - /* Indicate that the thread is not running */ - pstatus->top_req = NULL; - pstatus->wait_req = NULL; - } else { - pstatus->top_req = thrd->req[active].r; - pstatus->wait_req = !IS_FREE(&thrd->req[1 - active]) - ? thrd->req[1 - active].r : NULL; - } - - pstatus->src_addr = readl(regs + SA(thrd->id)); - pstatus->dst_addr = readl(regs + DA(thrd->id)); - - return 0; -} -EXPORT_SYMBOL(pl330_chan_status); - -/* Reserve an event */ -static inline int _alloc_event(struct pl330_thread *thrd) -{ - struct pl330_dmac *pl330 = thrd->dmac; - struct pl330_info *pi = pl330->pinfo; - int ev; - - for (ev = 0; ev < pi->pcfg.num_events; ev++) - if (pl330->events[ev] == -1) { - pl330->events[ev] = thrd->id; - return ev; - } - - return -1; -} - -static bool _chan_ns(const struct pl330_info *pi, int i) -{ - return pi->pcfg.irq_ns & (1 << i); -} - -/* Upon success, returns IdentityToken for the - * allocated channel, NULL otherwise. - */ -void *pl330_request_channel(const struct pl330_info *pi) -{ - struct pl330_thread *thrd = NULL; - struct pl330_dmac *pl330; - unsigned long flags; - int chans, i; - - if (!pi || !pi->pl330_data) - return NULL; - - pl330 = pi->pl330_data; - - if (pl330->state == DYING) - return NULL; - - chans = pi->pcfg.num_chan; - - spin_lock_irqsave(&pl330->lock, flags); - - for (i = 0; i < chans; i++) { - thrd = &pl330->channels[i]; - if ((thrd->free) && (!_manager_ns(thrd) || - _chan_ns(pi, i))) { - thrd->ev = _alloc_event(thrd); - if (thrd->ev >= 0) { - thrd->free = false; - thrd->lstenq = 1; - thrd->req[0].r = NULL; - mark_free(thrd, 0); - thrd->req[1].r = NULL; - mark_free(thrd, 1); - break; - } - } - thrd = NULL; - } - - spin_unlock_irqrestore(&pl330->lock, flags); - - return thrd; -} -EXPORT_SYMBOL(pl330_request_channel); - -/* Release an event */ -static inline void _free_event(struct pl330_thread *thrd, int ev) -{ - struct pl330_dmac *pl330 = thrd->dmac; - struct pl330_info *pi = pl330->pinfo; - - /* If the event is valid and was held by the thread */ - if (ev >= 0 && ev < pi->pcfg.num_events - && pl330->events[ev] == thrd->id) - pl330->events[ev] = -1; -} - -void pl330_release_channel(void *ch_id) -{ - struct pl330_thread *thrd = ch_id; - struct pl330_dmac *pl330; - unsigned long flags; - - if (!thrd || thrd->free) - return; - - _stop(thrd); - - _callback(thrd->req[1 - thrd->lstenq].r, PL330_ERR_ABORT); - _callback(thrd->req[thrd->lstenq].r, PL330_ERR_ABORT); - - pl330 = thrd->dmac; - - spin_lock_irqsave(&pl330->lock, flags); - _free_event(thrd, thrd->ev); - thrd->free = true; - spin_unlock_irqrestore(&pl330->lock, flags); -} -EXPORT_SYMBOL(pl330_release_channel); - -/* Initialize the structure for PL330 configuration, that can be used - * by the client driver the make best use of the DMAC - */ -static void read_dmac_config(struct pl330_info *pi) -{ - void __iomem *regs = pi->base; - u32 val; - - val = readl(regs + CRD) >> CRD_DATA_WIDTH_SHIFT; - val &= CRD_DATA_WIDTH_MASK; - pi->pcfg.data_bus_width = 8 * (1 << val); - - val = readl(regs + CRD) >> CRD_DATA_BUFF_SHIFT; - val &= CRD_DATA_BUFF_MASK; - pi->pcfg.data_buf_dep = val + 1; - - val = readl(regs + CR0) >> CR0_NUM_CHANS_SHIFT; - val &= CR0_NUM_CHANS_MASK; - val += 1; - pi->pcfg.num_chan = val; - - val = readl(regs + CR0); - if (val & CR0_PERIPH_REQ_SET) { - val = (val >> CR0_NUM_PERIPH_SHIFT) & CR0_NUM_PERIPH_MASK; - val += 1; - pi->pcfg.num_peri = val; - pi->pcfg.peri_ns = readl(regs + CR4); - } else { - pi->pcfg.num_peri = 0; - } - - val = readl(regs + CR0); - if (val & CR0_BOOT_MAN_NS) - pi->pcfg.mode |= DMAC_MODE_NS; - else - pi->pcfg.mode &= ~DMAC_MODE_NS; - - val = readl(regs + CR0) >> CR0_NUM_EVENTS_SHIFT; - val &= CR0_NUM_EVENTS_MASK; - val += 1; - pi->pcfg.num_events = val; - - pi->pcfg.irq_ns = readl(regs + CR3); - - pi->pcfg.periph_id = get_id(pi, PERIPH_ID); - pi->pcfg.pcell_id = get_id(pi, PCELL_ID); -} - -static inline void _reset_thread(struct pl330_thread *thrd) -{ - struct pl330_dmac *pl330 = thrd->dmac; - struct pl330_info *pi = pl330->pinfo; - - thrd->req[0].mc_cpu = pl330->mcode_cpu - + (thrd->id * pi->mcbufsz); - thrd->req[0].mc_bus = pl330->mcode_bus - + (thrd->id * pi->mcbufsz); - thrd->req[0].r = NULL; - mark_free(thrd, 0); - - thrd->req[1].mc_cpu = thrd->req[0].mc_cpu - + pi->mcbufsz / 2; - thrd->req[1].mc_bus = thrd->req[0].mc_bus - + pi->mcbufsz / 2; - thrd->req[1].r = NULL; - mark_free(thrd, 1); -} - -static int dmac_alloc_threads(struct pl330_dmac *pl330) -{ - struct pl330_info *pi = pl330->pinfo; - int chans = pi->pcfg.num_chan; - struct pl330_thread *thrd; - int i; - - /* Allocate 1 Manager and 'chans' Channel threads */ - pl330->channels = kzalloc((1 + chans) * sizeof(*thrd), - GFP_KERNEL); - if (!pl330->channels) - return -ENOMEM; - - /* Init Channel threads */ - for (i = 0; i < chans; i++) { - thrd = &pl330->channels[i]; - thrd->id = i; - thrd->dmac = pl330; - _reset_thread(thrd); - thrd->free = true; - } - - /* MANAGER is indexed at the end */ - thrd = &pl330->channels[chans]; - thrd->id = chans; - thrd->dmac = pl330; - thrd->free = false; - pl330->manager = thrd; - - return 0; -} - -static int dmac_alloc_resources(struct pl330_dmac *pl330) -{ - struct pl330_info *pi = pl330->pinfo; - int chans = pi->pcfg.num_chan; - int ret; - - /* - * Alloc MicroCode buffer for 'chans' Channel threads. - * A channel's buffer offset is (Channel_Id * MCODE_BUFF_PERCHAN) - */ - pl330->mcode_cpu = dma_alloc_coherent(pi->dev, - chans * pi->mcbufsz, - &pl330->mcode_bus, GFP_KERNEL); - if (!pl330->mcode_cpu) { - dev_err(pi->dev, "%s:%d Can't allocate memory!\n", - __func__, __LINE__); - return -ENOMEM; - } - - ret = dmac_alloc_threads(pl330); - if (ret) { - dev_err(pi->dev, "%s:%d Can't to create channels for DMAC!\n", - __func__, __LINE__); - dma_free_coherent(pi->dev, - chans * pi->mcbufsz, - pl330->mcode_cpu, pl330->mcode_bus); - return ret; - } - - return 0; -} - -int pl330_add(struct pl330_info *pi) -{ - struct pl330_dmac *pl330; - void __iomem *regs; - int i, ret; - - if (!pi || !pi->dev) - return -EINVAL; - - /* If already added */ - if (pi->pl330_data) - return -EINVAL; - - /* - * If the SoC can perform reset on the DMAC, then do it - * before reading its configuration. - */ - if (pi->dmac_reset) - pi->dmac_reset(pi); - - regs = pi->base; - - /* Check if we can handle this DMAC */ - if ((get_id(pi, PERIPH_ID) & 0xfffff) != PERIPH_ID_VAL - || get_id(pi, PCELL_ID) != PCELL_ID_VAL) { - dev_err(pi->dev, "PERIPH_ID 0x%x, PCELL_ID 0x%x !\n", - get_id(pi, PERIPH_ID), get_id(pi, PCELL_ID)); - return -EINVAL; - } - - /* Read the configuration of the DMAC */ - read_dmac_config(pi); - - if (pi->pcfg.num_events == 0) { - dev_err(pi->dev, "%s:%d Can't work without events!\n", - __func__, __LINE__); - return -EINVAL; - } - - pl330 = kzalloc(sizeof(*pl330), GFP_KERNEL); - if (!pl330) { - dev_err(pi->dev, "%s:%d Can't allocate memory!\n", - __func__, __LINE__); - return -ENOMEM; - } - - /* Assign the info structure and private data */ - pl330->pinfo = pi; - pi->pl330_data = pl330; - - spin_lock_init(&pl330->lock); - - INIT_LIST_HEAD(&pl330->req_done); - - /* Use default MC buffer size if not provided */ - if (!pi->mcbufsz) - pi->mcbufsz = MCODE_BUFF_PER_REQ * 2; - - /* Mark all events as free */ - for (i = 0; i < pi->pcfg.num_events; i++) - pl330->events[i] = -1; - - /* Allocate resources needed by the DMAC */ - ret = dmac_alloc_resources(pl330); - if (ret) { - dev_err(pi->dev, "Unable to create channels for DMAC\n"); - kfree(pl330); - return ret; - } - - tasklet_init(&pl330->tasks, pl330_dotask, (unsigned long) pl330); - - pl330->state = INIT; - - return 0; -} -EXPORT_SYMBOL(pl330_add); - -static int dmac_free_threads(struct pl330_dmac *pl330) -{ - struct pl330_info *pi = pl330->pinfo; - int chans = pi->pcfg.num_chan; - struct pl330_thread *thrd; - int i; - - /* Release Channel threads */ - for (i = 0; i < chans; i++) { - thrd = &pl330->channels[i]; - pl330_release_channel((void *)thrd); - } - - /* Free memory */ - kfree(pl330->channels); - - return 0; -} - -static void dmac_free_resources(struct pl330_dmac *pl330) -{ - struct pl330_info *pi = pl330->pinfo; - int chans = pi->pcfg.num_chan; - - dmac_free_threads(pl330); - - dma_free_coherent(pi->dev, chans * pi->mcbufsz, - pl330->mcode_cpu, pl330->mcode_bus); -} - -void pl330_del(struct pl330_info *pi) -{ - struct pl330_dmac *pl330; - - if (!pi || !pi->pl330_data) - return; - - pl330 = pi->pl330_data; - - pl330->state = UNINIT; - - tasklet_kill(&pl330->tasks); - - /* Free DMAC resources */ - dmac_free_resources(pl330); - - kfree(pl330); - pi->pl330_data = NULL; -} -EXPORT_SYMBOL(pl330_del); diff --git a/arch/arm/include/asm/hardware/pl330.h b/arch/arm/include/asm/hardware/pl330.h deleted file mode 100644 index 575fa8186ca0..000000000000 --- a/arch/arm/include/asm/hardware/pl330.h +++ /dev/null @@ -1,217 +0,0 @@ -/* linux/include/asm/hardware/pl330.h - * - * Copyright (C) 2010 Samsung Electronics Co. Ltd. - * Jaswinder Singh - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef __PL330_CORE_H -#define __PL330_CORE_H - -#define PL330_MAX_CHAN 8 -#define PL330_MAX_IRQS 32 -#define PL330_MAX_PERI 32 - -enum pl330_srccachectrl { - SCCTRL0 = 0, /* Noncacheable and nonbufferable */ - SCCTRL1, /* Bufferable only */ - SCCTRL2, /* Cacheable, but do not allocate */ - SCCTRL3, /* Cacheable and bufferable, but do not allocate */ - SINVALID1, - SINVALID2, - SCCTRL6, /* Cacheable write-through, allocate on reads only */ - SCCTRL7, /* Cacheable write-back, allocate on reads only */ -}; - -enum pl330_dstcachectrl { - DCCTRL0 = 0, /* Noncacheable and nonbufferable */ - DCCTRL1, /* Bufferable only */ - DCCTRL2, /* Cacheable, but do not allocate */ - DCCTRL3, /* Cacheable and bufferable, but do not allocate */ - DINVALID1 = 8, - DINVALID2, - DCCTRL6, /* Cacheable write-through, allocate on writes only */ - DCCTRL7, /* Cacheable write-back, allocate on writes only */ -}; - -/* Populated by the PL330 core driver for DMA API driver's info */ -struct pl330_config { - u32 periph_id; - u32 pcell_id; -#define DMAC_MODE_NS (1 << 0) - unsigned int mode; - unsigned int data_bus_width:10; /* In number of bits */ - unsigned int data_buf_dep:10; - unsigned int num_chan:4; - unsigned int num_peri:6; - u32 peri_ns; - unsigned int num_events:6; - u32 irq_ns; -}; - -/* Handle to the DMAC provided to the PL330 core */ -struct pl330_info { - /* Owning device */ - struct device *dev; - /* Size of MicroCode buffers for each channel. */ - unsigned mcbufsz; - /* ioremap'ed address of PL330 registers. */ - void __iomem *base; - /* Client can freely use it. */ - void *client_data; - /* PL330 core data, Client must not touch it. */ - void *pl330_data; - /* Populated by the PL330 core driver during pl330_add */ - struct pl330_config pcfg; - /* - * If the DMAC has some reset mechanism, then the - * client may want to provide pointer to the method. - */ - void (*dmac_reset)(struct pl330_info *pi); -}; - -enum pl330_byteswap { - SWAP_NO = 0, - SWAP_2, - SWAP_4, - SWAP_8, - SWAP_16, -}; - -/** - * Request Configuration. - * The PL330 core does not modify this and uses the last - * working configuration if the request doesn't provide any. - * - * The Client may want to provide this info only for the - * first request and a request with new settings. - */ -struct pl330_reqcfg { - /* Address Incrementing */ - unsigned dst_inc:1; - unsigned src_inc:1; - - /* - * For now, the SRC & DST protection levels - * and burst size/length are assumed same. - */ - bool nonsecure; - bool privileged; - bool insnaccess; - unsigned brst_len:5; - unsigned brst_size:3; /* in power of 2 */ - - enum pl330_dstcachectrl dcctl; - enum pl330_srccachectrl scctl; - enum pl330_byteswap swap; -}; - -/* - * One cycle of DMAC operation. - * There may be more than one xfer in a request. - */ -struct pl330_xfer { - u32 src_addr; - u32 dst_addr; - /* Size to xfer */ - u32 bytes; - /* - * Pointer to next xfer in the list. - * The last xfer in the req must point to NULL. - */ - struct pl330_xfer *next; -}; - -/* The xfer callbacks are made with one of these arguments. */ -enum pl330_op_err { - /* The all xfers in the request were success. */ - PL330_ERR_NONE, - /* If req aborted due to global error. */ - PL330_ERR_ABORT, - /* If req failed due to problem with Channel. */ - PL330_ERR_FAIL, -}; - -enum pl330_reqtype { - MEMTOMEM, - MEMTODEV, - DEVTOMEM, - DEVTODEV, -}; - -/* A request defining Scatter-Gather List ending with NULL xfer. */ -struct pl330_req { - enum pl330_reqtype rqtype; - /* Index of peripheral for the xfer. */ - unsigned peri:5; - /* Unique token for this xfer, set by the client. */ - void *token; - /* Callback to be called after xfer. */ - void (*xfer_cb)(void *token, enum pl330_op_err err); - /* If NULL, req will be done at last set parameters. */ - struct pl330_reqcfg *cfg; - /* Pointer to first xfer in the request. */ - struct pl330_xfer *x; -}; - -/* - * To know the status of the channel and DMAC, the client - * provides a pointer to this structure. The PL330 core - * fills it with current information. - */ -struct pl330_chanstatus { - /* - * If the DMAC engine halted due to some error, - * the client should remove-add DMAC. - */ - bool dmac_halted; - /* - * If channel is halted due to some error, - * the client should ABORT/FLUSH and START the channel. - */ - bool faulting; - /* Location of last load */ - u32 src_addr; - /* Location of last store */ - u32 dst_addr; - /* - * Pointer to the currently active req, NULL if channel is - * inactive, even though the requests may be present. - */ - struct pl330_req *top_req; - /* Pointer to req waiting second in the queue if any. */ - struct pl330_req *wait_req; -}; - -enum pl330_chan_op { - /* Start the channel */ - PL330_OP_START, - /* Abort the active xfer */ - PL330_OP_ABORT, - /* Stop xfer and flush queue */ - PL330_OP_FLUSH, -}; - -extern int pl330_add(struct pl330_info *); -extern void pl330_del(struct pl330_info *pi); -extern int pl330_update(const struct pl330_info *pi); -extern void pl330_release_channel(void *ch_id); -extern void *pl330_request_channel(const struct pl330_info *pi); -extern int pl330_chan_status(void *ch_id, struct pl330_chanstatus *pstatus); -extern int pl330_chan_ctrl(void *ch_id, enum pl330_chan_op op); -extern int pl330_submit_req(void *ch_id, struct pl330_req *r); - -#endif /* __PL330_CORE_H */ diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index f1a274994bb1..65c61dba66de 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -201,7 +201,6 @@ config PL330_DMA tristate "DMA API Driver for PL330" select DMA_ENGINE depends on ARM_AMBA - select PL330 help Select if your platform has one or more PL330 DMACs. You need to provide platform specific settings via diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 84ebea9bc53a..2e351f40fc49 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -1,4 +1,6 @@ -/* linux/drivers/dma/pl330.c +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com * * Copyright (C) 2010 Samsung Electronics Co. Ltd. * Jaswinder Singh @@ -9,10 +11,15 @@ * (at your option) any later version. */ +#include #include #include #include #include +#include +#include +#include +#include #include #include #include @@ -21,8 +28,489 @@ #include #include +#define PL330_MAX_CHAN 8 +#define PL330_MAX_IRQS 32 +#define PL330_MAX_PERI 32 + +enum pl330_srccachectrl { + SCCTRL0, /* Noncacheable and nonbufferable */ + SCCTRL1, /* Bufferable only */ + SCCTRL2, /* Cacheable, but do not allocate */ + SCCTRL3, /* Cacheable and bufferable, but do not allocate */ + SINVALID1, + SINVALID2, + SCCTRL6, /* Cacheable write-through, allocate on reads only */ + SCCTRL7, /* Cacheable write-back, allocate on reads only */ +}; + +enum pl330_dstcachectrl { + DCCTRL0, /* Noncacheable and nonbufferable */ + DCCTRL1, /* Bufferable only */ + DCCTRL2, /* Cacheable, but do not allocate */ + DCCTRL3, /* Cacheable and bufferable, but do not allocate */ + DINVALID1 = 8, + DINVALID2, + DCCTRL6, /* Cacheable write-through, allocate on writes only */ + DCCTRL7, /* Cacheable write-back, allocate on writes only */ +}; + +enum pl330_byteswap { + SWAP_NO, + SWAP_2, + SWAP_4, + SWAP_8, + SWAP_16, +}; + +enum pl330_reqtype { + MEMTOMEM, + MEMTODEV, + DEVTOMEM, + DEVTODEV, +}; + +/* Register and Bit field Definitions */ +#define DS 0x0 +#define DS_ST_STOP 0x0 +#define DS_ST_EXEC 0x1 +#define DS_ST_CMISS 0x2 +#define DS_ST_UPDTPC 0x3 +#define DS_ST_WFE 0x4 +#define DS_ST_ATBRR 0x5 +#define DS_ST_QBUSY 0x6 +#define DS_ST_WFP 0x7 +#define DS_ST_KILL 0x8 +#define DS_ST_CMPLT 0x9 +#define DS_ST_FLTCMP 0xe +#define DS_ST_FAULT 0xf + +#define DPC 0x4 +#define INTEN 0x20 +#define ES 0x24 +#define INTSTATUS 0x28 +#define INTCLR 0x2c +#define FSM 0x30 +#define FSC 0x34 +#define FTM 0x38 + +#define _FTC 0x40 +#define FTC(n) (_FTC + (n)*0x4) + +#define _CS 0x100 +#define CS(n) (_CS + (n)*0x8) +#define CS_CNS (1 << 21) + +#define _CPC 0x104 +#define CPC(n) (_CPC + (n)*0x8) + +#define _SA 0x400 +#define SA(n) (_SA + (n)*0x20) + +#define _DA 0x404 +#define DA(n) (_DA + (n)*0x20) + +#define _CC 0x408 +#define CC(n) (_CC + (n)*0x20) + +#define CC_SRCINC (1 << 0) +#define CC_DSTINC (1 << 14) +#define CC_SRCPRI (1 << 8) +#define CC_DSTPRI (1 << 22) +#define CC_SRCNS (1 << 9) +#define CC_DSTNS (1 << 23) +#define CC_SRCIA (1 << 10) +#define CC_DSTIA (1 << 24) +#define CC_SRCBRSTLEN_SHFT 4 +#define CC_DSTBRSTLEN_SHFT 18 +#define CC_SRCBRSTSIZE_SHFT 1 +#define CC_DSTBRSTSIZE_SHFT 15 +#define CC_SRCCCTRL_SHFT 11 +#define CC_SRCCCTRL_MASK 0x7 +#define CC_DSTCCTRL_SHFT 25 +#define CC_DRCCCTRL_MASK 0x7 +#define CC_SWAP_SHFT 28 + +#define _LC0 0x40c +#define LC0(n) (_LC0 + (n)*0x20) + +#define _LC1 0x410 +#define LC1(n) (_LC1 + (n)*0x20) + +#define DBGSTATUS 0xd00 +#define DBG_BUSY (1 << 0) + +#define DBGCMD 0xd04 +#define DBGINST0 0xd08 +#define DBGINST1 0xd0c + +#define CR0 0xe00 +#define CR1 0xe04 +#define CR2 0xe08 +#define CR3 0xe0c +#define CR4 0xe10 +#define CRD 0xe14 + +#define PERIPH_ID 0xfe0 +#define PCELL_ID 0xff0 + +#define CR0_PERIPH_REQ_SET (1 << 0) +#define CR0_BOOT_EN_SET (1 << 1) +#define CR0_BOOT_MAN_NS (1 << 2) +#define CR0_NUM_CHANS_SHIFT 4 +#define CR0_NUM_CHANS_MASK 0x7 +#define CR0_NUM_PERIPH_SHIFT 12 +#define CR0_NUM_PERIPH_MASK 0x1f +#define CR0_NUM_EVENTS_SHIFT 17 +#define CR0_NUM_EVENTS_MASK 0x1f + +#define CR1_ICACHE_LEN_SHIFT 0 +#define CR1_ICACHE_LEN_MASK 0x7 +#define CR1_NUM_ICACHELINES_SHIFT 4 +#define CR1_NUM_ICACHELINES_MASK 0xf + +#define CRD_DATA_WIDTH_SHIFT 0 +#define CRD_DATA_WIDTH_MASK 0x7 +#define CRD_WR_CAP_SHIFT 4 +#define CRD_WR_CAP_MASK 0x7 +#define CRD_WR_Q_DEP_SHIFT 8 +#define CRD_WR_Q_DEP_MASK 0xf +#define CRD_RD_CAP_SHIFT 12 +#define CRD_RD_CAP_MASK 0x7 +#define CRD_RD_Q_DEP_SHIFT 16 +#define CRD_RD_Q_DEP_MASK 0xf +#define CRD_DATA_BUFF_SHIFT 20 +#define CRD_DATA_BUFF_MASK 0x3ff + +#define PART 0x330 +#define DESIGNER 0x41 +#define REVISION 0x0 +#define INTEG_CFG 0x0 +#define PERIPH_ID_VAL ((PART << 0) | (DESIGNER << 12)) + +#define PCELL_ID_VAL 0xb105f00d + +#define PL330_STATE_STOPPED (1 << 0) +#define PL330_STATE_EXECUTING (1 << 1) +#define PL330_STATE_WFE (1 << 2) +#define PL330_STATE_FAULTING (1 << 3) +#define PL330_STATE_COMPLETING (1 << 4) +#define PL330_STATE_WFP (1 << 5) +#define PL330_STATE_KILLING (1 << 6) +#define PL330_STATE_FAULT_COMPLETING (1 << 7) +#define PL330_STATE_CACHEMISS (1 << 8) +#define PL330_STATE_UPDTPC (1 << 9) +#define PL330_STATE_ATBARRIER (1 << 10) +#define PL330_STATE_QUEUEBUSY (1 << 11) +#define PL330_STATE_INVALID (1 << 15) + +#define PL330_STABLE_STATES (PL330_STATE_STOPPED | PL330_STATE_EXECUTING \ + | PL330_STATE_WFE | PL330_STATE_FAULTING) + +#define CMD_DMAADDH 0x54 +#define CMD_DMAEND 0x00 +#define CMD_DMAFLUSHP 0x35 +#define CMD_DMAGO 0xa0 +#define CMD_DMALD 0x04 +#define CMD_DMALDP 0x25 +#define CMD_DMALP 0x20 +#define CMD_DMALPEND 0x28 +#define CMD_DMAKILL 0x01 +#define CMD_DMAMOV 0xbc +#define CMD_DMANOP 0x18 +#define CMD_DMARMB 0x12 +#define CMD_DMASEV 0x34 +#define CMD_DMAST 0x08 +#define CMD_DMASTP 0x29 +#define CMD_DMASTZ 0x0c +#define CMD_DMAWFE 0x36 +#define CMD_DMAWFP 0x30 +#define CMD_DMAWMB 0x13 + +#define SZ_DMAADDH 3 +#define SZ_DMAEND 1 +#define SZ_DMAFLUSHP 2 +#define SZ_DMALD 1 +#define SZ_DMALDP 2 +#define SZ_DMALP 2 +#define SZ_DMALPEND 2 +#define SZ_DMAKILL 1 +#define SZ_DMAMOV 6 +#define SZ_DMANOP 1 +#define SZ_DMARMB 1 +#define SZ_DMASEV 2 +#define SZ_DMAST 1 +#define SZ_DMASTP 2 +#define SZ_DMASTZ 1 +#define SZ_DMAWFE 2 +#define SZ_DMAWFP 2 +#define SZ_DMAWMB 1 +#define SZ_DMAGO 6 + +#define BRST_LEN(ccr) ((((ccr) >> CC_SRCBRSTLEN_SHFT) & 0xf) + 1) +#define BRST_SIZE(ccr) (1 << (((ccr) >> CC_SRCBRSTSIZE_SHFT) & 0x7)) + +#define BYTE_TO_BURST(b, ccr) ((b) / BRST_SIZE(ccr) / BRST_LEN(ccr)) +#define BURST_TO_BYTE(c, ccr) ((c) * BRST_SIZE(ccr) * BRST_LEN(ccr)) + +/* + * With 256 bytes, we can do more than 2.5MB and 5MB xfers per req + * at 1byte/burst for P<->M and M<->M respectively. + * For typical scenario, at 1word/burst, 10MB and 20MB xfers per req + * should be enough for P<->M and M<->M respectively. + */ +#define MCODE_BUFF_PER_REQ 256 + +/* If the _pl330_req is available to the client */ +#define IS_FREE(req) (*((u8 *)((req)->mc_cpu)) == CMD_DMAEND) + +/* Use this _only_ to wait on transient states */ +#define UNTIL(t, s) while (!(_state(t) & (s))) cpu_relax(); + +#ifdef PL330_DEBUG_MCGEN +static unsigned cmd_line; +#define PL330_DBGCMD_DUMP(off, x...) do { \ + printk("%x:", cmd_line); \ + printk(x); \ + cmd_line += off; \ + } while (0) +#define PL330_DBGMC_START(addr) (cmd_line = addr) +#else +#define PL330_DBGCMD_DUMP(off, x...) do {} while (0) +#define PL330_DBGMC_START(addr) do {} while (0) +#endif + +/* The number of default descriptors */ #define NR_DEFAULT_DESC 16 +/* Populated by the PL330 core driver for DMA API driver's info */ +struct pl330_config { + u32 periph_id; + u32 pcell_id; +#define DMAC_MODE_NS (1 << 0) + unsigned int mode; + unsigned int data_bus_width:10; /* In number of bits */ + unsigned int data_buf_dep:10; + unsigned int num_chan:4; + unsigned int num_peri:6; + u32 peri_ns; + unsigned int num_events:6; + u32 irq_ns; +}; + +/* Handle to the DMAC provided to the PL330 core */ +struct pl330_info { + /* Owning device */ + struct device *dev; + /* Size of MicroCode buffers for each channel. */ + unsigned mcbufsz; + /* ioremap'ed address of PL330 registers. */ + void __iomem *base; + /* Client can freely use it. */ + void *client_data; + /* PL330 core data, Client must not touch it. */ + void *pl330_data; + /* Populated by the PL330 core driver during pl330_add */ + struct pl330_config pcfg; + /* + * If the DMAC has some reset mechanism, then the + * client may want to provide pointer to the method. + */ + void (*dmac_reset)(struct pl330_info *pi); +}; + +/** + * Request Configuration. + * The PL330 core does not modify this and uses the last + * working configuration if the request doesn't provide any. + * + * The Client may want to provide this info only for the + * first request and a request with new settings. + */ +struct pl330_reqcfg { + /* Address Incrementing */ + unsigned dst_inc:1; + unsigned src_inc:1; + + /* + * For now, the SRC & DST protection levels + * and burst size/length are assumed same. + */ + bool nonsecure; + bool privileged; + bool insnaccess; + unsigned brst_len:5; + unsigned brst_size:3; /* in power of 2 */ + + enum pl330_dstcachectrl dcctl; + enum pl330_srccachectrl scctl; + enum pl330_byteswap swap; +}; + +/* + * One cycle of DMAC operation. + * There may be more than one xfer in a request. + */ +struct pl330_xfer { + u32 src_addr; + u32 dst_addr; + /* Size to xfer */ + u32 bytes; + /* + * Pointer to next xfer in the list. + * The last xfer in the req must point to NULL. + */ + struct pl330_xfer *next; +}; + +/* The xfer callbacks are made with one of these arguments. */ +enum pl330_op_err { + /* The all xfers in the request were success. */ + PL330_ERR_NONE, + /* If req aborted due to global error. */ + PL330_ERR_ABORT, + /* If req failed due to problem with Channel. */ + PL330_ERR_FAIL, +}; + +/* A request defining Scatter-Gather List ending with NULL xfer. */ +struct pl330_req { + enum pl330_reqtype rqtype; + /* Index of peripheral for the xfer. */ + unsigned peri:5; + /* Unique token for this xfer, set by the client. */ + void *token; + /* Callback to be called after xfer. */ + void (*xfer_cb)(void *token, enum pl330_op_err err); + /* If NULL, req will be done at last set parameters. */ + struct pl330_reqcfg *cfg; + /* Pointer to first xfer in the request. */ + struct pl330_xfer *x; +}; + +/* + * To know the status of the channel and DMAC, the client + * provides a pointer to this structure. The PL330 core + * fills it with current information. + */ +struct pl330_chanstatus { + /* + * If the DMAC engine halted due to some error, + * the client should remove-add DMAC. + */ + bool dmac_halted; + /* + * If channel is halted due to some error, + * the client should ABORT/FLUSH and START the channel. + */ + bool faulting; + /* Location of last load */ + u32 src_addr; + /* Location of last store */ + u32 dst_addr; + /* + * Pointer to the currently active req, NULL if channel is + * inactive, even though the requests may be present. + */ + struct pl330_req *top_req; + /* Pointer to req waiting second in the queue if any. */ + struct pl330_req *wait_req; +}; + +enum pl330_chan_op { + /* Start the channel */ + PL330_OP_START, + /* Abort the active xfer */ + PL330_OP_ABORT, + /* Stop xfer and flush queue */ + PL330_OP_FLUSH, +}; + +struct _xfer_spec { + u32 ccr; + struct pl330_req *r; + struct pl330_xfer *x; +}; + +enum dmamov_dst { + SAR = 0, + CCR, + DAR, +}; + +enum pl330_dst { + SRC = 0, + DST, +}; + +enum pl330_cond { + SINGLE, + BURST, + ALWAYS, +}; + +struct _pl330_req { + u32 mc_bus; + void *mc_cpu; + /* Number of bytes taken to setup MC for the req */ + u32 mc_len; + struct pl330_req *r; + /* Hook to attach to DMAC's list of reqs with due callback */ + struct list_head rqd; +}; + +/* ToBeDone for tasklet */ +struct _pl330_tbd { + bool reset_dmac; + bool reset_mngr; + u8 reset_chan; +}; + +/* A DMAC Thread */ +struct pl330_thread { + u8 id; + int ev; + /* If the channel is not yet acquired by any client */ + bool free; + /* Parent DMAC */ + struct pl330_dmac *dmac; + /* Only two at a time */ + struct _pl330_req req[2]; + /* Index of the last enqueued request */ + unsigned lstenq; + /* Index of the last submitted request or -1 if the DMA is stopped */ + int req_running; +}; + +enum pl330_dmac_state { + UNINIT, + INIT, + DYING, +}; + +/* A DMAC */ +struct pl330_dmac { + spinlock_t lock; + /* Holds list of reqs with due callbacks */ + struct list_head req_done; + /* Pointer to platform specific stuff */ + struct pl330_info *pinfo; + /* Maximum possible events/irqs */ + int events[32]; + /* BUS address of MicroCode buffer */ + u32 mcode_bus; + /* CPU address of MicroCode buffer */ + void *mcode_cpu; + /* List of all Channel threads */ + struct pl330_thread *channels; + /* Pointer to the MANAGER thread */ + struct pl330_thread *manager; + /* To handle bad news in interrupt */ + struct tasklet_struct tasks; + struct _pl330_tbd dmac_tbd; + /* State of DMAC operation */ + enum pl330_dmac_state state; +}; + enum desc_status { /* In the DMAC pool */ FREE, @@ -117,6 +605,1630 @@ struct dma_pl330_desc { struct dma_pl330_chan *pchan; }; +static inline void _callback(struct pl330_req *r, enum pl330_op_err err) +{ + if (r && r->xfer_cb) + r->xfer_cb(r->token, err); +} + +static inline bool _queue_empty(struct pl330_thread *thrd) +{ + return (IS_FREE(&thrd->req[0]) && IS_FREE(&thrd->req[1])) + ? true : false; +} + +static inline bool _queue_full(struct pl330_thread *thrd) +{ + return (IS_FREE(&thrd->req[0]) || IS_FREE(&thrd->req[1])) + ? false : true; +} + +static inline bool is_manager(struct pl330_thread *thrd) +{ + struct pl330_dmac *pl330 = thrd->dmac; + + /* MANAGER is indexed at the end */ + if (thrd->id == pl330->pinfo->pcfg.num_chan) + return true; + else + return false; +} + +/* If manager of the thread is in Non-Secure mode */ +static inline bool _manager_ns(struct pl330_thread *thrd) +{ + struct pl330_dmac *pl330 = thrd->dmac; + + return (pl330->pinfo->pcfg.mode & DMAC_MODE_NS) ? true : false; +} + +static inline u32 get_id(struct pl330_info *pi, u32 off) +{ + void __iomem *regs = pi->base; + u32 id = 0; + + id |= (readb(regs + off + 0x0) << 0); + id |= (readb(regs + off + 0x4) << 8); + id |= (readb(regs + off + 0x8) << 16); + id |= (readb(regs + off + 0xc) << 24); + + return id; +} + +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); + *((u16 *)&buf[1]) = val; + + 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) + return SZ_DMAEND; + + buf[0] = CMD_DMAEND; + + PL330_DBGCMD_DUMP(SZ_DMAEND, "\tDMAEND\n"); + + return SZ_DMAEND; +} + +static inline u32 _emit_FLUSHP(unsigned dry_run, u8 buf[], u8 peri) +{ + if (dry_run) + return SZ_DMAFLUSHP; + + buf[0] = CMD_DMAFLUSHP; + + peri &= 0x1f; + peri <<= 3; + buf[1] = peri; + + PL330_DBGCMD_DUMP(SZ_DMAFLUSHP, "\tDMAFLUSHP %u\n", peri >> 3); + + return SZ_DMAFLUSHP; +} + +static inline u32 _emit_LD(unsigned dry_run, u8 buf[], enum pl330_cond cond) +{ + if (dry_run) + return SZ_DMALD; + + buf[0] = CMD_DMALD; + + if (cond == SINGLE) + buf[0] |= (0 << 1) | (1 << 0); + else if (cond == BURST) + buf[0] |= (1 << 1) | (1 << 0); + + PL330_DBGCMD_DUMP(SZ_DMALD, "\tDMALD%c\n", + cond == SINGLE ? 'S' : (cond == BURST ? 'B' : 'A')); + + return SZ_DMALD; +} + +static inline u32 _emit_LDP(unsigned dry_run, u8 buf[], + enum pl330_cond cond, u8 peri) +{ + if (dry_run) + return SZ_DMALDP; + + buf[0] = CMD_DMALDP; + + if (cond == BURST) + buf[0] |= (1 << 1); + + peri &= 0x1f; + peri <<= 3; + buf[1] = peri; + + PL330_DBGCMD_DUMP(SZ_DMALDP, "\tDMALDP%c %u\n", + cond == SINGLE ? 'S' : 'B', peri >> 3); + + return SZ_DMALDP; +} + +static inline u32 _emit_LP(unsigned dry_run, u8 buf[], + unsigned loop, u8 cnt) +{ + if (dry_run) + return SZ_DMALP; + + buf[0] = CMD_DMALP; + + if (loop) + buf[0] |= (1 << 1); + + cnt--; /* DMAC increments by 1 internally */ + buf[1] = cnt; + + PL330_DBGCMD_DUMP(SZ_DMALP, "\tDMALP_%c %u\n", loop ? '1' : '0', cnt); + + return SZ_DMALP; +} + +struct _arg_LPEND { + enum pl330_cond cond; + bool forever; + unsigned loop; + u8 bjump; +}; + +static inline u32 _emit_LPEND(unsigned dry_run, u8 buf[], + const struct _arg_LPEND *arg) +{ + enum pl330_cond cond = arg->cond; + bool forever = arg->forever; + unsigned loop = arg->loop; + u8 bjump = arg->bjump; + + if (dry_run) + return SZ_DMALPEND; + + buf[0] = CMD_DMALPEND; + + if (loop) + buf[0] |= (1 << 2); + + if (!forever) + buf[0] |= (1 << 4); + + if (cond == SINGLE) + buf[0] |= (0 << 1) | (1 << 0); + else if (cond == BURST) + buf[0] |= (1 << 1) | (1 << 0); + + buf[1] = bjump; + + PL330_DBGCMD_DUMP(SZ_DMALPEND, "\tDMALP%s%c_%c bjmpto_%x\n", + forever ? "FE" : "END", + cond == SINGLE ? 'S' : (cond == BURST ? 'B' : 'A'), + loop ? '1' : '0', + bjump); + + return SZ_DMALPEND; +} + +static inline u32 _emit_KILL(unsigned dry_run, u8 buf[]) +{ + if (dry_run) + return SZ_DMAKILL; + + buf[0] = CMD_DMAKILL; + + return SZ_DMAKILL; +} + +static inline u32 _emit_MOV(unsigned dry_run, u8 buf[], + enum dmamov_dst dst, u32 val) +{ + if (dry_run) + return SZ_DMAMOV; + + buf[0] = CMD_DMAMOV; + buf[1] = dst; + *((u32 *)&buf[2]) = val; + + PL330_DBGCMD_DUMP(SZ_DMAMOV, "\tDMAMOV %s 0x%x\n", + dst == SAR ? "SAR" : (dst == DAR ? "DAR" : "CCR"), val); + + 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) + return SZ_DMARMB; + + buf[0] = CMD_DMARMB; + + PL330_DBGCMD_DUMP(SZ_DMARMB, "\tDMARMB\n"); + + return SZ_DMARMB; +} + +static inline u32 _emit_SEV(unsigned dry_run, u8 buf[], u8 ev) +{ + if (dry_run) + return SZ_DMASEV; + + buf[0] = CMD_DMASEV; + + ev &= 0x1f; + ev <<= 3; + buf[1] = ev; + + PL330_DBGCMD_DUMP(SZ_DMASEV, "\tDMASEV %u\n", ev >> 3); + + return SZ_DMASEV; +} + +static inline u32 _emit_ST(unsigned dry_run, u8 buf[], enum pl330_cond cond) +{ + if (dry_run) + return SZ_DMAST; + + buf[0] = CMD_DMAST; + + if (cond == SINGLE) + buf[0] |= (0 << 1) | (1 << 0); + else if (cond == BURST) + buf[0] |= (1 << 1) | (1 << 0); + + PL330_DBGCMD_DUMP(SZ_DMAST, "\tDMAST%c\n", + cond == SINGLE ? 'S' : (cond == BURST ? 'B' : 'A')); + + return SZ_DMAST; +} + +static inline u32 _emit_STP(unsigned dry_run, u8 buf[], + enum pl330_cond cond, u8 peri) +{ + if (dry_run) + return SZ_DMASTP; + + buf[0] = CMD_DMASTP; + + if (cond == BURST) + buf[0] |= (1 << 1); + + peri &= 0x1f; + peri <<= 3; + buf[1] = peri; + + PL330_DBGCMD_DUMP(SZ_DMASTP, "\tDMASTP%c %u\n", + cond == SINGLE ? 'S' : 'B', peri >> 3); + + 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) +{ + if (dry_run) + return SZ_DMAWFP; + + buf[0] = CMD_DMAWFP; + + if (cond == SINGLE) + buf[0] |= (0 << 1) | (0 << 0); + else if (cond == BURST) + buf[0] |= (1 << 1) | (0 << 0); + else + buf[0] |= (0 << 1) | (1 << 0); + + peri &= 0x1f; + peri <<= 3; + buf[1] = peri; + + PL330_DBGCMD_DUMP(SZ_DMAWFP, "\tDMAWFP%c %u\n", + cond == SINGLE ? 'S' : (cond == BURST ? 'B' : 'P'), peri >> 3); + + return SZ_DMAWFP; +} + +static inline u32 _emit_WMB(unsigned dry_run, u8 buf[]) +{ + if (dry_run) + return SZ_DMAWMB; + + buf[0] = CMD_DMAWMB; + + PL330_DBGCMD_DUMP(SZ_DMAWMB, "\tDMAWMB\n"); + + return SZ_DMAWMB; +} + +struct _arg_GO { + u8 chan; + u32 addr; + unsigned ns; +}; + +static inline u32 _emit_GO(unsigned dry_run, u8 buf[], + const struct _arg_GO *arg) +{ + u8 chan = arg->chan; + u32 addr = arg->addr; + unsigned ns = arg->ns; + + if (dry_run) + return SZ_DMAGO; + + buf[0] = CMD_DMAGO; + buf[0] |= (ns << 1); + + buf[1] = chan & 0x7; + + *((u32 *)&buf[2]) = addr; + + return SZ_DMAGO; +} + +#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t) + +/* Returns Time-Out */ +static bool _until_dmac_idle(struct pl330_thread *thrd) +{ + void __iomem *regs = thrd->dmac->pinfo->base; + unsigned long loops = msecs_to_loops(5); + + do { + /* Until Manager is Idle */ + if (!(readl(regs + DBGSTATUS) & DBG_BUSY)) + break; + + cpu_relax(); + } while (--loops); + + if (!loops) + return true; + + return false; +} + +static inline void _execute_DBGINSN(struct pl330_thread *thrd, + u8 insn[], bool as_manager) +{ + void __iomem *regs = thrd->dmac->pinfo->base; + u32 val; + + val = (insn[0] << 16) | (insn[1] << 24); + if (!as_manager) { + val |= (1 << 0); + val |= (thrd->id << 8); /* Channel Number */ + } + writel(val, regs + DBGINST0); + + val = *((u32 *)&insn[2]); + writel(val, regs + DBGINST1); + + /* If timed out due to halted state-machine */ + if (_until_dmac_idle(thrd)) { + dev_err(thrd->dmac->pinfo->dev, "DMAC halted!\n"); + return; + } + + /* Get going */ + writel(0, regs + DBGCMD); +} + +/* + * Mark a _pl330_req as free. + * We do it by writing DMAEND as the first instruction + * because no valid request is going to have DMAEND as + * its first instruction to execute. + */ +static void mark_free(struct pl330_thread *thrd, int idx) +{ + struct _pl330_req *req = &thrd->req[idx]; + + _emit_END(0, req->mc_cpu); + req->mc_len = 0; + + thrd->req_running = -1; +} + +static inline u32 _state(struct pl330_thread *thrd) +{ + void __iomem *regs = thrd->dmac->pinfo->base; + u32 val; + + if (is_manager(thrd)) + val = readl(regs + DS) & 0xf; + else + val = readl(regs + CS(thrd->id)) & 0xf; + + switch (val) { + case DS_ST_STOP: + return PL330_STATE_STOPPED; + case DS_ST_EXEC: + return PL330_STATE_EXECUTING; + case DS_ST_CMISS: + return PL330_STATE_CACHEMISS; + case DS_ST_UPDTPC: + return PL330_STATE_UPDTPC; + case DS_ST_WFE: + return PL330_STATE_WFE; + case DS_ST_FAULT: + return PL330_STATE_FAULTING; + case DS_ST_ATBRR: + if (is_manager(thrd)) + return PL330_STATE_INVALID; + else + return PL330_STATE_ATBARRIER; + case DS_ST_QBUSY: + if (is_manager(thrd)) + return PL330_STATE_INVALID; + else + return PL330_STATE_QUEUEBUSY; + case DS_ST_WFP: + if (is_manager(thrd)) + return PL330_STATE_INVALID; + else + return PL330_STATE_WFP; + case DS_ST_KILL: + if (is_manager(thrd)) + return PL330_STATE_INVALID; + else + return PL330_STATE_KILLING; + case DS_ST_CMPLT: + if (is_manager(thrd)) + return PL330_STATE_INVALID; + else + return PL330_STATE_COMPLETING; + case DS_ST_FLTCMP: + if (is_manager(thrd)) + return PL330_STATE_INVALID; + else + return PL330_STATE_FAULT_COMPLETING; + default: + return PL330_STATE_INVALID; + } +} + +static void _stop(struct pl330_thread *thrd) +{ + void __iomem *regs = thrd->dmac->pinfo->base; + u8 insn[6] = {0, 0, 0, 0, 0, 0}; + + if (_state(thrd) == PL330_STATE_FAULT_COMPLETING) + UNTIL(thrd, PL330_STATE_FAULTING | PL330_STATE_KILLING); + + /* Return if nothing needs to be done */ + if (_state(thrd) == PL330_STATE_COMPLETING + || _state(thrd) == PL330_STATE_KILLING + || _state(thrd) == PL330_STATE_STOPPED) + return; + + _emit_KILL(0, insn); + + /* Stop generating interrupts for SEV */ + writel(readl(regs + INTEN) & ~(1 << thrd->ev), regs + INTEN); + + _execute_DBGINSN(thrd, insn, is_manager(thrd)); +} + +/* Start doing req 'idx' of thread 'thrd' */ +static bool _trigger(struct pl330_thread *thrd) +{ + void __iomem *regs = thrd->dmac->pinfo->base; + struct _pl330_req *req; + struct pl330_req *r; + struct _arg_GO go; + unsigned ns; + u8 insn[6] = {0, 0, 0, 0, 0, 0}; + int idx; + + /* Return if already ACTIVE */ + if (_state(thrd) != PL330_STATE_STOPPED) + return true; + + idx = 1 - thrd->lstenq; + if (!IS_FREE(&thrd->req[idx])) + req = &thrd->req[idx]; + else { + idx = thrd->lstenq; + if (!IS_FREE(&thrd->req[idx])) + req = &thrd->req[idx]; + else + req = NULL; + } + + /* Return if no request */ + if (!req || !req->r) + return true; + + r = req->r; + + if (r->cfg) + ns = r->cfg->nonsecure ? 1 : 0; + else if (readl(regs + CS(thrd->id)) & CS_CNS) + ns = 1; + else + ns = 0; + + /* See 'Abort Sources' point-4 at Page 2-25 */ + if (_manager_ns(thrd) && !ns) + dev_info(thrd->dmac->pinfo->dev, "%s:%d Recipe for ABORT!\n", + __func__, __LINE__); + + go.chan = thrd->id; + go.addr = req->mc_bus; + go.ns = ns; + _emit_GO(0, insn, &go); + + /* Set to generate interrupts for SEV */ + writel(readl(regs + INTEN) | (1 << thrd->ev), regs + INTEN); + + /* Only manager can execute GO */ + _execute_DBGINSN(thrd, insn, true); + + thrd->req_running = idx; + + return true; +} + +static bool _start(struct pl330_thread *thrd) +{ + switch (_state(thrd)) { + case PL330_STATE_FAULT_COMPLETING: + UNTIL(thrd, PL330_STATE_FAULTING | PL330_STATE_KILLING); + + if (_state(thrd) == PL330_STATE_KILLING) + UNTIL(thrd, PL330_STATE_STOPPED) + + case PL330_STATE_FAULTING: + _stop(thrd); + + case PL330_STATE_KILLING: + case PL330_STATE_COMPLETING: + UNTIL(thrd, PL330_STATE_STOPPED) + + case PL330_STATE_STOPPED: + return _trigger(thrd); + + case PL330_STATE_WFP: + case PL330_STATE_QUEUEBUSY: + case PL330_STATE_ATBARRIER: + case PL330_STATE_UPDTPC: + case PL330_STATE_CACHEMISS: + case PL330_STATE_EXECUTING: + return true; + + case PL330_STATE_WFE: /* For RESUME, nothing yet */ + default: + return false; + } +} + +static inline int _ldst_memtomem(unsigned dry_run, u8 buf[], + const struct _xfer_spec *pxs, int cyc) +{ + int off = 0; + + while (cyc--) { + off += _emit_LD(dry_run, &buf[off], ALWAYS); + off += _emit_RMB(dry_run, &buf[off]); + off += _emit_ST(dry_run, &buf[off], ALWAYS); + off += _emit_WMB(dry_run, &buf[off]); + } + + return off; +} + +static inline int _ldst_devtomem(unsigned dry_run, u8 buf[], + const struct _xfer_spec *pxs, int cyc) +{ + int off = 0; + + while (cyc--) { + off += _emit_WFP(dry_run, &buf[off], SINGLE, pxs->r->peri); + off += _emit_LDP(dry_run, &buf[off], SINGLE, pxs->r->peri); + off += _emit_ST(dry_run, &buf[off], ALWAYS); + off += _emit_FLUSHP(dry_run, &buf[off], pxs->r->peri); + } + + return off; +} + +static inline int _ldst_memtodev(unsigned dry_run, u8 buf[], + const struct _xfer_spec *pxs, int cyc) +{ + int off = 0; + + while (cyc--) { + off += _emit_WFP(dry_run, &buf[off], SINGLE, pxs->r->peri); + off += _emit_LD(dry_run, &buf[off], ALWAYS); + off += _emit_STP(dry_run, &buf[off], SINGLE, pxs->r->peri); + off += _emit_FLUSHP(dry_run, &buf[off], pxs->r->peri); + } + + return off; +} + +static int _bursts(unsigned dry_run, u8 buf[], + const struct _xfer_spec *pxs, int cyc) +{ + int off = 0; + + switch (pxs->r->rqtype) { + case MEMTODEV: + off += _ldst_memtodev(dry_run, &buf[off], pxs, cyc); + break; + case DEVTOMEM: + off += _ldst_devtomem(dry_run, &buf[off], pxs, cyc); + break; + case MEMTOMEM: + off += _ldst_memtomem(dry_run, &buf[off], pxs, cyc); + break; + default: + off += 0x40000000; /* Scare off the Client */ + break; + } + + return off; +} + +/* Returns bytes consumed and updates bursts */ +static inline int _loop(unsigned dry_run, u8 buf[], + unsigned long *bursts, const struct _xfer_spec *pxs) +{ + int cyc, cycmax, szlp, szlpend, szbrst, off; + unsigned lcnt0, lcnt1, ljmp0, ljmp1; + struct _arg_LPEND lpend; + + /* Max iterations possible in DMALP is 256 */ + if (*bursts >= 256*256) { + lcnt1 = 256; + lcnt0 = 256; + cyc = *bursts / lcnt1 / lcnt0; + } else if (*bursts > 256) { + lcnt1 = 256; + lcnt0 = *bursts / lcnt1; + cyc = 1; + } else { + lcnt1 = *bursts; + lcnt0 = 0; + cyc = 1; + } + + szlp = _emit_LP(1, buf, 0, 0); + szbrst = _bursts(1, buf, pxs, 1); + + lpend.cond = ALWAYS; + lpend.forever = false; + lpend.loop = 0; + lpend.bjump = 0; + szlpend = _emit_LPEND(1, buf, &lpend); + + if (lcnt0) { + szlp *= 2; + szlpend *= 2; + } + + /* + * Max bursts that we can unroll due to limit on the + * size of backward jump that can be encoded in DMALPEND + * which is 8-bits and hence 255 + */ + cycmax = (255 - (szlp + szlpend)) / szbrst; + + cyc = (cycmax < cyc) ? cycmax : cyc; + + off = 0; + + if (lcnt0) { + off += _emit_LP(dry_run, &buf[off], 0, lcnt0); + ljmp0 = off; + } + + off += _emit_LP(dry_run, &buf[off], 1, lcnt1); + ljmp1 = off; + + off += _bursts(dry_run, &buf[off], pxs, cyc); + + lpend.cond = ALWAYS; + lpend.forever = false; + lpend.loop = 1; + lpend.bjump = off - ljmp1; + off += _emit_LPEND(dry_run, &buf[off], &lpend); + + if (lcnt0) { + lpend.cond = ALWAYS; + lpend.forever = false; + lpend.loop = 0; + lpend.bjump = off - ljmp0; + off += _emit_LPEND(dry_run, &buf[off], &lpend); + } + + *bursts = lcnt1 * cyc; + if (lcnt0) + *bursts *= lcnt0; + + return off; +} + +static inline int _setup_loops(unsigned dry_run, u8 buf[], + const struct _xfer_spec *pxs) +{ + struct pl330_xfer *x = pxs->x; + u32 ccr = pxs->ccr; + unsigned long c, bursts = BYTE_TO_BURST(x->bytes, ccr); + int off = 0; + + while (bursts) { + c = bursts; + off += _loop(dry_run, &buf[off], &c, pxs); + bursts -= c; + } + + return off; +} + +static inline int _setup_xfer(unsigned dry_run, u8 buf[], + const struct _xfer_spec *pxs) +{ + struct pl330_xfer *x = pxs->x; + int off = 0; + + /* DMAMOV SAR, x->src_addr */ + off += _emit_MOV(dry_run, &buf[off], SAR, x->src_addr); + /* DMAMOV DAR, x->dst_addr */ + off += _emit_MOV(dry_run, &buf[off], DAR, x->dst_addr); + + /* Setup Loop(s) */ + off += _setup_loops(dry_run, &buf[off], pxs); + + return off; +} + +/* + * A req is a sequence of one or more xfer units. + * Returns the number of bytes taken to setup the MC for the req. + */ +static int _setup_req(unsigned dry_run, struct pl330_thread *thrd, + unsigned index, struct _xfer_spec *pxs) +{ + struct _pl330_req *req = &thrd->req[index]; + struct pl330_xfer *x; + u8 *buf = req->mc_cpu; + int off = 0; + + PL330_DBGMC_START(req->mc_bus); + + /* DMAMOV CCR, ccr */ + off += _emit_MOV(dry_run, &buf[off], CCR, pxs->ccr); + + x = pxs->r->x; + do { + /* Error if xfer length is not aligned at burst size */ + if (x->bytes % (BRST_SIZE(pxs->ccr) * BRST_LEN(pxs->ccr))) + return -EINVAL; + + pxs->x = x; + off += _setup_xfer(dry_run, &buf[off], pxs); + + x = x->next; + } while (x); + + /* DMASEV peripheral/event */ + off += _emit_SEV(dry_run, &buf[off], thrd->ev); + /* DMAEND */ + off += _emit_END(dry_run, &buf[off]); + + return off; +} + +static inline u32 _prepare_ccr(const struct pl330_reqcfg *rqc) +{ + u32 ccr = 0; + + if (rqc->src_inc) + ccr |= CC_SRCINC; + + if (rqc->dst_inc) + ccr |= CC_DSTINC; + + /* We set same protection levels for Src and DST for now */ + if (rqc->privileged) + ccr |= CC_SRCPRI | CC_DSTPRI; + if (rqc->nonsecure) + ccr |= CC_SRCNS | CC_DSTNS; + if (rqc->insnaccess) + ccr |= CC_SRCIA | CC_DSTIA; + + ccr |= (((rqc->brst_len - 1) & 0xf) << CC_SRCBRSTLEN_SHFT); + ccr |= (((rqc->brst_len - 1) & 0xf) << CC_DSTBRSTLEN_SHFT); + + ccr |= (rqc->brst_size << CC_SRCBRSTSIZE_SHFT); + ccr |= (rqc->brst_size << CC_DSTBRSTSIZE_SHFT); + + ccr |= (rqc->scctl << CC_SRCCCTRL_SHFT); + ccr |= (rqc->dcctl << CC_DSTCCTRL_SHFT); + + ccr |= (rqc->swap << CC_SWAP_SHFT); + + return ccr; +} + +static inline bool _is_valid(u32 ccr) +{ + enum pl330_dstcachectrl dcctl; + enum pl330_srccachectrl scctl; + + dcctl = (ccr >> CC_DSTCCTRL_SHFT) & CC_DRCCCTRL_MASK; + scctl = (ccr >> CC_SRCCCTRL_SHFT) & CC_SRCCCTRL_MASK; + + if (dcctl == DINVALID1 || dcctl == DINVALID2 + || scctl == SINVALID1 || scctl == SINVALID2) + return false; + else + return true; +} + +/* + * Submit a list of xfers after which the client wants notification. + * Client is not notified after each xfer unit, just once after all + * xfer units are done or some error occurs. + */ +static int pl330_submit_req(void *ch_id, struct pl330_req *r) +{ + struct pl330_thread *thrd = ch_id; + struct pl330_dmac *pl330; + struct pl330_info *pi; + struct _xfer_spec xs; + unsigned long flags; + void __iomem *regs; + unsigned idx; + u32 ccr; + int ret = 0; + + /* No Req or Unacquired Channel or DMAC */ + if (!r || !thrd || thrd->free) + return -EINVAL; + + pl330 = thrd->dmac; + pi = pl330->pinfo; + regs = pi->base; + + if (pl330->state == DYING + || pl330->dmac_tbd.reset_chan & (1 << thrd->id)) { + dev_info(thrd->dmac->pinfo->dev, "%s:%d\n", + __func__, __LINE__); + return -EAGAIN; + } + + /* If request for non-existing peripheral */ + if (r->rqtype != MEMTOMEM && r->peri >= pi->pcfg.num_peri) { + dev_info(thrd->dmac->pinfo->dev, + "%s:%d Invalid peripheral(%u)!\n", + __func__, __LINE__, r->peri); + return -EINVAL; + } + + spin_lock_irqsave(&pl330->lock, flags); + + if (_queue_full(thrd)) { + ret = -EAGAIN; + goto xfer_exit; + } + + /* Prefer Secure Channel */ + if (!_manager_ns(thrd)) + r->cfg->nonsecure = 0; + else + r->cfg->nonsecure = 1; + + /* Use last settings, if not provided */ + if (r->cfg) + ccr = _prepare_ccr(r->cfg); + else + ccr = readl(regs + CC(thrd->id)); + + /* If this req doesn't have valid xfer settings */ + if (!_is_valid(ccr)) { + ret = -EINVAL; + dev_info(thrd->dmac->pinfo->dev, "%s:%d Invalid CCR(%x)!\n", + __func__, __LINE__, ccr); + goto xfer_exit; + } + + idx = IS_FREE(&thrd->req[0]) ? 0 : 1; + + xs.ccr = ccr; + xs.r = r; + + /* First dry run to check if req is acceptable */ + ret = _setup_req(1, thrd, idx, &xs); + if (ret < 0) + goto xfer_exit; + + if (ret > pi->mcbufsz / 2) { + dev_info(thrd->dmac->pinfo->dev, + "%s:%d Trying increasing mcbufsz\n", + __func__, __LINE__); + ret = -ENOMEM; + goto xfer_exit; + } + + /* Hook the request */ + thrd->lstenq = idx; + thrd->req[idx].mc_len = _setup_req(0, thrd, idx, &xs); + thrd->req[idx].r = r; + + ret = 0; + +xfer_exit: + spin_unlock_irqrestore(&pl330->lock, flags); + + return ret; +} + +static void pl330_dotask(unsigned long data) +{ + struct pl330_dmac *pl330 = (struct pl330_dmac *) data; + struct pl330_info *pi = pl330->pinfo; + unsigned long flags; + int i; + + spin_lock_irqsave(&pl330->lock, flags); + + /* The DMAC itself gone nuts */ + if (pl330->dmac_tbd.reset_dmac) { + pl330->state = DYING; + /* Reset the manager too */ + pl330->dmac_tbd.reset_mngr = true; + /* Clear the reset flag */ + pl330->dmac_tbd.reset_dmac = false; + } + + if (pl330->dmac_tbd.reset_mngr) { + _stop(pl330->manager); + /* Reset all channels */ + pl330->dmac_tbd.reset_chan = (1 << pi->pcfg.num_chan) - 1; + /* Clear the reset flag */ + pl330->dmac_tbd.reset_mngr = false; + } + + for (i = 0; i < pi->pcfg.num_chan; i++) { + + if (pl330->dmac_tbd.reset_chan & (1 << i)) { + struct pl330_thread *thrd = &pl330->channels[i]; + void __iomem *regs = pi->base; + enum pl330_op_err err; + + _stop(thrd); + + if (readl(regs + FSC) & (1 << thrd->id)) + err = PL330_ERR_FAIL; + else + err = PL330_ERR_ABORT; + + spin_unlock_irqrestore(&pl330->lock, flags); + + _callback(thrd->req[1 - thrd->lstenq].r, err); + _callback(thrd->req[thrd->lstenq].r, err); + + spin_lock_irqsave(&pl330->lock, flags); + + thrd->req[0].r = NULL; + thrd->req[1].r = NULL; + mark_free(thrd, 0); + mark_free(thrd, 1); + + /* Clear the reset flag */ + pl330->dmac_tbd.reset_chan &= ~(1 << i); + } + } + + spin_unlock_irqrestore(&pl330->lock, flags); + + return; +} + +/* Returns 1 if state was updated, 0 otherwise */ +static int pl330_update(const struct pl330_info *pi) +{ + struct _pl330_req *rqdone; + struct pl330_dmac *pl330; + unsigned long flags; + void __iomem *regs; + u32 val; + int id, ev, ret = 0; + + if (!pi || !pi->pl330_data) + return 0; + + regs = pi->base; + pl330 = pi->pl330_data; + + spin_lock_irqsave(&pl330->lock, flags); + + val = readl(regs + FSM) & 0x1; + if (val) + pl330->dmac_tbd.reset_mngr = true; + else + pl330->dmac_tbd.reset_mngr = false; + + val = readl(regs + FSC) & ((1 << pi->pcfg.num_chan) - 1); + pl330->dmac_tbd.reset_chan |= val; + if (val) { + int i = 0; + while (i < pi->pcfg.num_chan) { + if (val & (1 << i)) { + dev_info(pi->dev, + "Reset Channel-%d\t CS-%x FTC-%x\n", + i, readl(regs + CS(i)), + readl(regs + FTC(i))); + _stop(&pl330->channels[i]); + } + i++; + } + } + + /* Check which event happened i.e, thread notified */ + val = readl(regs + ES); + if (pi->pcfg.num_events < 32 + && val & ~((1 << pi->pcfg.num_events) - 1)) { + pl330->dmac_tbd.reset_dmac = true; + dev_err(pi->dev, "%s:%d Unexpected!\n", __func__, __LINE__); + ret = 1; + goto updt_exit; + } + + for (ev = 0; ev < pi->pcfg.num_events; ev++) { + if (val & (1 << ev)) { /* Event occurred */ + struct pl330_thread *thrd; + u32 inten = readl(regs + INTEN); + int active; + + /* Clear the event */ + if (inten & (1 << ev)) + writel(1 << ev, regs + INTCLR); + + ret = 1; + + id = pl330->events[ev]; + + thrd = &pl330->channels[id]; + + active = thrd->req_running; + if (active == -1) /* Aborted */ + continue; + + rqdone = &thrd->req[active]; + mark_free(thrd, active); + + /* Get going again ASAP */ + _start(thrd); + + /* For now, just make a list of callbacks to be done */ + list_add_tail(&rqdone->rqd, &pl330->req_done); + } + } + + /* Now that we are in no hurry, do the callbacks */ + while (!list_empty(&pl330->req_done)) { + struct pl330_req *r; + + rqdone = container_of(pl330->req_done.next, + struct _pl330_req, rqd); + + list_del_init(&rqdone->rqd); + + /* Detach the req */ + r = rqdone->r; + rqdone->r = NULL; + + spin_unlock_irqrestore(&pl330->lock, flags); + _callback(r, PL330_ERR_NONE); + spin_lock_irqsave(&pl330->lock, flags); + } + +updt_exit: + spin_unlock_irqrestore(&pl330->lock, flags); + + if (pl330->dmac_tbd.reset_dmac + || pl330->dmac_tbd.reset_mngr + || pl330->dmac_tbd.reset_chan) { + ret = 1; + tasklet_schedule(&pl330->tasks); + } + + return ret; +} + +static int pl330_chan_ctrl(void *ch_id, enum pl330_chan_op op) +{ + struct pl330_thread *thrd = ch_id; + struct pl330_dmac *pl330; + unsigned long flags; + int ret = 0, active = thrd->req_running; + + if (!thrd || thrd->free || thrd->dmac->state == DYING) + return -EINVAL; + + pl330 = thrd->dmac; + + spin_lock_irqsave(&pl330->lock, flags); + + switch (op) { + case PL330_OP_FLUSH: + /* Make sure the channel is stopped */ + _stop(thrd); + + thrd->req[0].r = NULL; + thrd->req[1].r = NULL; + mark_free(thrd, 0); + mark_free(thrd, 1); + break; + + case PL330_OP_ABORT: + /* Make sure the channel is stopped */ + _stop(thrd); + + /* ABORT is only for the active req */ + if (active == -1) + break; + + thrd->req[active].r = NULL; + mark_free(thrd, active); + + /* Start the next */ + case PL330_OP_START: + if ((active == -1) && !_start(thrd)) + ret = -EIO; + break; + + default: + ret = -EINVAL; + } + + spin_unlock_irqrestore(&pl330->lock, flags); + return ret; +} + +static int pl330_chan_status(void *ch_id, struct pl330_chanstatus *pstatus) +{ + struct pl330_thread *thrd = ch_id; + struct pl330_dmac *pl330; + struct pl330_info *pi; + void __iomem *regs; + int active; + u32 val; + + if (!pstatus || !thrd || thrd->free) + return -EINVAL; + + pl330 = thrd->dmac; + pi = pl330->pinfo; + regs = pi->base; + + /* The client should remove the DMAC and add again */ + if (pl330->state == DYING) + pstatus->dmac_halted = true; + else + pstatus->dmac_halted = false; + + val = readl(regs + FSC); + if (val & (1 << thrd->id)) + pstatus->faulting = true; + else + pstatus->faulting = false; + + active = thrd->req_running; + + if (active == -1) { + /* Indicate that the thread is not running */ + pstatus->top_req = NULL; + pstatus->wait_req = NULL; + } else { + pstatus->top_req = thrd->req[active].r; + pstatus->wait_req = !IS_FREE(&thrd->req[1 - active]) + ? thrd->req[1 - active].r : NULL; + } + + pstatus->src_addr = readl(regs + SA(thrd->id)); + pstatus->dst_addr = readl(regs + DA(thrd->id)); + + return 0; +} + +/* Reserve an event */ +static inline int _alloc_event(struct pl330_thread *thrd) +{ + struct pl330_dmac *pl330 = thrd->dmac; + struct pl330_info *pi = pl330->pinfo; + int ev; + + for (ev = 0; ev < pi->pcfg.num_events; ev++) + if (pl330->events[ev] == -1) { + pl330->events[ev] = thrd->id; + return ev; + } + + return -1; +} + +static bool _chan_ns(const struct pl330_info *pi, int i) +{ + return pi->pcfg.irq_ns & (1 << i); +} + +/* Upon success, returns IdentityToken for the + * allocated channel, NULL otherwise. + */ +static void *pl330_request_channel(const struct pl330_info *pi) +{ + struct pl330_thread *thrd = NULL; + struct pl330_dmac *pl330; + unsigned long flags; + int chans, i; + + if (!pi || !pi->pl330_data) + return NULL; + + pl330 = pi->pl330_data; + + if (pl330->state == DYING) + return NULL; + + chans = pi->pcfg.num_chan; + + spin_lock_irqsave(&pl330->lock, flags); + + for (i = 0; i < chans; i++) { + thrd = &pl330->channels[i]; + if ((thrd->free) && (!_manager_ns(thrd) || + _chan_ns(pi, i))) { + thrd->ev = _alloc_event(thrd); + if (thrd->ev >= 0) { + thrd->free = false; + thrd->lstenq = 1; + thrd->req[0].r = NULL; + mark_free(thrd, 0); + thrd->req[1].r = NULL; + mark_free(thrd, 1); + break; + } + } + thrd = NULL; + } + + spin_unlock_irqrestore(&pl330->lock, flags); + + return thrd; +} + +/* Release an event */ +static inline void _free_event(struct pl330_thread *thrd, int ev) +{ + struct pl330_dmac *pl330 = thrd->dmac; + struct pl330_info *pi = pl330->pinfo; + + /* If the event is valid and was held by the thread */ + if (ev >= 0 && ev < pi->pcfg.num_events + && pl330->events[ev] == thrd->id) + pl330->events[ev] = -1; +} + +static void pl330_release_channel(void *ch_id) +{ + struct pl330_thread *thrd = ch_id; + struct pl330_dmac *pl330; + unsigned long flags; + + if (!thrd || thrd->free) + return; + + _stop(thrd); + + _callback(thrd->req[1 - thrd->lstenq].r, PL330_ERR_ABORT); + _callback(thrd->req[thrd->lstenq].r, PL330_ERR_ABORT); + + pl330 = thrd->dmac; + + spin_lock_irqsave(&pl330->lock, flags); + _free_event(thrd, thrd->ev); + thrd->free = true; + spin_unlock_irqrestore(&pl330->lock, flags); +} + +/* Initialize the structure for PL330 configuration, that can be used + * by the client driver the make best use of the DMAC + */ +static void read_dmac_config(struct pl330_info *pi) +{ + void __iomem *regs = pi->base; + u32 val; + + val = readl(regs + CRD) >> CRD_DATA_WIDTH_SHIFT; + val &= CRD_DATA_WIDTH_MASK; + pi->pcfg.data_bus_width = 8 * (1 << val); + + val = readl(regs + CRD) >> CRD_DATA_BUFF_SHIFT; + val &= CRD_DATA_BUFF_MASK; + pi->pcfg.data_buf_dep = val + 1; + + val = readl(regs + CR0) >> CR0_NUM_CHANS_SHIFT; + val &= CR0_NUM_CHANS_MASK; + val += 1; + pi->pcfg.num_chan = val; + + val = readl(regs + CR0); + if (val & CR0_PERIPH_REQ_SET) { + val = (val >> CR0_NUM_PERIPH_SHIFT) & CR0_NUM_PERIPH_MASK; + val += 1; + pi->pcfg.num_peri = val; + pi->pcfg.peri_ns = readl(regs + CR4); + } else { + pi->pcfg.num_peri = 0; + } + + val = readl(regs + CR0); + if (val & CR0_BOOT_MAN_NS) + pi->pcfg.mode |= DMAC_MODE_NS; + else + pi->pcfg.mode &= ~DMAC_MODE_NS; + + val = readl(regs + CR0) >> CR0_NUM_EVENTS_SHIFT; + val &= CR0_NUM_EVENTS_MASK; + val += 1; + pi->pcfg.num_events = val; + + pi->pcfg.irq_ns = readl(regs + CR3); + + pi->pcfg.periph_id = get_id(pi, PERIPH_ID); + pi->pcfg.pcell_id = get_id(pi, PCELL_ID); +} + +static inline void _reset_thread(struct pl330_thread *thrd) +{ + struct pl330_dmac *pl330 = thrd->dmac; + struct pl330_info *pi = pl330->pinfo; + + thrd->req[0].mc_cpu = pl330->mcode_cpu + + (thrd->id * pi->mcbufsz); + thrd->req[0].mc_bus = pl330->mcode_bus + + (thrd->id * pi->mcbufsz); + thrd->req[0].r = NULL; + mark_free(thrd, 0); + + thrd->req[1].mc_cpu = thrd->req[0].mc_cpu + + pi->mcbufsz / 2; + thrd->req[1].mc_bus = thrd->req[0].mc_bus + + pi->mcbufsz / 2; + thrd->req[1].r = NULL; + mark_free(thrd, 1); +} + +static int dmac_alloc_threads(struct pl330_dmac *pl330) +{ + struct pl330_info *pi = pl330->pinfo; + int chans = pi->pcfg.num_chan; + struct pl330_thread *thrd; + int i; + + /* Allocate 1 Manager and 'chans' Channel threads */ + pl330->channels = kzalloc((1 + chans) * sizeof(*thrd), + GFP_KERNEL); + if (!pl330->channels) + return -ENOMEM; + + /* Init Channel threads */ + for (i = 0; i < chans; i++) { + thrd = &pl330->channels[i]; + thrd->id = i; + thrd->dmac = pl330; + _reset_thread(thrd); + thrd->free = true; + } + + /* MANAGER is indexed at the end */ + thrd = &pl330->channels[chans]; + thrd->id = chans; + thrd->dmac = pl330; + thrd->free = false; + pl330->manager = thrd; + + return 0; +} + +static int dmac_alloc_resources(struct pl330_dmac *pl330) +{ + struct pl330_info *pi = pl330->pinfo; + int chans = pi->pcfg.num_chan; + int ret; + + /* + * Alloc MicroCode buffer for 'chans' Channel threads. + * A channel's buffer offset is (Channel_Id * MCODE_BUFF_PERCHAN) + */ + pl330->mcode_cpu = dma_alloc_coherent(pi->dev, + chans * pi->mcbufsz, + &pl330->mcode_bus, GFP_KERNEL); + if (!pl330->mcode_cpu) { + dev_err(pi->dev, "%s:%d Can't allocate memory!\n", + __func__, __LINE__); + return -ENOMEM; + } + + ret = dmac_alloc_threads(pl330); + if (ret) { + dev_err(pi->dev, "%s:%d Can't to create channels for DMAC!\n", + __func__, __LINE__); + dma_free_coherent(pi->dev, + chans * pi->mcbufsz, + pl330->mcode_cpu, pl330->mcode_bus); + return ret; + } + + return 0; +} + +static int pl330_add(struct pl330_info *pi) +{ + struct pl330_dmac *pl330; + void __iomem *regs; + int i, ret; + + if (!pi || !pi->dev) + return -EINVAL; + + /* If already added */ + if (pi->pl330_data) + return -EINVAL; + + /* + * If the SoC can perform reset on the DMAC, then do it + * before reading its configuration. + */ + if (pi->dmac_reset) + pi->dmac_reset(pi); + + regs = pi->base; + + /* Check if we can handle this DMAC */ + if ((get_id(pi, PERIPH_ID) & 0xfffff) != PERIPH_ID_VAL + || get_id(pi, PCELL_ID) != PCELL_ID_VAL) { + dev_err(pi->dev, "PERIPH_ID 0x%x, PCELL_ID 0x%x !\n", + get_id(pi, PERIPH_ID), get_id(pi, PCELL_ID)); + return -EINVAL; + } + + /* Read the configuration of the DMAC */ + read_dmac_config(pi); + + if (pi->pcfg.num_events == 0) { + dev_err(pi->dev, "%s:%d Can't work without events!\n", + __func__, __LINE__); + return -EINVAL; + } + + pl330 = kzalloc(sizeof(*pl330), GFP_KERNEL); + if (!pl330) { + dev_err(pi->dev, "%s:%d Can't allocate memory!\n", + __func__, __LINE__); + return -ENOMEM; + } + + /* Assign the info structure and private data */ + pl330->pinfo = pi; + pi->pl330_data = pl330; + + spin_lock_init(&pl330->lock); + + INIT_LIST_HEAD(&pl330->req_done); + + /* Use default MC buffer size if not provided */ + if (!pi->mcbufsz) + pi->mcbufsz = MCODE_BUFF_PER_REQ * 2; + + /* Mark all events as free */ + for (i = 0; i < pi->pcfg.num_events; i++) + pl330->events[i] = -1; + + /* Allocate resources needed by the DMAC */ + ret = dmac_alloc_resources(pl330); + if (ret) { + dev_err(pi->dev, "Unable to create channels for DMAC\n"); + kfree(pl330); + return ret; + } + + tasklet_init(&pl330->tasks, pl330_dotask, (unsigned long) pl330); + + pl330->state = INIT; + + return 0; +} + +static int dmac_free_threads(struct pl330_dmac *pl330) +{ + struct pl330_info *pi = pl330->pinfo; + int chans = pi->pcfg.num_chan; + struct pl330_thread *thrd; + int i; + + /* Release Channel threads */ + for (i = 0; i < chans; i++) { + thrd = &pl330->channels[i]; + pl330_release_channel((void *)thrd); + } + + /* Free memory */ + kfree(pl330->channels); + + return 0; +} + +static void dmac_free_resources(struct pl330_dmac *pl330) +{ + struct pl330_info *pi = pl330->pinfo; + int chans = pi->pcfg.num_chan; + + dmac_free_threads(pl330); + + dma_free_coherent(pi->dev, chans * pi->mcbufsz, + pl330->mcode_cpu, pl330->mcode_bus); +} + +static void pl330_del(struct pl330_info *pi) +{ + struct pl330_dmac *pl330; + + if (!pi || !pi->pl330_data) + return; + + pl330 = pi->pl330_data; + + pl330->state = UNINIT; + + tasklet_kill(&pl330->tasks); + + /* Free DMAC resources */ + dmac_free_resources(pl330); + + kfree(pl330); + pi->pl330_data = NULL; +} + /* forward declaration */ static struct amba_driver pl330_driver; diff --git a/include/linux/amba/pl330.h b/include/linux/amba/pl330.h index 12e023c19ac1..fe93758e8403 100644 --- a/include/linux/amba/pl330.h +++ b/include/linux/amba/pl330.h @@ -13,7 +13,6 @@ #define __AMBA_PL330_H_ #include -#include struct dma_pl330_platdata { /* From b06db6e56c0850617291b8921582d04255022425 Mon Sep 17 00:00:00 2001 From: Boojin Kim Date: Mon, 26 Dec 2011 18:51:41 +0900 Subject: [PATCH 34/67] DMA: PL330: Removes useless function Cc: Jassi Brar Cc: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Boojin Kim Signed-off-by: Kukjin Kim Acked-by: Jassi Brar Signed-off-by: Vinod Koul --- drivers/dma/pl330.c | 46 --------------------------------------------- 1 file changed, 46 deletions(-) diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 2e351f40fc49..dd74c2478c6d 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -1824,52 +1824,6 @@ static int pl330_chan_ctrl(void *ch_id, enum pl330_chan_op op) return ret; } -static int pl330_chan_status(void *ch_id, struct pl330_chanstatus *pstatus) -{ - struct pl330_thread *thrd = ch_id; - struct pl330_dmac *pl330; - struct pl330_info *pi; - void __iomem *regs; - int active; - u32 val; - - if (!pstatus || !thrd || thrd->free) - return -EINVAL; - - pl330 = thrd->dmac; - pi = pl330->pinfo; - regs = pi->base; - - /* The client should remove the DMAC and add again */ - if (pl330->state == DYING) - pstatus->dmac_halted = true; - else - pstatus->dmac_halted = false; - - val = readl(regs + FSC); - if (val & (1 << thrd->id)) - pstatus->faulting = true; - else - pstatus->faulting = false; - - active = thrd->req_running; - - if (active == -1) { - /* Indicate that the thread is not running */ - pstatus->top_req = NULL; - pstatus->wait_req = NULL; - } else { - pstatus->top_req = thrd->req[active].r; - pstatus->wait_req = !IS_FREE(&thrd->req[1 - active]) - ? thrd->req[1 - active].r : NULL; - } - - pstatus->src_addr = readl(regs + SA(thrd->id)); - pstatus->dst_addr = readl(regs + DA(thrd->id)); - - return 0; -} - /* Reserve an event */ static inline int _alloc_event(struct pl330_thread *thrd) { From 3ecf51a45c0e218d70df2cc905b668fa0c115f73 Mon Sep 17 00:00:00 2001 From: Boojin Kim Date: Mon, 26 Dec 2011 18:55:47 +0900 Subject: [PATCH 35/67] DMA: PL330: Support MEMTOMEM transmit w/o RMB, WMB The DMAC PL330 r1p0 version fixed the lockup error being on r0p0. This patch supports the DMA transmission without memory barrier operation when the revision of DMAC PL330 is the next of r0p0. Cc: Jassi Brar Cc: Russell King Acked-by: Vinod Koul Signed-off-by: Boojin Kim Signed-off-by: Kukjin Kim Acked-by: Jassi Brar Signed-off-by: Vinod Koul --- drivers/dma/pl330.c | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index dd74c2478c6d..7253d17f05f8 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -151,6 +151,11 @@ enum pl330_reqtype { #define CRD 0xe14 #define PERIPH_ID 0xfe0 +#define PERIPH_REV_SHIFT 20 +#define PERIPH_REV_MASK 0xf +#define PERIPH_REV_R0P0 0 +#define PERIPH_REV_R1P0 1 +#define PERIPH_REV_R1P1 2 #define PCELL_ID 0xff0 #define CR0_PERIPH_REQ_SET (1 << 0) @@ -344,6 +349,7 @@ struct pl330_reqcfg { enum pl330_dstcachectrl dcctl; enum pl330_srccachectrl scctl; enum pl330_byteswap swap; + struct pl330_config *pcfg; }; /* @@ -655,6 +661,11 @@ static inline u32 get_id(struct pl330_info *pi, u32 off) return id; } +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) { @@ -1241,12 +1252,21 @@ static inline int _ldst_memtomem(unsigned dry_run, u8 buf[], const struct _xfer_spec *pxs, int cyc) { int off = 0; + struct pl330_config *pcfg = pxs->r->cfg->pcfg; - while (cyc--) { - off += _emit_LD(dry_run, &buf[off], ALWAYS); - off += _emit_RMB(dry_run, &buf[off]); - off += _emit_ST(dry_run, &buf[off], ALWAYS); - off += _emit_WMB(dry_run, &buf[off]); + /* check lock-up free version */ + if (get_revision(pcfg->periph_id) >= PERIPH_REV_R1P0) { + while (cyc--) { + off += _emit_LD(dry_run, &buf[off], ALWAYS); + off += _emit_ST(dry_run, &buf[off], ALWAYS); + } + } else { + while (cyc--) { + off += _emit_LD(dry_run, &buf[off], ALWAYS); + off += _emit_RMB(dry_run, &buf[off]); + off += _emit_ST(dry_run, &buf[off], ALWAYS); + off += _emit_WMB(dry_run, &buf[off]); + } } return off; @@ -2619,6 +2639,7 @@ static struct dma_pl330_desc *pl330_get_desc(struct dma_pl330_chan *pch) async_tx_ack(&desc->txd); desc->req.peri = peri_id ? pch->chan.chan_id : 0; + desc->rqcfg.pcfg = &pch->dmac->pif.pcfg; dma_async_tx_descriptor_init(&desc->txd, &pch->chan); From 5170c051a56244816d948c43592c1b2805ed4f3a Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Fri, 9 Mar 2012 14:55:25 +0530 Subject: [PATCH 36/67] Revert "drivers/dma: linux/module.h included twice" This reverts commit 865d9438eb1f7670d2e88849f059db551b320887. The module.h incsuion twice has been updated tree wide hence this is not required to be merged. Signed-off-by: Vinod Koul --- drivers/dma/imx-dma.c | 1 + drivers/dma/imx-sdma.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index 85b3d3c21d91..c32103f04fb3 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index c4958823518a..f0bfc0e07416 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include From 08714f60b0fc6ea3a060b69b32e77139f14e6045 Mon Sep 17 00:00:00 2001 From: Russell King - ARM Linux Date: Mon, 5 Mar 2012 20:15:11 +0000 Subject: [PATCH 37/67] dmaengine: mv_xor: remove write-only is_complete_cookie mv_xor's is_complete_cookie is only ever written to, but never read. This is silly, remove the write-only structure member. Signed-off-by: Russell King Tested-by: Linus Walleij Reviewed-by: Linus Walleij Acked-by: Jassi Brar [imx-sdma.c & mxs-dma.c] Tested-by: Shawn Guo Signed-off-by: Vinod Koul --- drivers/dma/mv_xor.c | 1 - drivers/dma/mv_xor.h | 1 - 2 files changed, 2 deletions(-) diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c index e779b434af45..ad7d03fe4cb4 100644 --- a/drivers/dma/mv_xor.c +++ b/drivers/dma/mv_xor.c @@ -826,7 +826,6 @@ static enum dma_status mv_xor_status(struct dma_chan *chan, last_used = chan->cookie; last_complete = mv_chan->completed_cookie; - mv_chan->is_complete_cookie = cookie; dma_set_tx_state(txstate, last_complete, last_used, 0); ret = dma_async_is_complete(cookie, last_complete, last_used); diff --git a/drivers/dma/mv_xor.h b/drivers/dma/mv_xor.h index 977b592e976b..da04ac23def3 100644 --- a/drivers/dma/mv_xor.h +++ b/drivers/dma/mv_xor.h @@ -109,7 +109,6 @@ struct mv_xor_chan { #ifdef USE_TIMER unsigned long cleanup_time; u32 current_on_last_cleanup; - dma_cookie_t is_complete_cookie; #endif }; From 4d4e58de32a192fea65ab84509d17d199bd291c8 Mon Sep 17 00:00:00 2001 From: Russell King - ARM Linux Date: Tue, 6 Mar 2012 22:34:06 +0000 Subject: [PATCH 38/67] dmaengine: move last completed cookie into generic dma_chan structure Every DMA engine implementation declares a last completed dma cookie in their private dma channel structures. This is pointless, and forces driver specific code. Move this out into the common dma_chan structure. Signed-off-by: Russell King Tested-by: Linus Walleij Reviewed-by: Linus Walleij Acked-by: Jassi Brar [imx-sdma.c & mxs-dma.c] Tested-by: Shawn Guo Signed-off-by: Vinod Koul --- arch/arm/include/asm/hardware/iop_adma.h | 2 -- drivers/dma/amba-pl08x.c | 8 ++++---- drivers/dma/at_hdmac.c | 10 +++++----- drivers/dma/at_hdmac_regs.h | 2 -- drivers/dma/coh901318.c | 7 +++---- drivers/dma/dw_dmac.c | 10 +++++----- drivers/dma/dw_dmac_regs.h | 1 - drivers/dma/ep93xx_dma.c | 8 +++----- drivers/dma/fsldma.c | 4 ++-- drivers/dma/fsldma.h | 1 - drivers/dma/imx-dma.c | 7 +++---- drivers/dma/imx-sdma.c | 5 ++--- drivers/dma/intel_mid_dma.c | 9 ++++----- drivers/dma/intel_mid_dma_regs.h | 2 -- drivers/dma/ioat/dma.c | 2 +- drivers/dma/ioat/dma.h | 4 +--- drivers/dma/ioat/dma_v2.c | 2 +- drivers/dma/ioat/dma_v3.c | 2 +- drivers/dma/iop-adma.c | 10 +++++----- drivers/dma/ipu/ipu_idmac.c | 10 ++++------ drivers/dma/mpc512x_dma.c | 7 +++---- drivers/dma/mv_xor.c | 6 +++--- drivers/dma/mv_xor.h | 2 -- drivers/dma/mxs-dma.c | 5 ++--- drivers/dma/pch_dma.c | 5 ++--- drivers/dma/pl330.c | 9 +++------ drivers/dma/ppc4xx/adma.c | 10 +++++----- drivers/dma/ppc4xx/adma.h | 2 -- drivers/dma/shdma.c | 10 +++++----- drivers/dma/shdma.h | 1 - drivers/dma/sirf-dma.c | 7 +++---- drivers/dma/ste_dma40.c | 10 +++------- drivers/dma/timb_dma.c | 7 +++---- drivers/dma/txx9dmac.c | 10 +++++----- drivers/dma/txx9dmac.h | 1 - include/linux/amba/pl08x.h | 2 -- include/linux/dmaengine.h | 2 ++ 37 files changed, 83 insertions(+), 119 deletions(-) diff --git a/arch/arm/include/asm/hardware/iop_adma.h b/arch/arm/include/asm/hardware/iop_adma.h index 59b8c3892f76..122f86d8c991 100644 --- a/arch/arm/include/asm/hardware/iop_adma.h +++ b/arch/arm/include/asm/hardware/iop_adma.h @@ -49,7 +49,6 @@ struct iop_adma_device { /** * struct iop_adma_chan - internal representation of an ADMA device * @pending: allows batching of hardware operations - * @completed_cookie: identifier for the most recently completed operation * @lock: serializes enqueue/dequeue operations to the slot pool * @mmr_base: memory mapped register base * @chain: device chain view of the descriptors @@ -62,7 +61,6 @@ struct iop_adma_device { */ struct iop_adma_chan { int pending; - dma_cookie_t completed_cookie; spinlock_t lock; /* protects the descriptor slot pool */ void __iomem *mmr_base; struct list_head chain; diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 513184b4fdd1..e510447a685a 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -971,7 +971,7 @@ static enum dma_status pl08x_dma_tx_status(struct dma_chan *chan, u32 bytesleft = 0; last_used = plchan->chan.cookie; - last_complete = plchan->lc; + last_complete = plchan->chan.completed_cookie; ret = dma_async_is_complete(cookie, last_complete, last_used); if (ret == DMA_SUCCESS) { @@ -983,7 +983,7 @@ static enum dma_status pl08x_dma_tx_status(struct dma_chan *chan, * This cookie not complete yet */ last_used = plchan->chan.cookie; - last_complete = plchan->lc; + last_complete = plchan->chan.completed_cookie; /* Get number of bytes left in the active transactions and queue */ bytesleft = pl08x_getbytes_chan(plchan); @@ -1543,7 +1543,7 @@ static void pl08x_tasklet(unsigned long data) if (txd) { /* Update last completed */ - plchan->lc = txd->tx.cookie; + plchan->chan.completed_cookie = txd->tx.cookie; } /* If a new descriptor is queued, set it up plchan->at is NULL here */ @@ -1725,7 +1725,7 @@ static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x, chan->chan.device = dmadev; chan->chan.cookie = 0; - chan->lc = 0; + chan->chan.completed_cookie = 0; spin_lock_init(&chan->lock); INIT_LIST_HEAD(&chan->pend_list); diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index f4aed5fc2cb6..6baf5d717262 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -269,7 +269,7 @@ atc_chain_complete(struct at_dma_chan *atchan, struct at_desc *desc) dev_vdbg(chan2dev(&atchan->chan_common), "descriptor %u complete\n", txd->cookie); - atchan->completed_cookie = txd->cookie; + atchan->chan_common.completed_cookie = txd->cookie; /* move children to free_list */ list_splice_init(&desc->tx_list, &atchan->free_list); @@ -1016,14 +1016,14 @@ atc_tx_status(struct dma_chan *chan, spin_lock_irqsave(&atchan->lock, flags); - last_complete = atchan->completed_cookie; + last_complete = chan->completed_cookie; last_used = chan->cookie; ret = dma_async_is_complete(cookie, last_complete, last_used); if (ret != DMA_SUCCESS) { atc_cleanup_descriptors(atchan); - last_complete = atchan->completed_cookie; + last_complete = chan->completed_cookie; last_used = chan->cookie; ret = dma_async_is_complete(cookie, last_complete, last_used); @@ -1129,7 +1129,7 @@ static int atc_alloc_chan_resources(struct dma_chan *chan) spin_lock_irqsave(&atchan->lock, flags); atchan->descs_allocated = i; list_splice(&tmp_list, &atchan->free_list); - atchan->completed_cookie = chan->cookie = 1; + chan->completed_cookie = chan->cookie = 1; spin_unlock_irqrestore(&atchan->lock, flags); /* channel parameters */ @@ -1329,7 +1329,7 @@ static int __init at_dma_probe(struct platform_device *pdev) struct at_dma_chan *atchan = &atdma->chan[i]; atchan->chan_common.device = &atdma->dma_common; - atchan->chan_common.cookie = atchan->completed_cookie = 1; + atchan->chan_common.cookie = atchan->chan_common.completed_cookie = 1; list_add_tail(&atchan->chan_common.device_node, &atdma->dma_common.channels); diff --git a/drivers/dma/at_hdmac_regs.h b/drivers/dma/at_hdmac_regs.h index a8d3277d60b5..08fd8a0ae797 100644 --- a/drivers/dma/at_hdmac_regs.h +++ b/drivers/dma/at_hdmac_regs.h @@ -208,7 +208,6 @@ enum atc_status { * @save_dscr: for cyclic operations, preserve next descriptor address in * the cyclic list on suspend/resume cycle * @lock: serializes enqueue/dequeue operations to descriptors lists - * @completed_cookie: identifier for the most recently completed operation * @active_list: list of descriptors dmaengine is being running on * @queue: list of descriptors ready to be submitted to engine * @free_list: list of descriptors usable by the channel @@ -227,7 +226,6 @@ struct at_dma_chan { spinlock_t lock; /* these other elements are all protected by lock */ - dma_cookie_t completed_cookie; struct list_head active_list; struct list_head queue; struct list_head free_list; diff --git a/drivers/dma/coh901318.c b/drivers/dma/coh901318.c index d65a718c0f9b..521434bc3130 100644 --- a/drivers/dma/coh901318.c +++ b/drivers/dma/coh901318.c @@ -59,7 +59,6 @@ struct coh901318_base { struct coh901318_chan { spinlock_t lock; int allocated; - int completed; int id; int stopped; @@ -705,7 +704,7 @@ static void dma_tasklet(unsigned long data) callback_param = cohd_fin->desc.callback_param; /* sign this job as completed on the channel */ - cohc->completed = cohd_fin->desc.cookie; + cohc->chan.completed_cookie = cohd_fin->desc.cookie; /* release the lli allocation and remove the descriptor */ coh901318_lli_free(&cohc->base->pool, &cohd_fin->lli); @@ -929,7 +928,7 @@ static int coh901318_alloc_chan_resources(struct dma_chan *chan) coh901318_config(cohc, NULL); cohc->allocated = 1; - cohc->completed = chan->cookie = 1; + chan->completed_cookie = chan->cookie = 1; spin_unlock_irqrestore(&cohc->lock, flags); @@ -1169,7 +1168,7 @@ coh901318_tx_status(struct dma_chan *chan, dma_cookie_t cookie, dma_cookie_t last_complete; int ret; - last_complete = cohc->completed; + last_complete = chan->completed_cookie; last_used = chan->cookie; ret = dma_async_is_complete(cookie, last_complete, last_used); diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 0e4b5c6a2f86..5bd23006ff4a 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -249,7 +249,7 @@ dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc, dev_vdbg(chan2dev(&dwc->chan), "descriptor %u complete\n", txd->cookie); spin_lock_irqsave(&dwc->lock, flags); - dwc->completed = txd->cookie; + dwc->chan.completed_cookie = txd->cookie; if (callback_required) { callback = txd->callback; param = txd->callback_param; @@ -997,14 +997,14 @@ dwc_tx_status(struct dma_chan *chan, dma_cookie_t last_complete; int ret; - last_complete = dwc->completed; + last_complete = chan->completed_cookie; last_used = chan->cookie; ret = dma_async_is_complete(cookie, last_complete, last_used); if (ret != DMA_SUCCESS) { dwc_scan_descriptors(to_dw_dma(chan->device), dwc); - last_complete = dwc->completed; + last_complete = chan->completed_cookie; last_used = chan->cookie; ret = dma_async_is_complete(cookie, last_complete, last_used); @@ -1046,7 +1046,7 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) return -EIO; } - dwc->completed = chan->cookie = 1; + chan->completed_cookie = chan->cookie = 1; /* * NOTE: some controllers may have additional features that we @@ -1474,7 +1474,7 @@ static int __init dw_probe(struct platform_device *pdev) struct dw_dma_chan *dwc = &dw->chan[i]; dwc->chan.device = &dw->dma; - dwc->chan.cookie = dwc->completed = 1; + dwc->chan.cookie = dwc->chan.completed_cookie = 1; if (pdata->chan_allocation_order == CHAN_ALLOCATION_ASCENDING) list_add_tail(&dwc->chan.device_node, &dw->dma.channels); diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index eec0481a12f7..f298f69ecbf9 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -158,7 +158,6 @@ struct dw_dma_chan { /* these other elements are all protected by lock */ unsigned long flags; - dma_cookie_t completed; struct list_head active_list; struct list_head queue; struct list_head free_list; diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c index 59e7a965772b..bc457878cffd 100644 --- a/drivers/dma/ep93xx_dma.c +++ b/drivers/dma/ep93xx_dma.c @@ -122,7 +122,6 @@ struct ep93xx_dma_desc { * @lock: lock protecting the fields following * @flags: flags for the channel * @buffer: which buffer to use next (0/1) - * @last_completed: last completed cookie value * @active: flattened chain of descriptors currently being processed * @queue: pending descriptors which are handled next * @free_list: list of free descriptors which can be used @@ -157,7 +156,6 @@ struct ep93xx_dma_chan { #define EP93XX_DMA_IS_CYCLIC 0 int buffer; - dma_cookie_t last_completed; struct list_head active; struct list_head queue; struct list_head free_list; @@ -703,7 +701,7 @@ static void ep93xx_dma_tasklet(unsigned long data) desc = ep93xx_dma_get_active(edmac); if (desc) { if (desc->complete) { - edmac->last_completed = desc->txd.cookie; + edmac->chan.completed_cookie = desc->txd.cookie; list_splice_init(&edmac->active, &list); } callback = desc->txd.callback; @@ -861,7 +859,7 @@ static int ep93xx_dma_alloc_chan_resources(struct dma_chan *chan) goto fail_clk_disable; spin_lock_irq(&edmac->lock); - edmac->last_completed = 1; + edmac->chan.completed_cookie = 1; edmac->chan.cookie = 1; ret = edmac->edma->hw_setup(edmac); spin_unlock_irq(&edmac->lock); @@ -1254,7 +1252,7 @@ static enum dma_status ep93xx_dma_tx_status(struct dma_chan *chan, spin_lock_irqsave(&edmac->lock, flags); last_used = chan->cookie; - last_completed = edmac->last_completed; + last_completed = chan->completed_cookie; spin_unlock_irqrestore(&edmac->lock, flags); ret = dma_async_is_complete(cookie, last_completed, last_used); diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index b98070c33ca9..9b5cb8a43cfa 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -990,7 +990,7 @@ static enum dma_status fsl_tx_status(struct dma_chan *dchan, spin_lock_irqsave(&chan->desc_lock, flags); - last_complete = chan->completed_cookie; + last_complete = dchan->completed_cookie; last_used = dchan->cookie; spin_unlock_irqrestore(&chan->desc_lock, flags); @@ -1088,7 +1088,7 @@ static void dma_do_tasklet(unsigned long data) desc = to_fsl_desc(chan->ld_running.prev); cookie = desc->async_tx.cookie; - chan->completed_cookie = cookie; + chan->common.completed_cookie = cookie; chan_dbg(chan, "completed_cookie=%d\n", cookie); } diff --git a/drivers/dma/fsldma.h b/drivers/dma/fsldma.h index 9cb5aa57c677..f5c38791fc74 100644 --- a/drivers/dma/fsldma.h +++ b/drivers/dma/fsldma.h @@ -137,7 +137,6 @@ struct fsldma_device { struct fsldma_chan { char name[8]; /* Channel name */ struct fsldma_chan_regs __iomem *regs; - dma_cookie_t completed_cookie; /* The maximum cookie completed */ spinlock_t desc_lock; /* Descriptor operation lock */ struct list_head ld_pending; /* Link descriptors queue */ struct list_head ld_running; /* Link descriptors queue */ diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index 3296a7337f25..d3ddcba87f81 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -41,7 +41,6 @@ struct imxdma_channel { struct dma_chan chan; spinlock_t lock; struct dma_async_tx_descriptor desc; - dma_cookie_t last_completed; enum dma_status status; int dma_request; struct scatterlist *sg_list; @@ -65,7 +64,7 @@ static void imxdma_handle(struct imxdma_channel *imxdmac) { if (imxdmac->desc.callback) imxdmac->desc.callback(imxdmac->desc.callback_param); - imxdmac->last_completed = imxdmac->desc.cookie; + imxdmac->chan.completed_cookie = imxdmac->desc.cookie; } static void imxdma_irq_handler(int channel, void *data) @@ -158,8 +157,8 @@ static enum dma_status imxdma_tx_status(struct dma_chan *chan, last_used = chan->cookie; - ret = dma_async_is_complete(cookie, imxdmac->last_completed, last_used); - dma_set_tx_state(txstate, imxdmac->last_completed, last_used, 0); + ret = dma_async_is_complete(cookie, chan->completed_cookie, last_used); + dma_set_tx_state(txstate, chan->completed_cookie, last_used, 0); return ret; } diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index bf736ad679ca..49aa4e876645 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -267,7 +267,6 @@ struct sdma_channel { struct dma_chan chan; spinlock_t lock; struct dma_async_tx_descriptor desc; - dma_cookie_t last_completed; enum dma_status status; unsigned int chn_count; unsigned int chn_real_count; @@ -529,7 +528,7 @@ static void mxc_sdma_handle_channel_normal(struct sdma_channel *sdmac) else sdmac->status = DMA_SUCCESS; - sdmac->last_completed = sdmac->desc.cookie; + sdmac->chan.completed_cookie = sdmac->desc.cookie; if (sdmac->desc.callback) sdmac->desc.callback(sdmac->desc.callback_param); } @@ -1127,7 +1126,7 @@ static enum dma_status sdma_tx_status(struct dma_chan *chan, last_used = chan->cookie; - dma_set_tx_state(txstate, sdmac->last_completed, last_used, + dma_set_tx_state(txstate, chan->completed_cookie, last_used, sdmac->chn_count - sdmac->chn_real_count); return sdmac->status; diff --git a/drivers/dma/intel_mid_dma.c b/drivers/dma/intel_mid_dma.c index 923476d74a5d..40e47e6c7ed8 100644 --- a/drivers/dma/intel_mid_dma.c +++ b/drivers/dma/intel_mid_dma.c @@ -288,7 +288,7 @@ static void midc_descriptor_complete(struct intel_mid_dma_chan *midc, struct intel_mid_dma_lli *llitem; void *param_txd = NULL; - midc->completed = txd->cookie; + midc->chan.completed_cookie = txd->cookie; callback_txd = txd->callback; param_txd = txd->callback_param; @@ -482,12 +482,11 @@ static enum dma_status intel_mid_dma_tx_status(struct dma_chan *chan, dma_cookie_t cookie, struct dma_tx_state *txstate) { - struct intel_mid_dma_chan *midc = to_intel_mid_dma_chan(chan); dma_cookie_t last_used; dma_cookie_t last_complete; int ret; - last_complete = midc->completed; + last_complete = chan->completed_cookie; last_used = chan->cookie; ret = dma_async_is_complete(cookie, last_complete, last_used); @@ -496,7 +495,7 @@ static enum dma_status intel_mid_dma_tx_status(struct dma_chan *chan, midc_scan_descriptors(to_middma_device(chan->device), midc); spin_unlock_bh(&midc->lock); - last_complete = midc->completed; + last_complete = chan->completed_cookie; last_used = chan->cookie; ret = dma_async_is_complete(cookie, last_complete, last_used); @@ -886,7 +885,7 @@ static int intel_mid_dma_alloc_chan_resources(struct dma_chan *chan) pm_runtime_put(&mid->pdev->dev); return -EIO; } - midc->completed = chan->cookie = 1; + chan->completed_cookie = chan->cookie = 1; spin_lock_bh(&midc->lock); while (midc->descs_allocated < DESCS_PER_CHANNEL) { diff --git a/drivers/dma/intel_mid_dma_regs.h b/drivers/dma/intel_mid_dma_regs.h index c83d35b97bd8..1bfa9268feaf 100644 --- a/drivers/dma/intel_mid_dma_regs.h +++ b/drivers/dma/intel_mid_dma_regs.h @@ -165,7 +165,6 @@ union intel_mid_dma_cfg_hi { * @dma_base: MMIO register space DMA engine base pointer * @ch_id: DMA channel id * @lock: channel spinlock - * @completed: DMA cookie * @active_list: current active descriptors * @queue: current queued up descriptors * @free_list: current free descriptors @@ -183,7 +182,6 @@ struct intel_mid_dma_chan { void __iomem *dma_base; int ch_id; spinlock_t lock; - dma_cookie_t completed; struct list_head active_list; struct list_head queue; struct list_head free_list; diff --git a/drivers/dma/ioat/dma.c b/drivers/dma/ioat/dma.c index a4d6cb0c0343..fab440af1f9a 100644 --- a/drivers/dma/ioat/dma.c +++ b/drivers/dma/ioat/dma.c @@ -603,7 +603,7 @@ static void __cleanup(struct ioat_dma_chan *ioat, unsigned long phys_complete) */ dump_desc_dbg(ioat, desc); if (tx->cookie) { - chan->completed_cookie = tx->cookie; + chan->common.completed_cookie = tx->cookie; tx->cookie = 0; ioat_dma_unmap(chan, tx->flags, desc->len, desc->hw); ioat->active -= desc->hw->tx_cnt; diff --git a/drivers/dma/ioat/dma.h b/drivers/dma/ioat/dma.h index 5216c8a92a21..9653b6b6a715 100644 --- a/drivers/dma/ioat/dma.h +++ b/drivers/dma/ioat/dma.h @@ -90,7 +90,6 @@ struct ioat_chan_common { void __iomem *reg_base; unsigned long last_completion; spinlock_t cleanup_lock; - dma_cookie_t completed_cookie; unsigned long state; #define IOAT_COMPLETION_PENDING 0 #define IOAT_COMPLETION_ACK 1 @@ -153,12 +152,11 @@ static inline enum dma_status ioat_tx_status(struct dma_chan *c, dma_cookie_t cookie, struct dma_tx_state *txstate) { - struct ioat_chan_common *chan = to_chan_common(c); dma_cookie_t last_used; dma_cookie_t last_complete; last_used = c->cookie; - last_complete = chan->completed_cookie; + last_complete = c->completed_cookie; dma_set_tx_state(txstate, last_complete, last_used, 0); diff --git a/drivers/dma/ioat/dma_v2.c b/drivers/dma/ioat/dma_v2.c index 5d65f8377971..d3f0aff2c02a 100644 --- a/drivers/dma/ioat/dma_v2.c +++ b/drivers/dma/ioat/dma_v2.c @@ -147,7 +147,7 @@ static void __cleanup(struct ioat2_dma_chan *ioat, unsigned long phys_complete) dump_desc_dbg(ioat, desc); if (tx->cookie) { ioat_dma_unmap(chan, tx->flags, desc->len, desc->hw); - chan->completed_cookie = tx->cookie; + chan->common.completed_cookie = tx->cookie; tx->cookie = 0; if (tx->callback) { tx->callback(tx->callback_param); diff --git a/drivers/dma/ioat/dma_v3.c b/drivers/dma/ioat/dma_v3.c index f519c93a61e7..d4afac741e8a 100644 --- a/drivers/dma/ioat/dma_v3.c +++ b/drivers/dma/ioat/dma_v3.c @@ -277,7 +277,7 @@ static void __cleanup(struct ioat2_dma_chan *ioat, unsigned long phys_complete) dump_desc_dbg(ioat, desc); tx = &desc->txd; if (tx->cookie) { - chan->completed_cookie = tx->cookie; + chan->common.completed_cookie = tx->cookie; ioat3_dma_unmap(ioat, desc, idx + i); tx->cookie = 0; if (tx->callback) { diff --git a/drivers/dma/iop-adma.c b/drivers/dma/iop-adma.c index 04be90b645b8..d8027c2b42c0 100644 --- a/drivers/dma/iop-adma.c +++ b/drivers/dma/iop-adma.c @@ -317,7 +317,7 @@ static void __iop_adma_slot_cleanup(struct iop_adma_chan *iop_chan) } if (cookie > 0) { - iop_chan->completed_cookie = cookie; + iop_chan->common.completed_cookie = cookie; pr_debug("\tcompleted cookie %d\n", cookie); } } @@ -909,7 +909,7 @@ static enum dma_status iop_adma_status(struct dma_chan *chan, enum dma_status ret; last_used = chan->cookie; - last_complete = iop_chan->completed_cookie; + last_complete = chan->completed_cookie; dma_set_tx_state(txstate, last_complete, last_used, 0); ret = dma_async_is_complete(cookie, last_complete, last_used); if (ret == DMA_SUCCESS) @@ -918,7 +918,7 @@ static enum dma_status iop_adma_status(struct dma_chan *chan, iop_adma_slot_cleanup(iop_chan); last_used = chan->cookie; - last_complete = iop_chan->completed_cookie; + last_complete = chan->completed_cookie; dma_set_tx_state(txstate, last_complete, last_used, 0); return dma_async_is_complete(cookie, last_complete, last_used); @@ -1650,7 +1650,7 @@ static void iop_chan_start_null_memcpy(struct iop_adma_chan *iop_chan) /* initialize the completed cookie to be less than * the most recently used cookie */ - iop_chan->completed_cookie = cookie - 1; + iop_chan->common.completed_cookie = cookie - 1; iop_chan->common.cookie = sw_desc->async_tx.cookie = cookie; /* channel should not be busy */ @@ -1707,7 +1707,7 @@ static void iop_chan_start_null_xor(struct iop_adma_chan *iop_chan) /* initialize the completed cookie to be less than * the most recently used cookie */ - iop_chan->completed_cookie = cookie - 1; + iop_chan->common.completed_cookie = cookie - 1; iop_chan->common.cookie = sw_desc->async_tx.cookie = cookie; /* channel should not be busy */ diff --git a/drivers/dma/ipu/ipu_idmac.c b/drivers/dma/ipu/ipu_idmac.c index 6212b16e8cf2..9149ade6a5d9 100644 --- a/drivers/dma/ipu/ipu_idmac.c +++ b/drivers/dma/ipu/ipu_idmac.c @@ -1295,7 +1295,7 @@ static irqreturn_t idmac_interrupt(int irq, void *dev_id) /* Flip the active buffer - even if update above failed */ ichan->active_buffer = !ichan->active_buffer; if (done) - ichan->completed = desc->txd.cookie; + ichan->dma_chan.completed_cookie = desc->txd.cookie; callback = desc->txd.callback; callback_param = desc->txd.callback_param; @@ -1511,7 +1511,7 @@ static int idmac_alloc_chan_resources(struct dma_chan *chan) WARN_ON(ichan->status != IPU_CHANNEL_FREE); chan->cookie = 1; - ichan->completed = -ENXIO; + chan->completed_cookie = -ENXIO; ret = ipu_irq_map(chan->chan_id); if (ret < 0) @@ -1600,9 +1600,7 @@ static void idmac_free_chan_resources(struct dma_chan *chan) static enum dma_status idmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie, struct dma_tx_state *txstate) { - struct idmac_channel *ichan = to_idmac_chan(chan); - - dma_set_tx_state(txstate, ichan->completed, chan->cookie, 0); + dma_set_tx_state(txstate, chan->completed_cookie, chan->cookie, 0); if (cookie != chan->cookie) return DMA_ERROR; return DMA_SUCCESS; @@ -1638,11 +1636,11 @@ static int __init ipu_idmac_init(struct ipu *ipu) ichan->status = IPU_CHANNEL_FREE; ichan->sec_chan_en = false; - ichan->completed = -ENXIO; snprintf(ichan->eof_name, sizeof(ichan->eof_name), "IDMAC EOF %d", i); dma_chan->device = &idmac->dma; dma_chan->cookie = 1; + dma_chan->completed_cookie = -ENXIO; dma_chan->chan_id = i; list_add_tail(&dma_chan->device_node, &dma->channels); } diff --git a/drivers/dma/mpc512x_dma.c b/drivers/dma/mpc512x_dma.c index 4d6d4cf66949..39a5cde9f428 100644 --- a/drivers/dma/mpc512x_dma.c +++ b/drivers/dma/mpc512x_dma.c @@ -188,7 +188,6 @@ struct mpc_dma_chan { struct list_head completed; struct mpc_dma_tcd *tcd; dma_addr_t tcd_paddr; - dma_cookie_t completed_cookie; /* Lock for this structure */ spinlock_t lock; @@ -365,7 +364,7 @@ static void mpc_dma_process_completed(struct mpc_dma *mdma) /* Free descriptors */ spin_lock_irqsave(&mchan->lock, flags); list_splice_tail_init(&list, &mchan->free); - mchan->completed_cookie = last_cookie; + mchan->chan.completed_cookie = last_cookie; spin_unlock_irqrestore(&mchan->lock, flags); } } @@ -568,7 +567,7 @@ mpc_dma_tx_status(struct dma_chan *chan, dma_cookie_t cookie, spin_lock_irqsave(&mchan->lock, flags); last_used = mchan->chan.cookie; - last_complete = mchan->completed_cookie; + last_complete = mchan->chan.completed_cookie; spin_unlock_irqrestore(&mchan->lock, flags); dma_set_tx_state(txstate, last_complete, last_used, 0); @@ -742,7 +741,7 @@ static int __devinit mpc_dma_probe(struct platform_device *op) mchan->chan.device = dma; mchan->chan.cookie = 1; - mchan->completed_cookie = mchan->chan.cookie; + mchan->chan.completed_cookie = mchan->chan.cookie; INIT_LIST_HEAD(&mchan->free); INIT_LIST_HEAD(&mchan->prepared); diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c index ad7d03fe4cb4..c6a84dac112c 100644 --- a/drivers/dma/mv_xor.c +++ b/drivers/dma/mv_xor.c @@ -435,7 +435,7 @@ static void __mv_xor_slot_cleanup(struct mv_xor_chan *mv_chan) } if (cookie > 0) - mv_chan->completed_cookie = cookie; + mv_chan->common.completed_cookie = cookie; } static void @@ -825,7 +825,7 @@ static enum dma_status mv_xor_status(struct dma_chan *chan, enum dma_status ret; last_used = chan->cookie; - last_complete = mv_chan->completed_cookie; + last_complete = chan->completed_cookie; dma_set_tx_state(txstate, last_complete, last_used, 0); ret = dma_async_is_complete(cookie, last_complete, last_used); @@ -836,7 +836,7 @@ static enum dma_status mv_xor_status(struct dma_chan *chan, mv_xor_slot_cleanup(mv_chan); last_used = chan->cookie; - last_complete = mv_chan->completed_cookie; + last_complete = chan->completed_cookie; dma_set_tx_state(txstate, last_complete, last_used, 0); return dma_async_is_complete(cookie, last_complete, last_used); diff --git a/drivers/dma/mv_xor.h b/drivers/dma/mv_xor.h index da04ac23def3..654876b7ba1d 100644 --- a/drivers/dma/mv_xor.h +++ b/drivers/dma/mv_xor.h @@ -78,7 +78,6 @@ struct mv_xor_device { /** * struct mv_xor_chan - internal representation of a XOR channel * @pending: allows batching of hardware operations - * @completed_cookie: identifier for the most recently completed operation * @lock: serializes enqueue/dequeue operations to the descriptors pool * @mmr_base: memory mapped register base * @idx: the index of the xor channel @@ -93,7 +92,6 @@ struct mv_xor_device { */ struct mv_xor_chan { int pending; - dma_cookie_t completed_cookie; spinlock_t lock; /* protects the descriptor slot pool */ void __iomem *mmr_base; unsigned int idx; diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index b06cd4ca626f..3696e6e4143a 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c @@ -111,7 +111,6 @@ struct mxs_dma_chan { struct mxs_dma_ccw *ccw; dma_addr_t ccw_phys; int desc_count; - dma_cookie_t last_completed; enum dma_status status; unsigned int flags; #define MXS_DMA_SG_LOOP (1 << 0) @@ -274,7 +273,7 @@ static irqreturn_t mxs_dma_int_handler(int irq, void *dev_id) stat1 &= ~(1 << channel); if (mxs_chan->status == DMA_SUCCESS) - mxs_chan->last_completed = mxs_chan->desc.cookie; + mxs_chan->chan.completed_cookie = mxs_chan->desc.cookie; /* schedule tasklet on this channel */ tasklet_schedule(&mxs_chan->tasklet); @@ -538,7 +537,7 @@ static enum dma_status mxs_dma_tx_status(struct dma_chan *chan, dma_cookie_t last_used; last_used = chan->cookie; - dma_set_tx_state(txstate, mxs_chan->last_completed, last_used, 0); + dma_set_tx_state(txstate, chan->completed_cookie, last_used, 0); return mxs_chan->status; } diff --git a/drivers/dma/pch_dma.c b/drivers/dma/pch_dma.c index 823f58179f9d..79a71858497c 100644 --- a/drivers/dma/pch_dma.c +++ b/drivers/dma/pch_dma.c @@ -105,7 +105,6 @@ struct pch_dma_chan { spinlock_t lock; - dma_cookie_t completed_cookie; struct list_head active_list; struct list_head queue; struct list_head free_list; @@ -544,7 +543,7 @@ static int pd_alloc_chan_resources(struct dma_chan *chan) spin_lock_irq(&pd_chan->lock); list_splice(&tmp_list, &pd_chan->free_list); pd_chan->descs_allocated = i; - pd_chan->completed_cookie = chan->cookie = 1; + chan->completed_cookie = chan->cookie = 1; spin_unlock_irq(&pd_chan->lock); pdc_enable_irq(chan, 1); @@ -583,7 +582,7 @@ static enum dma_status pd_tx_status(struct dma_chan *chan, dma_cookie_t cookie, int ret; spin_lock_irq(&pd_chan->lock); - last_completed = pd_chan->completed_cookie; + last_completed = chan->completed_cookie; last_used = chan->cookie; spin_unlock_irq(&pd_chan->lock); diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 84ebea9bc53a..99c31a76e74e 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -51,9 +51,6 @@ struct dma_pl330_chan { /* DMA-Engine Channel */ struct dma_chan chan; - /* Last completed cookie */ - dma_cookie_t completed; - /* List of to be xfered descriptors */ struct list_head work_list; @@ -234,7 +231,7 @@ static void pl330_tasklet(unsigned long data) /* Pick up ripe tomatoes */ list_for_each_entry_safe(desc, _dt, &pch->work_list, node) if (desc->status == DONE) { - pch->completed = desc->txd.cookie; + pch->chan.completed_cookie = desc->txd.cookie; list_move_tail(&desc->node, &list); } @@ -305,7 +302,7 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan) spin_lock_irqsave(&pch->lock, flags); - pch->completed = chan->cookie = 1; + chan->completed_cookie = chan->cookie = 1; pch->cyclic = false; pch->pl330_chid = pl330_request_channel(&pdmac->pif); @@ -400,7 +397,7 @@ pl330_tx_status(struct dma_chan *chan, dma_cookie_t cookie, dma_cookie_t last_done, last_used; int ret; - last_done = pch->completed; + last_done = chan->completed_cookie; last_used = chan->cookie; ret = dma_async_is_complete(cookie, last_done, last_used); diff --git a/drivers/dma/ppc4xx/adma.c b/drivers/dma/ppc4xx/adma.c index fc457a7e8832..f878322ecbcb 100644 --- a/drivers/dma/ppc4xx/adma.c +++ b/drivers/dma/ppc4xx/adma.c @@ -1930,7 +1930,7 @@ static void __ppc440spe_adma_slot_cleanup(struct ppc440spe_adma_chan *chan) if (end_of_chain && slot_cnt) { /* Should wait for ZeroSum completion */ if (cookie > 0) - chan->completed_cookie = cookie; + chan->common.completed_cookie = cookie; return; } @@ -1960,7 +1960,7 @@ static void __ppc440spe_adma_slot_cleanup(struct ppc440spe_adma_chan *chan) BUG_ON(!seen_current); if (cookie > 0) { - chan->completed_cookie = cookie; + chan->common.completed_cookie = cookie; pr_debug("\tcompleted cookie %d\n", cookie); } @@ -3950,7 +3950,7 @@ static enum dma_status ppc440spe_adma_tx_status(struct dma_chan *chan, ppc440spe_chan = to_ppc440spe_adma_chan(chan); last_used = chan->cookie; - last_complete = ppc440spe_chan->completed_cookie; + last_complete = chan->completed_cookie; dma_set_tx_state(txstate, last_complete, last_used, 0); @@ -3961,7 +3961,7 @@ static enum dma_status ppc440spe_adma_tx_status(struct dma_chan *chan, ppc440spe_adma_slot_cleanup(ppc440spe_chan); last_used = chan->cookie; - last_complete = ppc440spe_chan->completed_cookie; + last_complete = chan->completed_cookie; dma_set_tx_state(txstate, last_complete, last_used, 0); @@ -4058,7 +4058,7 @@ static void ppc440spe_chan_start_null_xor(struct ppc440spe_adma_chan *chan) /* initialize the completed cookie to be less than * the most recently used cookie */ - chan->completed_cookie = cookie - 1; + chan->common.completed_cookie = cookie - 1; chan->common.cookie = sw_desc->async_tx.cookie = cookie; /* channel should not be busy */ diff --git a/drivers/dma/ppc4xx/adma.h b/drivers/dma/ppc4xx/adma.h index 8ada5a812e3b..26b7a5ed9ac7 100644 --- a/drivers/dma/ppc4xx/adma.h +++ b/drivers/dma/ppc4xx/adma.h @@ -81,7 +81,6 @@ struct ppc440spe_adma_device { * @common: common dmaengine channel object members * @all_slots: complete domain of slots usable by the channel * @pending: allows batching of hardware operations - * @completed_cookie: identifier for the most recently completed operation * @slots_allocated: records the actual size of the descriptor slot pool * @hw_chain_inited: h/w descriptor chain initialization flag * @irq_tasklet: bottom half where ppc440spe_adma_slot_cleanup runs @@ -99,7 +98,6 @@ struct ppc440spe_adma_chan { struct list_head all_slots; struct ppc440spe_adma_desc_slot *last_used; int pending; - dma_cookie_t completed_cookie; int slots_allocated; int hw_chain_inited; struct tasklet_struct irq_tasklet; diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c index 812fd76e9c18..ae84c12e3865 100644 --- a/drivers/dma/shdma.c +++ b/drivers/dma/shdma.c @@ -764,12 +764,12 @@ static dma_async_tx_callback __ld_cleanup(struct sh_dmae_chan *sh_chan, bool all cookie = tx->cookie; if (desc->mark == DESC_COMPLETED && desc->chunks == 1) { - if (sh_chan->completed_cookie != desc->cookie - 1) + if (sh_chan->common.completed_cookie != desc->cookie - 1) dev_dbg(sh_chan->dev, "Completing cookie %d, expected %d\n", desc->cookie, - sh_chan->completed_cookie + 1); - sh_chan->completed_cookie = desc->cookie; + sh_chan->common.completed_cookie + 1); + sh_chan->common.completed_cookie = desc->cookie; } /* Call callback on the last chunk */ @@ -823,7 +823,7 @@ static dma_async_tx_callback __ld_cleanup(struct sh_dmae_chan *sh_chan, bool all * Terminating and the loop completed normally: forgive * uncompleted cookies */ - sh_chan->completed_cookie = sh_chan->common.cookie; + sh_chan->common.completed_cookie = sh_chan->common.cookie; spin_unlock_irqrestore(&sh_chan->desc_lock, flags); @@ -891,7 +891,7 @@ static enum dma_status sh_dmae_tx_status(struct dma_chan *chan, sh_dmae_chan_ld_cleanup(sh_chan, false); /* First read completed cookie to avoid a skew */ - last_complete = sh_chan->completed_cookie; + last_complete = chan->completed_cookie; rmb(); last_used = chan->cookie; BUG_ON(last_complete < 0); diff --git a/drivers/dma/shdma.h b/drivers/dma/shdma.h index 2b55a276dc5b..0b1d2c105f02 100644 --- a/drivers/dma/shdma.h +++ b/drivers/dma/shdma.h @@ -30,7 +30,6 @@ enum dmae_pm_state { }; struct sh_dmae_chan { - dma_cookie_t completed_cookie; /* The maximum cookie completed */ spinlock_t desc_lock; /* Descriptor operation lock */ struct list_head ld_queue; /* Link descriptors queue */ struct list_head ld_free; /* Link descriptors free */ diff --git a/drivers/dma/sirf-dma.c b/drivers/dma/sirf-dma.c index 2333810d1688..60473f00cf1c 100644 --- a/drivers/dma/sirf-dma.c +++ b/drivers/dma/sirf-dma.c @@ -59,7 +59,6 @@ struct sirfsoc_dma_chan { struct list_head queued; struct list_head active; struct list_head completed; - dma_cookie_t completed_cookie; unsigned long happened_cyclic; unsigned long completed_cyclic; @@ -208,7 +207,7 @@ static void sirfsoc_dma_process_completed(struct sirfsoc_dma *sdma) /* Free descriptors */ spin_lock_irqsave(&schan->lock, flags); list_splice_tail_init(&list, &schan->free); - schan->completed_cookie = last_cookie; + schan->chan.completed_cookie = last_cookie; spin_unlock_irqrestore(&schan->lock, flags); } else { /* for cyclic channel, desc is always in active list */ @@ -419,7 +418,7 @@ sirfsoc_dma_tx_status(struct dma_chan *chan, dma_cookie_t cookie, spin_lock_irqsave(&schan->lock, flags); last_used = schan->chan.cookie; - last_complete = schan->completed_cookie; + last_complete = schan->chan.completed_cookie; spin_unlock_irqrestore(&schan->lock, flags); dma_set_tx_state(txstate, last_complete, last_used, 0); @@ -636,7 +635,7 @@ static int __devinit sirfsoc_dma_probe(struct platform_device *op) schan->chan.device = dma; schan->chan.cookie = 1; - schan->completed_cookie = schan->chan.cookie; + schan->chan.completed_cookie = schan->chan.cookie; INIT_LIST_HEAD(&schan->free); INIT_LIST_HEAD(&schan->prepared); diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index cc5ecbc067a3..cfca2a06d1af 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -220,8 +220,6 @@ struct d40_base; * * @lock: A spinlock to protect this struct. * @log_num: The logical number, if any of this channel. - * @completed: Starts with 1, after first interrupt it is set to dma engine's - * current cookie. * @pending_tx: The number of pending transfers. Used between interrupt handler * and tasklet. * @busy: Set to true when transfer is ongoing on this channel. @@ -250,8 +248,6 @@ struct d40_base; struct d40_chan { spinlock_t lock; int log_num; - /* ID of the most recent completed transfer */ - int completed; int pending_tx; bool busy; struct d40_phy_res *phy_chan; @@ -1357,7 +1353,7 @@ static void dma_tasklet(unsigned long data) goto err; if (!d40d->cyclic) - d40c->completed = d40d->txd.cookie; + d40c->chan.completed_cookie = d40d->txd.cookie; /* * If terminating a channel pending_tx is set to zero. @@ -2182,7 +2178,7 @@ static int d40_alloc_chan_resources(struct dma_chan *chan) bool is_free_phy; spin_lock_irqsave(&d40c->lock, flags); - d40c->completed = chan->cookie = 1; + chan->completed_cookie = chan->cookie = 1; /* If no dma configuration is set use default configuration (memcpy) */ if (!d40c->configured) { @@ -2351,7 +2347,7 @@ static enum dma_status d40_tx_status(struct dma_chan *chan, return -EINVAL; } - last_complete = d40c->completed; + last_complete = chan->completed_cookie; last_used = chan->cookie; if (d40_is_paused(d40c)) diff --git a/drivers/dma/timb_dma.c b/drivers/dma/timb_dma.c index a6f9c1684a0f..a1d15598cf7e 100644 --- a/drivers/dma/timb_dma.c +++ b/drivers/dma/timb_dma.c @@ -84,7 +84,6 @@ struct timb_dma_chan { especially the lists and descriptors, from races between the tasklet and calls from above */ - dma_cookie_t last_completed_cookie; bool ongoing; struct list_head active_list; struct list_head queue; @@ -284,7 +283,7 @@ static void __td_finish(struct timb_dma_chan *td_chan) else iowrite32(0, td_chan->membase + TIMBDMA_OFFS_TX_DLAR); */ - td_chan->last_completed_cookie = txd->cookie; + td_chan->chan.completed_cookie = txd->cookie; td_chan->ongoing = false; callback = txd->callback; @@ -481,7 +480,7 @@ static int td_alloc_chan_resources(struct dma_chan *chan) } spin_lock_bh(&td_chan->lock); - td_chan->last_completed_cookie = 1; + chan->completed_cookie = 1; chan->cookie = 1; spin_unlock_bh(&td_chan->lock); @@ -523,7 +522,7 @@ static enum dma_status td_tx_status(struct dma_chan *chan, dma_cookie_t cookie, dev_dbg(chan2dev(chan), "%s: Entry\n", __func__); - last_complete = td_chan->last_completed_cookie; + last_complete = chan->completed_cookie; last_used = chan->cookie; ret = dma_async_is_complete(cookie, last_complete, last_used); diff --git a/drivers/dma/txx9dmac.c b/drivers/dma/txx9dmac.c index 6122c364cf11..a917b6723bad 100644 --- a/drivers/dma/txx9dmac.c +++ b/drivers/dma/txx9dmac.c @@ -424,7 +424,7 @@ txx9dmac_descriptor_complete(struct txx9dmac_chan *dc, dev_vdbg(chan2dev(&dc->chan), "descriptor %u %p complete\n", txd->cookie, desc); - dc->completed = txd->cookie; + dc->chan.completed_cookie = txd->cookie; callback = txd->callback; param = txd->callback_param; @@ -976,7 +976,7 @@ txx9dmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie, dma_cookie_t last_complete; int ret; - last_complete = dc->completed; + last_complete = chan->completed_cookie; last_used = chan->cookie; ret = dma_async_is_complete(cookie, last_complete, last_used); @@ -985,7 +985,7 @@ txx9dmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie, txx9dmac_scan_descriptors(dc); spin_unlock_bh(&dc->lock); - last_complete = dc->completed; + last_complete = chan->completed_cookie; last_used = chan->cookie; ret = dma_async_is_complete(cookie, last_complete, last_used); @@ -1057,7 +1057,7 @@ static int txx9dmac_alloc_chan_resources(struct dma_chan *chan) return -EIO; } - dc->completed = chan->cookie = 1; + chan->completed_cookie = chan->cookie = 1; dc->ccr = TXX9_DMA_CCR_IMMCHN | TXX9_DMA_CCR_INTENE | CCR_LE; txx9dmac_chan_set_SMPCHN(dc); @@ -1186,7 +1186,7 @@ static int __init txx9dmac_chan_probe(struct platform_device *pdev) dc->ddev->chan[ch] = dc; dc->chan.device = &dc->dma; list_add_tail(&dc->chan.device_node, &dc->chan.device->channels); - dc->chan.cookie = dc->completed = 1; + dc->chan.cookie = dc->chan.completed_cookie = 1; if (is_dmac64(dc)) dc->ch_regs = &__txx9dmac_regs(dc->ddev)->CHAN[ch]; diff --git a/drivers/dma/txx9dmac.h b/drivers/dma/txx9dmac.h index 365d42366b9f..f5a760598882 100644 --- a/drivers/dma/txx9dmac.h +++ b/drivers/dma/txx9dmac.h @@ -172,7 +172,6 @@ struct txx9dmac_chan { spinlock_t lock; /* these other elements are all protected by lock */ - dma_cookie_t completed; struct list_head active_list; struct list_head queue; struct list_head free_list; diff --git a/include/linux/amba/pl08x.h b/include/linux/amba/pl08x.h index 2c58853ca423..e64ce2cfee99 100644 --- a/include/linux/amba/pl08x.h +++ b/include/linux/amba/pl08x.h @@ -172,7 +172,6 @@ enum pl08x_dma_chan_state { * @runtime_addr: address for RX/TX according to the runtime config * @runtime_direction: current direction of this channel according to * runtime config - * @lc: last completed transaction on this channel * @pend_list: queued transactions pending on this channel * @at: active transaction on this channel * @lock: a lock for this channel data @@ -197,7 +196,6 @@ struct pl08x_dma_chan { u32 src_cctl; u32 dst_cctl; enum dma_transfer_direction runtime_direction; - dma_cookie_t lc; struct list_head pend_list; struct pl08x_txd *at; spinlock_t lock; diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 7e640bf27d2d..c59c4f0c2cc9 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -258,6 +258,7 @@ struct dma_chan_percpu { * struct dma_chan - devices supply DMA channels, clients use them * @device: ptr to the dma device who supplies this channel, always !%NULL * @cookie: last cookie value returned to client + * @completed_cookie: last completed cookie for this channel * @chan_id: channel ID for sysfs * @dev: class device for sysfs * @device_node: used to add this to the device chan list @@ -269,6 +270,7 @@ struct dma_chan_percpu { struct dma_chan { struct dma_device *device; dma_cookie_t cookie; + dma_cookie_t completed_cookie; /* sysfs */ int chan_id; From d2ebfb335b0426deb1a4fb14e4e926d81ecd8235 Mon Sep 17 00:00:00 2001 From: Russell King - ARM Linux Date: Tue, 6 Mar 2012 22:34:26 +0000 Subject: [PATCH 39/67] dmaengine: add private header file Add a local private header file to contain definitions and declarations which should only be used by DMA engine drivers. We also fix linux/dmaengine.h to use LINUX_DMAENGINE_H to guard against multiple inclusion. Signed-off-by: Russell King Tested-by: Linus Walleij Reviewed-by: Linus Walleij Acked-by: Jassi Brar [imx-sdma.c & mxs-dma.c] Tested-by: Shawn Guo Signed-off-by: Vinod Koul --- drivers/dma/amba-pl08x.c | 2 ++ drivers/dma/at_hdmac.c | 1 + drivers/dma/coh901318.c | 1 + drivers/dma/dmaengine.h | 10 ++++++++++ drivers/dma/dw_dmac.c | 1 + drivers/dma/ep93xx_dma.c | 2 ++ drivers/dma/fsldma.c | 1 + drivers/dma/imx-dma.c | 2 ++ drivers/dma/imx-sdma.c | 2 ++ drivers/dma/intel_mid_dma.c | 2 ++ drivers/dma/ioat/dma.c | 2 ++ drivers/dma/ioat/dma_v2.c | 2 ++ drivers/dma/iop-adma.c | 2 ++ drivers/dma/ipu/ipu_idmac.c | 1 + drivers/dma/mpc512x_dma.c | 2 ++ drivers/dma/mv_xor.c | 2 ++ drivers/dma/mxs-dma.c | 2 ++ drivers/dma/pch_dma.c | 2 ++ drivers/dma/pl330.c | 2 ++ drivers/dma/ppc4xx/adma.c | 1 + drivers/dma/shdma.c | 2 ++ drivers/dma/ste_dma40.c | 1 + drivers/dma/timb_dma.c | 2 ++ drivers/dma/txx9dmac.c | 2 ++ include/linux/dmaengine.h | 4 ++-- 25 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 drivers/dma/dmaengine.h diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index e510447a685a..45f5e66e1c84 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -85,6 +85,8 @@ #include #include +#include "dmaengine.h" + #define DRIVER_NAME "pl08xdmac" static struct amba_driver pl08x_amba_driver; diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 6baf5d717262..ce26ba381144 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -27,6 +27,7 @@ #include #include "at_hdmac_regs.h" +#include "dmaengine.h" /* * Glossary diff --git a/drivers/dma/coh901318.c b/drivers/dma/coh901318.c index 521434bc3130..fb0d1245ade5 100644 --- a/drivers/dma/coh901318.c +++ b/drivers/dma/coh901318.c @@ -24,6 +24,7 @@ #include #include "coh901318_lli.h" +#include "dmaengine.h" #define COHC_2_DEV(cohc) (&cohc->chan.dev->device) diff --git a/drivers/dma/dmaengine.h b/drivers/dma/dmaengine.h new file mode 100644 index 000000000000..968570dde2eb --- /dev/null +++ b/drivers/dma/dmaengine.h @@ -0,0 +1,10 @@ +/* + * The contents of this file are private to DMA engine drivers, and is not + * part of the API to be used by DMA engine users. + */ +#ifndef DMAENGINE_H +#define DMAENGINE_H + +#include + +#endif diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 5bd23006ff4a..b279e1920725 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -23,6 +23,7 @@ #include #include "dw_dmac_regs.h" +#include "dmaengine.h" /* * This supports the Synopsys "DesignWare AHB Central DMA Controller", diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c index bc457878cffd..326019832a13 100644 --- a/drivers/dma/ep93xx_dma.c +++ b/drivers/dma/ep93xx_dma.c @@ -28,6 +28,8 @@ #include +#include "dmaengine.h" + /* M2P registers */ #define M2P_CONTROL 0x0000 #define M2P_CONTROL_STALLINT BIT(0) diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index 9b5cb8a43cfa..2ebbe572f9e0 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -35,6 +35,7 @@ #include #include +#include "dmaengine.h" #include "fsldma.h" #define chan_dbg(chan, fmt, arg...) \ diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index d3ddcba87f81..cead5e4bd38c 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -30,6 +30,8 @@ #include #include +#include "dmaengine.h" + struct imxdma_channel { struct imxdma_engine *imxdma; unsigned int channel; diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 49aa4e876645..48a791f93adc 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -43,6 +43,8 @@ #include #include +#include "dmaengine.h" + /* SDMA registers */ #define SDMA_H_C0PTR 0x000 #define SDMA_H_INTR 0x004 diff --git a/drivers/dma/intel_mid_dma.c b/drivers/dma/intel_mid_dma.c index 40e47e6c7ed8..55d0451670b0 100644 --- a/drivers/dma/intel_mid_dma.c +++ b/drivers/dma/intel_mid_dma.c @@ -29,6 +29,8 @@ #include #include +#include "dmaengine.h" + #define MAX_CHAN 4 /*max ch across controllers*/ #include "intel_mid_dma_regs.h" diff --git a/drivers/dma/ioat/dma.c b/drivers/dma/ioat/dma.c index fab440af1f9a..dfe411b2014f 100644 --- a/drivers/dma/ioat/dma.c +++ b/drivers/dma/ioat/dma.c @@ -40,6 +40,8 @@ #include "registers.h" #include "hw.h" +#include "../dmaengine.h" + int ioat_pending_level = 4; module_param(ioat_pending_level, int, 0644); MODULE_PARM_DESC(ioat_pending_level, diff --git a/drivers/dma/ioat/dma_v2.c b/drivers/dma/ioat/dma_v2.c index d3f0aff2c02a..6c1e6754d9bd 100644 --- a/drivers/dma/ioat/dma_v2.c +++ b/drivers/dma/ioat/dma_v2.c @@ -41,6 +41,8 @@ #include "registers.h" #include "hw.h" +#include "../dmaengine.h" + int ioat_ring_alloc_order = 8; module_param(ioat_ring_alloc_order, int, 0644); MODULE_PARM_DESC(ioat_ring_alloc_order, diff --git a/drivers/dma/iop-adma.c b/drivers/dma/iop-adma.c index d8027c2b42c0..650bf1e185e8 100644 --- a/drivers/dma/iop-adma.c +++ b/drivers/dma/iop-adma.c @@ -36,6 +36,8 @@ #include +#include "dmaengine.h" + #define to_iop_adma_chan(chan) container_of(chan, struct iop_adma_chan, common) #define to_iop_adma_device(dev) \ container_of(dev, struct iop_adma_device, common) diff --git a/drivers/dma/ipu/ipu_idmac.c b/drivers/dma/ipu/ipu_idmac.c index 9149ade6a5d9..0fcff6508fb1 100644 --- a/drivers/dma/ipu/ipu_idmac.c +++ b/drivers/dma/ipu/ipu_idmac.c @@ -25,6 +25,7 @@ #include +#include "../dmaengine.h" #include "ipu_intern.h" #define FS_VF_IN_VALID 0x00000002 diff --git a/drivers/dma/mpc512x_dma.c b/drivers/dma/mpc512x_dma.c index 39a5cde9f428..c56b3fe5d3fa 100644 --- a/drivers/dma/mpc512x_dma.c +++ b/drivers/dma/mpc512x_dma.c @@ -44,6 +44,8 @@ #include +#include "dmaengine.h" + /* Number of DMA Transfer descriptors allocated per channel */ #define MPC_DMA_DESCRIPTORS 64 diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c index c6a84dac112c..ee61778ba8a2 100644 --- a/drivers/dma/mv_xor.c +++ b/drivers/dma/mv_xor.c @@ -26,6 +26,8 @@ #include #include #include + +#include "dmaengine.h" #include "mv_xor.h" static void mv_xor_issue_pending(struct dma_chan *chan); diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index 3696e6e4143a..daa84ee2a187 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c @@ -28,6 +28,8 @@ #include #include +#include "dmaengine.h" + /* * NOTE: The term "PIO" throughout the mxs-dma implementation means * PIO mode of mxs apbh-dma and apbx-dma. With this working mode, diff --git a/drivers/dma/pch_dma.c b/drivers/dma/pch_dma.c index 79a71858497c..2b3479d850c9 100644 --- a/drivers/dma/pch_dma.c +++ b/drivers/dma/pch_dma.c @@ -25,6 +25,8 @@ #include #include +#include "dmaengine.h" + #define DRV_NAME "pch-dma" #define DMA_CTL0_DISABLE 0x0 diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 99c31a76e74e..2db70748403f 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -21,6 +21,8 @@ #include #include +#include "dmaengine.h" + #define NR_DEFAULT_DESC 16 enum desc_status { diff --git a/drivers/dma/ppc4xx/adma.c b/drivers/dma/ppc4xx/adma.c index f878322ecbcb..40082ec8326c 100644 --- a/drivers/dma/ppc4xx/adma.c +++ b/drivers/dma/ppc4xx/adma.c @@ -46,6 +46,7 @@ #include #include #include "adma.h" +#include "../dmaengine.h" enum ppc_adma_init_code { PPC_ADMA_INIT_OK = 0, diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c index ae84c12e3865..c2914330b8fc 100644 --- a/drivers/dma/shdma.c +++ b/drivers/dma/shdma.c @@ -30,6 +30,8 @@ #include #include #include + +#include "dmaengine.h" #include "shdma.h" /* DMA descriptor control */ diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index cfca2a06d1af..156b98f661a3 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -21,6 +21,7 @@ #include +#include "dmaengine.h" #include "ste_dma40_ll.h" #define D40_NAME "dma40" diff --git a/drivers/dma/timb_dma.c b/drivers/dma/timb_dma.c index a1d15598cf7e..4b61879284d1 100644 --- a/drivers/dma/timb_dma.c +++ b/drivers/dma/timb_dma.c @@ -31,6 +31,8 @@ #include +#include "dmaengine.h" + #define DRIVER_NAME "timb-dma" /* Global DMA registers */ diff --git a/drivers/dma/txx9dmac.c b/drivers/dma/txx9dmac.c index a917b6723bad..db6d809d4d04 100644 --- a/drivers/dma/txx9dmac.c +++ b/drivers/dma/txx9dmac.c @@ -15,6 +15,8 @@ #include #include #include + +#include "dmaengine.h" #include "txx9dmac.h" static struct txx9dmac_chan *to_txx9dmac_chan(struct dma_chan *chan) diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index c59c4f0c2cc9..715babf4bffe 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -18,8 +18,8 @@ * The full GNU General Public License is included in this distribution in the * file called COPYING. */ -#ifndef DMAENGINE_H -#define DMAENGINE_H +#ifndef LINUX_DMAENGINE_H +#define LINUX_DMAENGINE_H #include #include From 884485e1f12dcd39390f042e772cdbefc9ebb750 Mon Sep 17 00:00:00 2001 From: Russell King - ARM Linux Date: Tue, 6 Mar 2012 22:34:46 +0000 Subject: [PATCH 40/67] dmaengine: consolidate assignment of DMA cookies Everyone deals with assigning DMA cookies in the same way (it's part of the API so they should be), so lets consolidate the common code into a helper function to avoid this duplication. Signed-off-by: Russell King Tested-by: Linus Walleij Reviewed-by: Linus Walleij Acked-by: Jassi Brar [imx-sdma.c & mxs-dma.c] Tested-by: Shawn Guo Signed-off-by: Vinod Koul --- drivers/dma/amba-pl08x.c | 9 +++------ drivers/dma/at_hdmac.c | 23 +---------------------- drivers/dma/coh901318.c | 20 +++----------------- drivers/dma/dmaengine.h | 20 ++++++++++++++++++++ drivers/dma/dw_dmac.c | 17 +---------------- drivers/dma/ep93xx_dma.c | 9 +-------- drivers/dma/fsldma.c | 9 +-------- drivers/dma/imx-dma.c | 15 +-------------- drivers/dma/imx-sdma.c | 16 +--------------- drivers/dma/intel_mid_dma.c | 9 +-------- drivers/dma/ioat/dma.c | 7 +------ drivers/dma/ioat/dma_v2.c | 8 ++------ drivers/dma/iop-adma.c | 14 +------------- drivers/dma/ipu/ipu_idmac.c | 9 +-------- drivers/dma/mpc512x_dma.c | 8 +------- drivers/dma/mv_xor.c | 14 +------------- drivers/dma/mxs-dma.c | 15 +-------------- drivers/dma/pch_dma.c | 16 +--------------- drivers/dma/pl330.c | 14 ++------------ drivers/dma/ppc4xx/adma.c | 19 +------------------ drivers/dma/shdma.c | 8 +------- drivers/dma/sirf-dma.c | 8 +------- drivers/dma/ste_dma40.c | 13 +++---------- drivers/dma/timb_dma.c | 7 +------ drivers/dma/txx9dmac.c | 17 +---------------- 25 files changed, 52 insertions(+), 272 deletions(-) diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 45f5e66e1c84..d8d3dc273f29 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -921,13 +921,10 @@ static dma_cookie_t pl08x_tx_submit(struct dma_async_tx_descriptor *tx) struct pl08x_dma_chan *plchan = to_pl08x_chan(tx->chan); struct pl08x_txd *txd = to_pl08x_txd(tx); unsigned long flags; + dma_cookie_t cookie; spin_lock_irqsave(&plchan->lock, flags); - - plchan->chan.cookie += 1; - if (plchan->chan.cookie < 0) - plchan->chan.cookie = 1; - tx->cookie = plchan->chan.cookie; + cookie = dma_cookie_assign(tx); /* Put this onto the pending list */ list_add_tail(&txd->node, &plchan->pend_list); @@ -947,7 +944,7 @@ static dma_cookie_t pl08x_tx_submit(struct dma_async_tx_descriptor *tx) spin_unlock_irqrestore(&plchan->lock, flags); - return tx->cookie; + return cookie; } static struct dma_async_tx_descriptor *pl08x_prep_dma_interrupt( diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index ce26ba381144..df47e7d6164b 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -192,27 +192,6 @@ static void atc_desc_chain(struct at_desc **first, struct at_desc **prev, *prev = desc; } -/** - * atc_assign_cookie - compute and assign new cookie - * @atchan: channel we work on - * @desc: descriptor to assign cookie for - * - * Called with atchan->lock held and bh disabled - */ -static dma_cookie_t -atc_assign_cookie(struct at_dma_chan *atchan, struct at_desc *desc) -{ - dma_cookie_t cookie = atchan->chan_common.cookie; - - if (++cookie < 0) - cookie = 1; - - atchan->chan_common.cookie = cookie; - desc->txd.cookie = cookie; - - return cookie; -} - /** * atc_dostart - starts the DMA engine for real * @atchan: the channel we want to start @@ -548,7 +527,7 @@ static dma_cookie_t atc_tx_submit(struct dma_async_tx_descriptor *tx) unsigned long flags; spin_lock_irqsave(&atchan->lock, flags); - cookie = atc_assign_cookie(atchan, desc); + cookie = dma_cookie_assign(tx); if (list_empty(&atchan->active_list)) { dev_vdbg(chan2dev(tx->chan), "tx_submit: started %u\n", diff --git a/drivers/dma/coh901318.c b/drivers/dma/coh901318.c index fb0d1245ade5..843a1a3b8a81 100644 --- a/drivers/dma/coh901318.c +++ b/drivers/dma/coh901318.c @@ -318,20 +318,6 @@ static int coh901318_prep_linked_list(struct coh901318_chan *cohc, return 0; } -static dma_cookie_t -coh901318_assign_cookie(struct coh901318_chan *cohc, - struct coh901318_desc *cohd) -{ - dma_cookie_t cookie = cohc->chan.cookie; - - if (++cookie < 0) - cookie = 1; - - cohc->chan.cookie = cookie; - cohd->desc.cookie = cookie; - - return cookie; -} static struct coh901318_desc * coh901318_desc_get(struct coh901318_chan *cohc) @@ -966,16 +952,16 @@ coh901318_tx_submit(struct dma_async_tx_descriptor *tx) desc); struct coh901318_chan *cohc = to_coh901318_chan(tx->chan); unsigned long flags; + dma_cookie_t cookie; spin_lock_irqsave(&cohc->lock, flags); - - tx->cookie = coh901318_assign_cookie(cohc, cohd); + cookie = dma_cookie_assign(tx); coh901318_desc_queue(cohc, cohd); spin_unlock_irqrestore(&cohc->lock, flags); - return tx->cookie; + return cookie; } static struct dma_async_tx_descriptor * diff --git a/drivers/dma/dmaengine.h b/drivers/dma/dmaengine.h index 968570dde2eb..7692c8644045 100644 --- a/drivers/dma/dmaengine.h +++ b/drivers/dma/dmaengine.h @@ -7,4 +7,24 @@ #include +/** + * dma_cookie_assign - assign a DMA engine cookie to the descriptor + * @tx: descriptor needing cookie + * + * Assign a unique non-zero per-channel cookie to the descriptor. + * Note: caller is expected to hold a lock to prevent concurrency. + */ +static inline dma_cookie_t dma_cookie_assign(struct dma_async_tx_descriptor *tx) +{ + struct dma_chan *chan = tx->chan; + dma_cookie_t cookie; + + cookie = chan->cookie + 1; + if (cookie < DMA_MIN_COOKIE) + cookie = DMA_MIN_COOKIE; + tx->cookie = chan->cookie = cookie; + + return cookie; +} + #endif diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index b279e1920725..3a4ca67ace02 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -157,21 +157,6 @@ static void dwc_desc_put(struct dw_dma_chan *dwc, struct dw_desc *desc) } } -/* Called with dwc->lock held and bh disabled */ -static dma_cookie_t -dwc_assign_cookie(struct dw_dma_chan *dwc, struct dw_desc *desc) -{ - dma_cookie_t cookie = dwc->chan.cookie; - - if (++cookie < 0) - cookie = 1; - - dwc->chan.cookie = cookie; - desc->txd.cookie = cookie; - - return cookie; -} - static void dwc_initialize(struct dw_dma_chan *dwc) { struct dw_dma *dw = to_dw_dma(dwc->chan.device); @@ -603,7 +588,7 @@ static dma_cookie_t dwc_tx_submit(struct dma_async_tx_descriptor *tx) unsigned long flags; spin_lock_irqsave(&dwc->lock, flags); - cookie = dwc_assign_cookie(dwc, desc); + cookie = dma_cookie_assign(tx); /* * REVISIT: We should attempt to chain as many descriptors as diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c index 326019832a13..e5aaae87ddfb 100644 --- a/drivers/dma/ep93xx_dma.c +++ b/drivers/dma/ep93xx_dma.c @@ -783,17 +783,10 @@ static dma_cookie_t ep93xx_dma_tx_submit(struct dma_async_tx_descriptor *tx) unsigned long flags; spin_lock_irqsave(&edmac->lock, flags); - - cookie = edmac->chan.cookie; - - if (++cookie < 0) - cookie = 1; + cookie = dma_cookie_assign(tx); desc = container_of(tx, struct ep93xx_dma_desc, txd); - edmac->chan.cookie = cookie; - desc->txd.cookie = cookie; - /* * If nothing is currently prosessed, we push this descriptor * directly to the hardware. Otherwise we put the descriptor diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index 2ebbe572f9e0..04b4347ba4e9 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -414,17 +414,10 @@ static dma_cookie_t fsl_dma_tx_submit(struct dma_async_tx_descriptor *tx) * assign cookies to all of the software descriptors * that make up this transaction */ - cookie = chan->common.cookie; list_for_each_entry(child, &desc->tx_list, node) { - cookie++; - if (cookie < DMA_MIN_COOKIE) - cookie = DMA_MIN_COOKIE; - - child->async_tx.cookie = cookie; + cookie = dma_cookie_assign(&child->async_tx); } - chan->common.cookie = cookie; - /* put this transaction onto the tail of the pending queue */ append_ld_queue(chan, desc); diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index cead5e4bd38c..687fc687aaf6 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -165,19 +165,6 @@ static enum dma_status imxdma_tx_status(struct dma_chan *chan, return ret; } -static dma_cookie_t imxdma_assign_cookie(struct imxdma_channel *imxdma) -{ - dma_cookie_t cookie = imxdma->chan.cookie; - - if (++cookie < 0) - cookie = 1; - - imxdma->chan.cookie = cookie; - imxdma->desc.cookie = cookie; - - return cookie; -} - static dma_cookie_t imxdma_tx_submit(struct dma_async_tx_descriptor *tx) { struct imxdma_channel *imxdmac = to_imxdma_chan(tx->chan); @@ -185,7 +172,7 @@ static dma_cookie_t imxdma_tx_submit(struct dma_async_tx_descriptor *tx) spin_lock_irq(&imxdmac->lock); - cookie = imxdma_assign_cookie(imxdmac); + cookie = dma_cookie_assign(tx); spin_unlock_irq(&imxdmac->lock); diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 48a791f93adc..3f0c002933f3 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -815,19 +815,6 @@ out: return ret; } -static dma_cookie_t sdma_assign_cookie(struct sdma_channel *sdmac) -{ - dma_cookie_t cookie = sdmac->chan.cookie; - - if (++cookie < 0) - cookie = 1; - - sdmac->chan.cookie = cookie; - sdmac->desc.cookie = cookie; - - return cookie; -} - static struct sdma_channel *to_sdma_chan(struct dma_chan *chan) { return container_of(chan, struct sdma_channel, chan); @@ -841,7 +828,7 @@ static dma_cookie_t sdma_tx_submit(struct dma_async_tx_descriptor *tx) spin_lock_irqsave(&sdmac->lock, flags); - cookie = sdma_assign_cookie(sdmac); + cookie = dma_cookie_assign(tx); spin_unlock_irqrestore(&sdmac->lock, flags); @@ -1140,7 +1127,6 @@ static void sdma_issue_pending(struct dma_chan *chan) struct sdma_engine *sdma = sdmac->sdma; if (sdmac->status == DMA_IN_PROGRESS) - sdma_enable_channel(sdma, sdmac->channel); } #define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1 34 diff --git a/drivers/dma/intel_mid_dma.c b/drivers/dma/intel_mid_dma.c index 55d0451670b0..e9217c390b76 100644 --- a/drivers/dma/intel_mid_dma.c +++ b/drivers/dma/intel_mid_dma.c @@ -436,14 +436,7 @@ static dma_cookie_t intel_mid_dma_tx_submit(struct dma_async_tx_descriptor *tx) dma_cookie_t cookie; spin_lock_bh(&midc->lock); - cookie = midc->chan.cookie; - - if (++cookie < 0) - cookie = 1; - - midc->chan.cookie = cookie; - desc->txd.cookie = cookie; - + cookie = dma_cookie_assign(tx); if (list_empty(&midc->active_list)) list_add_tail(&desc->desc_node, &midc->active_list); diff --git a/drivers/dma/ioat/dma.c b/drivers/dma/ioat/dma.c index dfe411b2014f..5c06117ac682 100644 --- a/drivers/dma/ioat/dma.c +++ b/drivers/dma/ioat/dma.c @@ -237,12 +237,7 @@ static dma_cookie_t ioat1_tx_submit(struct dma_async_tx_descriptor *tx) spin_lock_bh(&ioat->desc_lock); /* cookie incr and addition to used_list must be atomic */ - cookie = c->cookie; - cookie++; - if (cookie < 0) - cookie = 1; - c->cookie = cookie; - tx->cookie = cookie; + cookie = dma_cookie_assign(tx); dev_dbg(to_dev(&ioat->base), "%s: cookie: %d\n", __func__, cookie); /* write address into NextDescriptor field of last desc in chain */ diff --git a/drivers/dma/ioat/dma_v2.c b/drivers/dma/ioat/dma_v2.c index 6c1e6754d9bd..17ecacb70d40 100644 --- a/drivers/dma/ioat/dma_v2.c +++ b/drivers/dma/ioat/dma_v2.c @@ -400,13 +400,9 @@ static dma_cookie_t ioat2_tx_submit_unlock(struct dma_async_tx_descriptor *tx) struct dma_chan *c = tx->chan; struct ioat2_dma_chan *ioat = to_ioat2_chan(c); struct ioat_chan_common *chan = &ioat->base; - dma_cookie_t cookie = c->cookie; + dma_cookie_t cookie; - cookie++; - if (cookie < 0) - cookie = 1; - tx->cookie = cookie; - c->cookie = cookie; + cookie = dma_cookie_assign(tx); dev_dbg(to_dev(&ioat->base), "%s: cookie: %d\n", __func__, cookie); if (!test_and_set_bit(IOAT_COMPLETION_PENDING, &chan->state)) diff --git a/drivers/dma/iop-adma.c b/drivers/dma/iop-adma.c index 650bf1e185e8..f2392d59568d 100644 --- a/drivers/dma/iop-adma.c +++ b/drivers/dma/iop-adma.c @@ -440,18 +440,6 @@ retry: return NULL; } -static dma_cookie_t -iop_desc_assign_cookie(struct iop_adma_chan *iop_chan, - struct iop_adma_desc_slot *desc) -{ - dma_cookie_t cookie = iop_chan->common.cookie; - cookie++; - if (cookie < 0) - cookie = 1; - iop_chan->common.cookie = desc->async_tx.cookie = cookie; - return cookie; -} - static void iop_adma_check_threshold(struct iop_adma_chan *iop_chan) { dev_dbg(iop_chan->device->common.dev, "pending: %d\n", @@ -479,7 +467,7 @@ iop_adma_tx_submit(struct dma_async_tx_descriptor *tx) slots_per_op = grp_start->slots_per_op; spin_lock_bh(&iop_chan->lock); - cookie = iop_desc_assign_cookie(iop_chan, sw_desc); + cookie = dma_cookie_assign(tx); old_chain_tail = list_entry(iop_chan->chain.prev, struct iop_adma_desc_slot, chain_node); diff --git a/drivers/dma/ipu/ipu_idmac.c b/drivers/dma/ipu/ipu_idmac.c index 0fcff6508fb1..d4620c53fd2d 100644 --- a/drivers/dma/ipu/ipu_idmac.c +++ b/drivers/dma/ipu/ipu_idmac.c @@ -867,14 +867,7 @@ static dma_cookie_t idmac_tx_submit(struct dma_async_tx_descriptor *tx) dev_dbg(dev, "Submitting sg %p\n", &desc->sg[0]); - cookie = ichan->dma_chan.cookie; - - if (++cookie < 0) - cookie = 1; - - /* from dmaengine.h: "last cookie value returned to client" */ - ichan->dma_chan.cookie = cookie; - tx->cookie = cookie; + cookie = dma_cookie_assign(tx); /* ipu->lock can be taken under ichan->lock, but not v.v. */ spin_lock_irqsave(&ichan->lock, flags); diff --git a/drivers/dma/mpc512x_dma.c b/drivers/dma/mpc512x_dma.c index c56b3fe5d3fa..0253d5aecdb1 100644 --- a/drivers/dma/mpc512x_dma.c +++ b/drivers/dma/mpc512x_dma.c @@ -439,13 +439,7 @@ static dma_cookie_t mpc_dma_tx_submit(struct dma_async_tx_descriptor *txd) mpc_dma_execute(mchan); /* Update cookie */ - cookie = mchan->chan.cookie + 1; - if (cookie <= 0) - cookie = 1; - - mchan->chan.cookie = cookie; - mdesc->desc.cookie = cookie; - + cookie = dma_cookie_assign(txd); spin_unlock_irqrestore(&mchan->lock, flags); return cookie; diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c index ee61778ba8a2..d9810ce3794c 100644 --- a/drivers/dma/mv_xor.c +++ b/drivers/dma/mv_xor.c @@ -536,18 +536,6 @@ retry: return NULL; } -static dma_cookie_t -mv_desc_assign_cookie(struct mv_xor_chan *mv_chan, - struct mv_xor_desc_slot *desc) -{ - dma_cookie_t cookie = mv_chan->common.cookie; - - if (++cookie < 0) - cookie = 1; - mv_chan->common.cookie = desc->async_tx.cookie = cookie; - return cookie; -} - /************************ DMA engine API functions ****************************/ static dma_cookie_t mv_xor_tx_submit(struct dma_async_tx_descriptor *tx) @@ -565,7 +553,7 @@ mv_xor_tx_submit(struct dma_async_tx_descriptor *tx) grp_start = sw_desc->group_head; spin_lock_bh(&mv_chan->lock); - cookie = mv_desc_assign_cookie(mv_chan, sw_desc); + cookie = dma_cookie_assign(tx); if (list_empty(&mv_chan->chain)) list_splice_init(&sw_desc->tx_list, &mv_chan->chain); diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index daa84ee2a187..4d3b6ff3050f 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c @@ -194,19 +194,6 @@ static void mxs_dma_resume_chan(struct mxs_dma_chan *mxs_chan) mxs_chan->status = DMA_IN_PROGRESS; } -static dma_cookie_t mxs_dma_assign_cookie(struct mxs_dma_chan *mxs_chan) -{ - dma_cookie_t cookie = mxs_chan->chan.cookie; - - if (++cookie < 0) - cookie = 1; - - mxs_chan->chan.cookie = cookie; - mxs_chan->desc.cookie = cookie; - - return cookie; -} - static struct mxs_dma_chan *to_mxs_dma_chan(struct dma_chan *chan) { return container_of(chan, struct mxs_dma_chan, chan); @@ -218,7 +205,7 @@ static dma_cookie_t mxs_dma_tx_submit(struct dma_async_tx_descriptor *tx) mxs_dma_enable_chan(mxs_chan); - return mxs_dma_assign_cookie(mxs_chan); + return dma_cookie_assign(tx); } static void mxs_dma_tasklet(unsigned long data) diff --git a/drivers/dma/pch_dma.c b/drivers/dma/pch_dma.c index 2b3479d850c9..5218e48aed0e 100644 --- a/drivers/dma/pch_dma.c +++ b/drivers/dma/pch_dma.c @@ -417,20 +417,6 @@ static void pdc_advance_work(struct pch_dma_chan *pd_chan) } } -static dma_cookie_t pdc_assign_cookie(struct pch_dma_chan *pd_chan, - struct pch_dma_desc *desc) -{ - dma_cookie_t cookie = pd_chan->chan.cookie; - - if (++cookie < 0) - cookie = 1; - - pd_chan->chan.cookie = cookie; - desc->txd.cookie = cookie; - - return cookie; -} - static dma_cookie_t pd_tx_submit(struct dma_async_tx_descriptor *txd) { struct pch_dma_desc *desc = to_pd_desc(txd); @@ -438,7 +424,7 @@ static dma_cookie_t pd_tx_submit(struct dma_async_tx_descriptor *txd) dma_cookie_t cookie; spin_lock(&pd_chan->lock); - cookie = pdc_assign_cookie(pd_chan, desc); + cookie = dma_cookie_assign(txd); if (list_empty(&pd_chan->active_list)) { list_add_tail(&desc->desc_node, &pd_chan->active_list); diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 2db70748403f..644eb789958b 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -429,26 +429,16 @@ static dma_cookie_t pl330_tx_submit(struct dma_async_tx_descriptor *tx) spin_lock_irqsave(&pch->lock, flags); /* Assign cookies to all nodes */ - cookie = tx->chan->cookie; - while (!list_empty(&last->node)) { desc = list_entry(last->node.next, struct dma_pl330_desc, node); - if (++cookie < 0) - cookie = 1; - desc->txd.cookie = cookie; + dma_cookie_assign(&desc->txd); list_move_tail(&desc->node, &pch->work_list); } - if (++cookie < 0) - cookie = 1; - last->txd.cookie = cookie; - + cookie = dma_cookie_assign(&last->txd); list_add_tail(&last->node, &pch->work_list); - - tx->chan->cookie = cookie; - spin_unlock_irqrestore(&pch->lock, flags); return cookie; diff --git a/drivers/dma/ppc4xx/adma.c b/drivers/dma/ppc4xx/adma.c index 40082ec8326c..12e94dd6fc3d 100644 --- a/drivers/dma/ppc4xx/adma.c +++ b/drivers/dma/ppc4xx/adma.c @@ -2150,22 +2150,6 @@ static int ppc440spe_adma_alloc_chan_resources(struct dma_chan *chan) return (i > 0) ? i : -ENOMEM; } -/** - * ppc440spe_desc_assign_cookie - assign a cookie - */ -static dma_cookie_t ppc440spe_desc_assign_cookie( - struct ppc440spe_adma_chan *chan, - struct ppc440spe_adma_desc_slot *desc) -{ - dma_cookie_t cookie = chan->common.cookie; - - cookie++; - if (cookie < 0) - cookie = 1; - chan->common.cookie = desc->async_tx.cookie = cookie; - return cookie; -} - /** * ppc440spe_rxor_set_region_data - */ @@ -2236,8 +2220,7 @@ static dma_cookie_t ppc440spe_adma_tx_submit(struct dma_async_tx_descriptor *tx) slots_per_op = group_start->slots_per_op; spin_lock_bh(&chan->lock); - - cookie = ppc440spe_desc_assign_cookie(chan, sw_desc); + cookie = dma_cookie_assign(tx); if (unlikely(list_empty(&chan->chain))) { /* first peer */ diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c index c2914330b8fc..96d0a4fe8dd9 100644 --- a/drivers/dma/shdma.c +++ b/drivers/dma/shdma.c @@ -298,13 +298,7 @@ static dma_cookie_t sh_dmae_tx_submit(struct dma_async_tx_descriptor *tx) else power_up = false; - cookie = sh_chan->common.cookie; - cookie++; - if (cookie < 0) - cookie = 1; - - sh_chan->common.cookie = cookie; - tx->cookie = cookie; + cookie = dma_cookie_assign(tx); /* Mark all chunks of this descriptor as submitted, move to the queue */ list_for_each_entry_safe(chunk, c, desc->node.prev, node) { diff --git a/drivers/dma/sirf-dma.c b/drivers/dma/sirf-dma.c index 60473f00cf1c..7bb154a85332 100644 --- a/drivers/dma/sirf-dma.c +++ b/drivers/dma/sirf-dma.c @@ -257,13 +257,7 @@ static dma_cookie_t sirfsoc_dma_tx_submit(struct dma_async_tx_descriptor *txd) /* Move descriptor to queue */ list_move_tail(&sdesc->node, &schan->queued); - /* Update cookie */ - cookie = schan->chan.cookie + 1; - if (cookie <= 0) - cookie = 1; - - schan->chan.cookie = cookie; - sdesc->desc.cookie = cookie; + cookie = dma_cookie_assign(txd); spin_unlock_irqrestore(&schan->lock, flags); diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 156b98f661a3..23e2edc4afd4 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -1220,21 +1220,14 @@ static dma_cookie_t d40_tx_submit(struct dma_async_tx_descriptor *tx) chan); struct d40_desc *d40d = container_of(tx, struct d40_desc, txd); unsigned long flags; + dma_cookie_t cookie; spin_lock_irqsave(&d40c->lock, flags); - - d40c->chan.cookie++; - - if (d40c->chan.cookie < 0) - d40c->chan.cookie = 1; - - d40d->txd.cookie = d40c->chan.cookie; - + cookie = dma_cookie_assign(tx); d40_desc_queue(d40c, d40d); - spin_unlock_irqrestore(&d40c->lock, flags); - return tx->cookie; + return cookie; } static int d40_start(struct d40_chan *d40c) diff --git a/drivers/dma/timb_dma.c b/drivers/dma/timb_dma.c index 4b61879284d1..b6e83fc27c4e 100644 --- a/drivers/dma/timb_dma.c +++ b/drivers/dma/timb_dma.c @@ -350,12 +350,7 @@ static dma_cookie_t td_tx_submit(struct dma_async_tx_descriptor *txd) dma_cookie_t cookie; spin_lock_bh(&td_chan->lock); - - cookie = txd->chan->cookie; - if (++cookie < 0) - cookie = 1; - txd->chan->cookie = cookie; - txd->cookie = cookie; + cookie = dma_cookie_assign(txd); if (list_empty(&td_chan->active_list)) { dev_dbg(chan2dev(txd->chan), "%s: started %u\n", __func__, diff --git a/drivers/dma/txx9dmac.c b/drivers/dma/txx9dmac.c index db6d809d4d04..66f8fca1bd3c 100644 --- a/drivers/dma/txx9dmac.c +++ b/drivers/dma/txx9dmac.c @@ -281,21 +281,6 @@ static void txx9dmac_desc_put(struct txx9dmac_chan *dc, } } -/* Called with dc->lock held and bh disabled */ -static dma_cookie_t -txx9dmac_assign_cookie(struct txx9dmac_chan *dc, struct txx9dmac_desc *desc) -{ - dma_cookie_t cookie = dc->chan.cookie; - - if (++cookie < 0) - cookie = 1; - - dc->chan.cookie = cookie; - desc->txd.cookie = cookie; - - return cookie; -} - /*----------------------------------------------------------------------*/ static void txx9dmac_dump_regs(struct txx9dmac_chan *dc) @@ -740,7 +725,7 @@ static dma_cookie_t txx9dmac_tx_submit(struct dma_async_tx_descriptor *tx) dma_cookie_t cookie; spin_lock_bh(&dc->lock); - cookie = txx9dmac_assign_cookie(dc, desc); + cookie = dma_cookie_assign(tx); dev_vdbg(chan2dev(tx->chan), "tx_submit: queued %u %p\n", desc->txd.cookie, desc); From f7fbce07c6ce26a25b4e0cb5f241c361fde87901 Mon Sep 17 00:00:00 2001 From: Russell King - ARM Linux Date: Tue, 6 Mar 2012 22:35:07 +0000 Subject: [PATCH 41/67] dmaengine: provide a common function for completing a dma descriptor Provide a common function to do the cookie mechanics for completing a DMA descriptor. Signed-off-by: Russell King Tested-by: Linus Walleij Reviewed-by: Linus Walleij Acked-by: Jassi Brar [imx-sdma.c & mxs-dma.c] Tested-by: Shawn Guo Signed-off-by: Vinod Koul --- drivers/dma/amba-pl08x.c | 2 +- drivers/dma/at_hdmac.c | 2 +- drivers/dma/coh901318.c | 2 +- drivers/dma/dmaengine.h | 18 ++++++++++++++++++ drivers/dma/dw_dmac.c | 2 +- drivers/dma/ep93xx_dma.c | 2 +- drivers/dma/fsldma.c | 2 +- drivers/dma/imx-dma.c | 2 +- drivers/dma/imx-sdma.c | 2 +- drivers/dma/intel_mid_dma.c | 2 +- drivers/dma/ioat/dma.c | 3 +-- drivers/dma/ioat/dma_v2.c | 3 +-- drivers/dma/ioat/dma_v3.c | 3 +-- drivers/dma/ipu/ipu_idmac.c | 2 +- drivers/dma/mxs-dma.c | 2 +- drivers/dma/pl330.c | 2 +- drivers/dma/ste_dma40.c | 2 +- drivers/dma/timb_dma.c | 2 +- drivers/dma/txx9dmac.c | 2 +- 19 files changed, 36 insertions(+), 21 deletions(-) diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index d8d3dc273f29..346327572cfb 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -1542,7 +1542,7 @@ static void pl08x_tasklet(unsigned long data) if (txd) { /* Update last completed */ - plchan->chan.completed_cookie = txd->tx.cookie; + dma_cookie_complete(&txd->tx); } /* If a new descriptor is queued, set it up plchan->at is NULL here */ diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index df47e7d6164b..b2826304da24 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -249,7 +249,7 @@ atc_chain_complete(struct at_dma_chan *atchan, struct at_desc *desc) dev_vdbg(chan2dev(&atchan->chan_common), "descriptor %u complete\n", txd->cookie); - atchan->chan_common.completed_cookie = txd->cookie; + dma_cookie_complete(txd); /* move children to free_list */ list_splice_init(&desc->tx_list, &atchan->free_list); diff --git a/drivers/dma/coh901318.c b/drivers/dma/coh901318.c index 843a1a3b8a81..24837d700951 100644 --- a/drivers/dma/coh901318.c +++ b/drivers/dma/coh901318.c @@ -691,7 +691,7 @@ static void dma_tasklet(unsigned long data) callback_param = cohd_fin->desc.callback_param; /* sign this job as completed on the channel */ - cohc->chan.completed_cookie = cohd_fin->desc.cookie; + dma_cookie_complete(&cohd_fin->desc); /* release the lli allocation and remove the descriptor */ coh901318_lli_free(&cohc->base->pool, &cohd_fin->lli); diff --git a/drivers/dma/dmaengine.h b/drivers/dma/dmaengine.h index 7692c8644045..47e099772b8e 100644 --- a/drivers/dma/dmaengine.h +++ b/drivers/dma/dmaengine.h @@ -5,6 +5,7 @@ #ifndef DMAENGINE_H #define DMAENGINE_H +#include #include /** @@ -27,4 +28,21 @@ static inline dma_cookie_t dma_cookie_assign(struct dma_async_tx_descriptor *tx) return cookie; } +/** + * dma_cookie_complete - complete a descriptor + * @tx: descriptor to complete + * + * Mark this descriptor complete by updating the channels completed + * cookie marker. Zero the descriptors cookie to prevent accidental + * repeated completions. + * + * Note: caller is expected to hold a lock to prevent concurrency. + */ +static inline void dma_cookie_complete(struct dma_async_tx_descriptor *tx) +{ + BUG_ON(tx->cookie < DMA_MIN_COOKIE); + tx->chan->completed_cookie = tx->cookie; + tx->cookie = 0; +} + #endif diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 3a4ca67ace02..12ea60b2a1be 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -235,7 +235,7 @@ dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc, dev_vdbg(chan2dev(&dwc->chan), "descriptor %u complete\n", txd->cookie); spin_lock_irqsave(&dwc->lock, flags); - dwc->chan.completed_cookie = txd->cookie; + dma_cookie_complete(txd); if (callback_required) { callback = txd->callback; param = txd->callback_param; diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c index e5aaae87ddfb..1c56f75d9faf 100644 --- a/drivers/dma/ep93xx_dma.c +++ b/drivers/dma/ep93xx_dma.c @@ -703,7 +703,7 @@ static void ep93xx_dma_tasklet(unsigned long data) desc = ep93xx_dma_get_active(edmac); if (desc) { if (desc->complete) { - edmac->chan.completed_cookie = desc->txd.cookie; + dma_cookie_complete(&desc->txd); list_splice_init(&edmac->active, &list); } callback = desc->txd.callback; diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index 04b4347ba4e9..f36e8b18cba2 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -1081,8 +1081,8 @@ static void dma_do_tasklet(unsigned long data) desc = to_fsl_desc(chan->ld_running.prev); cookie = desc->async_tx.cookie; + dma_cookie_complete(&desc->async_tx); - chan->common.completed_cookie = cookie; chan_dbg(chan, "completed_cookie=%d\n", cookie); } diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index 687fc687aaf6..9a3cbac3d695 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -66,7 +66,7 @@ static void imxdma_handle(struct imxdma_channel *imxdmac) { if (imxdmac->desc.callback) imxdmac->desc.callback(imxdmac->desc.callback_param); - imxdmac->chan.completed_cookie = imxdmac->desc.cookie; + dma_cookie_complete(&imxdmac->desc); } static void imxdma_irq_handler(int channel, void *data) diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 3f0c002933f3..981071ebd5c8 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -530,7 +530,7 @@ static void mxc_sdma_handle_channel_normal(struct sdma_channel *sdmac) else sdmac->status = DMA_SUCCESS; - sdmac->chan.completed_cookie = sdmac->desc.cookie; + dma_cookie_complete(&sdmac->desc); if (sdmac->desc.callback) sdmac->desc.callback(sdmac->desc.callback_param); } diff --git a/drivers/dma/intel_mid_dma.c b/drivers/dma/intel_mid_dma.c index e9217c390b76..cee79f6e035d 100644 --- a/drivers/dma/intel_mid_dma.c +++ b/drivers/dma/intel_mid_dma.c @@ -290,7 +290,7 @@ static void midc_descriptor_complete(struct intel_mid_dma_chan *midc, struct intel_mid_dma_lli *llitem; void *param_txd = NULL; - midc->chan.completed_cookie = txd->cookie; + dma_cookie_complete(txd); callback_txd = txd->callback; param_txd = txd->callback_param; diff --git a/drivers/dma/ioat/dma.c b/drivers/dma/ioat/dma.c index 5c06117ac682..b0517c86c1bb 100644 --- a/drivers/dma/ioat/dma.c +++ b/drivers/dma/ioat/dma.c @@ -600,8 +600,7 @@ static void __cleanup(struct ioat_dma_chan *ioat, unsigned long phys_complete) */ dump_desc_dbg(ioat, desc); if (tx->cookie) { - chan->common.completed_cookie = tx->cookie; - tx->cookie = 0; + dma_cookie_complete(tx); ioat_dma_unmap(chan, tx->flags, desc->len, desc->hw); ioat->active -= desc->hw->tx_cnt; if (tx->callback) { diff --git a/drivers/dma/ioat/dma_v2.c b/drivers/dma/ioat/dma_v2.c index 17ecacb70d40..e8e110ff3d96 100644 --- a/drivers/dma/ioat/dma_v2.c +++ b/drivers/dma/ioat/dma_v2.c @@ -149,8 +149,7 @@ static void __cleanup(struct ioat2_dma_chan *ioat, unsigned long phys_complete) dump_desc_dbg(ioat, desc); if (tx->cookie) { ioat_dma_unmap(chan, tx->flags, desc->len, desc->hw); - chan->common.completed_cookie = tx->cookie; - tx->cookie = 0; + dma_cookie_complete(tx); if (tx->callback) { tx->callback(tx->callback_param); tx->callback = NULL; diff --git a/drivers/dma/ioat/dma_v3.c b/drivers/dma/ioat/dma_v3.c index d4afac741e8a..1bda46c43bd7 100644 --- a/drivers/dma/ioat/dma_v3.c +++ b/drivers/dma/ioat/dma_v3.c @@ -277,9 +277,8 @@ static void __cleanup(struct ioat2_dma_chan *ioat, unsigned long phys_complete) dump_desc_dbg(ioat, desc); tx = &desc->txd; if (tx->cookie) { - chan->common.completed_cookie = tx->cookie; + dma_cookie_complete(tx); ioat3_dma_unmap(ioat, desc, idx + i); - tx->cookie = 0; if (tx->callback) { tx->callback(tx->callback_param); tx->callback = NULL; diff --git a/drivers/dma/ipu/ipu_idmac.c b/drivers/dma/ipu/ipu_idmac.c index d4620c53fd2d..bff9250a59ce 100644 --- a/drivers/dma/ipu/ipu_idmac.c +++ b/drivers/dma/ipu/ipu_idmac.c @@ -1289,7 +1289,7 @@ static irqreturn_t idmac_interrupt(int irq, void *dev_id) /* Flip the active buffer - even if update above failed */ ichan->active_buffer = !ichan->active_buffer; if (done) - ichan->dma_chan.completed_cookie = desc->txd.cookie; + dma_cookie_complete(&desc->txd); callback = desc->txd.callback; callback_param = desc->txd.callback_param; diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index 4d3b6ff3050f..5f3492e5d28f 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c @@ -262,7 +262,7 @@ static irqreturn_t mxs_dma_int_handler(int irq, void *dev_id) stat1 &= ~(1 << channel); if (mxs_chan->status == DMA_SUCCESS) - mxs_chan->chan.completed_cookie = mxs_chan->desc.cookie; + dma_cookie_complete(&mxs_chan->desc); /* schedule tasklet on this channel */ tasklet_schedule(&mxs_chan->tasklet); diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 644eb789958b..a81d0a5f8191 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -233,7 +233,7 @@ static void pl330_tasklet(unsigned long data) /* Pick up ripe tomatoes */ list_for_each_entry_safe(desc, _dt, &pch->work_list, node) if (desc->status == DONE) { - pch->chan.completed_cookie = desc->txd.cookie; + dma_cookie_complete(&desc->txd); list_move_tail(&desc->node, &list); } diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 23e2edc4afd4..c2463758fed1 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -1347,7 +1347,7 @@ static void dma_tasklet(unsigned long data) goto err; if (!d40d->cyclic) - d40c->chan.completed_cookie = d40d->txd.cookie; + dma_cookie_complete(&d40d->txd); /* * If terminating a channel pending_tx is set to zero. diff --git a/drivers/dma/timb_dma.c b/drivers/dma/timb_dma.c index b6e83fc27c4e..1845ac9d6e88 100644 --- a/drivers/dma/timb_dma.c +++ b/drivers/dma/timb_dma.c @@ -285,7 +285,7 @@ static void __td_finish(struct timb_dma_chan *td_chan) else iowrite32(0, td_chan->membase + TIMBDMA_OFFS_TX_DLAR); */ - td_chan->chan.completed_cookie = txd->cookie; + dma_cookie_complete(txd); td_chan->ongoing = false; callback = txd->callback; diff --git a/drivers/dma/txx9dmac.c b/drivers/dma/txx9dmac.c index 66f8fca1bd3c..8a5225bf9bc9 100644 --- a/drivers/dma/txx9dmac.c +++ b/drivers/dma/txx9dmac.c @@ -411,7 +411,7 @@ txx9dmac_descriptor_complete(struct txx9dmac_chan *dc, dev_vdbg(chan2dev(&dc->chan), "descriptor %u %p complete\n", txd->cookie, desc); - dc->chan.completed_cookie = txd->cookie; + dma_cookie_complete(txd); callback = txd->callback; param = txd->callback_param; From 96a2af41c78b1fbb1f567a3486bdc63f7b31c5fd Mon Sep 17 00:00:00 2001 From: Russell King - ARM Linux Date: Tue, 6 Mar 2012 22:35:27 +0000 Subject: [PATCH 42/67] dmaengine: consolidate tx_status functions Now that we have the completed cookie in the dma_chan structure, we can consolidate the tx_status functions by providing a function to set the txstate structure and returning the DMA status. We also provide a separate helper to set the residue for cookies which are still in progress. Signed-off-by: Russell King Tested-by: Linus Walleij Reviewed-by: Linus Walleij Acked-by: Jassi Brar [imx-sdma.c & mxs-dma.c] Tested-by: Shawn Guo Signed-off-by: Vinod Koul --- drivers/dma/amba-pl08x.c | 22 ++++------------------ drivers/dma/at_hdmac.c | 18 ++++++------------ drivers/dma/coh901318.c | 13 ++++--------- drivers/dma/dmaengine.h | 31 +++++++++++++++++++++++++++++++ drivers/dma/dw_dmac.c | 19 ++++--------------- drivers/dma/ep93xx_dma.c | 7 +------ drivers/dma/fsldma.c | 11 +++-------- drivers/dma/imx-dma.c | 11 +---------- drivers/dma/intel_mid_dma.c | 19 +++---------------- drivers/dma/ioat/dma.c | 8 +++++--- drivers/dma/ioat/dma.h | 21 --------------------- drivers/dma/ioat/dma_v3.c | 8 +++++--- drivers/dma/iop-adma.c | 14 ++------------ drivers/dma/mpc512x_dma.c | 9 +++------ drivers/dma/mv_xor.c | 14 ++------------ drivers/dma/pch_dma.c | 11 ++--------- drivers/dma/pl330.c | 13 +------------ drivers/dma/ppc4xx/adma.c | 16 ++-------------- drivers/dma/shdma.c | 11 +---------- drivers/dma/sirf-dma.c | 9 +++------ drivers/dma/ste_dma40.c | 14 ++++---------- drivers/dma/timb_dma.c | 11 ++--------- drivers/dma/txx9dmac.c | 16 +++------------- 23 files changed, 92 insertions(+), 234 deletions(-) diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 346327572cfb..810f696eda4e 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -964,31 +964,17 @@ static enum dma_status pl08x_dma_tx_status(struct dma_chan *chan, dma_cookie_t cookie, struct dma_tx_state *txstate) { struct pl08x_dma_chan *plchan = to_pl08x_chan(chan); - dma_cookie_t last_used; - dma_cookie_t last_complete; enum dma_status ret; - u32 bytesleft = 0; - last_used = plchan->chan.cookie; - last_complete = plchan->chan.completed_cookie; - - ret = dma_async_is_complete(cookie, last_complete, last_used); - if (ret == DMA_SUCCESS) { - dma_set_tx_state(txstate, last_complete, last_used, 0); + ret = dma_cookie_status(chan, cookie, txstate); + if (ret == DMA_SUCCESS) return ret; - } /* * This cookie not complete yet + * Get number of bytes left in the active transactions and queue */ - last_used = plchan->chan.cookie; - last_complete = plchan->chan.completed_cookie; - - /* Get number of bytes left in the active transactions and queue */ - bytesleft = pl08x_getbytes_chan(plchan); - - dma_set_tx_state(txstate, last_complete, last_used, - bytesleft); + dma_set_residue(txstate, pl08x_getbytes_chan(plchan)); if (plchan->state == PL08X_CHAN_PAUSED) return DMA_PAUSED; diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index b2826304da24..8a3297418cf0 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -996,26 +996,20 @@ atc_tx_status(struct dma_chan *chan, spin_lock_irqsave(&atchan->lock, flags); - last_complete = chan->completed_cookie; - last_used = chan->cookie; - - ret = dma_async_is_complete(cookie, last_complete, last_used); + ret = dma_cookie_status(chan, cookie, txstate); if (ret != DMA_SUCCESS) { atc_cleanup_descriptors(atchan); - last_complete = chan->completed_cookie; - last_used = chan->cookie; - - ret = dma_async_is_complete(cookie, last_complete, last_used); + ret = dma_cookie_status(chan, cookie, txstate); } + last_complete = chan->completed_cookie; + last_used = chan->cookie; + spin_unlock_irqrestore(&atchan->lock, flags); if (ret != DMA_SUCCESS) - dma_set_tx_state(txstate, last_complete, last_used, - atc_first_active(atchan)->len); - else - dma_set_tx_state(txstate, last_complete, last_used, 0); + dma_set_residue(txstate, atc_first_active(atchan)->len); if (atc_chan_is_paused(atchan)) ret = DMA_PAUSED; diff --git a/drivers/dma/coh901318.c b/drivers/dma/coh901318.c index 24837d700951..f3505178ff41 100644 --- a/drivers/dma/coh901318.c +++ b/drivers/dma/coh901318.c @@ -1151,17 +1151,12 @@ coh901318_tx_status(struct dma_chan *chan, dma_cookie_t cookie, struct dma_tx_state *txstate) { struct coh901318_chan *cohc = to_coh901318_chan(chan); - dma_cookie_t last_used; - dma_cookie_t last_complete; - int ret; + enum dma_status ret; - last_complete = chan->completed_cookie; - last_used = chan->cookie; + ret = dma_cookie_status(chan, cookie, txstate); + /* FIXME: should be conditional on ret != DMA_SUCCESS? */ + dma_set_residue(txstate, coh901318_get_bytes_left(chan)); - ret = dma_async_is_complete(cookie, last_complete, last_used); - - dma_set_tx_state(txstate, last_complete, last_used, - coh901318_get_bytes_left(chan)); if (ret == DMA_IN_PROGRESS && cohc->stopped) ret = DMA_PAUSED; diff --git a/drivers/dma/dmaengine.h b/drivers/dma/dmaengine.h index 47e099772b8e..1ca5e0e633f4 100644 --- a/drivers/dma/dmaengine.h +++ b/drivers/dma/dmaengine.h @@ -45,4 +45,35 @@ static inline void dma_cookie_complete(struct dma_async_tx_descriptor *tx) tx->cookie = 0; } +/** + * dma_cookie_status - report cookie status + * @chan: dma channel + * @cookie: cookie we are interested in + * @state: dma_tx_state structure to return last/used cookies + * + * Report the status of the cookie, filling in the state structure if + * non-NULL. No locking is required. + */ +static inline enum dma_status dma_cookie_status(struct dma_chan *chan, + dma_cookie_t cookie, struct dma_tx_state *state) +{ + dma_cookie_t used, complete; + + used = chan->cookie; + complete = chan->completed_cookie; + barrier(); + if (state) { + state->last = complete; + state->used = used; + state->residue = 0; + } + return dma_async_is_complete(cookie, complete, used); +} + +static inline void dma_set_residue(struct dma_tx_state *state, u32 residue) +{ + if (state) + state->residue = residue; +} + #endif diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 12ea60b2a1be..33bde5da850d 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -979,28 +979,17 @@ dwc_tx_status(struct dma_chan *chan, struct dma_tx_state *txstate) { struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - dma_cookie_t last_used; - dma_cookie_t last_complete; - int ret; + enum dma_status ret; - last_complete = chan->completed_cookie; - last_used = chan->cookie; - - ret = dma_async_is_complete(cookie, last_complete, last_used); + ret = dma_cookie_status(chan, cookie, txstate); if (ret != DMA_SUCCESS) { dwc_scan_descriptors(to_dw_dma(chan->device), dwc); - last_complete = chan->completed_cookie; - last_used = chan->cookie; - - ret = dma_async_is_complete(cookie, last_complete, last_used); + ret = dma_cookie_status(chan, cookie, txstate); } if (ret != DMA_SUCCESS) - dma_set_tx_state(txstate, last_complete, last_used, - dwc_first_active(dwc)->len); - else - dma_set_tx_state(txstate, last_complete, last_used, 0); + dma_set_residue(txstate, dwc_first_active(dwc)->len); if (dwc->paused) return DMA_PAUSED; diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c index 1c56f75d9faf..142ebf0cd316 100644 --- a/drivers/dma/ep93xx_dma.c +++ b/drivers/dma/ep93xx_dma.c @@ -1241,18 +1241,13 @@ static enum dma_status ep93xx_dma_tx_status(struct dma_chan *chan, struct dma_tx_state *state) { struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan); - dma_cookie_t last_used, last_completed; enum dma_status ret; unsigned long flags; spin_lock_irqsave(&edmac->lock, flags); - last_used = chan->cookie; - last_completed = chan->completed_cookie; + ret = dma_cookie_status(chan, cookie, state); spin_unlock_irqrestore(&edmac->lock, flags); - ret = dma_async_is_complete(cookie, last_completed, last_used); - dma_set_tx_state(state, last_completed, last_used, 0); - return ret; } diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index f36e8b18cba2..2f6c806126e2 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -978,19 +978,14 @@ static enum dma_status fsl_tx_status(struct dma_chan *dchan, struct dma_tx_state *txstate) { struct fsldma_chan *chan = to_fsl_chan(dchan); - dma_cookie_t last_complete; - dma_cookie_t last_used; + enum dma_status ret; unsigned long flags; spin_lock_irqsave(&chan->desc_lock, flags); - - last_complete = dchan->completed_cookie; - last_used = dchan->cookie; - + ret = dma_cookie_status(dchan, cookie, txstate); spin_unlock_irqrestore(&chan->desc_lock, flags); - dma_set_tx_state(txstate, last_complete, last_used, 0); - return dma_async_is_complete(cookie, last_complete, last_used); + return ret; } /*----------------------------------------------------------------------------*/ diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index 9a3cbac3d695..6731f1918c55 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -153,16 +153,7 @@ static enum dma_status imxdma_tx_status(struct dma_chan *chan, dma_cookie_t cookie, struct dma_tx_state *txstate) { - struct imxdma_channel *imxdmac = to_imxdma_chan(chan); - dma_cookie_t last_used; - enum dma_status ret; - - last_used = chan->cookie; - - ret = dma_async_is_complete(cookie, chan->completed_cookie, last_used); - dma_set_tx_state(txstate, chan->completed_cookie, last_used, 0); - - return ret; + return dma_cookie_status(chan, cookie, txstate); } static dma_cookie_t imxdma_tx_submit(struct dma_async_tx_descriptor *tx) diff --git a/drivers/dma/intel_mid_dma.c b/drivers/dma/intel_mid_dma.c index cee79f6e035d..c9ab4ac18e41 100644 --- a/drivers/dma/intel_mid_dma.c +++ b/drivers/dma/intel_mid_dma.c @@ -477,30 +477,17 @@ static enum dma_status intel_mid_dma_tx_status(struct dma_chan *chan, dma_cookie_t cookie, struct dma_tx_state *txstate) { - dma_cookie_t last_used; - dma_cookie_t last_complete; - int ret; + enum dma_status ret; - last_complete = chan->completed_cookie; - last_used = chan->cookie; - - ret = dma_async_is_complete(cookie, last_complete, last_used); + ret = dma_cookie_status(chan, cookie, txstate); if (ret != DMA_SUCCESS) { spin_lock_bh(&midc->lock); midc_scan_descriptors(to_middma_device(chan->device), midc); spin_unlock_bh(&midc->lock); - last_complete = chan->completed_cookie; - last_used = chan->cookie; - - ret = dma_async_is_complete(cookie, last_complete, last_used); + ret = dma_cookie_status(chan, cookie, txstate); } - if (txstate) { - txstate->last = last_complete; - txstate->used = last_used; - txstate->residue = 0; - } return ret; } diff --git a/drivers/dma/ioat/dma.c b/drivers/dma/ioat/dma.c index b0517c86c1bb..97e100ce43eb 100644 --- a/drivers/dma/ioat/dma.c +++ b/drivers/dma/ioat/dma.c @@ -729,13 +729,15 @@ ioat_dma_tx_status(struct dma_chan *c, dma_cookie_t cookie, { struct ioat_chan_common *chan = to_chan_common(c); struct ioatdma_device *device = chan->device; + enum dma_status ret; - if (ioat_tx_status(c, cookie, txstate) == DMA_SUCCESS) - return DMA_SUCCESS; + ret = dma_cookie_status(c, cookie, txstate); + if (ret == DMA_SUCCESS) + return ret; device->cleanup_fn((unsigned long) c); - return ioat_tx_status(c, cookie, txstate); + return dma_cookie_status(c, cookie, txstate); } static void ioat1_dma_start_null_desc(struct ioat_dma_chan *ioat) diff --git a/drivers/dma/ioat/dma.h b/drivers/dma/ioat/dma.h index 9653b6b6a715..c7888bccd974 100644 --- a/drivers/dma/ioat/dma.h +++ b/drivers/dma/ioat/dma.h @@ -142,27 +142,6 @@ static inline struct ioat_dma_chan *to_ioat_chan(struct dma_chan *c) return container_of(chan, struct ioat_dma_chan, base); } -/** - * ioat_tx_status - poll the status of an ioat transaction - * @c: channel handle - * @cookie: transaction identifier - * @txstate: if set, updated with the transaction state - */ -static inline enum dma_status -ioat_tx_status(struct dma_chan *c, dma_cookie_t cookie, - struct dma_tx_state *txstate) -{ - dma_cookie_t last_used; - dma_cookie_t last_complete; - - last_used = c->cookie; - last_complete = c->completed_cookie; - - dma_set_tx_state(txstate, last_complete, last_used, 0); - - return dma_async_is_complete(cookie, last_complete, last_used); -} - /* wrapper around hardware descriptor format + additional software fields */ /** diff --git a/drivers/dma/ioat/dma_v3.c b/drivers/dma/ioat/dma_v3.c index 1bda46c43bd7..145eda241dee 100644 --- a/drivers/dma/ioat/dma_v3.c +++ b/drivers/dma/ioat/dma_v3.c @@ -410,13 +410,15 @@ ioat3_tx_status(struct dma_chan *c, dma_cookie_t cookie, struct dma_tx_state *txstate) { struct ioat2_dma_chan *ioat = to_ioat2_chan(c); + enum dma_status ret; - if (ioat_tx_status(c, cookie, txstate) == DMA_SUCCESS) - return DMA_SUCCESS; + ret = dma_cookie_status(c, cookie, txstate); + if (ret == DMA_SUCCESS) + return ret; ioat3_cleanup(ioat); - return ioat_tx_status(c, cookie, txstate); + return dma_cookie_status(c, cookie, txstate); } static struct dma_async_tx_descriptor * diff --git a/drivers/dma/iop-adma.c b/drivers/dma/iop-adma.c index f2392d59568d..b1e3be089c52 100644 --- a/drivers/dma/iop-adma.c +++ b/drivers/dma/iop-adma.c @@ -894,24 +894,14 @@ static enum dma_status iop_adma_status(struct dma_chan *chan, struct dma_tx_state *txstate) { struct iop_adma_chan *iop_chan = to_iop_adma_chan(chan); - dma_cookie_t last_used; - dma_cookie_t last_complete; - enum dma_status ret; - last_used = chan->cookie; - last_complete = chan->completed_cookie; - dma_set_tx_state(txstate, last_complete, last_used, 0); - ret = dma_async_is_complete(cookie, last_complete, last_used); + ret = dma_cookie_status(chan, cookie, txstate); if (ret == DMA_SUCCESS) return ret; iop_adma_slot_cleanup(iop_chan); - last_used = chan->cookie; - last_complete = chan->completed_cookie; - dma_set_tx_state(txstate, last_complete, last_used, 0); - - return dma_async_is_complete(cookie, last_complete, last_used); + return dma_cookie_status(chan, cookie, txstate); } static irqreturn_t iop_adma_eot_handler(int irq, void *data) diff --git a/drivers/dma/mpc512x_dma.c b/drivers/dma/mpc512x_dma.c index 0253d5aecdb1..138271591ae9 100644 --- a/drivers/dma/mpc512x_dma.c +++ b/drivers/dma/mpc512x_dma.c @@ -557,17 +557,14 @@ mpc_dma_tx_status(struct dma_chan *chan, dma_cookie_t cookie, struct dma_tx_state *txstate) { struct mpc_dma_chan *mchan = dma_chan_to_mpc_dma_chan(chan); + enum dma_status ret; unsigned long flags; - dma_cookie_t last_used; - dma_cookie_t last_complete; spin_lock_irqsave(&mchan->lock, flags); - last_used = mchan->chan.cookie; - last_complete = mchan->chan.completed_cookie; + ret = dma_cookie_status(chan, cookie, txstate); spin_unlock_irqrestore(&mchan->lock, flags); - dma_set_tx_state(txstate, last_complete, last_used, 0); - return dma_async_is_complete(cookie, last_complete, last_used); + return ret; } /* Prepare descriptor for memory to memory copy */ diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c index d9810ce3794c..486353e60a0a 100644 --- a/drivers/dma/mv_xor.c +++ b/drivers/dma/mv_xor.c @@ -810,26 +810,16 @@ static enum dma_status mv_xor_status(struct dma_chan *chan, struct dma_tx_state *txstate) { struct mv_xor_chan *mv_chan = to_mv_xor_chan(chan); - dma_cookie_t last_used; - dma_cookie_t last_complete; enum dma_status ret; - last_used = chan->cookie; - last_complete = chan->completed_cookie; - dma_set_tx_state(txstate, last_complete, last_used, 0); - - ret = dma_async_is_complete(cookie, last_complete, last_used); + ret = dma_cookie_status(chan, cookie, txstate); if (ret == DMA_SUCCESS) { mv_xor_clean_completed_slots(mv_chan); return ret; } mv_xor_slot_cleanup(mv_chan); - last_used = chan->cookie; - last_complete = chan->completed_cookie; - - dma_set_tx_state(txstate, last_complete, last_used, 0); - return dma_async_is_complete(cookie, last_complete, last_used); + return dma_cookie_status(chan, cookie, txstate); } static void mv_dump_xor_regs(struct mv_xor_chan *chan) diff --git a/drivers/dma/pch_dma.c b/drivers/dma/pch_dma.c index 5218e48aed0e..c30f63ee32c5 100644 --- a/drivers/dma/pch_dma.c +++ b/drivers/dma/pch_dma.c @@ -565,19 +565,12 @@ static enum dma_status pd_tx_status(struct dma_chan *chan, dma_cookie_t cookie, struct dma_tx_state *txstate) { struct pch_dma_chan *pd_chan = to_pd_chan(chan); - dma_cookie_t last_used; - dma_cookie_t last_completed; - int ret; + enum dma_status ret; spin_lock_irq(&pd_chan->lock); - last_completed = chan->completed_cookie; - last_used = chan->cookie; + ret = dma_cookie_status(chan, cookie, txstate); spin_unlock_irq(&pd_chan->lock); - ret = dma_async_is_complete(cookie, last_completed, last_used); - - dma_set_tx_state(txstate, last_completed, last_used, 0); - return ret; } diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index a81d0a5f8191..d43019fc3496 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -395,18 +395,7 @@ static enum dma_status pl330_tx_status(struct dma_chan *chan, dma_cookie_t cookie, struct dma_tx_state *txstate) { - struct dma_pl330_chan *pch = to_pchan(chan); - dma_cookie_t last_done, last_used; - int ret; - - last_done = chan->completed_cookie; - last_used = chan->cookie; - - ret = dma_async_is_complete(cookie, last_done, last_used); - - dma_set_tx_state(txstate, last_done, last_used, 0); - - return ret; + return dma_cookie_status(chan, cookie, txstate); } static void pl330_issue_pending(struct dma_chan *chan) diff --git a/drivers/dma/ppc4xx/adma.c b/drivers/dma/ppc4xx/adma.c index 12e94dd6fc3d..86239ea01898 100644 --- a/drivers/dma/ppc4xx/adma.c +++ b/drivers/dma/ppc4xx/adma.c @@ -3928,28 +3928,16 @@ static enum dma_status ppc440spe_adma_tx_status(struct dma_chan *chan, dma_cookie_t cookie, struct dma_tx_state *txstate) { struct ppc440spe_adma_chan *ppc440spe_chan; - dma_cookie_t last_used; - dma_cookie_t last_complete; enum dma_status ret; ppc440spe_chan = to_ppc440spe_adma_chan(chan); - last_used = chan->cookie; - last_complete = chan->completed_cookie; - - dma_set_tx_state(txstate, last_complete, last_used, 0); - - ret = dma_async_is_complete(cookie, last_complete, last_used); + ret = dma_cookie_status(chan, cookie, txstate); if (ret == DMA_SUCCESS) return ret; ppc440spe_adma_slot_cleanup(ppc440spe_chan); - last_used = chan->cookie; - last_complete = chan->completed_cookie; - - dma_set_tx_state(txstate, last_complete, last_used, 0); - - return dma_async_is_complete(cookie, last_complete, last_used); + return dma_cookie_status(chan, cookie, txstate); } /** diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c index 96d0a4fe8dd9..50510ef7db72 100644 --- a/drivers/dma/shdma.c +++ b/drivers/dma/shdma.c @@ -879,23 +879,14 @@ static enum dma_status sh_dmae_tx_status(struct dma_chan *chan, struct dma_tx_state *txstate) { struct sh_dmae_chan *sh_chan = to_sh_chan(chan); - dma_cookie_t last_used; - dma_cookie_t last_complete; enum dma_status status; unsigned long flags; sh_dmae_chan_ld_cleanup(sh_chan, false); - /* First read completed cookie to avoid a skew */ - last_complete = chan->completed_cookie; - rmb(); - last_used = chan->cookie; - BUG_ON(last_complete < 0); - dma_set_tx_state(txstate, last_complete, last_used, 0); - spin_lock_irqsave(&sh_chan->desc_lock, flags); - status = dma_async_is_complete(cookie, last_complete, last_used); + status = dma_cookie_status(chan, cookie, txstate); /* * If we don't find cookie on the queue, it has been aborted and we have diff --git a/drivers/dma/sirf-dma.c b/drivers/dma/sirf-dma.c index 7bb154a85332..a760d981ece0 100644 --- a/drivers/dma/sirf-dma.c +++ b/drivers/dma/sirf-dma.c @@ -407,16 +407,13 @@ sirfsoc_dma_tx_status(struct dma_chan *chan, dma_cookie_t cookie, { struct sirfsoc_dma_chan *schan = dma_chan_to_sirfsoc_dma_chan(chan); unsigned long flags; - dma_cookie_t last_used; - dma_cookie_t last_complete; + enum dma_status ret; spin_lock_irqsave(&schan->lock, flags); - last_used = schan->chan.cookie; - last_complete = schan->chan.completed_cookie; + ret = dma_cookie_status(chan, cookie, txstate); spin_unlock_irqrestore(&schan->lock, flags); - dma_set_tx_state(txstate, last_complete, last_used, 0); - return dma_async_is_complete(cookie, last_complete, last_used); + return ret; } static struct dma_async_tx_descriptor *sirfsoc_dma_prep_interleaved( diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index c2463758fed1..07b82e367ffd 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -2332,25 +2332,19 @@ static enum dma_status d40_tx_status(struct dma_chan *chan, struct dma_tx_state *txstate) { struct d40_chan *d40c = container_of(chan, struct d40_chan, chan); - dma_cookie_t last_used; - dma_cookie_t last_complete; - int ret; + enum dma_status ret; if (d40c->phy_chan == NULL) { chan_err(d40c, "Cannot read status of unallocated channel\n"); return -EINVAL; } - last_complete = chan->completed_cookie; - last_used = chan->cookie; + ret = dma_cookie_status(chan, cookie, txstate); + if (ret != DMA_SUCCESS) + dma_set_residue(txstate, stedma40_residue(chan)); if (d40_is_paused(d40c)) ret = DMA_PAUSED; - else - ret = dma_async_is_complete(cookie, last_complete, last_used); - - dma_set_tx_state(txstate, last_complete, last_used, - stedma40_residue(chan)); return ret; } diff --git a/drivers/dma/timb_dma.c b/drivers/dma/timb_dma.c index 1845ac9d6e88..6383abbecce6 100644 --- a/drivers/dma/timb_dma.c +++ b/drivers/dma/timb_dma.c @@ -513,18 +513,11 @@ static enum dma_status td_tx_status(struct dma_chan *chan, dma_cookie_t cookie, { struct timb_dma_chan *td_chan = container_of(chan, struct timb_dma_chan, chan); - dma_cookie_t last_used; - dma_cookie_t last_complete; - int ret; + enum dma_status ret; dev_dbg(chan2dev(chan), "%s: Entry\n", __func__); - last_complete = chan->completed_cookie; - last_used = chan->cookie; - - ret = dma_async_is_complete(cookie, last_complete, last_used); - - dma_set_tx_state(txstate, last_complete, last_used, 0); + ret = dma_cookie_status(chan, cookie, txstate); dev_dbg(chan2dev(chan), "%s: exit, ret: %d, last_complete: %d, last_used: %d\n", diff --git a/drivers/dma/txx9dmac.c b/drivers/dma/txx9dmac.c index 8a5225bf9bc9..bb7b3d96ac6f 100644 --- a/drivers/dma/txx9dmac.c +++ b/drivers/dma/txx9dmac.c @@ -959,27 +959,17 @@ txx9dmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie, struct dma_tx_state *txstate) { struct txx9dmac_chan *dc = to_txx9dmac_chan(chan); - dma_cookie_t last_used; - dma_cookie_t last_complete; - int ret; + enum dma_status ret; - last_complete = chan->completed_cookie; - last_used = chan->cookie; - - ret = dma_async_is_complete(cookie, last_complete, last_used); + ret = dma_cookie_status(chan, cookie, txstate); if (ret != DMA_SUCCESS) { spin_lock_bh(&dc->lock); txx9dmac_scan_descriptors(dc); spin_unlock_bh(&dc->lock); - last_complete = chan->completed_cookie; - last_used = chan->cookie; - - ret = dma_async_is_complete(cookie, last_complete, last_used); + ret = dma_cookie_status(chan, cookie, txstate); } - dma_set_tx_state(txstate, last_complete, last_used, 0); - return ret; } From d3ee98cdcd6198ea1cf75c603178acc8a805b69b Mon Sep 17 00:00:00 2001 From: Russell King - ARM Linux Date: Tue, 6 Mar 2012 22:35:47 +0000 Subject: [PATCH 43/67] dmaengine: consolidate initialization of cookies Provide a common function to initialize a channels cookie values. Signed-off-by: Russell King Tested-by: Linus Walleij Reviewed-by: Linus Walleij Acked-by: Jassi Brar [imx-sdma.c & mxs-dma.c] Tested-by: Shawn Guo Signed-off-by: Vinod Koul --- drivers/dma/amba-pl08x.c | 3 +-- drivers/dma/at_hdmac.c | 4 ++-- drivers/dma/coh901318.c | 2 +- drivers/dma/dmaengine.h | 10 ++++++++++ drivers/dma/dw_dmac.c | 4 ++-- drivers/dma/ep93xx_dma.c | 3 +-- drivers/dma/intel_mid_dma.c | 4 ++-- drivers/dma/ipu/ipu_idmac.c | 6 ++---- drivers/dma/mpc512x_dma.c | 3 +-- drivers/dma/pch_dma.c | 4 ++-- drivers/dma/pl330.c | 2 +- drivers/dma/sirf-dma.c | 3 +-- drivers/dma/ste_dma40.c | 2 +- drivers/dma/timb_dma.c | 5 ++--- drivers/dma/txx9dmac.c | 4 ++-- 15 files changed, 31 insertions(+), 28 deletions(-) diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 810f696eda4e..1b53f2605250 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -1709,8 +1709,7 @@ static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x, chan->name); chan->chan.device = dmadev; - chan->chan.cookie = 0; - chan->chan.completed_cookie = 0; + dma_cookie_init(&chan->chan); spin_lock_init(&chan->lock); INIT_LIST_HEAD(&chan->pend_list); diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 8a3297418cf0..5d225ddc7698 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -1103,7 +1103,7 @@ static int atc_alloc_chan_resources(struct dma_chan *chan) spin_lock_irqsave(&atchan->lock, flags); atchan->descs_allocated = i; list_splice(&tmp_list, &atchan->free_list); - chan->completed_cookie = chan->cookie = 1; + dma_cookie_init(chan); spin_unlock_irqrestore(&atchan->lock, flags); /* channel parameters */ @@ -1303,7 +1303,7 @@ static int __init at_dma_probe(struct platform_device *pdev) struct at_dma_chan *atchan = &atdma->chan[i]; atchan->chan_common.device = &atdma->dma_common; - atchan->chan_common.cookie = atchan->chan_common.completed_cookie = 1; + dma_cookie_init(&atchan->chan_common); list_add_tail(&atchan->chan_common.device_node, &atdma->dma_common.channels); diff --git a/drivers/dma/coh901318.c b/drivers/dma/coh901318.c index f3505178ff41..187bb9eef4a2 100644 --- a/drivers/dma/coh901318.c +++ b/drivers/dma/coh901318.c @@ -915,7 +915,7 @@ static int coh901318_alloc_chan_resources(struct dma_chan *chan) coh901318_config(cohc, NULL); cohc->allocated = 1; - chan->completed_cookie = chan->cookie = 1; + dma_cookie_init(chan); spin_unlock_irqrestore(&cohc->lock, flags); diff --git a/drivers/dma/dmaengine.h b/drivers/dma/dmaengine.h index 1ca5e0e633f4..17f983a4e9ba 100644 --- a/drivers/dma/dmaengine.h +++ b/drivers/dma/dmaengine.h @@ -8,6 +8,16 @@ #include #include +/** + * dma_cookie_init - initialize the cookies for a DMA channel + * @chan: dma channel to initialize + */ +static inline void dma_cookie_init(struct dma_chan *chan) +{ + chan->cookie = DMA_MIN_COOKIE; + chan->completed_cookie = DMA_MIN_COOKIE; +} + /** * dma_cookie_assign - assign a DMA engine cookie to the descriptor * @tx: descriptor needing cookie diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 33bde5da850d..cb173bbdcfdf 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1021,7 +1021,7 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) return -EIO; } - chan->completed_cookie = chan->cookie = 1; + dma_cookie_init(chan); /* * NOTE: some controllers may have additional features that we @@ -1449,7 +1449,7 @@ static int __init dw_probe(struct platform_device *pdev) struct dw_dma_chan *dwc = &dw->chan[i]; dwc->chan.device = &dw->dma; - dwc->chan.cookie = dwc->chan.completed_cookie = 1; + dma_cookie_init(&dwc->chan); if (pdata->chan_allocation_order == CHAN_ALLOCATION_ASCENDING) list_add_tail(&dwc->chan.device_node, &dw->dma.channels); diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c index 142ebf0cd316..f25e83bf5678 100644 --- a/drivers/dma/ep93xx_dma.c +++ b/drivers/dma/ep93xx_dma.c @@ -854,8 +854,7 @@ static int ep93xx_dma_alloc_chan_resources(struct dma_chan *chan) goto fail_clk_disable; spin_lock_irq(&edmac->lock); - edmac->chan.completed_cookie = 1; - edmac->chan.cookie = 1; + dma_cookie_init(&edmac->chan); ret = edmac->edma->hw_setup(edmac); spin_unlock_irq(&edmac->lock); diff --git a/drivers/dma/intel_mid_dma.c b/drivers/dma/intel_mid_dma.c index c9ab4ac18e41..d599d96a57b6 100644 --- a/drivers/dma/intel_mid_dma.c +++ b/drivers/dma/intel_mid_dma.c @@ -867,7 +867,7 @@ static int intel_mid_dma_alloc_chan_resources(struct dma_chan *chan) pm_runtime_put(&mid->pdev->dev); return -EIO; } - chan->completed_cookie = chan->cookie = 1; + dma_cookie_init(chan); spin_lock_bh(&midc->lock); while (midc->descs_allocated < DESCS_PER_CHANNEL) { @@ -1100,7 +1100,7 @@ static int mid_setup_dma(struct pci_dev *pdev) struct intel_mid_dma_chan *midch = &dma->ch[i]; midch->chan.device = &dma->common; - midch->chan.cookie = 1; + dma_cookie_init(&midch->chan); midch->ch_id = dma->chan_base + i; pr_debug("MDMA:Init CH %d, ID %d\n", i, midch->ch_id); diff --git a/drivers/dma/ipu/ipu_idmac.c b/drivers/dma/ipu/ipu_idmac.c index bff9250a59ce..1880274b0850 100644 --- a/drivers/dma/ipu/ipu_idmac.c +++ b/drivers/dma/ipu/ipu_idmac.c @@ -1504,8 +1504,7 @@ static int idmac_alloc_chan_resources(struct dma_chan *chan) BUG_ON(chan->client_count > 1); WARN_ON(ichan->status != IPU_CHANNEL_FREE); - chan->cookie = 1; - chan->completed_cookie = -ENXIO; + dma_cookie_init(chan); ret = ipu_irq_map(chan->chan_id); if (ret < 0) @@ -1633,8 +1632,7 @@ static int __init ipu_idmac_init(struct ipu *ipu) snprintf(ichan->eof_name, sizeof(ichan->eof_name), "IDMAC EOF %d", i); dma_chan->device = &idmac->dma; - dma_chan->cookie = 1; - dma_chan->completed_cookie = -ENXIO; + dma_cookie_init(dma_chan); dma_chan->chan_id = i; list_add_tail(&dma_chan->device_node, &dma->channels); } diff --git a/drivers/dma/mpc512x_dma.c b/drivers/dma/mpc512x_dma.c index 138271591ae9..2ab0a3d0eed5 100644 --- a/drivers/dma/mpc512x_dma.c +++ b/drivers/dma/mpc512x_dma.c @@ -733,8 +733,7 @@ static int __devinit mpc_dma_probe(struct platform_device *op) mchan = &mdma->channels[i]; mchan->chan.device = dma; - mchan->chan.cookie = 1; - mchan->chan.completed_cookie = mchan->chan.cookie; + dma_cookie_init(&mchan->chan); INIT_LIST_HEAD(&mchan->free); INIT_LIST_HEAD(&mchan->prepared); diff --git a/drivers/dma/pch_dma.c b/drivers/dma/pch_dma.c index c30f63ee32c5..c93bb0459972 100644 --- a/drivers/dma/pch_dma.c +++ b/drivers/dma/pch_dma.c @@ -531,7 +531,7 @@ static int pd_alloc_chan_resources(struct dma_chan *chan) spin_lock_irq(&pd_chan->lock); list_splice(&tmp_list, &pd_chan->free_list); pd_chan->descs_allocated = i; - chan->completed_cookie = chan->cookie = 1; + dma_cookie_init(chan); spin_unlock_irq(&pd_chan->lock); pdc_enable_irq(chan, 1); @@ -912,7 +912,7 @@ static int __devinit pch_dma_probe(struct pci_dev *pdev, struct pch_dma_chan *pd_chan = &pd->channels[i]; pd_chan->chan.device = &pd->dma; - pd_chan->chan.cookie = 1; + dma_cookie_init(&pd_chan->chan); pd_chan->membase = ®s->desc[i]; diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index d43019fc3496..7d6d7b416f68 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -304,7 +304,7 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan) spin_lock_irqsave(&pch->lock, flags); - chan->completed_cookie = chan->cookie = 1; + dma_cookie_init(chan); pch->cyclic = false; pch->pl330_chid = pl330_request_channel(&pdmac->pif); diff --git a/drivers/dma/sirf-dma.c b/drivers/dma/sirf-dma.c index a760d981ece0..a2cde8585239 100644 --- a/drivers/dma/sirf-dma.c +++ b/drivers/dma/sirf-dma.c @@ -625,8 +625,7 @@ static int __devinit sirfsoc_dma_probe(struct platform_device *op) schan = &sdma->channels[i]; schan->chan.device = dma; - schan->chan.cookie = 1; - schan->chan.completed_cookie = schan->chan.cookie; + dma_cookie_init(&schan->chan); INIT_LIST_HEAD(&schan->free); INIT_LIST_HEAD(&schan->prepared); diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 07b82e367ffd..1ea6d02d08ab 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -2172,7 +2172,7 @@ static int d40_alloc_chan_resources(struct dma_chan *chan) bool is_free_phy; spin_lock_irqsave(&d40c->lock, flags); - chan->completed_cookie = chan->cookie = 1; + dma_cookie_init(chan); /* If no dma configuration is set use default configuration (memcpy) */ if (!d40c->configured) { diff --git a/drivers/dma/timb_dma.c b/drivers/dma/timb_dma.c index 6383abbecce6..7805996661b8 100644 --- a/drivers/dma/timb_dma.c +++ b/drivers/dma/timb_dma.c @@ -477,8 +477,7 @@ static int td_alloc_chan_resources(struct dma_chan *chan) } spin_lock_bh(&td_chan->lock); - chan->completed_cookie = 1; - chan->cookie = 1; + dma_cookie_init(chan); spin_unlock_bh(&td_chan->lock); return 0; @@ -755,7 +754,7 @@ static int __devinit td_probe(struct platform_device *pdev) } td_chan->chan.device = &td->dma; - td_chan->chan.cookie = 1; + dma_cookie_init(&td_chan->chan); spin_lock_init(&td_chan->lock); INIT_LIST_HEAD(&td_chan->active_list); INIT_LIST_HEAD(&td_chan->queue); diff --git a/drivers/dma/txx9dmac.c b/drivers/dma/txx9dmac.c index bb7b3d96ac6f..40440f946385 100644 --- a/drivers/dma/txx9dmac.c +++ b/drivers/dma/txx9dmac.c @@ -1034,7 +1034,7 @@ static int txx9dmac_alloc_chan_resources(struct dma_chan *chan) return -EIO; } - chan->completed_cookie = chan->cookie = 1; + dma_cookie_init(chan); dc->ccr = TXX9_DMA_CCR_IMMCHN | TXX9_DMA_CCR_INTENE | CCR_LE; txx9dmac_chan_set_SMPCHN(dc); @@ -1163,7 +1163,7 @@ static int __init txx9dmac_chan_probe(struct platform_device *pdev) dc->ddev->chan[ch] = dc; dc->chan.device = &dc->dma; list_add_tail(&dc->chan.device_node, &dc->chan.device->channels); - dc->chan.cookie = dc->chan.completed_cookie = 1; + dma_cookie_init(&dc->chan); if (is_dmac64(dc)) dc->ch_regs = &__txx9dmac_regs(dc->ddev)->CHAN[ch]; From 2a926e46022ad7a03e0ac167d8c2b0d88c12c5a8 Mon Sep 17 00:00:00 2001 From: Russell King - ARM Linux Date: Tue, 6 Mar 2012 22:36:07 +0000 Subject: [PATCH 44/67] dmaengine: fix cookie handling in iop-adma.c and ppc4xx/adma.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dan Williams said: > > Russell King wrote: > > Firstly, we have DMA_MIN_COOKIE which has value 1 - so any cookies below > > that aren't valid.  That seems sane. > > > > We seem to have different behaviours: > > > > -       cookie = c->cookie; > > -       cookie++; > > -       if (cookie < 0) > > -               cookie = 1; > > -       c->cookie = cookie; > > -       tx->cookie = cookie; > > > > c->cookie here is initialized to zero, so the first cookie given out will > > be 1.  This is how most DMA engine drivers implement this. > > > > Then we have this: > > > >                cookie = chan->common.cookie; > >                cookie++; > >                if (cookie <= 1) > >                        cookie = 2; > > > >                /* initialize the completed cookie to be less than > >                 * the most recently used cookie > >                 */ > >                chan->common.completed_cookie = cookie - 1; > >                chan->common.cookie = sw_desc->async_tx.cookie = cookie; > > > > Again, chan->common.cookie starts off at 0.  The first cookie given out > > will be 2, and 1 will never be used.  There are three drivers which > > implement it this way. > > > > Why is there this difference, and can these three be corrected to behave > > the same way as the first (and therefore the assignment of cookies > > consolidated?) > > Yes, they should be consolidated, and I believe they have drifted only > because there were no good common helpers and murphy's law took over. So lets fix this up to use the common dma_cookie_assign() helper. Signed-off-by: Russell King Tested-by: Linus Walleij Reviewed-by: Linus Walleij Acked-by: Jassi Brar [imx-sdma.c & mxs-dma.c] Tested-by: Shawn Guo Signed-off-by: Vinod Koul --- drivers/dma/iop-adma.c | 12 ++---------- drivers/dma/ppc4xx/adma.c | 6 +----- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/drivers/dma/iop-adma.c b/drivers/dma/iop-adma.c index b1e3be089c52..4370b1015f73 100644 --- a/drivers/dma/iop-adma.c +++ b/drivers/dma/iop-adma.c @@ -1622,16 +1622,12 @@ static void iop_chan_start_null_memcpy(struct iop_adma_chan *iop_chan) iop_desc_set_dest_addr(grp_start, iop_chan, 0); iop_desc_set_memcpy_src_addr(grp_start, 0); - cookie = iop_chan->common.cookie; - cookie++; - if (cookie <= 1) - cookie = 2; + cookie = dma_cookie_assign(&sw_desc->async_tx); /* initialize the completed cookie to be less than * the most recently used cookie */ iop_chan->common.completed_cookie = cookie - 1; - iop_chan->common.cookie = sw_desc->async_tx.cookie = cookie; /* channel should not be busy */ BUG_ON(iop_chan_is_busy(iop_chan)); @@ -1679,16 +1675,12 @@ static void iop_chan_start_null_xor(struct iop_adma_chan *iop_chan) iop_desc_set_xor_src_addr(grp_start, 0, 0); iop_desc_set_xor_src_addr(grp_start, 1, 0); - cookie = iop_chan->common.cookie; - cookie++; - if (cookie <= 1) - cookie = 2; + cookie = dma_cookie_assign(&sw_desc->async_tx); /* initialize the completed cookie to be less than * the most recently used cookie */ iop_chan->common.completed_cookie = cookie - 1; - iop_chan->common.cookie = sw_desc->async_tx.cookie = cookie; /* channel should not be busy */ BUG_ON(iop_chan_is_busy(iop_chan)); diff --git a/drivers/dma/ppc4xx/adma.c b/drivers/dma/ppc4xx/adma.c index 86239ea01898..975206257f6e 100644 --- a/drivers/dma/ppc4xx/adma.c +++ b/drivers/dma/ppc4xx/adma.c @@ -4022,16 +4022,12 @@ static void ppc440spe_chan_start_null_xor(struct ppc440spe_adma_chan *chan) async_tx_ack(&sw_desc->async_tx); ppc440spe_desc_init_null_xor(group_start); - cookie = chan->common.cookie; - cookie++; - if (cookie <= 1) - cookie = 2; + cookie = dma_cookie_assign(&sw_desc->async_tx); /* initialize the completed cookie to be less than * the most recently used cookie */ chan->common.completed_cookie = cookie - 1; - chan->common.cookie = sw_desc->async_tx.cookie = cookie; /* channel should not be busy */ BUG_ON(ppc440spe_chan_is_busy(chan)); From 8ac695463f37af902e953d575d3f782e32e170da Mon Sep 17 00:00:00 2001 From: Russell King - ARM Linux Date: Tue, 6 Mar 2012 22:36:27 +0000 Subject: [PATCH 45/67] dmaengine: ensure all DMA engine drivers initialize their cookies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ensure all DMA engine drivers initialize their cookies in the same way, so that they all behave in a similar fashion. This means their first issued cookie will be 2 rather than 1, and will increment to INT_MAX before returning 1 and starting over. In connection with this, Dan Williams said: > Russell King wrote: > > Secondly, some DMA engine drivers initialize the dma_chan cookie to 0, > > others to 1.  Is there a reason for this, or are these all buggy? > > I know that ioat and iop-adma expect 0 to mean "I have cleaned up this > descriptor and it is idle", and would break if zero was an in-flight > cookie value. The reserved usage of zero is an driver internal > concern, but I have no problem formalizing it as a reserved value. Signed-off-by: Russell King Tested-by: Linus Walleij Reviewed-by: Linus Walleij Acked-by: Jassi Brar [imx-sdma.c & mxs-dma.c] Tested-by: Shawn Guo Signed-off-by: Vinod Koul --- drivers/dma/fsldma.c | 1 + drivers/dma/imx-dma.c | 1 + drivers/dma/imx-sdma.c | 1 + drivers/dma/ioat/dma.c | 1 + drivers/dma/iop-adma.c | 1 + drivers/dma/mv_xor.c | 1 + drivers/dma/mxs-dma.c | 1 + drivers/dma/ppc4xx/adma.c | 1 + drivers/dma/shdma.c | 1 + 9 files changed, 9 insertions(+) diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index 2f6c806126e2..7d7384b34621 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -1292,6 +1292,7 @@ static int __devinit fsl_dma_chan_probe(struct fsldma_device *fdev, chan->idle = true; chan->common.device = &fdev->common; + dma_cookie_init(&chan->common); /* find the IRQ line, if it exists in the device tree */ chan->irq = irq_of_parse_and_map(node, 0); diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index 6731f1918c55..f0485c0a685a 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -347,6 +347,7 @@ static int __init imxdma_probe(struct platform_device *pdev) spin_lock_init(&imxdmac->lock); imxdmac->chan.device = &imxdma->dma_device; + dma_cookie_init(&imxdmac->chan); imxdmac->channel = i; /* Add the channel to the DMAC list */ diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 981071ebd5c8..ccfc7c425c52 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -1355,6 +1355,7 @@ static int __init sdma_probe(struct platform_device *pdev) spin_lock_init(&sdmac->lock); sdmac->chan.device = &sdma->dma_device; + dma_cookie_init(&sdmac->chan); sdmac->channel = i; /* diff --git a/drivers/dma/ioat/dma.c b/drivers/dma/ioat/dma.c index 97e100ce43eb..31493d80e0e9 100644 --- a/drivers/dma/ioat/dma.c +++ b/drivers/dma/ioat/dma.c @@ -109,6 +109,7 @@ void ioat_init_channel(struct ioatdma_device *device, struct ioat_chan_common *c chan->reg_base = device->reg_base + (0x80 * (idx + 1)); spin_lock_init(&chan->cleanup_lock); chan->common.device = dma; + dma_cookie_init(&chan->common); list_add_tail(&chan->common.device_node, &dma->channels); device->idx[idx] = chan; init_timer(&chan->timer); diff --git a/drivers/dma/iop-adma.c b/drivers/dma/iop-adma.c index 4370b1015f73..1f3a703ed0e0 100644 --- a/drivers/dma/iop-adma.c +++ b/drivers/dma/iop-adma.c @@ -1545,6 +1545,7 @@ static int __devinit iop_adma_probe(struct platform_device *pdev) INIT_LIST_HEAD(&iop_chan->chain); INIT_LIST_HEAD(&iop_chan->all_slots); iop_chan->common.device = dma_dev; + dma_cookie_init(&iop_chan->common); list_add_tail(&iop_chan->common.device_node, &dma_dev->channels); if (dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask)) { diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c index 486353e60a0a..fa5d55fea46c 100644 --- a/drivers/dma/mv_xor.c +++ b/drivers/dma/mv_xor.c @@ -1193,6 +1193,7 @@ static int __devinit mv_xor_probe(struct platform_device *pdev) INIT_LIST_HEAD(&mv_chan->completed_slots); INIT_LIST_HEAD(&mv_chan->all_slots); mv_chan->common.device = dma_dev; + dma_cookie_init(&mv_chan->common); list_add_tail(&mv_chan->common.device_node, &dma_dev->channels); diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index 5f3492e5d28f..a2267f9ab568 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c @@ -618,6 +618,7 @@ static int __init mxs_dma_probe(struct platform_device *pdev) mxs_chan->mxs_dma = mxs_dma; mxs_chan->chan.device = &mxs_dma->dma_device; + dma_cookie_init(&mxs_chan->chan); tasklet_init(&mxs_chan->tasklet, mxs_dma_tasklet, (unsigned long) mxs_chan); diff --git a/drivers/dma/ppc4xx/adma.c b/drivers/dma/ppc4xx/adma.c index 975206257f6e..ced98826684a 100644 --- a/drivers/dma/ppc4xx/adma.c +++ b/drivers/dma/ppc4xx/adma.c @@ -4497,6 +4497,7 @@ static int __devinit ppc440spe_adma_probe(struct platform_device *ofdev) INIT_LIST_HEAD(&chan->all_slots); chan->device = adev; chan->common.device = &adev->common; + dma_cookie_init(&chan->common); list_add_tail(&chan->common.device_node, &adev->common.channels); tasklet_init(&chan->irq_tasklet, ppc440spe_adma_tasklet, (unsigned long)chan); diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c index 50510ef7db72..5c4088603dd4 100644 --- a/drivers/dma/shdma.c +++ b/drivers/dma/shdma.c @@ -1089,6 +1089,7 @@ static int __devinit sh_dmae_chan_probe(struct sh_dmae_device *shdev, int id, /* reference struct dma_device */ new_sh_chan->common.device = &shdev->common; + dma_cookie_init(&new_sh_chan->common); new_sh_chan->dev = shdev->common.dev; new_sh_chan->id = id; From 8867bd508f88eae8b9c54394f17422f49e387b26 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Tue, 13 Mar 2012 11:56:44 +0530 Subject: [PATCH 46/67] dmaengine: pl330: fix the pl330 build after cookie cleanup drivers/dma/pl330.c: In function 'pl330_control': drivers/dma/pl330.c:342: error: 'struct dma_pl330_chan' has no member named 'completed' Reported by: Jassi Brar Suggested by: Jassi Brar Signed-off-by: Vinod Koul --- drivers/dma/pl330.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 7d6d7b416f68..0bb332c1ccb0 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -339,7 +339,6 @@ static int pl330_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned /* Mark all desc done */ list_for_each_entry_safe(desc, _dt, &pch->work_list , node) { desc->status = DONE; - pch->completed = desc->txd.cookie; list_move_tail(&desc->node, &list); } From 949ff5b8d46b5e3435d21b2651ce3a2599208d44 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Tue, 13 Mar 2012 11:58:12 +0530 Subject: [PATCH 47/67] dmaengine: fix for cookie changes and merge Fixed trivial issues in drivers: drivers/dma/imx-sdma.c drivers/dma/intel_mid_dma.c drivers/dma/ioat/dma_v3.c drivers/dma/iop-adma.c drivers/dma/sirf-dma.c drivers/dma/timb_dma.c Signed-off-by: Vinod Koul --- drivers/dma/imx-sdma.c | 1 + drivers/dma/intel_mid_dma.c | 1 + drivers/dma/ioat/dma_v3.c | 1 + drivers/dma/iop-adma.c | 1 + drivers/dma/sirf-dma.c | 2 ++ drivers/dma/timb_dma.c | 6 +----- 6 files changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index ccfc7c425c52..81f9d57ec70f 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -1127,6 +1127,7 @@ static void sdma_issue_pending(struct dma_chan *chan) struct sdma_engine *sdma = sdmac->sdma; if (sdmac->status == DMA_IN_PROGRESS) + sdma_enable_channel(sdma, sdmac->channel); } #define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1 34 diff --git a/drivers/dma/intel_mid_dma.c b/drivers/dma/intel_mid_dma.c index d599d96a57b6..2449812f5464 100644 --- a/drivers/dma/intel_mid_dma.c +++ b/drivers/dma/intel_mid_dma.c @@ -477,6 +477,7 @@ static enum dma_status intel_mid_dma_tx_status(struct dma_chan *chan, dma_cookie_t cookie, struct dma_tx_state *txstate) { + struct intel_mid_dma_chan *midc = to_intel_mid_dma_chan(chan); enum dma_status ret; ret = dma_cookie_status(chan, cookie, txstate); diff --git a/drivers/dma/ioat/dma_v3.c b/drivers/dma/ioat/dma_v3.c index 145eda241dee..2c4476c0e405 100644 --- a/drivers/dma/ioat/dma_v3.c +++ b/drivers/dma/ioat/dma_v3.c @@ -61,6 +61,7 @@ #include #include #include +#include "../dmaengine.h" #include "registers.h" #include "hw.h" #include "dma.h" diff --git a/drivers/dma/iop-adma.c b/drivers/dma/iop-adma.c index 1f3a703ed0e0..4499f88789bc 100644 --- a/drivers/dma/iop-adma.c +++ b/drivers/dma/iop-adma.c @@ -894,6 +894,7 @@ static enum dma_status iop_adma_status(struct dma_chan *chan, struct dma_tx_state *txstate) { struct iop_adma_chan *iop_chan = to_iop_adma_chan(chan); + int ret; ret = dma_cookie_status(chan, cookie, txstate); if (ret == DMA_SUCCESS) diff --git a/drivers/dma/sirf-dma.c b/drivers/dma/sirf-dma.c index a2cde8585239..45ba352fb871 100644 --- a/drivers/dma/sirf-dma.c +++ b/drivers/dma/sirf-dma.c @@ -18,6 +18,8 @@ #include #include +#include "dmaengine.h" + #define SIRFSOC_DMA_DESCRIPTORS 16 #define SIRFSOC_DMA_CHANNELS 16 diff --git a/drivers/dma/timb_dma.c b/drivers/dma/timb_dma.c index 7805996661b8..d408c2206023 100644 --- a/drivers/dma/timb_dma.c +++ b/drivers/dma/timb_dma.c @@ -510,17 +510,13 @@ static void td_free_chan_resources(struct dma_chan *chan) static enum dma_status td_tx_status(struct dma_chan *chan, dma_cookie_t cookie, struct dma_tx_state *txstate) { - struct timb_dma_chan *td_chan = - container_of(chan, struct timb_dma_chan, chan); enum dma_status ret; dev_dbg(chan2dev(chan), "%s: Entry\n", __func__); ret = dma_cookie_status(chan, cookie, txstate); - dev_dbg(chan2dev(chan), - "%s: exit, ret: %d, last_complete: %d, last_used: %d\n", - __func__, ret, last_complete, last_used); + dev_dbg(chan2dev(chan), "%s: exit, ret: %d\n", __func__, ret); return ret; } From ad1122e545b55013089e6cc18ac37b47b46f2a7d Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 8 Mar 2012 09:26:39 -0300 Subject: [PATCH 48/67] dma: imx-sdma: Treat firmware messages as warnings instead of erros As the SDMA controller can operate without an external firmware being loaded, treat the firmware related messages as warnings rather than errors. Signed-off-by: Fabio Estevam Acked-by: Sascha Hauer Signed-off-by: Vinod Koul --- drivers/dma/imx-sdma.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 5da552d1f92d..f8259ee49f86 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -1379,7 +1379,7 @@ static int __init sdma_probe(struct platform_device *pdev) if (pdata) { ret = sdma_get_firmware(sdma, pdata->fw_name); if (ret) - dev_err(&pdev->dev, "failed to get firmware from platform data\n"); + dev_warn(&pdev->dev, "failed to get firmware from platform data\n"); } else { /* * Because that device tree does not encode ROM script address, @@ -1389,11 +1389,11 @@ static int __init sdma_probe(struct platform_device *pdev) ret = of_property_read_string(np, "fsl,sdma-ram-script-name", &fw_name); if (ret) - dev_err(&pdev->dev, "failed to get firmware name\n"); + dev_warn(&pdev->dev, "failed to get firmware name\n"); else { ret = sdma_get_firmware(sdma, fw_name); if (ret) - dev_err(&pdev->dev, "failed to get firmware from device tree\n"); + dev_warn(&pdev->dev, "failed to get firmware from device tree\n"); } } From 16052827d98fbc13c31ebad560af4bd53e2b4dd5 Mon Sep 17 00:00:00 2001 From: Alexandre Bounine Date: Thu, 8 Mar 2012 16:11:18 -0500 Subject: [PATCH 49/67] dmaengine/dma_slave: introduce inline wrappers Add inline wrappers for device_prep_slave_sg() and device_prep_dma_cyclic() interfaces to hide new parameter from current users of affected interfaces. Convert current users to use new wrappers instead of direct calls. Suggested by Russell King [https://lkml.org/lkml/2012/2/3/269]. Signed-off-by: Alexandre Bounine Signed-off-by: Vinod Koul --- .../arm/plat-nomadik/include/plat/ste_dma40.h | 3 +-- arch/arm/plat-samsung/dma-ops.c | 4 +-- drivers/media/video/mx3_camera.c | 2 +- drivers/media/video/timblogiw.c | 2 +- drivers/mmc/host/atmel-mci.c | 2 +- drivers/mmc/host/mmci.c | 2 +- drivers/mmc/host/mxcmmc.c | 2 +- drivers/mmc/host/mxs-mmc.c | 2 +- drivers/mmc/host/sh_mmcif.c | 4 +-- drivers/mmc/host/tmio_mmc_dma.c | 4 +-- drivers/mtd/nand/gpmi-nand/gpmi-lib.c | 25 ++++++++----------- drivers/net/ethernet/micrel/ks8842.c | 4 +-- drivers/spi/spi-dw-mid.c | 4 +-- drivers/spi/spi-ep93xx.c | 4 +-- drivers/spi/spi-pl022.c | 4 +-- drivers/spi/spi-topcliff-pch.c | 4 +-- drivers/tty/serial/amba-pl011.c | 6 ++--- drivers/tty/serial/pch_uart.c | 4 +-- drivers/tty/serial/sh-sci.c | 4 +-- drivers/usb/musb/ux500_dma.c | 3 +-- drivers/usb/renesas_usbhs/fifo.c | 5 ++-- drivers/video/mx3fb.c | 4 +-- include/linux/dmaengine.h | 16 ++++++++++++ sound/soc/ep93xx/ep93xx-pcm.c | 3 +-- sound/soc/imx/imx-pcm-dma-mx2.c | 2 +- sound/soc/mxs/mxs-pcm.c | 2 +- sound/soc/sh/siu_pcm.c | 4 +-- sound/soc/txx9/txx9aclc.c | 2 +- 28 files changed, 67 insertions(+), 60 deletions(-) diff --git a/arch/arm/plat-nomadik/include/plat/ste_dma40.h b/arch/arm/plat-nomadik/include/plat/ste_dma40.h index fd0ee84c45d1..9ff93b065686 100644 --- a/arch/arm/plat-nomadik/include/plat/ste_dma40.h +++ b/arch/arm/plat-nomadik/include/plat/ste_dma40.h @@ -200,8 +200,7 @@ dma_async_tx_descriptor *stedma40_slave_mem(struct dma_chan *chan, sg.dma_address = addr; sg.length = size; - return chan->device->device_prep_slave_sg(chan, &sg, 1, - direction, flags); + return dmaengine_prep_slave_sg(chan, &sg, 1, direction, flags); } #else diff --git a/arch/arm/plat-samsung/dma-ops.c b/arch/arm/plat-samsung/dma-ops.c index 0747c77a2fd5..a6ef3961134c 100644 --- a/arch/arm/plat-samsung/dma-ops.c +++ b/arch/arm/plat-samsung/dma-ops.c @@ -79,11 +79,11 @@ static int samsung_dmadev_prepare(unsigned ch, info->len, offset_in_page(info->buf)); sg_dma_address(&sg) = info->buf; - desc = chan->device->device_prep_slave_sg(chan, + desc = dmaengine_prep_slave_sg(chan, &sg, 1, info->direction, DMA_PREP_INTERRUPT); break; case DMA_CYCLIC: - desc = chan->device->device_prep_dma_cyclic(chan, + desc = dmaengine_prep_dma_cyclic(chan, info->buf, info->len, info->period, info->direction); break; default: diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c index 74522773e934..93c35ef5f0ad 100644 --- a/drivers/media/video/mx3_camera.c +++ b/drivers/media/video/mx3_camera.c @@ -286,7 +286,7 @@ static void mx3_videobuf_queue(struct vb2_buffer *vb) sg_dma_address(sg) = vb2_dma_contig_plane_dma_addr(vb, 0); sg_dma_len(sg) = new_size; - txd = ichan->dma_chan.device->device_prep_slave_sg( + txd = dmaengine_prep_slave_sg( &ichan->dma_chan, sg, 1, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT); if (!txd) diff --git a/drivers/media/video/timblogiw.c b/drivers/media/video/timblogiw.c index 4ed1c7c28ae7..02194c056b00 100644 --- a/drivers/media/video/timblogiw.c +++ b/drivers/media/video/timblogiw.c @@ -564,7 +564,7 @@ static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) spin_unlock_irq(&fh->queue_lock); - desc = fh->chan->device->device_prep_slave_sg(fh->chan, + desc = dmaengine_prep_slave_sg(fh->chan, buf->sg, sg_elems, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_SRC_UNMAP); if (!desc) { diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 3ba865ddebc4..492854b09d89 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -875,7 +875,7 @@ atmci_prepare_data_dma(struct atmel_mci *host, struct mmc_data *data) data->sg_len, direction); dmaengine_slave_config(chan, &host->dma_conf); - desc = chan->device->device_prep_slave_sg(chan, + desc = dmaengine_prep_slave_sg(chan, data->sg, sglen, slave_dirn, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index a09c06ba046c..c55f9663eb13 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -413,7 +413,7 @@ static int mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data, return -EINVAL; dmaengine_slave_config(chan, &conf); - desc = device->device_prep_slave_sg(chan, data->sg, nr_sg, + desc = dmaengine_prep_slave_sg(chan, data->sg, nr_sg, conf.direction, DMA_CTRL_ACK); if (!desc) goto unmap_exit; diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c index 68b69a91e5ef..b2058b432320 100644 --- a/drivers/mmc/host/mxcmmc.c +++ b/drivers/mmc/host/mxcmmc.c @@ -255,7 +255,7 @@ static int mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data) if (nents != data->sg_len) return -EINVAL; - host->desc = host->dma->device->device_prep_slave_sg(host->dma, + host->desc = dmaengine_prep_slave_sg(host->dma, data->sg, data->sg_len, slave_dirn, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index 382c835d217c..65f36cf2ff33 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -324,7 +324,7 @@ static struct dma_async_tx_descriptor *mxs_mmc_prep_dma( sg_len = SSP_PIO_NUM; } - desc = host->dmach->device->device_prep_slave_sg(host->dmach, + desc = dmaengine_prep_slave_sg(host->dmach, sgl, sg_len, host->slave_dirn, append); if (desc) { desc->callback = mxs_mmc_dma_irq_callback; diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index f5d8b53be333..3d00f1aab87d 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -285,7 +285,7 @@ static void sh_mmcif_start_dma_rx(struct sh_mmcif_host *host) DMA_FROM_DEVICE); if (ret > 0) { host->dma_active = true; - desc = chan->device->device_prep_slave_sg(chan, sg, ret, + desc = dmaengine_prep_slave_sg(chan, sg, ret, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); } @@ -334,7 +334,7 @@ static void sh_mmcif_start_dma_tx(struct sh_mmcif_host *host) DMA_TO_DEVICE); if (ret > 0) { host->dma_active = true; - desc = chan->device->device_prep_slave_sg(chan, sg, ret, + desc = dmaengine_prep_slave_sg(chan, sg, ret, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); } diff --git a/drivers/mmc/host/tmio_mmc_dma.c b/drivers/mmc/host/tmio_mmc_dma.c index 7a6e6cc8f8b8..def9c54f73f5 100644 --- a/drivers/mmc/host/tmio_mmc_dma.c +++ b/drivers/mmc/host/tmio_mmc_dma.c @@ -76,7 +76,7 @@ static void tmio_mmc_start_dma_rx(struct tmio_mmc_host *host) ret = dma_map_sg(chan->device->dev, sg, host->sg_len, DMA_FROM_DEVICE); if (ret > 0) - desc = chan->device->device_prep_slave_sg(chan, sg, ret, + desc = dmaengine_prep_slave_sg(chan, sg, ret, DMA_DEV_TO_MEM, DMA_CTRL_ACK); if (desc) { @@ -157,7 +157,7 @@ static void tmio_mmc_start_dma_tx(struct tmio_mmc_host *host) ret = dma_map_sg(chan->device->dev, sg, host->sg_len, DMA_TO_DEVICE); if (ret > 0) - desc = chan->device->device_prep_slave_sg(chan, sg, ret, + desc = dmaengine_prep_slave_sg(chan, sg, ret, DMA_MEM_TO_DEV, DMA_CTRL_ACK); if (desc) { diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c index 7f680420bfab..2a200ba0bd1a 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c @@ -825,7 +825,7 @@ int gpmi_send_command(struct gpmi_nand_data *this) | BM_GPMI_CTRL0_ADDRESS_INCREMENT | BF_GPMI_CTRL0_XFER_COUNT(this->command_length); pio[1] = pio[2] = 0; - desc = channel->device->device_prep_slave_sg(channel, + desc = dmaengine_prep_slave_sg(channel, (struct scatterlist *)pio, ARRAY_SIZE(pio), DMA_TRANS_NONE, 0); if (!desc) { @@ -838,8 +838,7 @@ int gpmi_send_command(struct gpmi_nand_data *this) sg_init_one(sgl, this->cmd_buffer, this->command_length); dma_map_sg(this->dev, sgl, 1, DMA_TO_DEVICE); - desc = channel->device->device_prep_slave_sg(channel, - sgl, 1, DMA_MEM_TO_DEV, 1); + desc = dmaengine_prep_slave_sg(channel, sgl, 1, DMA_MEM_TO_DEV, 1); if (!desc) { pr_err("step 2 error\n"); return -1; @@ -870,8 +869,7 @@ int gpmi_send_data(struct gpmi_nand_data *this) | BF_GPMI_CTRL0_ADDRESS(address) | BF_GPMI_CTRL0_XFER_COUNT(this->upper_len); pio[1] = 0; - desc = channel->device->device_prep_slave_sg(channel, - (struct scatterlist *)pio, + desc = dmaengine_prep_slave_sg(channel, (struct scatterlist *)pio, ARRAY_SIZE(pio), DMA_TRANS_NONE, 0); if (!desc) { pr_err("step 1 error\n"); @@ -880,7 +878,7 @@ int gpmi_send_data(struct gpmi_nand_data *this) /* [2] send DMA request */ prepare_data_dma(this, DMA_TO_DEVICE); - desc = channel->device->device_prep_slave_sg(channel, &this->data_sgl, + desc = dmaengine_prep_slave_sg(channel, &this->data_sgl, 1, DMA_MEM_TO_DEV, 1); if (!desc) { pr_err("step 2 error\n"); @@ -906,7 +904,7 @@ int gpmi_read_data(struct gpmi_nand_data *this) | BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_DATA) | BF_GPMI_CTRL0_XFER_COUNT(this->upper_len); pio[1] = 0; - desc = channel->device->device_prep_slave_sg(channel, + desc = dmaengine_prep_slave_sg(channel, (struct scatterlist *)pio, ARRAY_SIZE(pio), DMA_TRANS_NONE, 0); if (!desc) { @@ -916,8 +914,8 @@ int gpmi_read_data(struct gpmi_nand_data *this) /* [2] : send DMA request */ prepare_data_dma(this, DMA_FROM_DEVICE); - desc = channel->device->device_prep_slave_sg(channel, &this->data_sgl, - 1, DMA_DEV_TO_MEM, 1); + desc = dmaengine_prep_slave_sg(channel, &this->data_sgl, + 1, DMA_DEV_TO_MEM, 1); if (!desc) { pr_err("step 2 error\n"); return -1; @@ -962,8 +960,7 @@ int gpmi_send_page(struct gpmi_nand_data *this, pio[4] = payload; pio[5] = auxiliary; - desc = channel->device->device_prep_slave_sg(channel, - (struct scatterlist *)pio, + desc = dmaengine_prep_slave_sg(channel, (struct scatterlist *)pio, ARRAY_SIZE(pio), DMA_TRANS_NONE, 0); if (!desc) { pr_err("step 2 error\n"); @@ -997,7 +994,7 @@ int gpmi_read_page(struct gpmi_nand_data *this, | BF_GPMI_CTRL0_ADDRESS(address) | BF_GPMI_CTRL0_XFER_COUNT(0); pio[1] = 0; - desc = channel->device->device_prep_slave_sg(channel, + desc = dmaengine_prep_slave_sg(channel, (struct scatterlist *)pio, 2, DMA_TRANS_NONE, 0); if (!desc) { @@ -1026,7 +1023,7 @@ int gpmi_read_page(struct gpmi_nand_data *this, pio[3] = geo->page_size; pio[4] = payload; pio[5] = auxiliary; - desc = channel->device->device_prep_slave_sg(channel, + desc = dmaengine_prep_slave_sg(channel, (struct scatterlist *)pio, ARRAY_SIZE(pio), DMA_TRANS_NONE, 1); if (!desc) { @@ -1045,7 +1042,7 @@ int gpmi_read_page(struct gpmi_nand_data *this, | BF_GPMI_CTRL0_ADDRESS(address) | BF_GPMI_CTRL0_XFER_COUNT(geo->page_size); pio[1] = 0; - desc = channel->device->device_prep_slave_sg(channel, + desc = dmaengine_prep_slave_sg(channel, (struct scatterlist *)pio, 2, DMA_TRANS_NONE, 1); if (!desc) { diff --git a/drivers/net/ethernet/micrel/ks8842.c b/drivers/net/ethernet/micrel/ks8842.c index 0a85690a1321..554fef3f0130 100644 --- a/drivers/net/ethernet/micrel/ks8842.c +++ b/drivers/net/ethernet/micrel/ks8842.c @@ -458,7 +458,7 @@ static int ks8842_tx_frame_dma(struct sk_buff *skb, struct net_device *netdev) if (sg_dma_len(&ctl->sg) % 4) sg_dma_len(&ctl->sg) += 4 - sg_dma_len(&ctl->sg) % 4; - ctl->adesc = ctl->chan->device->device_prep_slave_sg(ctl->chan, + ctl->adesc = dmaengine_prep_slave_sg(ctl->chan, &ctl->sg, 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_SRC_UNMAP); if (!ctl->adesc) @@ -570,7 +570,7 @@ static int __ks8842_start_new_rx_dma(struct net_device *netdev) sg_dma_len(sg) = DMA_BUFFER_SIZE; - ctl->adesc = ctl->chan->device->device_prep_slave_sg(ctl->chan, + ctl->adesc = dmaengine_prep_slave_sg(ctl->chan, sg, 1, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_SRC_UNMAP); diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index 04d6c1b31383..b9f0192758d6 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c @@ -146,7 +146,7 @@ static int mid_spi_dma_transfer(struct dw_spi *dws, int cs_change) dws->tx_sgl.dma_address = dws->tx_dma; dws->tx_sgl.length = dws->len; - txdesc = txchan->device->device_prep_slave_sg(txchan, + txdesc = dmaengine_prep_slave_sg(txchan, &dws->tx_sgl, 1, DMA_MEM_TO_DEV, @@ -169,7 +169,7 @@ static int mid_spi_dma_transfer(struct dw_spi *dws, int cs_change) dws->rx_sgl.dma_address = dws->rx_dma; dws->rx_sgl.length = dws->len; - rxdesc = rxchan->device->device_prep_slave_sg(rxchan, + rxdesc = dmaengine_prep_slave_sg(rxchan, &dws->rx_sgl, 1, DMA_DEV_TO_MEM, diff --git a/drivers/spi/spi-ep93xx.c b/drivers/spi/spi-ep93xx.c index d46e55c720b7..6db2887852d6 100644 --- a/drivers/spi/spi-ep93xx.c +++ b/drivers/spi/spi-ep93xx.c @@ -633,8 +633,8 @@ ep93xx_spi_dma_prepare(struct ep93xx_spi *espi, enum dma_data_direction dir) if (!nents) return ERR_PTR(-ENOMEM); - txd = chan->device->device_prep_slave_sg(chan, sgt->sgl, nents, - slave_dirn, DMA_CTRL_ACK); + txd = dmaengine_prep_slave_sg(chan, sgt->sgl, nents, + slave_dirn, DMA_CTRL_ACK); if (!txd) { dma_unmap_sg(chan->device->dev, sgt->sgl, sgt->nents, dir); return ERR_PTR(-ENOMEM); diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 1dc667f8a305..a209f3b7b05b 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -1040,7 +1040,7 @@ static int configure_dma(struct pl022 *pl022) goto err_tx_sgmap; /* Send both scatterlists */ - rxdesc = rxchan->device->device_prep_slave_sg(rxchan, + rxdesc = dmaengine_prep_slave_sg(rxchan, pl022->sgt_rx.sgl, rx_sglen, DMA_DEV_TO_MEM, @@ -1048,7 +1048,7 @@ static int configure_dma(struct pl022 *pl022) if (!rxdesc) goto err_rxdesc; - txdesc = txchan->device->device_prep_slave_sg(txchan, + txdesc = dmaengine_prep_slave_sg(txchan, pl022->sgt_tx.sgl, tx_sglen, DMA_MEM_TO_DEV, diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c index 2a6429d8c363..ea4c8d57667a 100644 --- a/drivers/spi/spi-topcliff-pch.c +++ b/drivers/spi/spi-topcliff-pch.c @@ -1078,7 +1078,7 @@ static void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw) sg_dma_address(sg) = dma->rx_buf_dma + sg->offset; } sg = dma->sg_rx_p; - desc_rx = dma->chan_rx->device->device_prep_slave_sg(dma->chan_rx, sg, + desc_rx = dmaengine_prep_slave_sg(dma->chan_rx, sg, num, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc_rx) { @@ -1123,7 +1123,7 @@ static void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw) sg_dma_address(sg) = dma->tx_buf_dma + sg->offset; } sg = dma->sg_tx_p; - desc_tx = dma->chan_tx->device->device_prep_slave_sg(dma->chan_tx, + desc_tx = dmaengine_prep_slave_sg(dma->chan_tx, sg, num, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc_tx) { diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index db7f88a1bdee..f9dcb5379b92 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -483,7 +483,7 @@ static int pl011_dma_tx_refill(struct uart_amba_port *uap) return -EBUSY; } - desc = dma_dev->device_prep_slave_sg(chan, &dmatx->sg, 1, DMA_MEM_TO_DEV, + desc = dmaengine_prep_slave_sg(chan, &dmatx->sg, 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { dma_unmap_sg(dma_dev->dev, &dmatx->sg, 1, DMA_TO_DEVICE); @@ -666,7 +666,6 @@ static void pl011_dma_rx_callback(void *data); static int pl011_dma_rx_trigger_dma(struct uart_amba_port *uap) { struct dma_chan *rxchan = uap->dmarx.chan; - struct dma_device *dma_dev; struct pl011_dmarx_data *dmarx = &uap->dmarx; struct dma_async_tx_descriptor *desc; struct pl011_sgbuf *sgbuf; @@ -677,8 +676,7 @@ static int pl011_dma_rx_trigger_dma(struct uart_amba_port *uap) /* Start the RX DMA job */ sgbuf = uap->dmarx.use_buf_b ? &uap->dmarx.sgbuf_b : &uap->dmarx.sgbuf_a; - dma_dev = rxchan->device; - desc = rxchan->device->device_prep_slave_sg(rxchan, &sgbuf->sg, 1, + desc = dmaengine_prep_slave_sg(rxchan, &sgbuf->sg, 1, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); /* diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index 17ae65762d1a..61743bdc439b 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -763,7 +763,7 @@ static int dma_handle_rx(struct eg20t_port *priv) sg_dma_address(sg) = priv->rx_buf_dma; - desc = priv->chan_rx->device->device_prep_slave_sg(priv->chan_rx, + desc = dmaengine_prep_slave_sg(priv->chan_rx, sg, 1, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); @@ -922,7 +922,7 @@ static unsigned int dma_handle_tx(struct eg20t_port *priv) sg_dma_len(sg) = size; } - desc = priv->chan_tx->device->device_prep_slave_sg(priv->chan_tx, + desc = dmaengine_prep_slave_sg(priv->chan_tx, priv->sg_tx_p, nent, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 75085795528e..872557f89cca 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1338,7 +1338,7 @@ static void sci_submit_rx(struct sci_port *s) struct scatterlist *sg = &s->sg_rx[i]; struct dma_async_tx_descriptor *desc; - desc = chan->device->device_prep_slave_sg(chan, + desc = dmaengine_prep_slave_sg(chan, sg, 1, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT); if (desc) { @@ -1453,7 +1453,7 @@ static void work_fn_tx(struct work_struct *work) BUG_ON(!sg_dma_len(sg)); - desc = chan->device->device_prep_slave_sg(chan, + desc = dmaengine_prep_slave_sg(chan, sg, s->sg_len_tx, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { diff --git a/drivers/usb/musb/ux500_dma.c b/drivers/usb/musb/ux500_dma.c index 2a36bf37d7aa..d05c7fbbb703 100644 --- a/drivers/usb/musb/ux500_dma.c +++ b/drivers/usb/musb/ux500_dma.c @@ -120,8 +120,7 @@ static bool ux500_configure_channel(struct dma_channel *channel, dma_chan->device->device_control(dma_chan, DMA_SLAVE_CONFIG, (unsigned long) &slave_conf); - dma_desc = dma_chan->device-> - device_prep_slave_sg(dma_chan, &sg, 1, direction, + dma_desc = dmaengine_prep_slave_sg(dma_chan, &sg, 1, direction, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!dma_desc) return false; diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 72339bd6fcab..7b4309339f3d 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -783,9 +783,8 @@ static void usbhsf_dma_prepare_tasklet(unsigned long data) sg_dma_address(&sg) = pkt->dma + pkt->actual; sg_dma_len(&sg) = pkt->trans; - desc = chan->device->device_prep_slave_sg(chan, &sg, 1, dir, - DMA_PREP_INTERRUPT | - DMA_CTRL_ACK); + desc = dmaengine_prep_slave_sg(chan, &sg, 1, dir, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) return; diff --git a/drivers/video/mx3fb.c b/drivers/video/mx3fb.c index 727a5149d818..eec0d7b748eb 100644 --- a/drivers/video/mx3fb.c +++ b/drivers/video/mx3fb.c @@ -337,7 +337,7 @@ static void sdc_enable_channel(struct mx3fb_info *mx3_fbi) /* This enables the channel */ if (mx3_fbi->cookie < 0) { - mx3_fbi->txd = dma_chan->device->device_prep_slave_sg(dma_chan, + mx3_fbi->txd = dmaengine_prep_slave_sg(dma_chan, &mx3_fbi->sg[0], 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT); if (!mx3_fbi->txd) { dev_err(mx3fb->dev, "Cannot allocate descriptor on %d\n", @@ -1091,7 +1091,7 @@ static int mx3fb_pan_display(struct fb_var_screeninfo *var, if (mx3_fbi->txd) async_tx_ack(mx3_fbi->txd); - txd = dma_chan->device->device_prep_slave_sg(dma_chan, sg + + txd = dmaengine_prep_slave_sg(dma_chan, sg + mx3_fbi->cur_ipu_buf, 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT); if (!txd) { dev_err(fbi->device, diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 715babf4bffe..03d68b7e5705 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -622,6 +622,22 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_slave_single( return chan->device->device_prep_slave_sg(chan, &sg, 1, dir, flags); } +static inline struct dma_async_tx_descriptor *dmaengine_prep_slave_sg( + struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, + enum dma_transfer_direction dir, unsigned long flags) +{ + return chan->device->device_prep_slave_sg(chan, sgl, sg_len, + dir, flags); +} + +static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_cyclic( + struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, + size_t period_len, enum dma_transfer_direction dir) +{ + return chan->device->device_prep_dma_cyclic(chan, buf_addr, buf_len, + period_len, dir); +} + static inline int dmaengine_terminate_all(struct dma_chan *chan) { return dmaengine_device_control(chan, DMA_TERMINATE_ALL, 0); diff --git a/sound/soc/ep93xx/ep93xx-pcm.c b/sound/soc/ep93xx/ep93xx-pcm.c index de8390449873..50593e50ad57 100644 --- a/sound/soc/ep93xx/ep93xx-pcm.c +++ b/sound/soc/ep93xx/ep93xx-pcm.c @@ -142,11 +142,10 @@ static int ep93xx_pcm_dma_submit(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; struct ep93xx_runtime_data *rtd = runtime->private_data; struct dma_chan *chan = rtd->dma_chan; - struct dma_device *dma_dev = chan->device; struct dma_async_tx_descriptor *desc; rtd->pointer_bytes = 0; - desc = dma_dev->device_prep_dma_cyclic(chan, runtime->dma_addr, + desc = dmaengine_prep_dma_cyclic(chan, runtime->dma_addr, rtd->period_bytes * rtd->periods, rtd->period_bytes, rtd->dma_data.direction); diff --git a/sound/soc/imx/imx-pcm-dma-mx2.c b/sound/soc/imx/imx-pcm-dma-mx2.c index 52b0dedbf996..af46b4e2b438 100644 --- a/sound/soc/imx/imx-pcm-dma-mx2.c +++ b/sound/soc/imx/imx-pcm-dma-mx2.c @@ -160,7 +160,7 @@ static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream, iprtd->buf = (unsigned int *)substream->dma_buffer.area; - iprtd->desc = chan->device->device_prep_dma_cyclic(chan, dma_addr, + iprtd->desc = dmaengine_prep_dma_cyclic(chan, dma_addr, iprtd->period_bytes * iprtd->periods, iprtd->period_bytes, substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? diff --git a/sound/soc/mxs/mxs-pcm.c b/sound/soc/mxs/mxs-pcm.c index 105f42a394df..661b678d08a8 100644 --- a/sound/soc/mxs/mxs-pcm.c +++ b/sound/soc/mxs/mxs-pcm.c @@ -132,7 +132,7 @@ static int snd_mxs_pcm_hw_params(struct snd_pcm_substream *substream, iprtd->buf = substream->dma_buffer.area; - iprtd->desc = chan->device->device_prep_dma_cyclic(chan, dma_addr, + iprtd->desc = dmaengine_prep_dma_cyclic(chan, dma_addr, iprtd->period_bytes * iprtd->periods, iprtd->period_bytes, substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? diff --git a/sound/soc/sh/siu_pcm.c b/sound/soc/sh/siu_pcm.c index 0193e595d415..5cfcc655e95f 100644 --- a/sound/soc/sh/siu_pcm.c +++ b/sound/soc/sh/siu_pcm.c @@ -130,7 +130,7 @@ static int siu_pcm_wr_set(struct siu_port *port_info, sg_dma_len(&sg) = size; sg_dma_address(&sg) = buff; - desc = siu_stream->chan->device->device_prep_slave_sg(siu_stream->chan, + desc = dmaengine_prep_slave_sg(siu_stream->chan, &sg, 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { dev_err(dev, "Failed to allocate a dma descriptor\n"); @@ -180,7 +180,7 @@ static int siu_pcm_rd_set(struct siu_port *port_info, sg_dma_len(&sg) = size; sg_dma_address(&sg) = buff; - desc = siu_stream->chan->device->device_prep_slave_sg(siu_stream->chan, + desc = dmaengine_prep_slave_sg(siu_stream->chan, &sg, 1, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { dev_err(dev, "Failed to allocate dma descriptor\n"); diff --git a/sound/soc/txx9/txx9aclc.c b/sound/soc/txx9/txx9aclc.c index 21554611557c..b609d2c64c55 100644 --- a/sound/soc/txx9/txx9aclc.c +++ b/sound/soc/txx9/txx9aclc.c @@ -132,7 +132,7 @@ txx9aclc_dma_submit(struct txx9aclc_dmadata *dmadata, dma_addr_t buf_dma_addr) sg_set_page(&sg, pfn_to_page(PFN_DOWN(buf_dma_addr)), dmadata->frag_bytes, buf_dma_addr & (PAGE_SIZE - 1)); sg_dma_address(&sg) = buf_dma_addr; - desc = chan->device->device_prep_slave_sg(chan, &sg, 1, + desc = dmaengine_prep_slave_sg(chan, &sg, 1, dmadata->substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); From 185ecb5f4fd43911c35956d4cc7d94a1da30417f Mon Sep 17 00:00:00 2001 From: Alexandre Bounine Date: Thu, 8 Mar 2012 15:35:13 -0500 Subject: [PATCH 50/67] dmaengine: add context parameter to prep_slave_sg and prep_dma_cyclic Add context parameter to device_prep_slave_sg() and device_prep_dma_cyclic() interfaces to allow passing client/target specific information associated with the data transfer. Modify all affected DMA engine drivers. Signed-off-by: Alexandre Bounine Acked-by: Linus Walleij Acked-by: Felipe Balbi Signed-off-by: Vinod Koul --- drivers/dma/amba-pl08x.c | 2 +- drivers/dma/at_hdmac.c | 7 +++++-- drivers/dma/coh901318.c | 2 +- drivers/dma/dw_dmac.c | 2 +- drivers/dma/ep93xx_dma.c | 6 ++++-- drivers/dma/fsldma.c | 4 +++- drivers/dma/imx-dma.c | 5 +++-- drivers/dma/imx-sdma.c | 5 +++-- drivers/dma/intel_mid_dma.c | 3 ++- drivers/dma/ipu/ipu_idmac.c | 3 ++- drivers/dma/mxs-dma.c | 5 +++-- drivers/dma/pch_dma.c | 3 ++- drivers/dma/pl330.c | 5 +++-- drivers/dma/shdma.c | 3 ++- drivers/dma/sirf-dma.c | 2 +- drivers/dma/ste_dma40.c | 5 +++-- drivers/dma/timb_dma.c | 3 ++- drivers/dma/txx9dmac.c | 2 +- include/linux/dmaengine.h | 12 +++++++----- 19 files changed, 49 insertions(+), 30 deletions(-) diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 1b53f2605250..c301a8ec31aa 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -1313,7 +1313,7 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy( static struct dma_async_tx_descriptor *pl08x_prep_slave_sg( struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, enum dma_transfer_direction direction, - unsigned long flags) + unsigned long flags, void *context) { struct pl08x_dma_chan *plchan = to_pl08x_chan(chan); struct pl08x_driver_data *pl08x = plchan->host; diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 5d225ddc7698..f24b16e455fd 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -639,11 +639,12 @@ err_desc_get: * @sg_len: number of entries in @scatterlist * @direction: DMA direction * @flags: tx descriptor status flags + * @context: transaction context (ignored) */ static struct dma_async_tx_descriptor * atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, enum dma_transfer_direction direction, - unsigned long flags) + unsigned long flags, void *context) { struct at_dma_chan *atchan = to_at_dma_chan(chan); struct at_dma_slave *atslave = chan->private; @@ -840,10 +841,12 @@ atc_dma_cyclic_fill_desc(struct at_dma_slave *atslave, struct at_desc *desc, * @buf_len: total number of bytes for the entire buffer * @period_len: number of bytes for each period * @direction: transfer direction, to or from device + * @context: transfer context (ignored) */ static struct dma_async_tx_descriptor * atc_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, - size_t period_len, enum dma_transfer_direction direction) + size_t period_len, enum dma_transfer_direction direction, + void *context) { struct at_dma_chan *atchan = to_at_dma_chan(chan); struct at_dma_slave *atslave = chan->private; diff --git a/drivers/dma/coh901318.c b/drivers/dma/coh901318.c index 187bb9eef4a2..dc89455f5550 100644 --- a/drivers/dma/coh901318.c +++ b/drivers/dma/coh901318.c @@ -1021,7 +1021,7 @@ coh901318_prep_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, static struct dma_async_tx_descriptor * coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, enum dma_transfer_direction direction, - unsigned long flags) + unsigned long flags, void *context) { struct coh901318_chan *cohc = to_coh901318_chan(chan); struct coh901318_lli *lli; diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index cb173bbdcfdf..7439079f5eed 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -704,7 +704,7 @@ err_desc_get: static struct dma_async_tx_descriptor * dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, enum dma_transfer_direction direction, - unsigned long flags) + unsigned long flags, void *context) { struct dw_dma_chan *dwc = to_dw_dma_chan(chan); struct dw_dma_slave *dws = chan->private; diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c index f25e83bf5678..e6f133b78dc2 100644 --- a/drivers/dma/ep93xx_dma.c +++ b/drivers/dma/ep93xx_dma.c @@ -975,13 +975,14 @@ fail: * @sg_len: number of entries in @sgl * @dir: direction of tha DMA transfer * @flags: flags for the descriptor + * @context: operation context (ignored) * * Returns a valid DMA descriptor or %NULL in case of failure. */ static struct dma_async_tx_descriptor * ep93xx_dma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, enum dma_transfer_direction dir, - unsigned long flags) + unsigned long flags, void *context) { struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan); struct ep93xx_dma_desc *desc, *first; @@ -1048,6 +1049,7 @@ fail: * @buf_len: length of the buffer (in bytes) * @period_len: lenght of a single period * @dir: direction of the operation + * @context: operation context (ignored) * * Prepares a descriptor for cyclic DMA operation. This means that once the * descriptor is submitted, we will be submitting in a @period_len sized @@ -1060,7 +1062,7 @@ fail: static struct dma_async_tx_descriptor * ep93xx_dma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len, size_t period_len, - enum dma_transfer_direction dir) + enum dma_transfer_direction dir, void *context) { struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan); struct ep93xx_dma_desc *desc, *first; diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index 7d7384b34621..8f84761f98ba 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -759,6 +759,7 @@ fail: * @sg_len: number of entries in @scatterlist * @direction: DMA direction * @flags: DMAEngine flags + * @context: transaction context (ignored) * * Prepare a set of descriptors for a DMA_SLAVE transaction. Following the * DMA_SLAVE API, this gets the device-specific information from the @@ -766,7 +767,8 @@ fail: */ static struct dma_async_tx_descriptor *fsl_dma_prep_slave_sg( struct dma_chan *dchan, struct scatterlist *sgl, unsigned int sg_len, - enum dma_transfer_direction direction, unsigned long flags) + enum dma_transfer_direction direction, unsigned long flags, + void *context) { /* * This operation is not supported on the Freescale DMA controller diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index 20c1565a7486..304839a99ae5 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -354,7 +354,7 @@ static void imxdma_free_chan_resources(struct dma_chan *chan) static struct dma_async_tx_descriptor *imxdma_prep_slave_sg( struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, enum dma_transfer_direction direction, - unsigned long flags) + unsigned long flags, void *context) { struct imxdma_channel *imxdmac = to_imxdma_chan(chan); struct scatterlist *sg; @@ -405,7 +405,8 @@ static struct dma_async_tx_descriptor *imxdma_prep_slave_sg( static struct dma_async_tx_descriptor *imxdma_prep_dma_cyclic( struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len, - size_t period_len, enum dma_transfer_direction direction) + size_t period_len, enum dma_transfer_direction direction, + void *context) { struct imxdma_channel *imxdmac = to_imxdma_chan(chan); struct imxdma_engine *imxdma = imxdmac->imxdma; diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index f8259ee49f86..434fb610aa1a 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -903,7 +903,7 @@ static void sdma_free_chan_resources(struct dma_chan *chan) static struct dma_async_tx_descriptor *sdma_prep_slave_sg( struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, enum dma_transfer_direction direction, - unsigned long flags) + unsigned long flags, void *context) { struct sdma_channel *sdmac = to_sdma_chan(chan); struct sdma_engine *sdma = sdmac->sdma; @@ -1001,7 +1001,8 @@ err_out: static struct dma_async_tx_descriptor *sdma_prep_dma_cyclic( struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len, - size_t period_len, enum dma_transfer_direction direction) + size_t period_len, enum dma_transfer_direction direction, + void *context) { struct sdma_channel *sdmac = to_sdma_chan(chan); struct sdma_engine *sdma = sdmac->sdma; diff --git a/drivers/dma/intel_mid_dma.c b/drivers/dma/intel_mid_dma.c index 2449812f5464..c900ca7aaec4 100644 --- a/drivers/dma/intel_mid_dma.c +++ b/drivers/dma/intel_mid_dma.c @@ -714,13 +714,14 @@ err_desc_get: * @sg_len: length of sg txn * @direction: DMA transfer dirtn * @flags: DMA flags + * @context: transfer context (ignored) * * Prepares LLI based periphral transfer */ static struct dma_async_tx_descriptor *intel_mid_dma_prep_slave_sg( struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, enum dma_transfer_direction direction, - unsigned long flags) + unsigned long flags, void *context) { struct intel_mid_dma_chan *midc = NULL; struct intel_mid_dma_slave *mids = NULL; diff --git a/drivers/dma/ipu/ipu_idmac.c b/drivers/dma/ipu/ipu_idmac.c index 1880274b0850..62e3f8ec2461 100644 --- a/drivers/dma/ipu/ipu_idmac.c +++ b/drivers/dma/ipu/ipu_idmac.c @@ -1335,7 +1335,8 @@ static void ipu_gc_tasklet(unsigned long arg) /* Allocate and initialise a transfer descriptor. */ static struct dma_async_tx_descriptor *idmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, - enum dma_transfer_direction direction, unsigned long tx_flags) + enum dma_transfer_direction direction, unsigned long tx_flags, + void *context) { struct idmac_channel *ichan = to_idmac_chan(chan); struct idmac_tx_desc *desc = NULL; diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index a2267f9ab568..65334c49b71e 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c @@ -340,7 +340,7 @@ static void mxs_dma_free_chan_resources(struct dma_chan *chan) static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg( struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, enum dma_transfer_direction direction, - unsigned long append) + unsigned long append, void *context) { struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan); struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; @@ -435,7 +435,8 @@ err_out: static struct dma_async_tx_descriptor *mxs_dma_prep_dma_cyclic( struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len, - size_t period_len, enum dma_transfer_direction direction) + size_t period_len, enum dma_transfer_direction direction, + void *context) { struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan); struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; diff --git a/drivers/dma/pch_dma.c b/drivers/dma/pch_dma.c index c93bb0459972..65c0495a6d40 100644 --- a/drivers/dma/pch_dma.c +++ b/drivers/dma/pch_dma.c @@ -587,7 +587,8 @@ static void pd_issue_pending(struct dma_chan *chan) static struct dma_async_tx_descriptor *pd_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, - enum dma_transfer_direction direction, unsigned long flags) + enum dma_transfer_direction direction, unsigned long flags, + void *context) { struct pch_dma_chan *pd_chan = to_pd_chan(chan); struct pch_dma_slave *pd_slave = chan->private; diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index e863d7fc465a..87d752a77f5e 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -2685,7 +2685,8 @@ static inline int get_burst_len(struct dma_pl330_desc *desc, size_t len) static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic( struct dma_chan *chan, dma_addr_t dma_addr, size_t len, - size_t period_len, enum dma_transfer_direction direction) + size_t period_len, enum dma_transfer_direction direction, + void *context) { struct dma_pl330_desc *desc; struct dma_pl330_chan *pch = to_pchan(chan); @@ -2775,7 +2776,7 @@ pl330_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dst, static struct dma_async_tx_descriptor * pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, enum dma_transfer_direction direction, - unsigned long flg) + unsigned long flg, void *context) { struct dma_pl330_desc *first, *desc = NULL; struct dma_pl330_chan *pch = to_pchan(chan); diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c index 5c4088603dd4..19d7a8d3975d 100644 --- a/drivers/dma/shdma.c +++ b/drivers/dma/shdma.c @@ -669,7 +669,8 @@ static struct dma_async_tx_descriptor *sh_dmae_prep_memcpy( static struct dma_async_tx_descriptor *sh_dmae_prep_slave_sg( struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, - enum dma_transfer_direction direction, unsigned long flags) + enum dma_transfer_direction direction, unsigned long flags, + void *context) { struct sh_dmae_slave *param; struct sh_dmae_chan *sh_chan; diff --git a/drivers/dma/sirf-dma.c b/drivers/dma/sirf-dma.c index 45ba352fb871..434ad31174f2 100644 --- a/drivers/dma/sirf-dma.c +++ b/drivers/dma/sirf-dma.c @@ -489,7 +489,7 @@ err_dir: static struct dma_async_tx_descriptor * sirfsoc_dma_prep_cyclic(struct dma_chan *chan, dma_addr_t addr, size_t buf_len, size_t period_len, - enum dma_transfer_direction direction) + enum dma_transfer_direction direction, void *context) { struct sirfsoc_dma_chan *schan = dma_chan_to_sirfsoc_dma_chan(chan); struct sirfsoc_dma_desc *sdesc = NULL; diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 1ea6d02d08ab..bdd41d4bfa8d 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -2289,7 +2289,8 @@ static struct dma_async_tx_descriptor *d40_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, enum dma_transfer_direction direction, - unsigned long dma_flags) + unsigned long dma_flags, + void *context) { if (direction != DMA_DEV_TO_MEM && direction != DMA_MEM_TO_DEV) return NULL; @@ -2300,7 +2301,7 @@ static struct dma_async_tx_descriptor *d40_prep_slave_sg(struct dma_chan *chan, static struct dma_async_tx_descriptor * dma40_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len, size_t period_len, - enum dma_transfer_direction direction) + enum dma_transfer_direction direction, void *context) { unsigned int periods = buf_len / period_len; struct dma_async_tx_descriptor *txd; diff --git a/drivers/dma/timb_dma.c b/drivers/dma/timb_dma.c index d408c2206023..4e0dff59901d 100644 --- a/drivers/dma/timb_dma.c +++ b/drivers/dma/timb_dma.c @@ -542,7 +542,8 @@ static void td_issue_pending(struct dma_chan *chan) static struct dma_async_tx_descriptor *td_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, - enum dma_transfer_direction direction, unsigned long flags) + enum dma_transfer_direction direction, unsigned long flags, + void *context) { struct timb_dma_chan *td_chan = container_of(chan, struct timb_dma_chan, chan); diff --git a/drivers/dma/txx9dmac.c b/drivers/dma/txx9dmac.c index 40440f946385..913f55c76c99 100644 --- a/drivers/dma/txx9dmac.c +++ b/drivers/dma/txx9dmac.c @@ -833,7 +833,7 @@ txx9dmac_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, static struct dma_async_tx_descriptor * txx9dmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, enum dma_transfer_direction direction, - unsigned long flags) + unsigned long flags, void *context) { struct txx9dmac_chan *dc = to_txx9dmac_chan(chan); struct txx9dmac_dev *ddev = dc->ddev; diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 03d68b7e5705..b3b5b38776f0 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -582,10 +582,11 @@ struct dma_device { struct dma_async_tx_descriptor *(*device_prep_slave_sg)( struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, enum dma_transfer_direction direction, - unsigned long flags); + unsigned long flags, void *context); struct dma_async_tx_descriptor *(*device_prep_dma_cyclic)( struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, - size_t period_len, enum dma_transfer_direction direction); + size_t period_len, enum dma_transfer_direction direction, + void *context); struct dma_async_tx_descriptor *(*device_prep_interleaved_dma)( struct dma_chan *chan, struct dma_interleaved_template *xt, unsigned long flags); @@ -619,7 +620,8 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_slave_single( struct scatterlist sg; sg_init_one(&sg, buf, len); - return chan->device->device_prep_slave_sg(chan, &sg, 1, dir, flags); + return chan->device->device_prep_slave_sg(chan, &sg, 1, + dir, flags, NULL); } static inline struct dma_async_tx_descriptor *dmaengine_prep_slave_sg( @@ -627,7 +629,7 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_slave_sg( enum dma_transfer_direction dir, unsigned long flags) { return chan->device->device_prep_slave_sg(chan, sgl, sg_len, - dir, flags); + dir, flags, NULL); } static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_cyclic( @@ -635,7 +637,7 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_cyclic( size_t period_len, enum dma_transfer_direction dir) { return chan->device->device_prep_dma_cyclic(chan, buf_addr, buf_len, - period_len, dir); + period_len, dir, NULL); } static inline int dmaengine_terminate_all(struct dma_chan *chan) From beeaa103eecc7a132682c40867f0ef70655383a5 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Wed, 14 Mar 2012 12:41:43 +0100 Subject: [PATCH 51/67] dmaengine: at_hdmac: add slave config operation This patch introduces DMA_SLAVE_CONFIG to at_hdmac Atmel DMA driver. It is needed to fix a regression in the use of atmel-mci.c driver on Atmel AT91 platforms brouth by e2b35f3: "dmaengine/dw_dmac: Fix dw_dmac user drivers to adapt to slave_config changes" We remove some parts of the private structure "at_dma_slave" and use the information provided by "struct dma_slave_config": source/destination peripheral registers and access width. AT_DMA_SLAVE_WIDTH_* values used previously are not needed anymore as we now use the standard ones. Although some conversion functions are needed to match register expected values. Some AT91 sub-architecture specific files are slightly touched by this patch but it cannot be split because it can break compilation. Signed-off-by: Nicolas Ferre Signed-off-by: Vinod Koul --- arch/arm/mach-at91/at91sam9g45_devices.c | 1 - arch/arm/mach-at91/include/mach/at_hdmac.h | 15 ------ drivers/dma/at_hdmac.c | 56 +++++++++++++++++----- drivers/dma/at_hdmac_regs.h | 32 +++++++++++++ 4 files changed, 75 insertions(+), 29 deletions(-) diff --git a/arch/arm/mach-at91/at91sam9g45_devices.c b/arch/arm/mach-at91/at91sam9g45_devices.c index b7582dd10dc3..1f89b206c26f 100644 --- a/arch/arm/mach-at91/at91sam9g45_devices.c +++ b/arch/arm/mach-at91/at91sam9g45_devices.c @@ -431,7 +431,6 @@ void __init at91_add_device_mci(short mmc_id, struct mci_platform_data *data) /* DMA slave channel configuration */ atslave->dma_dev = &at_hdmac_device.dev; - atslave->reg_width = AT_DMA_SLAVE_WIDTH_32BIT; atslave->cfg = ATC_FIFOCFG_HALFFIFO | ATC_SRC_H2SEL_HW | ATC_DST_H2SEL_HW; atslave->ctrla = ATC_SCSIZE_16 | ATC_DCSIZE_16; diff --git a/arch/arm/mach-at91/include/mach/at_hdmac.h b/arch/arm/mach-at91/include/mach/at_hdmac.h index 187cb58345c0..fff48d1a0f4e 100644 --- a/arch/arm/mach-at91/include/mach/at_hdmac.h +++ b/arch/arm/mach-at91/include/mach/at_hdmac.h @@ -23,18 +23,6 @@ struct at_dma_platform_data { dma_cap_mask_t cap_mask; }; -/** - * enum at_dma_slave_width - DMA slave register access width. - * @AT_DMA_SLAVE_WIDTH_8BIT: Do 8-bit slave register accesses - * @AT_DMA_SLAVE_WIDTH_16BIT: Do 16-bit slave register accesses - * @AT_DMA_SLAVE_WIDTH_32BIT: Do 32-bit slave register accesses - */ -enum at_dma_slave_width { - AT_DMA_SLAVE_WIDTH_8BIT = 0, - AT_DMA_SLAVE_WIDTH_16BIT, - AT_DMA_SLAVE_WIDTH_32BIT, -}; - /** * struct at_dma_slave - Controller-specific information about a slave * @dma_dev: required DMA master device @@ -48,9 +36,6 @@ enum at_dma_slave_width { */ struct at_dma_slave { struct device *dma_dev; - dma_addr_t tx_reg; - dma_addr_t rx_reg; - enum at_dma_slave_width reg_width; u32 cfg; u32 ctrla; }; diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index f24b16e455fd..7aa58d204892 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -648,6 +648,7 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, { 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; struct at_desc *first = NULL; struct at_desc *prev = NULL; u32 ctrla; @@ -669,19 +670,18 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, return NULL; } - reg_width = atslave->reg_width; - ctrla = ATC_DEFAULT_CTRLA | atslave->ctrla; ctrlb = ATC_IEN; switch (direction) { case DMA_MEM_TO_DEV: + reg_width = convert_buswidth(sconfig->dst_addr_width); ctrla |= ATC_DST_WIDTH(reg_width); ctrlb |= ATC_DST_ADDR_MODE_FIXED | ATC_SRC_ADDR_MODE_INCR | ATC_FC_MEM2PER | ATC_SIF(AT_DMA_MEM_IF) | ATC_DIF(AT_DMA_PER_IF); - reg = atslave->tx_reg; + reg = sconfig->dst_addr; for_each_sg(sgl, sg, sg_len, i) { struct at_desc *desc; u32 len; @@ -709,13 +709,14 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, } break; case DMA_DEV_TO_MEM: + reg_width = convert_buswidth(sconfig->src_addr_width); ctrla |= ATC_SRC_WIDTH(reg_width); ctrlb |= ATC_DST_ADDR_MODE_INCR | ATC_SRC_ADDR_MODE_FIXED | ATC_FC_PER2MEM | ATC_SIF(AT_DMA_PER_IF) | ATC_DIF(AT_DMA_MEM_IF); - reg = atslave->rx_reg; + reg = sconfig->src_addr; for_each_sg(sgl, sg, sg_len, i) { struct at_desc *desc; u32 len; @@ -791,12 +792,15 @@ err_out: * atc_dma_cyclic_fill_desc - Fill one period decriptor */ static int -atc_dma_cyclic_fill_desc(struct at_dma_slave *atslave, struct at_desc *desc, +atc_dma_cyclic_fill_desc(struct dma_chan *chan, struct at_desc *desc, unsigned int period_index, dma_addr_t buf_addr, - size_t period_len, enum dma_transfer_direction direction) + unsigned int reg_width, size_t period_len, + enum dma_transfer_direction direction) { - u32 ctrla; - unsigned int reg_width = atslave->reg_width; + 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 = ATC_DEFAULT_CTRLA | atslave->ctrla @@ -807,7 +811,7 @@ atc_dma_cyclic_fill_desc(struct at_dma_slave *atslave, struct at_desc *desc, switch (direction) { case DMA_MEM_TO_DEV: desc->lli.saddr = buf_addr + (period_len * period_index); - desc->lli.daddr = atslave->tx_reg; + desc->lli.daddr = sconfig->dst_addr; desc->lli.ctrla = ctrla; desc->lli.ctrlb = ATC_DST_ADDR_MODE_FIXED | ATC_SRC_ADDR_MODE_INCR @@ -817,7 +821,7 @@ atc_dma_cyclic_fill_desc(struct at_dma_slave *atslave, struct at_desc *desc, break; case DMA_DEV_TO_MEM: - desc->lli.saddr = atslave->rx_reg; + desc->lli.saddr = sconfig->src_addr; desc->lli.daddr = buf_addr + (period_len * period_index); desc->lli.ctrla = ctrla; desc->lli.ctrlb = ATC_DST_ADDR_MODE_INCR @@ -850,9 +854,11 @@ atc_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, { 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; struct at_desc *first = NULL; struct at_desc *prev = NULL; unsigned long was_cyclic; + unsigned int reg_width; unsigned int periods = buf_len / period_len; unsigned int i; @@ -872,8 +878,13 @@ atc_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, return NULL; } + if (sconfig->direction == DMA_MEM_TO_DEV) + reg_width = convert_buswidth(sconfig->dst_addr_width); + else + reg_width = convert_buswidth(sconfig->src_addr_width); + /* Check for too big/unaligned periods and unaligned DMA buffer */ - if (atc_dma_cyclic_check_values(atslave->reg_width, buf_addr, + if (atc_dma_cyclic_check_values(reg_width, buf_addr, period_len, direction)) goto err_out; @@ -885,8 +896,8 @@ atc_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, if (!desc) goto err_desc_get; - if (atc_dma_cyclic_fill_desc(atslave, desc, i, buf_addr, - period_len, direction)) + if (atc_dma_cyclic_fill_desc(chan, desc, i, buf_addr, + reg_width, period_len, direction)) goto err_desc_get; atc_desc_chain(&first, &prev, desc); @@ -909,6 +920,23 @@ err_out: return NULL; } +static int set_runtime_config(struct dma_chan *chan, + struct dma_slave_config *sconfig) +{ + struct at_dma_chan *atchan = to_at_dma_chan(chan); + + /* Check if it is chan is configured for slave transfers */ + if (!chan->private) + return -EINVAL; + + memcpy(&atchan->dma_sconfig, sconfig, sizeof(*sconfig)); + + convert_burst(&atchan->dma_sconfig.src_maxburst); + convert_burst(&atchan->dma_sconfig.dst_maxburst); + + return 0; +} + static int atc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long arg) @@ -969,6 +997,8 @@ static int atc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, clear_bit(ATC_IS_CYCLIC, &atchan->status); spin_unlock_irqrestore(&atchan->lock, flags); + } else if (cmd == DMA_SLAVE_CONFIG) { + return set_runtime_config(chan, (struct dma_slave_config *)arg); } else { return -ENXIO; } diff --git a/drivers/dma/at_hdmac_regs.h b/drivers/dma/at_hdmac_regs.h index 08fd8a0ae797..897a8bcaec90 100644 --- a/drivers/dma/at_hdmac_regs.h +++ b/drivers/dma/at_hdmac_regs.h @@ -207,6 +207,7 @@ enum atc_status { * @save_cfg: configuration register that is saved on suspend/resume cycle * @save_dscr: for cyclic operations, preserve next descriptor address in * the cyclic list on suspend/resume cycle + * @dma_sconfig: configuration for slave transfers, passed via DMA_SLAVE_CONFIG * @lock: serializes enqueue/dequeue operations to descriptors lists * @active_list: list of descriptors dmaengine is being running on * @queue: list of descriptors ready to be submitted to engine @@ -222,6 +223,7 @@ struct at_dma_chan { struct tasklet_struct tasklet; u32 save_cfg; u32 save_dscr; + struct dma_slave_config dma_sconfig; spinlock_t lock; @@ -243,6 +245,36 @@ static inline struct at_dma_chan *to_at_dma_chan(struct dma_chan *dchan) return container_of(dchan, struct at_dma_chan, chan_common); } +/* + * Fix sconfig's burst size according to at_hdmac. We need to convert them as: + * 1 -> 0, 4 -> 1, 8 -> 2, 16 -> 3, 32 -> 4, 64 -> 5, 128 -> 6, 256 -> 7. + * + * This can be done by finding most significant bit set. + */ +static inline void convert_burst(u32 *maxburst) +{ + if (*maxburst > 1) + *maxburst = fls(*maxburst) - 2; + else + *maxburst = 0; +} + +/* + * Fix sconfig's bus width according to at_hdmac. + * 1 byte -> 0, 2 bytes -> 1, 4 bytes -> 2. + */ +static inline u8 convert_buswidth(enum dma_slave_buswidth addr_width) +{ + switch (addr_width) { + case DMA_SLAVE_BUSWIDTH_2_BYTES: + return 1; + case DMA_SLAVE_BUSWIDTH_4_BYTES: + return 2; + default: + /* For 1 byte width or fallback */ + return 0; + } +} /*-- Controller ------------------------------------------------------*/ From 6bd081277ea03e2b165fc68534b61bc64db93990 Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Thu, 22 Mar 2012 14:54:01 +0100 Subject: [PATCH 52/67] dmaengine: imx-dma: merge old dma-v1.c with imx-dma.c It is mainly a simple merge changing the prefix of some functions to fit the imx-dma namings. As there are no users of the old dma-v1.c api we can safely remove this file. Signed-off-by: Javier Martin Acked-by: Sascha Hauer Signed-off-by: Vinod Koul --- arch/arm/mach-imx/Kconfig | 6 - arch/arm/mach-imx/Makefile | 2 - arch/arm/mach-imx/dma-v1.c | 846 ------------------------ arch/arm/mach-imx/include/mach/dma-v1.h | 103 --- drivers/dma/Kconfig | 1 - drivers/dma/imx-dma.c | 612 +++++++++++++++-- 6 files changed, 556 insertions(+), 1014 deletions(-) delete mode 100644 arch/arm/mach-imx/dma-v1.c delete mode 100644 arch/arm/mach-imx/include/mach/dma-v1.h diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig index 0e6de366c648..3da1421c944f 100644 --- a/arch/arm/mach-imx/Kconfig +++ b/arch/arm/mach-imx/Kconfig @@ -1,6 +1,3 @@ -config IMX_HAVE_DMA_V1 - bool - config HAVE_IMX_GPC bool @@ -26,7 +23,6 @@ config SOC_IMX1 bool select ARCH_MX1 select CPU_ARM920T - select IMX_HAVE_DMA_V1 select IMX_HAVE_IOMUX_V1 select MXC_AVIC @@ -35,7 +31,6 @@ config SOC_IMX21 select MACH_MX21 select CPU_ARM926T select ARCH_MXC_AUDMUX_V1 - select IMX_HAVE_DMA_V1 select IMX_HAVE_IOMUX_V1 select MXC_AVIC @@ -52,7 +47,6 @@ config SOC_IMX27 select MACH_MX27 select CPU_ARM926T select ARCH_MXC_AUDMUX_V1 - select IMX_HAVE_DMA_V1 select IMX_HAVE_IOMUX_V1 select MXC_AVIC diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile index f5920c24f7d7..a62dc3ac9dc6 100644 --- a/arch/arm/mach-imx/Makefile +++ b/arch/arm/mach-imx/Makefile @@ -1,5 +1,3 @@ -obj-$(CONFIG_IMX_HAVE_DMA_V1) += dma-v1.o - obj-$(CONFIG_SOC_IMX1) += clock-imx1.o mm-imx1.o obj-$(CONFIG_SOC_IMX21) += clock-imx21.o mm-imx21.o diff --git a/arch/arm/mach-imx/dma-v1.c b/arch/arm/mach-imx/dma-v1.c deleted file mode 100644 index 42afc29a7da8..000000000000 --- a/arch/arm/mach-imx/dma-v1.c +++ /dev/null @@ -1,846 +0,0 @@ -/* - * linux/arch/arm/plat-mxc/dma-v1.c - * - * i.MX DMA registration and IRQ dispatching - * - * Copyright 2006 Pavel Pisa - * Copyright 2008 Juergen Beisert, - * Copyright 2008 Sascha Hauer, - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#define DMA_DCR 0x00 /* Control Register */ -#define DMA_DISR 0x04 /* Interrupt status Register */ -#define DMA_DIMR 0x08 /* Interrupt mask Register */ -#define DMA_DBTOSR 0x0c /* Burst timeout status Register */ -#define DMA_DRTOSR 0x10 /* Request timeout Register */ -#define DMA_DSESR 0x14 /* Transfer Error Status Register */ -#define DMA_DBOSR 0x18 /* Buffer overflow status Register */ -#define DMA_DBTOCR 0x1c /* Burst timeout control Register */ -#define DMA_WSRA 0x40 /* W-Size Register A */ -#define DMA_XSRA 0x44 /* X-Size Register A */ -#define DMA_YSRA 0x48 /* Y-Size Register A */ -#define DMA_WSRB 0x4c /* W-Size Register B */ -#define DMA_XSRB 0x50 /* X-Size Register B */ -#define DMA_YSRB 0x54 /* Y-Size Register B */ -#define DMA_SAR(x) (0x80 + ((x) << 6)) /* Source Address Registers */ -#define DMA_DAR(x) (0x84 + ((x) << 6)) /* Destination Address Registers */ -#define DMA_CNTR(x) (0x88 + ((x) << 6)) /* Count Registers */ -#define DMA_CCR(x) (0x8c + ((x) << 6)) /* Control Registers */ -#define DMA_RSSR(x) (0x90 + ((x) << 6)) /* Request source select Registers */ -#define DMA_BLR(x) (0x94 + ((x) << 6)) /* Burst length Registers */ -#define DMA_RTOR(x) (0x98 + ((x) << 6)) /* Request timeout Registers */ -#define DMA_BUCR(x) (0x98 + ((x) << 6)) /* Bus Utilization Registers */ -#define DMA_CCNR(x) (0x9C + ((x) << 6)) /* Channel counter Registers */ - -#define DCR_DRST (1<<1) -#define DCR_DEN (1<<0) -#define DBTOCR_EN (1<<15) -#define DBTOCR_CNT(x) ((x) & 0x7fff) -#define CNTR_CNT(x) ((x) & 0xffffff) -#define CCR_ACRPT (1<<14) -#define CCR_DMOD_LINEAR (0x0 << 12) -#define CCR_DMOD_2D (0x1 << 12) -#define CCR_DMOD_FIFO (0x2 << 12) -#define CCR_DMOD_EOBFIFO (0x3 << 12) -#define CCR_SMOD_LINEAR (0x0 << 10) -#define CCR_SMOD_2D (0x1 << 10) -#define CCR_SMOD_FIFO (0x2 << 10) -#define CCR_SMOD_EOBFIFO (0x3 << 10) -#define CCR_MDIR_DEC (1<<9) -#define CCR_MSEL_B (1<<8) -#define CCR_DSIZ_32 (0x0 << 6) -#define CCR_DSIZ_8 (0x1 << 6) -#define CCR_DSIZ_16 (0x2 << 6) -#define CCR_SSIZ_32 (0x0 << 4) -#define CCR_SSIZ_8 (0x1 << 4) -#define CCR_SSIZ_16 (0x2 << 4) -#define CCR_REN (1<<3) -#define CCR_RPT (1<<2) -#define CCR_FRC (1<<1) -#define CCR_CEN (1<<0) -#define RTOR_EN (1<<15) -#define RTOR_CLK (1<<14) -#define RTOR_PSC (1<<13) - -/* - * struct imx_dma_channel - i.MX specific DMA extension - * @name: name specified by DMA client - * @irq_handler: client callback for end of transfer - * @err_handler: client callback for error condition - * @data: clients context data for callbacks - * @dma_mode: direction of the transfer %DMA_MODE_READ or %DMA_MODE_WRITE - * @sg: pointer to the actual read/written chunk for scatter-gather emulation - * @resbytes: total residual number of bytes to transfer - * (it can be lower or same as sum of SG mapped chunk sizes) - * @sgcount: number of chunks to be read/written - * - * Structure is used for IMX DMA processing. It would be probably good - * @struct dma_struct in the future for external interfacing and use - * @struct imx_dma_channel only as extension to it. - */ - -struct imx_dma_channel { - const char *name; - void (*irq_handler) (int, void *); - void (*err_handler) (int, void *, int errcode); - void (*prog_handler) (int, void *, struct scatterlist *); - void *data; - unsigned int dma_mode; - struct scatterlist *sg; - unsigned int resbytes; - int dma_num; - - int in_use; - - u32 ccr_from_device; - u32 ccr_to_device; - - struct timer_list watchdog; - - int hw_chaining; -}; - -static void __iomem *imx_dmav1_baseaddr; - -static void imx_dmav1_writel(unsigned val, unsigned offset) -{ - __raw_writel(val, imx_dmav1_baseaddr + offset); -} - -static unsigned imx_dmav1_readl(unsigned offset) -{ - return __raw_readl(imx_dmav1_baseaddr + offset); -} - -static struct imx_dma_channel imx_dma_channels[IMX_DMA_CHANNELS]; - -static struct clk *dma_clk; - -static int imx_dma_hw_chain(struct imx_dma_channel *imxdma) -{ - if (cpu_is_mx27()) - return imxdma->hw_chaining; - else - return 0; -} - -/* - * imx_dma_sg_next - prepare next chunk for scatter-gather DMA emulation - */ -static inline int imx_dma_sg_next(int channel, struct scatterlist *sg) -{ - struct imx_dma_channel *imxdma = &imx_dma_channels[channel]; - unsigned long now; - - if (!imxdma->name) { - printk(KERN_CRIT "%s: called for not allocated channel %d\n", - __func__, channel); - return 0; - } - - now = min(imxdma->resbytes, sg->length); - if (imxdma->resbytes != IMX_DMA_LENGTH_LOOP) - imxdma->resbytes -= now; - - if ((imxdma->dma_mode & DMA_MODE_MASK) == DMA_MODE_READ) - imx_dmav1_writel(sg->dma_address, DMA_DAR(channel)); - else - imx_dmav1_writel(sg->dma_address, DMA_SAR(channel)); - - imx_dmav1_writel(now, DMA_CNTR(channel)); - - pr_debug("imxdma%d: next sg chunk dst 0x%08x, src 0x%08x, " - "size 0x%08x\n", channel, - imx_dmav1_readl(DMA_DAR(channel)), - imx_dmav1_readl(DMA_SAR(channel)), - imx_dmav1_readl(DMA_CNTR(channel))); - - return now; -} - -/** - * imx_dma_setup_single - setup i.MX DMA channel for linear memory to/from - * device transfer - * - * @channel: i.MX DMA channel number - * @dma_address: the DMA/physical memory address of the linear data block - * to transfer - * @dma_length: length of the data block in bytes - * @dev_addr: physical device port address - * @dmamode: DMA transfer mode, %DMA_MODE_READ from the device to the memory - * or %DMA_MODE_WRITE from memory to the device - * - * Return value: if incorrect parameters are provided -%EINVAL. - * Zero indicates success. - */ -int -imx_dma_setup_single(int channel, dma_addr_t dma_address, - unsigned int dma_length, unsigned int dev_addr, - unsigned int dmamode) -{ - struct imx_dma_channel *imxdma = &imx_dma_channels[channel]; - - imxdma->sg = NULL; - imxdma->dma_mode = dmamode; - - if (!dma_address) { - printk(KERN_ERR "imxdma%d: imx_dma_setup_single null address\n", - channel); - return -EINVAL; - } - - if (!dma_length) { - printk(KERN_ERR "imxdma%d: imx_dma_setup_single zero length\n", - channel); - return -EINVAL; - } - - if ((dmamode & DMA_MODE_MASK) == DMA_MODE_READ) { - pr_debug("imxdma%d: %s dma_addressg=0x%08x dma_length=%d " - "dev_addr=0x%08x for read\n", - channel, __func__, (unsigned int)dma_address, - dma_length, dev_addr); - - imx_dmav1_writel(dev_addr, DMA_SAR(channel)); - imx_dmav1_writel(dma_address, DMA_DAR(channel)); - imx_dmav1_writel(imxdma->ccr_from_device, DMA_CCR(channel)); - } else if ((dmamode & DMA_MODE_MASK) == DMA_MODE_WRITE) { - pr_debug("imxdma%d: %s dma_addressg=0x%08x dma_length=%d " - "dev_addr=0x%08x for write\n", - channel, __func__, (unsigned int)dma_address, - dma_length, dev_addr); - - imx_dmav1_writel(dma_address, DMA_SAR(channel)); - imx_dmav1_writel(dev_addr, DMA_DAR(channel)); - imx_dmav1_writel(imxdma->ccr_to_device, - DMA_CCR(channel)); - } else { - printk(KERN_ERR "imxdma%d: imx_dma_setup_single bad dmamode\n", - channel); - return -EINVAL; - } - - imx_dmav1_writel(dma_length, DMA_CNTR(channel)); - - return 0; -} -EXPORT_SYMBOL(imx_dma_setup_single); - -/** - * imx_dma_setup_sg - setup i.MX DMA channel SG list to/from device transfer - * @channel: i.MX DMA channel number - * @sg: pointer to the scatter-gather list/vector - * @sgcount: scatter-gather list hungs count - * @dma_length: total length of the transfer request in bytes - * @dev_addr: physical device port address - * @dmamode: DMA transfer mode, %DMA_MODE_READ from the device to the memory - * or %DMA_MODE_WRITE from memory to the device - * - * The function sets up DMA channel state and registers to be ready for - * transfer specified by provided parameters. The scatter-gather emulation - * is set up according to the parameters. - * - * The full preparation of the transfer requires setup of more register - * by the caller before imx_dma_enable() can be called. - * - * %BLR(channel) holds transfer burst length in bytes, 0 means 64 bytes - * - * %RSSR(channel) has to be set to the DMA request line source %DMA_REQ_xxx - * - * %CCR(channel) has to specify transfer parameters, the next settings is - * typical for linear or simple scatter-gather transfers if %DMA_MODE_READ is - * specified - * - * %CCR_DMOD_LINEAR | %CCR_DSIZ_32 | %CCR_SMOD_FIFO | %CCR_SSIZ_x - * - * The typical setup for %DMA_MODE_WRITE is specified by next options - * combination - * - * %CCR_SMOD_LINEAR | %CCR_SSIZ_32 | %CCR_DMOD_FIFO | %CCR_DSIZ_x - * - * Be careful here and do not mistakenly mix source and target device - * port sizes constants, they are really different: - * %CCR_SSIZ_8, %CCR_SSIZ_16, %CCR_SSIZ_32, - * %CCR_DSIZ_8, %CCR_DSIZ_16, %CCR_DSIZ_32 - * - * Return value: if incorrect parameters are provided -%EINVAL. - * Zero indicates success. - */ -int -imx_dma_setup_sg(int channel, - struct scatterlist *sg, unsigned int sgcount, - unsigned int dma_length, unsigned int dev_addr, - unsigned int dmamode) -{ - struct imx_dma_channel *imxdma = &imx_dma_channels[channel]; - - if (imxdma->in_use) - return -EBUSY; - - imxdma->sg = sg; - imxdma->dma_mode = dmamode; - imxdma->resbytes = dma_length; - - if (!sg || !sgcount) { - printk(KERN_ERR "imxdma%d: imx_dma_setup_sg empty sg list\n", - channel); - return -EINVAL; - } - - if (!sg->length) { - printk(KERN_ERR "imxdma%d: imx_dma_setup_sg zero length\n", - channel); - return -EINVAL; - } - - if ((dmamode & DMA_MODE_MASK) == DMA_MODE_READ) { - pr_debug("imxdma%d: %s sg=%p sgcount=%d total length=%d " - "dev_addr=0x%08x for read\n", - channel, __func__, sg, sgcount, dma_length, dev_addr); - - imx_dmav1_writel(dev_addr, DMA_SAR(channel)); - imx_dmav1_writel(imxdma->ccr_from_device, DMA_CCR(channel)); - } else if ((dmamode & DMA_MODE_MASK) == DMA_MODE_WRITE) { - pr_debug("imxdma%d: %s sg=%p sgcount=%d total length=%d " - "dev_addr=0x%08x for write\n", - channel, __func__, sg, sgcount, dma_length, dev_addr); - - imx_dmav1_writel(dev_addr, DMA_DAR(channel)); - imx_dmav1_writel(imxdma->ccr_to_device, DMA_CCR(channel)); - } else { - printk(KERN_ERR "imxdma%d: imx_dma_setup_sg bad dmamode\n", - channel); - return -EINVAL; - } - - imx_dma_sg_next(channel, sg); - - return 0; -} -EXPORT_SYMBOL(imx_dma_setup_sg); - -int -imx_dma_config_channel(int channel, unsigned int config_port, - unsigned int config_mem, unsigned int dmareq, int hw_chaining) -{ - struct imx_dma_channel *imxdma = &imx_dma_channels[channel]; - u32 dreq = 0; - - imxdma->hw_chaining = 0; - - if (hw_chaining) { - imxdma->hw_chaining = 1; - if (!imx_dma_hw_chain(imxdma)) - return -EINVAL; - } - - if (dmareq) - dreq = CCR_REN; - - imxdma->ccr_from_device = config_port | (config_mem << 2) | dreq; - imxdma->ccr_to_device = config_mem | (config_port << 2) | dreq; - - imx_dmav1_writel(dmareq, DMA_RSSR(channel)); - - return 0; -} -EXPORT_SYMBOL(imx_dma_config_channel); - -void imx_dma_config_burstlen(int channel, unsigned int burstlen) -{ - imx_dmav1_writel(burstlen, DMA_BLR(channel)); -} -EXPORT_SYMBOL(imx_dma_config_burstlen); - -/** - * imx_dma_setup_handlers - setup i.MX DMA channel end and error notification - * handlers - * @channel: i.MX DMA channel number - * @irq_handler: the pointer to the function called if the transfer - * ends successfully - * @err_handler: the pointer to the function called if the premature - * end caused by error occurs - * @data: user specified value to be passed to the handlers - */ -int -imx_dma_setup_handlers(int channel, - void (*irq_handler) (int, void *), - void (*err_handler) (int, void *, int), - void *data) -{ - struct imx_dma_channel *imxdma = &imx_dma_channels[channel]; - unsigned long flags; - - if (!imxdma->name) { - printk(KERN_CRIT "%s: called for not allocated channel %d\n", - __func__, channel); - return -ENODEV; - } - - local_irq_save(flags); - imx_dmav1_writel(1 << channel, DMA_DISR); - imxdma->irq_handler = irq_handler; - imxdma->err_handler = err_handler; - imxdma->data = data; - local_irq_restore(flags); - return 0; -} -EXPORT_SYMBOL(imx_dma_setup_handlers); - -/** - * imx_dma_setup_progression_handler - setup i.MX DMA channel progression - * handlers - * @channel: i.MX DMA channel number - * @prog_handler: the pointer to the function called if the transfer progresses - */ -int -imx_dma_setup_progression_handler(int channel, - void (*prog_handler) (int, void*, struct scatterlist*)) -{ - struct imx_dma_channel *imxdma = &imx_dma_channels[channel]; - unsigned long flags; - - if (!imxdma->name) { - printk(KERN_CRIT "%s: called for not allocated channel %d\n", - __func__, channel); - return -ENODEV; - } - - local_irq_save(flags); - imxdma->prog_handler = prog_handler; - local_irq_restore(flags); - return 0; -} -EXPORT_SYMBOL(imx_dma_setup_progression_handler); - -/** - * imx_dma_enable - function to start i.MX DMA channel operation - * @channel: i.MX DMA channel number - * - * The channel has to be allocated by driver through imx_dma_request() - * or imx_dma_request_by_prio() function. - * The transfer parameters has to be set to the channel registers through - * call of the imx_dma_setup_single() or imx_dma_setup_sg() function - * and registers %BLR(channel), %RSSR(channel) and %CCR(channel) has to - * be set prior this function call by the channel user. - */ -void imx_dma_enable(int channel) -{ - struct imx_dma_channel *imxdma = &imx_dma_channels[channel]; - unsigned long flags; - - pr_debug("imxdma%d: imx_dma_enable\n", channel); - - if (!imxdma->name) { - printk(KERN_CRIT "%s: called for not allocated channel %d\n", - __func__, channel); - return; - } - - if (imxdma->in_use) - return; - - local_irq_save(flags); - - imx_dmav1_writel(1 << channel, DMA_DISR); - imx_dmav1_writel(imx_dmav1_readl(DMA_DIMR) & ~(1 << channel), DMA_DIMR); - imx_dmav1_writel(imx_dmav1_readl(DMA_CCR(channel)) | CCR_CEN | - CCR_ACRPT, DMA_CCR(channel)); - - if ((cpu_is_mx21() || cpu_is_mx27()) && - imxdma->sg && imx_dma_hw_chain(imxdma)) { - imxdma->sg = sg_next(imxdma->sg); - if (imxdma->sg) { - u32 tmp; - imx_dma_sg_next(channel, imxdma->sg); - tmp = imx_dmav1_readl(DMA_CCR(channel)); - imx_dmav1_writel(tmp | CCR_RPT | CCR_ACRPT, - DMA_CCR(channel)); - } - } - imxdma->in_use = 1; - - local_irq_restore(flags); -} -EXPORT_SYMBOL(imx_dma_enable); - -/** - * imx_dma_disable - stop, finish i.MX DMA channel operatin - * @channel: i.MX DMA channel number - */ -void imx_dma_disable(int channel) -{ - struct imx_dma_channel *imxdma = &imx_dma_channels[channel]; - unsigned long flags; - - pr_debug("imxdma%d: imx_dma_disable\n", channel); - - if (imx_dma_hw_chain(imxdma)) - del_timer(&imxdma->watchdog); - - local_irq_save(flags); - imx_dmav1_writel(imx_dmav1_readl(DMA_DIMR) | (1 << channel), DMA_DIMR); - imx_dmav1_writel(imx_dmav1_readl(DMA_CCR(channel)) & ~CCR_CEN, - DMA_CCR(channel)); - imx_dmav1_writel(1 << channel, DMA_DISR); - imxdma->in_use = 0; - local_irq_restore(flags); -} -EXPORT_SYMBOL(imx_dma_disable); - -static void imx_dma_watchdog(unsigned long chno) -{ - struct imx_dma_channel *imxdma = &imx_dma_channels[chno]; - - imx_dmav1_writel(0, DMA_CCR(chno)); - imxdma->in_use = 0; - imxdma->sg = NULL; - - if (imxdma->err_handler) - imxdma->err_handler(chno, imxdma->data, IMX_DMA_ERR_TIMEOUT); -} - -static irqreturn_t dma_err_handler(int irq, void *dev_id) -{ - int i, disr; - struct imx_dma_channel *imxdma; - unsigned int err_mask; - int errcode; - - disr = imx_dmav1_readl(DMA_DISR); - - err_mask = imx_dmav1_readl(DMA_DBTOSR) | - imx_dmav1_readl(DMA_DRTOSR) | - imx_dmav1_readl(DMA_DSESR) | - imx_dmav1_readl(DMA_DBOSR); - - if (!err_mask) - return IRQ_HANDLED; - - imx_dmav1_writel(disr & err_mask, DMA_DISR); - - for (i = 0; i < IMX_DMA_CHANNELS; i++) { - if (!(err_mask & (1 << i))) - continue; - imxdma = &imx_dma_channels[i]; - errcode = 0; - - if (imx_dmav1_readl(DMA_DBTOSR) & (1 << i)) { - imx_dmav1_writel(1 << i, DMA_DBTOSR); - errcode |= IMX_DMA_ERR_BURST; - } - if (imx_dmav1_readl(DMA_DRTOSR) & (1 << i)) { - imx_dmav1_writel(1 << i, DMA_DRTOSR); - errcode |= IMX_DMA_ERR_REQUEST; - } - if (imx_dmav1_readl(DMA_DSESR) & (1 << i)) { - imx_dmav1_writel(1 << i, DMA_DSESR); - errcode |= IMX_DMA_ERR_TRANSFER; - } - if (imx_dmav1_readl(DMA_DBOSR) & (1 << i)) { - imx_dmav1_writel(1 << i, DMA_DBOSR); - errcode |= IMX_DMA_ERR_BUFFER; - } - if (imxdma->name && imxdma->err_handler) { - imxdma->err_handler(i, imxdma->data, errcode); - continue; - } - - imx_dma_channels[i].sg = NULL; - - printk(KERN_WARNING - "DMA timeout on channel %d (%s) -%s%s%s%s\n", - i, imxdma->name, - errcode & IMX_DMA_ERR_BURST ? " burst" : "", - errcode & IMX_DMA_ERR_REQUEST ? " request" : "", - errcode & IMX_DMA_ERR_TRANSFER ? " transfer" : "", - errcode & IMX_DMA_ERR_BUFFER ? " buffer" : ""); - } - return IRQ_HANDLED; -} - -static void dma_irq_handle_channel(int chno) -{ - struct imx_dma_channel *imxdma = &imx_dma_channels[chno]; - - if (!imxdma->name) { - /* - * IRQ for an unregistered DMA channel: - * let's clear the interrupts and disable it. - */ - printk(KERN_WARNING - "spurious IRQ for DMA channel %d\n", chno); - return; - } - - if (imxdma->sg) { - u32 tmp; - struct scatterlist *current_sg = imxdma->sg; - imxdma->sg = sg_next(imxdma->sg); - - if (imxdma->sg) { - imx_dma_sg_next(chno, imxdma->sg); - - tmp = imx_dmav1_readl(DMA_CCR(chno)); - - if (imx_dma_hw_chain(imxdma)) { - /* FIXME: The timeout should probably be - * configurable - */ - mod_timer(&imxdma->watchdog, - jiffies + msecs_to_jiffies(500)); - - tmp |= CCR_CEN | CCR_RPT | CCR_ACRPT; - imx_dmav1_writel(tmp, DMA_CCR(chno)); - } else { - imx_dmav1_writel(tmp & ~CCR_CEN, DMA_CCR(chno)); - tmp |= CCR_CEN; - } - - imx_dmav1_writel(tmp, DMA_CCR(chno)); - - if (imxdma->prog_handler) - imxdma->prog_handler(chno, imxdma->data, - current_sg); - - return; - } - - if (imx_dma_hw_chain(imxdma)) { - del_timer(&imxdma->watchdog); - return; - } - } - - imx_dmav1_writel(0, DMA_CCR(chno)); - imxdma->in_use = 0; - if (imxdma->irq_handler) - imxdma->irq_handler(chno, imxdma->data); -} - -static irqreturn_t dma_irq_handler(int irq, void *dev_id) -{ - int i, disr; - - if (cpu_is_mx21() || cpu_is_mx27()) - dma_err_handler(irq, dev_id); - - disr = imx_dmav1_readl(DMA_DISR); - - pr_debug("imxdma: dma_irq_handler called, disr=0x%08x\n", - disr); - - imx_dmav1_writel(disr, DMA_DISR); - for (i = 0; i < IMX_DMA_CHANNELS; i++) { - if (disr & (1 << i)) - dma_irq_handle_channel(i); - } - - return IRQ_HANDLED; -} - -/** - * imx_dma_request - request/allocate specified channel number - * @channel: i.MX DMA channel number - * @name: the driver/caller own non-%NULL identification - */ -int imx_dma_request(int channel, const char *name) -{ - struct imx_dma_channel *imxdma = &imx_dma_channels[channel]; - unsigned long flags; - int ret = 0; - - /* basic sanity checks */ - if (!name) - return -EINVAL; - - if (channel >= IMX_DMA_CHANNELS) { - printk(KERN_CRIT "%s: called for non-existed channel %d\n", - __func__, channel); - return -EINVAL; - } - - local_irq_save(flags); - if (imxdma->name) { - local_irq_restore(flags); - return -EBUSY; - } - memset(imxdma, 0, sizeof(*imxdma)); - imxdma->name = name; - local_irq_restore(flags); /* request_irq() can block */ - - if (cpu_is_mx21() || cpu_is_mx27()) { - ret = request_irq(MX2x_INT_DMACH0 + channel, - dma_irq_handler, 0, "DMA", NULL); - if (ret) { - imxdma->name = NULL; - pr_crit("Can't register IRQ %d for DMA channel %d\n", - MX2x_INT_DMACH0 + channel, channel); - return ret; - } - init_timer(&imxdma->watchdog); - imxdma->watchdog.function = &imx_dma_watchdog; - imxdma->watchdog.data = channel; - } - - return ret; -} -EXPORT_SYMBOL(imx_dma_request); - -/** - * imx_dma_free - release previously acquired channel - * @channel: i.MX DMA channel number - */ -void imx_dma_free(int channel) -{ - unsigned long flags; - struct imx_dma_channel *imxdma = &imx_dma_channels[channel]; - - if (!imxdma->name) { - printk(KERN_CRIT - "%s: trying to free free channel %d\n", - __func__, channel); - return; - } - - local_irq_save(flags); - /* Disable interrupts */ - imx_dma_disable(channel); - imxdma->name = NULL; - - if (cpu_is_mx21() || cpu_is_mx27()) - free_irq(MX2x_INT_DMACH0 + channel, NULL); - - local_irq_restore(flags); -} -EXPORT_SYMBOL(imx_dma_free); - -/** - * imx_dma_request_by_prio - find and request some of free channels best - * suiting requested priority - * @channel: i.MX DMA channel number - * @name: the driver/caller own non-%NULL identification - * - * This function tries to find a free channel in the specified priority group - * if the priority cannot be achieved it tries to look for free channel - * in the higher and then even lower priority groups. - * - * Return value: If there is no free channel to allocate, -%ENODEV is returned. - * On successful allocation channel is returned. - */ -int imx_dma_request_by_prio(const char *name, enum imx_dma_prio prio) -{ - int i; - int best; - - switch (prio) { - case (DMA_PRIO_HIGH): - best = 8; - break; - case (DMA_PRIO_MEDIUM): - best = 4; - break; - case (DMA_PRIO_LOW): - default: - best = 0; - break; - } - - for (i = best; i < IMX_DMA_CHANNELS; i++) - if (!imx_dma_request(i, name)) - return i; - - for (i = best - 1; i >= 0; i--) - if (!imx_dma_request(i, name)) - return i; - - printk(KERN_ERR "%s: no free DMA channel found\n", __func__); - - return -ENODEV; -} -EXPORT_SYMBOL(imx_dma_request_by_prio); - -static int __init imx_dma_init(void) -{ - int ret = 0; - int i; - - if (cpu_is_mx1()) - imx_dmav1_baseaddr = MX1_IO_ADDRESS(MX1_DMA_BASE_ADDR); - else if (cpu_is_mx21()) - imx_dmav1_baseaddr = MX21_IO_ADDRESS(MX21_DMA_BASE_ADDR); - else if (cpu_is_mx27()) - imx_dmav1_baseaddr = MX27_IO_ADDRESS(MX27_DMA_BASE_ADDR); - else - return 0; - - dma_clk = clk_get(NULL, "dma"); - if (IS_ERR(dma_clk)) - return PTR_ERR(dma_clk); - clk_enable(dma_clk); - - /* reset DMA module */ - imx_dmav1_writel(DCR_DRST, DMA_DCR); - - if (cpu_is_mx1()) { - ret = request_irq(MX1_DMA_INT, dma_irq_handler, 0, "DMA", NULL); - if (ret) { - pr_crit("Wow! Can't register IRQ for DMA\n"); - return ret; - } - - ret = request_irq(MX1_DMA_ERR, dma_err_handler, 0, "DMA", NULL); - if (ret) { - pr_crit("Wow! Can't register ERRIRQ for DMA\n"); - free_irq(MX1_DMA_INT, NULL); - return ret; - } - } - - /* enable DMA module */ - imx_dmav1_writel(DCR_DEN, DMA_DCR); - - /* clear all interrupts */ - imx_dmav1_writel((1 << IMX_DMA_CHANNELS) - 1, DMA_DISR); - - /* disable interrupts */ - imx_dmav1_writel((1 << IMX_DMA_CHANNELS) - 1, DMA_DIMR); - - for (i = 0; i < IMX_DMA_CHANNELS; i++) { - imx_dma_channels[i].sg = NULL; - imx_dma_channels[i].dma_num = i; - } - - return ret; -} - -arch_initcall(imx_dma_init); diff --git a/arch/arm/mach-imx/include/mach/dma-v1.h b/arch/arm/mach-imx/include/mach/dma-v1.h deleted file mode 100644 index ac6fd713828a..000000000000 --- a/arch/arm/mach-imx/include/mach/dma-v1.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * linux/arch/arm/mach-imx/include/mach/dma-v1.h - * - * i.MX DMA registration and IRQ dispatching - * - * Copyright 2006 Pavel Pisa - * Copyright 2008 Juergen Beisert, - * Copyright 2008 Sascha Hauer, - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#ifndef __MACH_DMA_V1_H__ -#define __MACH_DMA_V1_H__ - -#define imx_has_dma_v1() (cpu_is_mx1() || cpu_is_mx21() || cpu_is_mx27()) - -#include - -#define IMX_DMA_CHANNELS 16 - -#define DMA_MODE_READ 0 -#define DMA_MODE_WRITE 1 -#define DMA_MODE_MASK 1 - -#define MX1_DMA_REG(offset) MX1_IO_ADDRESS(MX1_DMA_BASE_ADDR + (offset)) - -/* DMA Interrupt Mask Register */ -#define MX1_DMA_DIMR MX1_DMA_REG(0x08) - -/* Channel Control Register */ -#define MX1_DMA_CCR(x) MX1_DMA_REG(0x8c + ((x) << 6)) - -#define IMX_DMA_MEMSIZE_32 (0 << 4) -#define IMX_DMA_MEMSIZE_8 (1 << 4) -#define IMX_DMA_MEMSIZE_16 (2 << 4) -#define IMX_DMA_TYPE_LINEAR (0 << 10) -#define IMX_DMA_TYPE_2D (1 << 10) -#define IMX_DMA_TYPE_FIFO (2 << 10) - -#define IMX_DMA_ERR_BURST (1 << 0) -#define IMX_DMA_ERR_REQUEST (1 << 1) -#define IMX_DMA_ERR_TRANSFER (1 << 2) -#define IMX_DMA_ERR_BUFFER (1 << 3) -#define IMX_DMA_ERR_TIMEOUT (1 << 4) - -int -imx_dma_config_channel(int channel, unsigned int config_port, - unsigned int config_mem, unsigned int dmareq, int hw_chaining); - -void -imx_dma_config_burstlen(int channel, unsigned int burstlen); - -int -imx_dma_setup_single(int channel, dma_addr_t dma_address, - unsigned int dma_length, unsigned int dev_addr, - unsigned int dmamode); - - -/* - * Use this flag as the dma_length argument to imx_dma_setup_sg() - * to create an endless running dma loop. The end of the scatterlist - * must be linked to the beginning for this to work. - */ -#define IMX_DMA_LENGTH_LOOP ((unsigned int)-1) - -int -imx_dma_setup_sg(int channel, struct scatterlist *sg, - unsigned int sgcount, unsigned int dma_length, - unsigned int dev_addr, unsigned int dmamode); - -int -imx_dma_setup_handlers(int channel, - void (*irq_handler) (int, void *), - void (*err_handler) (int, void *, int), void *data); - -int -imx_dma_setup_progression_handler(int channel, - void (*prog_handler) (int, void*, struct scatterlist*)); - -void imx_dma_enable(int channel); - -void imx_dma_disable(int channel); - -int imx_dma_request(int channel, const char *name); - -void imx_dma_free(int channel); - -int imx_dma_request_by_prio(const char *name, enum imx_dma_prio prio); - -#endif /* __MACH_DMA_V1_H__ */ diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 65c61dba66de..b4a53a0eeaf2 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -230,7 +230,6 @@ config IMX_SDMA config IMX_DMA tristate "i.MX DMA support" - depends on IMX_HAVE_DMA_V1 select DMA_ENGINE help Support the i.MX DMA engine. This engine is integrated into diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index 304839a99ae5..fbb1aaad6128 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -14,7 +14,6 @@ * http://www.opensource.org/licenses/gpl-license.html * http://www.gnu.org/copyleft/gpl.html */ - #include #include #include @@ -25,15 +24,89 @@ #include #include #include +#include #include #include #include -#include +#include #include #include "dmaengine.h" #define IMXDMA_MAX_CHAN_DESCRIPTORS 16 +#define IMX_DMA_CHANNELS 16 + +#define DMA_MODE_READ 0 +#define DMA_MODE_WRITE 1 +#define DMA_MODE_MASK 1 + +#define IMX_DMA_LENGTH_LOOP ((unsigned int)-1) +#define IMX_DMA_MEMSIZE_32 (0 << 4) +#define IMX_DMA_MEMSIZE_8 (1 << 4) +#define IMX_DMA_MEMSIZE_16 (2 << 4) +#define IMX_DMA_TYPE_LINEAR (0 << 10) +#define IMX_DMA_TYPE_2D (1 << 10) +#define IMX_DMA_TYPE_FIFO (2 << 10) + +#define IMX_DMA_ERR_BURST (1 << 0) +#define IMX_DMA_ERR_REQUEST (1 << 1) +#define IMX_DMA_ERR_TRANSFER (1 << 2) +#define IMX_DMA_ERR_BUFFER (1 << 3) +#define IMX_DMA_ERR_TIMEOUT (1 << 4) + +#define DMA_DCR 0x00 /* Control Register */ +#define DMA_DISR 0x04 /* Interrupt status Register */ +#define DMA_DIMR 0x08 /* Interrupt mask Register */ +#define DMA_DBTOSR 0x0c /* Burst timeout status Register */ +#define DMA_DRTOSR 0x10 /* Request timeout Register */ +#define DMA_DSESR 0x14 /* Transfer Error Status Register */ +#define DMA_DBOSR 0x18 /* Buffer overflow status Register */ +#define DMA_DBTOCR 0x1c /* Burst timeout control Register */ +#define DMA_WSRA 0x40 /* W-Size Register A */ +#define DMA_XSRA 0x44 /* X-Size Register A */ +#define DMA_YSRA 0x48 /* Y-Size Register A */ +#define DMA_WSRB 0x4c /* W-Size Register B */ +#define DMA_XSRB 0x50 /* X-Size Register B */ +#define DMA_YSRB 0x54 /* Y-Size Register B */ +#define DMA_SAR(x) (0x80 + ((x) << 6)) /* Source Address Registers */ +#define DMA_DAR(x) (0x84 + ((x) << 6)) /* Destination Address Registers */ +#define DMA_CNTR(x) (0x88 + ((x) << 6)) /* Count Registers */ +#define DMA_CCR(x) (0x8c + ((x) << 6)) /* Control Registers */ +#define DMA_RSSR(x) (0x90 + ((x) << 6)) /* Request source select Registers */ +#define DMA_BLR(x) (0x94 + ((x) << 6)) /* Burst length Registers */ +#define DMA_RTOR(x) (0x98 + ((x) << 6)) /* Request timeout Registers */ +#define DMA_BUCR(x) (0x98 + ((x) << 6)) /* Bus Utilization Registers */ +#define DMA_CCNR(x) (0x9C + ((x) << 6)) /* Channel counter Registers */ + +#define DCR_DRST (1<<1) +#define DCR_DEN (1<<0) +#define DBTOCR_EN (1<<15) +#define DBTOCR_CNT(x) ((x) & 0x7fff) +#define CNTR_CNT(x) ((x) & 0xffffff) +#define CCR_ACRPT (1<<14) +#define CCR_DMOD_LINEAR (0x0 << 12) +#define CCR_DMOD_2D (0x1 << 12) +#define CCR_DMOD_FIFO (0x2 << 12) +#define CCR_DMOD_EOBFIFO (0x3 << 12) +#define CCR_SMOD_LINEAR (0x0 << 10) +#define CCR_SMOD_2D (0x1 << 10) +#define CCR_SMOD_FIFO (0x2 << 10) +#define CCR_SMOD_EOBFIFO (0x3 << 10) +#define CCR_MDIR_DEC (1<<9) +#define CCR_MSEL_B (1<<8) +#define CCR_DSIZ_32 (0x0 << 6) +#define CCR_DSIZ_8 (0x1 << 6) +#define CCR_DSIZ_16 (0x2 << 6) +#define CCR_SSIZ_32 (0x0 << 4) +#define CCR_SSIZ_8 (0x1 << 4) +#define CCR_SSIZ_16 (0x2 << 4) +#define CCR_REN (1<<3) +#define CCR_RPT (1<<2) +#define CCR_FRC (1<<1) +#define CCR_CEN (1<<0) +#define RTOR_EN (1<<15) +#define RTOR_CLK (1<<14) +#define RTOR_PSC (1<<13) enum imxdma_prep_type { IMXDMA_DESC_MEMCPY, @@ -42,6 +115,39 @@ enum imxdma_prep_type { IMXDMA_DESC_CYCLIC, }; +/* + * struct imxdma_channel_internal - i.MX specific DMA extension + * @name: name specified by DMA client + * @irq_handler: client callback for end of transfer + * @err_handler: client callback for error condition + * @data: clients context data for callbacks + * @dma_mode: direction of the transfer %DMA_MODE_READ or %DMA_MODE_WRITE + * @sg: pointer to the actual read/written chunk for scatter-gather emulation + * @resbytes: total residual number of bytes to transfer + * (it can be lower or same as sum of SG mapped chunk sizes) + * @sgcount: number of chunks to be read/written + * + * Structure is used for IMX DMA processing. It would be probably good + * @struct dma_struct in the future for external interfacing and use + * @struct imxdma_channel_internal only as extension to it. + */ + +struct imxdma_channel_internal { + void *data; + unsigned int dma_mode; + struct scatterlist *sg; + unsigned int resbytes; + + int in_use; + + u32 ccr_from_device; + u32 ccr_to_device; + + struct timer_list watchdog; + + int hw_chaining; +}; + struct imxdma_desc { struct list_head node; struct dma_async_tx_descriptor desc; @@ -64,9 +170,9 @@ struct imxdma_desc { }; struct imxdma_channel { + struct imxdma_channel_internal internal; struct imxdma_engine *imxdma; unsigned int channel; - unsigned int imxdma_channel; struct tasklet_struct dma_tasklet; struct list_head ld_free; @@ -84,13 +190,11 @@ struct imxdma_channel { struct scatterlist *sg_list; }; -#define MAX_DMA_CHANNELS 8 - struct imxdma_engine { struct device *dev; struct device_dma_parameters dma_parms; struct dma_device dma_device; - struct imxdma_channel channel[MAX_DMA_CHANNELS]; + struct imxdma_channel channel[IMX_DMA_CHANNELS]; }; static struct imxdma_channel *to_imxdma_chan(struct dma_chan *chan) @@ -111,26 +215,379 @@ static inline bool imxdma_chan_is_doing_cyclic(struct imxdma_channel *imxdmac) return false; } -static void imxdma_irq_handler(int channel, void *data) -{ - struct imxdma_channel *imxdmac = data; +/* TODO: put this inside any struct */ +static void __iomem *imx_dmav1_baseaddr; +static struct clk *dma_clk; +static void imx_dmav1_writel(unsigned val, unsigned offset) +{ + __raw_writel(val, imx_dmav1_baseaddr + offset); +} + +static unsigned imx_dmav1_readl(unsigned offset) +{ + return __raw_readl(imx_dmav1_baseaddr + offset); +} + +static int imxdma_hw_chain(struct imxdma_channel_internal *imxdma) +{ + if (cpu_is_mx27()) + return imxdma->hw_chaining; + else + return 0; +} + +/* + * imxdma_sg_next - prepare next chunk for scatter-gather DMA emulation + */ +static inline int imxdma_sg_next(struct imxdma_channel *imxdmac, struct scatterlist *sg) +{ + struct imxdma_channel_internal *imxdma = &imxdmac->internal; + unsigned long now; + + now = min(imxdma->resbytes, sg->length); + if (imxdma->resbytes != IMX_DMA_LENGTH_LOOP) + imxdma->resbytes -= now; + + if ((imxdma->dma_mode & DMA_MODE_MASK) == DMA_MODE_READ) + imx_dmav1_writel(sg->dma_address, DMA_DAR(imxdmac->channel)); + else + imx_dmav1_writel(sg->dma_address, DMA_SAR(imxdmac->channel)); + + imx_dmav1_writel(now, DMA_CNTR(imxdmac->channel)); + + pr_debug("imxdma%d: next sg chunk dst 0x%08x, src 0x%08x, " + "size 0x%08x\n", imxdmac->channel, + imx_dmav1_readl(DMA_DAR(imxdmac->channel)), + imx_dmav1_readl(DMA_SAR(imxdmac->channel)), + imx_dmav1_readl(DMA_CNTR(imxdmac->channel))); + + return now; +} + +static int +imxdma_setup_single_hw(struct imxdma_channel *imxdmac, dma_addr_t dma_address, + unsigned int dma_length, unsigned int dev_addr, + unsigned int dmamode) +{ + int channel = imxdmac->channel; + + imxdmac->internal.sg = NULL; + imxdmac->internal.dma_mode = dmamode; + + if (!dma_address) { + printk(KERN_ERR "imxdma%d: imx_dma_setup_single null address\n", + channel); + return -EINVAL; + } + + if (!dma_length) { + printk(KERN_ERR "imxdma%d: imx_dma_setup_single zero length\n", + channel); + return -EINVAL; + } + + if ((dmamode & DMA_MODE_MASK) == DMA_MODE_READ) { + pr_debug("imxdma%d: %s dma_addressg=0x%08x dma_length=%d " + "dev_addr=0x%08x for read\n", + channel, __func__, (unsigned int)dma_address, + dma_length, dev_addr); + + imx_dmav1_writel(dev_addr, DMA_SAR(channel)); + imx_dmav1_writel(dma_address, DMA_DAR(channel)); + imx_dmav1_writel(imxdmac->internal.ccr_from_device, DMA_CCR(channel)); + } else if ((dmamode & DMA_MODE_MASK) == DMA_MODE_WRITE) { + pr_debug("imxdma%d: %s dma_addressg=0x%08x dma_length=%d " + "dev_addr=0x%08x for write\n", + channel, __func__, (unsigned int)dma_address, + dma_length, dev_addr); + + imx_dmav1_writel(dma_address, DMA_SAR(channel)); + imx_dmav1_writel(dev_addr, DMA_DAR(channel)); + imx_dmav1_writel(imxdmac->internal.ccr_to_device, + DMA_CCR(channel)); + } else { + printk(KERN_ERR "imxdma%d: imx_dma_setup_single bad dmamode\n", + channel); + return -EINVAL; + } + + imx_dmav1_writel(dma_length, DMA_CNTR(channel)); + + return 0; +} + +static void imxdma_enable_hw(struct imxdma_channel *imxdmac) +{ + int channel = imxdmac->channel; + unsigned long flags; + + pr_debug("imxdma%d: imx_dma_enable\n", channel); + + if (imxdmac->internal.in_use) + return; + + local_irq_save(flags); + + imx_dmav1_writel(1 << channel, DMA_DISR); + imx_dmav1_writel(imx_dmav1_readl(DMA_DIMR) & ~(1 << channel), DMA_DIMR); + imx_dmav1_writel(imx_dmav1_readl(DMA_CCR(channel)) | CCR_CEN | + CCR_ACRPT, DMA_CCR(channel)); + + if ((cpu_is_mx21() || cpu_is_mx27()) && + imxdmac->internal.sg && imxdma_hw_chain(&imxdmac->internal)) { + imxdmac->internal.sg = sg_next(imxdmac->internal.sg); + if (imxdmac->internal.sg) { + u32 tmp; + imxdma_sg_next(imxdmac, imxdmac->internal.sg); + tmp = imx_dmav1_readl(DMA_CCR(channel)); + imx_dmav1_writel(tmp | CCR_RPT | CCR_ACRPT, + DMA_CCR(channel)); + } + } + imxdmac->internal.in_use = 1; + + local_irq_restore(flags); +} + +static void imxdma_disable_hw(struct imxdma_channel *imxdmac) +{ + int channel = imxdmac->channel; + unsigned long flags; + + pr_debug("imxdma%d: imx_dma_disable\n", channel); + + if (imxdma_hw_chain(&imxdmac->internal)) + del_timer(&imxdmac->internal.watchdog); + + local_irq_save(flags); + imx_dmav1_writel(imx_dmav1_readl(DMA_DIMR) | (1 << channel), DMA_DIMR); + imx_dmav1_writel(imx_dmav1_readl(DMA_CCR(channel)) & ~CCR_CEN, + DMA_CCR(channel)); + imx_dmav1_writel(1 << channel, DMA_DISR); + imxdmac->internal.in_use = 0; + local_irq_restore(flags); +} + +static int +imxdma_config_channel_hw(struct imxdma_channel *imxdmac, unsigned int config_port, + unsigned int config_mem, unsigned int dmareq, int hw_chaining) +{ + int channel = imxdmac->channel; + u32 dreq = 0; + + imxdmac->internal.hw_chaining = 0; + + if (hw_chaining) { + imxdmac->internal.hw_chaining = 1; + if (!imxdma_hw_chain(&imxdmac->internal)) + return -EINVAL; + } + + if (dmareq) + dreq = CCR_REN; + + imxdmac->internal.ccr_from_device = config_port | (config_mem << 2) | dreq; + imxdmac->internal.ccr_to_device = config_mem | (config_port << 2) | dreq; + + imx_dmav1_writel(dmareq, DMA_RSSR(channel)); + + return 0; +} + +static int +imxdma_setup_sg_hw(struct imxdma_channel *imxdmac, + struct scatterlist *sg, unsigned int sgcount, + unsigned int dma_length, unsigned int dev_addr, + unsigned int dmamode) +{ + int channel = imxdmac->channel; + + if (imxdmac->internal.in_use) + return -EBUSY; + + imxdmac->internal.sg = sg; + imxdmac->internal.dma_mode = dmamode; + imxdmac->internal.resbytes = dma_length; + + if (!sg || !sgcount) { + printk(KERN_ERR "imxdma%d: imx_dma_setup_sg empty sg list\n", + channel); + return -EINVAL; + } + + if (!sg->length) { + printk(KERN_ERR "imxdma%d: imx_dma_setup_sg zero length\n", + channel); + return -EINVAL; + } + + if ((dmamode & DMA_MODE_MASK) == DMA_MODE_READ) { + pr_debug("imxdma%d: %s sg=%p sgcount=%d total length=%d " + "dev_addr=0x%08x for read\n", + channel, __func__, sg, sgcount, dma_length, dev_addr); + + imx_dmav1_writel(dev_addr, DMA_SAR(channel)); + imx_dmav1_writel(imxdmac->internal.ccr_from_device, DMA_CCR(channel)); + } else if ((dmamode & DMA_MODE_MASK) == DMA_MODE_WRITE) { + pr_debug("imxdma%d: %s sg=%p sgcount=%d total length=%d " + "dev_addr=0x%08x for write\n", + channel, __func__, sg, sgcount, dma_length, dev_addr); + + imx_dmav1_writel(dev_addr, DMA_DAR(channel)); + imx_dmav1_writel(imxdmac->internal.ccr_to_device, DMA_CCR(channel)); + } else { + printk(KERN_ERR "imxdma%d: imx_dma_setup_sg bad dmamode\n", + channel); + return -EINVAL; + } + + imxdma_sg_next(imxdmac, sg); + + return 0; +} + +static void imxdma_watchdog(unsigned long data) +{ + struct imxdma_channel *imxdmac = (struct imxdma_channel *)data; + int channel = imxdmac->channel; + + imx_dmav1_writel(0, DMA_CCR(channel)); + imxdmac->internal.in_use = 0; + imxdmac->internal.sg = NULL; + + /* Tasklet watchdog error handler */ + tasklet_schedule(&imxdmac->dma_tasklet); + pr_debug("imxdma%d: watchdog timeout!\n", imxdmac->channel); +} + +static irqreturn_t imxdma_err_handler(int irq, void *dev_id) +{ + struct imxdma_engine *imxdma = dev_id; + struct imxdma_channel_internal *internal; + unsigned int err_mask; + int i, disr; + int errcode; + + disr = imx_dmav1_readl(DMA_DISR); + + err_mask = imx_dmav1_readl(DMA_DBTOSR) | + imx_dmav1_readl(DMA_DRTOSR) | + imx_dmav1_readl(DMA_DSESR) | + imx_dmav1_readl(DMA_DBOSR); + + if (!err_mask) + return IRQ_HANDLED; + + imx_dmav1_writel(disr & err_mask, DMA_DISR); + + for (i = 0; i < IMX_DMA_CHANNELS; i++) { + if (!(err_mask & (1 << i))) + continue; + internal = &imxdma->channel[i].internal; + errcode = 0; + + if (imx_dmav1_readl(DMA_DBTOSR) & (1 << i)) { + imx_dmav1_writel(1 << i, DMA_DBTOSR); + errcode |= IMX_DMA_ERR_BURST; + } + if (imx_dmav1_readl(DMA_DRTOSR) & (1 << i)) { + imx_dmav1_writel(1 << i, DMA_DRTOSR); + errcode |= IMX_DMA_ERR_REQUEST; + } + if (imx_dmav1_readl(DMA_DSESR) & (1 << i)) { + imx_dmav1_writel(1 << i, DMA_DSESR); + errcode |= IMX_DMA_ERR_TRANSFER; + } + if (imx_dmav1_readl(DMA_DBOSR) & (1 << i)) { + imx_dmav1_writel(1 << i, DMA_DBOSR); + errcode |= IMX_DMA_ERR_BUFFER; + } + /* Tasklet error handler */ + tasklet_schedule(&imxdma->channel[i].dma_tasklet); + + printk(KERN_WARNING + "DMA timeout on channel %d -%s%s%s%s\n", i, + errcode & IMX_DMA_ERR_BURST ? " burst" : "", + errcode & IMX_DMA_ERR_REQUEST ? " request" : "", + errcode & IMX_DMA_ERR_TRANSFER ? " transfer" : "", + errcode & IMX_DMA_ERR_BUFFER ? " buffer" : ""); + } + return IRQ_HANDLED; +} + +static void dma_irq_handle_channel(struct imxdma_channel *imxdmac) +{ + struct imxdma_channel_internal *imxdma = &imxdmac->internal; + int chno = imxdmac->channel; + + if (imxdma->sg) { + u32 tmp; + imxdma->sg = sg_next(imxdma->sg); + + if (imxdma->sg) { + imxdma_sg_next(imxdmac, imxdma->sg); + + tmp = imx_dmav1_readl(DMA_CCR(chno)); + + if (imxdma_hw_chain(imxdma)) { + /* FIXME: The timeout should probably be + * configurable + */ + mod_timer(&imxdma->watchdog, + jiffies + msecs_to_jiffies(500)); + + tmp |= CCR_CEN | CCR_RPT | CCR_ACRPT; + imx_dmav1_writel(tmp, DMA_CCR(chno)); + } else { + imx_dmav1_writel(tmp & ~CCR_CEN, DMA_CCR(chno)); + tmp |= CCR_CEN; + } + + imx_dmav1_writel(tmp, DMA_CCR(chno)); + + if (imxdma_chan_is_doing_cyclic(imxdmac)) + /* Tasklet progression */ + tasklet_schedule(&imxdmac->dma_tasklet); + + return; + } + + if (imxdma_hw_chain(imxdma)) { + del_timer(&imxdma->watchdog); + return; + } + } + + imx_dmav1_writel(0, DMA_CCR(chno)); + imxdma->in_use = 0; + /* Tasklet irq */ tasklet_schedule(&imxdmac->dma_tasklet); } -static void imxdma_err_handler(int channel, void *data, int error) +static irqreturn_t dma_irq_handler(int irq, void *dev_id) { - struct imxdma_channel *imxdmac = data; + struct imxdma_engine *imxdma = dev_id; + struct imxdma_channel_internal *internal; + int i, disr; - tasklet_schedule(&imxdmac->dma_tasklet); -} + if (cpu_is_mx21() || cpu_is_mx27()) + imxdma_err_handler(irq, dev_id); -static void imxdma_progression(int channel, void *data, - struct scatterlist *sg) -{ - struct imxdma_channel *imxdmac = data; + disr = imx_dmav1_readl(DMA_DISR); - tasklet_schedule(&imxdmac->dma_tasklet); + pr_debug("imxdma: dma_irq_handler called, disr=0x%08x\n", + disr); + + imx_dmav1_writel(disr, DMA_DISR); + for (i = 0; i < IMX_DMA_CHANNELS; i++) { + if (disr & (1 << i)) { + internal = &imxdma->channel[i].internal; + dma_irq_handle_channel(&imxdma->channel[i]); + } + } + + return IRQ_HANDLED; } static int imxdma_xfer_desc(struct imxdma_desc *d) @@ -141,31 +598,24 @@ static int imxdma_xfer_desc(struct imxdma_desc *d) /* Configure and enable */ switch (d->type) { case IMXDMA_DESC_MEMCPY: - ret = imx_dma_config_channel(imxdmac->imxdma_channel, + ret = imxdma_config_channel_hw(imxdmac, d->config_port, d->config_mem, 0, 0); if (ret < 0) return ret; - ret = imx_dma_setup_single(imxdmac->imxdma_channel, d->src, + ret = imxdma_setup_single_hw(imxdmac, d->src, d->len, d->dest, d->dmamode); if (ret < 0) return ret; break; + + /* Cyclic transfer is the same as slave_sg with special sg configuration. */ case IMXDMA_DESC_CYCLIC: - ret = imx_dma_setup_progression_handler(imxdmac->imxdma_channel, - imxdma_progression); - if (ret < 0) - return ret; - /* - * We fall through here since cyclic transfer is the same as - * slave_sg adding a progression handler and a specific sg - * configuration which is done in 'imxdma_prep_dma_cyclic'. - */ case IMXDMA_DESC_SLAVE_SG: if (d->dmamode == DMA_MODE_READ) - ret = imx_dma_setup_sg(imxdmac->imxdma_channel, d->sg, + ret = imxdma_setup_sg_hw(imxdmac, d->sg, d->sgcount, d->len, d->src, d->dmamode); else - ret = imx_dma_setup_sg(imxdmac->imxdma_channel, d->sg, + ret = imxdma_setup_sg_hw(imxdmac, d->sg, d->sgcount, d->len, d->dest, d->dmamode); if (ret < 0) return ret; @@ -173,7 +623,7 @@ static int imxdma_xfer_desc(struct imxdma_desc *d) default: return -EINVAL; } - imx_dma_enable(imxdmac->imxdma_channel); + imxdma_enable_hw(imxdmac); return 0; } @@ -225,7 +675,7 @@ static int imxdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, switch (cmd) { case DMA_TERMINATE_ALL: - imx_dma_disable(imxdmac->imxdma_channel); + imxdma_disable_hw(imxdmac); spin_lock_irqsave(&imxdmac->lock, flags); list_splice_tail_init(&imxdmac->ld_active, &imxdmac->ld_free); @@ -255,16 +705,16 @@ static int imxdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, mode = IMX_DMA_MEMSIZE_32; break; } - ret = imx_dma_config_channel(imxdmac->imxdma_channel, + ret = imxdma_config_channel_hw(imxdmac, mode | IMX_DMA_TYPE_FIFO, IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR, imxdmac->dma_request, 1); if (ret) return ret; - - imx_dma_config_burstlen(imxdmac->imxdma_channel, - imxdmac->watermark_level * imxdmac->word_size); + /* Set burst length */ + imx_dmav1_writel(imxdmac->watermark_level * imxdmac->word_size, + DMA_BLR(imxdmac->channel)); return 0; default: @@ -333,7 +783,7 @@ static void imxdma_free_chan_resources(struct dma_chan *chan) spin_lock_irqsave(&imxdmac->lock, flags); - imx_dma_disable(imxdmac->imxdma_channel); + imxdma_disable_hw(imxdmac); list_splice_tail_init(&imxdmac->ld_active, &imxdmac->ld_free); list_splice_tail_init(&imxdmac->ld_queue, &imxdmac->ld_free); @@ -520,10 +970,51 @@ static void imxdma_issue_pending(struct dma_chan *chan) } static int __init imxdma_probe(struct platform_device *pdev) -{ + { struct imxdma_engine *imxdma; int ret, i; + if (cpu_is_mx1()) + imx_dmav1_baseaddr = MX1_IO_ADDRESS(MX1_DMA_BASE_ADDR); + else if (cpu_is_mx21()) + imx_dmav1_baseaddr = MX21_IO_ADDRESS(MX21_DMA_BASE_ADDR); + else if (cpu_is_mx27()) + imx_dmav1_baseaddr = MX27_IO_ADDRESS(MX27_DMA_BASE_ADDR); + else + return 0; + + dma_clk = clk_get(NULL, "dma"); + if (IS_ERR(dma_clk)) + return PTR_ERR(dma_clk); + clk_enable(dma_clk); + + /* reset DMA module */ + imx_dmav1_writel(DCR_DRST, DMA_DCR); + + if (cpu_is_mx1()) { + ret = request_irq(MX1_DMA_INT, dma_irq_handler, 0, "DMA", imxdma); + if (ret) { + pr_crit("Can't register IRQ for DMA\n"); + return ret; + } + + ret = request_irq(MX1_DMA_ERR, imxdma_err_handler, 0, "DMA", imxdma); + if (ret) { + pr_crit("Can't register ERRIRQ for DMA\n"); + free_irq(MX1_DMA_INT, NULL); + return ret; + } + } + + /* enable DMA module */ + imx_dmav1_writel(DCR_DEN, DMA_DCR); + + /* clear all interrupts */ + imx_dmav1_writel((1 << IMX_DMA_CHANNELS) - 1, DMA_DISR); + + /* disable interrupts */ + imx_dmav1_writel((1 << IMX_DMA_CHANNELS) - 1, DMA_DIMR); + imxdma = kzalloc(sizeof(*imxdma), GFP_KERNEL); if (!imxdma) return -ENOMEM; @@ -535,19 +1026,22 @@ static int __init imxdma_probe(struct platform_device *pdev) dma_cap_set(DMA_MEMCPY, imxdma->dma_device.cap_mask); /* Initialize channel parameters */ - for (i = 0; i < MAX_DMA_CHANNELS; i++) { + for (i = 0; i < IMX_DMA_CHANNELS; i++) { struct imxdma_channel *imxdmac = &imxdma->channel[i]; - - imxdmac->imxdma_channel = imx_dma_request_by_prio("dmaengine", - DMA_PRIO_MEDIUM); - if ((int)imxdmac->channel < 0) { - ret = -ENODEV; - goto err_init; + memset(&imxdmac->internal, 0, sizeof(imxdmac->internal)); + if (cpu_is_mx21() || cpu_is_mx27()) { + ret = request_irq(MX2x_INT_DMACH0 + i, + dma_irq_handler, 0, "DMA", imxdma); + if (ret) { + pr_crit("Can't register IRQ %d for DMA channel %d\n", + MX2x_INT_DMACH0 + i, i); + goto err_init; + } + init_timer(&imxdmac->internal.watchdog); + imxdmac->internal.watchdog.function = &imxdma_watchdog; + imxdmac->internal.watchdog.data = (unsigned long)imxdmac; } - imx_dma_setup_handlers(imxdmac->imxdma_channel, - imxdma_irq_handler, imxdma_err_handler, imxdmac); - imxdmac->imxdma = imxdma; spin_lock_init(&imxdmac->lock); @@ -593,9 +1087,13 @@ static int __init imxdma_probe(struct platform_device *pdev) return 0; err_init: - while (--i >= 0) { - struct imxdma_channel *imxdmac = &imxdma->channel[i]; - imx_dma_free(imxdmac->imxdma_channel); + + if (cpu_is_mx21() || cpu_is_mx27()) { + while (--i >= 0) + free_irq(MX2x_INT_DMACH0 + i, NULL); + } else if cpu_is_mx1() { + free_irq(MX1_DMA_INT, NULL); + free_irq(MX1_DMA_ERR, NULL); } kfree(imxdma); @@ -609,10 +1107,12 @@ static int __exit imxdma_remove(struct platform_device *pdev) dma_async_device_unregister(&imxdma->dma_device); - for (i = 0; i < MAX_DMA_CHANNELS; i++) { - struct imxdma_channel *imxdmac = &imxdma->channel[i]; - - imx_dma_free(imxdmac->imxdma_channel); + if (cpu_is_mx21() || cpu_is_mx27()) { + for (i = 0; i < IMX_DMA_CHANNELS; i++) + free_irq(MX2x_INT_DMACH0 + i, NULL); + } else if cpu_is_mx1() { + free_irq(MX1_DMA_INT, NULL); + free_irq(MX1_DMA_ERR, NULL); } kfree(imxdma); From 232e3c2c7961fb3312a80df3747f1c29f0ed512e Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Thu, 22 Mar 2012 14:54:02 +0100 Subject: [PATCH 53/67] dmaengine: imx-dma: remove data member from internal structure. Internal structure is just an auxiliary structure used for the initial merge which is meant to be gone. As data member is not use anywhere we can simply remove it. Signed-off-by: Javier Martin Acked-by: Sascha Hauer Signed-off-by: Vinod Koul --- drivers/dma/imx-dma.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index fbb1aaad6128..8603c75b0e1b 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -133,7 +133,6 @@ enum imxdma_prep_type { */ struct imxdma_channel_internal { - void *data; unsigned int dma_mode; struct scatterlist *sg; unsigned int resbytes; From 2efc3449d7b11f36f532180cb738364fd2c28e03 Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Thu, 22 Mar 2012 14:54:03 +0100 Subject: [PATCH 54/67] dmaengine: imx-dma: remove dma_mode member of internal structure. dmaengine now provides 'enum dma_transfer_direction' to properly specify DMA transfer direction. For this reason, DMA_MODE_* defines are replaced by this new type and therefore dma_mode member becomes redundant. Signed-off-by: Javier Martin Acked-by: Sascha Hauer Signed-off-by: Vinod Koul --- drivers/dma/imx-dma.c | 103 ++++++++++++++++++------------------------ 1 file changed, 45 insertions(+), 58 deletions(-) diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index 8603c75b0e1b..04a2c1446dc2 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -36,10 +36,6 @@ #define IMXDMA_MAX_CHAN_DESCRIPTORS 16 #define IMX_DMA_CHANNELS 16 -#define DMA_MODE_READ 0 -#define DMA_MODE_WRITE 1 -#define DMA_MODE_MASK 1 - #define IMX_DMA_LENGTH_LOOP ((unsigned int)-1) #define IMX_DMA_MEMSIZE_32 (0 << 4) #define IMX_DMA_MEMSIZE_8 (1 << 4) @@ -133,7 +129,6 @@ enum imxdma_prep_type { */ struct imxdma_channel_internal { - unsigned int dma_mode; struct scatterlist *sg; unsigned int resbytes; @@ -154,7 +149,7 @@ struct imxdma_desc { dma_addr_t src; dma_addr_t dest; size_t len; - unsigned int dmamode; + enum dma_transfer_direction direction; enum imxdma_prep_type type; /* For memcpy and interleaved */ unsigned int config_port; @@ -239,8 +234,9 @@ static int imxdma_hw_chain(struct imxdma_channel_internal *imxdma) /* * imxdma_sg_next - prepare next chunk for scatter-gather DMA emulation */ -static inline int imxdma_sg_next(struct imxdma_channel *imxdmac, struct scatterlist *sg) +static inline int imxdma_sg_next(struct imxdma_desc *d, struct scatterlist *sg) { + struct imxdma_channel *imxdmac = to_imxdma_chan(d->desc.chan); struct imxdma_channel_internal *imxdma = &imxdmac->internal; unsigned long now; @@ -248,7 +244,7 @@ static inline int imxdma_sg_next(struct imxdma_channel *imxdmac, struct scatterl if (imxdma->resbytes != IMX_DMA_LENGTH_LOOP) imxdma->resbytes -= now; - if ((imxdma->dma_mode & DMA_MODE_MASK) == DMA_MODE_READ) + if (d->direction == DMA_DEV_TO_MEM) imx_dmav1_writel(sg->dma_address, DMA_DAR(imxdmac->channel)); else imx_dmav1_writel(sg->dma_address, DMA_SAR(imxdmac->channel)); @@ -265,14 +261,12 @@ static inline int imxdma_sg_next(struct imxdma_channel *imxdmac, struct scatterl } static int -imxdma_setup_single_hw(struct imxdma_channel *imxdmac, dma_addr_t dma_address, - unsigned int dma_length, unsigned int dev_addr, - unsigned int dmamode) +imxdma_setup_mem2mem_hw(struct imxdma_channel *imxdmac, dma_addr_t dma_address, + unsigned int dma_length, unsigned int dev_addr) { int channel = imxdmac->channel; imxdmac->internal.sg = NULL; - imxdmac->internal.dma_mode = dmamode; if (!dma_address) { printk(KERN_ERR "imxdma%d: imx_dma_setup_single null address\n", @@ -286,38 +280,24 @@ imxdma_setup_single_hw(struct imxdma_channel *imxdmac, dma_addr_t dma_address, return -EINVAL; } - if ((dmamode & DMA_MODE_MASK) == DMA_MODE_READ) { - pr_debug("imxdma%d: %s dma_addressg=0x%08x dma_length=%d " - "dev_addr=0x%08x for read\n", - channel, __func__, (unsigned int)dma_address, - dma_length, dev_addr); + pr_debug("imxdma%d: %s dma_addressg=0x%08x dma_length=%d " + "dev_addr=0x%08x for write\n", + channel, __func__, (unsigned int)dma_address, + dma_length, dev_addr); - imx_dmav1_writel(dev_addr, DMA_SAR(channel)); - imx_dmav1_writel(dma_address, DMA_DAR(channel)); - imx_dmav1_writel(imxdmac->internal.ccr_from_device, DMA_CCR(channel)); - } else if ((dmamode & DMA_MODE_MASK) == DMA_MODE_WRITE) { - pr_debug("imxdma%d: %s dma_addressg=0x%08x dma_length=%d " - "dev_addr=0x%08x for write\n", - channel, __func__, (unsigned int)dma_address, - dma_length, dev_addr); - - imx_dmav1_writel(dma_address, DMA_SAR(channel)); - imx_dmav1_writel(dev_addr, DMA_DAR(channel)); - imx_dmav1_writel(imxdmac->internal.ccr_to_device, - DMA_CCR(channel)); - } else { - printk(KERN_ERR "imxdma%d: imx_dma_setup_single bad dmamode\n", - channel); - return -EINVAL; - } + imx_dmav1_writel(dma_address, DMA_SAR(channel)); + imx_dmav1_writel(dev_addr, DMA_DAR(channel)); + imx_dmav1_writel(imxdmac->internal.ccr_to_device, + DMA_CCR(channel)); imx_dmav1_writel(dma_length, DMA_CNTR(channel)); return 0; } -static void imxdma_enable_hw(struct imxdma_channel *imxdmac) +static void imxdma_enable_hw(struct imxdma_desc *d) { + struct imxdma_channel *imxdmac = to_imxdma_chan(d->desc.chan); int channel = imxdmac->channel; unsigned long flags; @@ -338,7 +318,7 @@ static void imxdma_enable_hw(struct imxdma_channel *imxdmac) imxdmac->internal.sg = sg_next(imxdmac->internal.sg); if (imxdmac->internal.sg) { u32 tmp; - imxdma_sg_next(imxdmac, imxdmac->internal.sg); + imxdma_sg_next(d, imxdmac->internal.sg); tmp = imx_dmav1_readl(DMA_CCR(channel)); imx_dmav1_writel(tmp | CCR_RPT | CCR_ACRPT, DMA_CCR(channel)); @@ -395,18 +375,18 @@ imxdma_config_channel_hw(struct imxdma_channel *imxdmac, unsigned int config_por } static int -imxdma_setup_sg_hw(struct imxdma_channel *imxdmac, +imxdma_setup_sg_hw(struct imxdma_desc *d, struct scatterlist *sg, unsigned int sgcount, unsigned int dma_length, unsigned int dev_addr, - unsigned int dmamode) + enum dma_transfer_direction direction) { + struct imxdma_channel *imxdmac = to_imxdma_chan(d->desc.chan); int channel = imxdmac->channel; if (imxdmac->internal.in_use) return -EBUSY; imxdmac->internal.sg = sg; - imxdmac->internal.dma_mode = dmamode; imxdmac->internal.resbytes = dma_length; if (!sg || !sgcount) { @@ -421,14 +401,14 @@ imxdma_setup_sg_hw(struct imxdma_channel *imxdmac, return -EINVAL; } - if ((dmamode & DMA_MODE_MASK) == DMA_MODE_READ) { + if (direction == DMA_DEV_TO_MEM) { pr_debug("imxdma%d: %s sg=%p sgcount=%d total length=%d " "dev_addr=0x%08x for read\n", channel, __func__, sg, sgcount, dma_length, dev_addr); imx_dmav1_writel(dev_addr, DMA_SAR(channel)); imx_dmav1_writel(imxdmac->internal.ccr_from_device, DMA_CCR(channel)); - } else if ((dmamode & DMA_MODE_MASK) == DMA_MODE_WRITE) { + } else if (direction == DMA_MEM_TO_DEV) { pr_debug("imxdma%d: %s sg=%p sgcount=%d total length=%d " "dev_addr=0x%08x for write\n", channel, __func__, sg, sgcount, dma_length, dev_addr); @@ -441,7 +421,7 @@ imxdma_setup_sg_hw(struct imxdma_channel *imxdmac, return -EINVAL; } - imxdma_sg_next(imxdmac, sg); + imxdma_sg_next(d, sg); return 0; } @@ -519,13 +499,26 @@ static void dma_irq_handle_channel(struct imxdma_channel *imxdmac) { struct imxdma_channel_internal *imxdma = &imxdmac->internal; int chno = imxdmac->channel; + struct imxdma_desc *desc; if (imxdma->sg) { u32 tmp; imxdma->sg = sg_next(imxdma->sg); if (imxdma->sg) { - imxdma_sg_next(imxdmac, imxdma->sg); + + spin_lock(&imxdmac->lock); + if (list_empty(&imxdmac->ld_active)) { + spin_unlock(&imxdmac->lock); + goto out; + } + + desc = list_first_entry(&imxdmac->ld_active, + struct imxdma_desc, + node); + spin_unlock(&imxdmac->lock); + + imxdma_sg_next(desc, imxdma->sg); tmp = imx_dmav1_readl(DMA_CCR(chno)); @@ -558,6 +551,7 @@ static void dma_irq_handle_channel(struct imxdma_channel *imxdmac) } } +out: imx_dmav1_writel(0, DMA_CCR(chno)); imxdma->in_use = 0; /* Tasklet irq */ @@ -601,8 +595,7 @@ static int imxdma_xfer_desc(struct imxdma_desc *d) d->config_port, d->config_mem, 0, 0); if (ret < 0) return ret; - ret = imxdma_setup_single_hw(imxdmac, d->src, - d->len, d->dest, d->dmamode); + ret = imxdma_setup_mem2mem_hw(imxdmac, d->src, d->len, d->dest); if (ret < 0) return ret; break; @@ -610,19 +603,15 @@ static int imxdma_xfer_desc(struct imxdma_desc *d) /* Cyclic transfer is the same as slave_sg with special sg configuration. */ case IMXDMA_DESC_CYCLIC: case IMXDMA_DESC_SLAVE_SG: - if (d->dmamode == DMA_MODE_READ) - ret = imxdma_setup_sg_hw(imxdmac, d->sg, - d->sgcount, d->len, d->src, d->dmamode); - else - ret = imxdma_setup_sg_hw(imxdmac, d->sg, - d->sgcount, d->len, d->dest, d->dmamode); + ret = imxdma_setup_sg_hw(d, d->sg, d->sgcount, d->len, + imxdmac->per_address, d->direction); if (ret < 0) return ret; break; default: return -EINVAL; } - imxdma_enable_hw(imxdmac); + imxdma_enable_hw(d); return 0; } @@ -839,11 +828,10 @@ static struct dma_async_tx_descriptor *imxdma_prep_slave_sg( desc->sg = sgl; desc->sgcount = sg_len; desc->len = dma_length; + desc->direction = direction; if (direction == DMA_DEV_TO_MEM) { - desc->dmamode = DMA_MODE_READ; desc->src = imxdmac->per_address; } else { - desc->dmamode = DMA_MODE_WRITE; desc->dest = imxdmac->per_address; } desc->desc.callback = NULL; @@ -900,11 +888,10 @@ static struct dma_async_tx_descriptor *imxdma_prep_dma_cyclic( desc->sg = imxdmac->sg_list; desc->sgcount = periods; desc->len = IMX_DMA_LENGTH_LOOP; + desc->direction = direction; if (direction == DMA_DEV_TO_MEM) { - desc->dmamode = DMA_MODE_READ; desc->src = imxdmac->per_address; } else { - desc->dmamode = DMA_MODE_WRITE; desc->dest = imxdmac->per_address; } desc->desc.callback = NULL; @@ -934,7 +921,7 @@ static struct dma_async_tx_descriptor *imxdma_prep_dma_memcpy( desc->src = src; desc->dest = dest; desc->len = len; - desc->dmamode = DMA_MODE_WRITE; + desc->direction = DMA_MEM_TO_MEM; desc->config_port = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR; desc->config_mem = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR; desc->desc.callback = NULL; From 3b4b6dfc202dc5bedb03f2fae4ccc3f5b95dd563 Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Thu, 22 Mar 2012 14:54:04 +0100 Subject: [PATCH 55/67] dmaengine: imx-dma: remove 'imxdma_setup_mem2mem_hw' function. This function is only used once in the driver and has a lot of checks that are not needed anymore. For this reason it's been merged with 'imxdma_enable_hw'. Signed-off-by: Javier Martin Acked-by: Sascha Hauer Signed-off-by: Vinod Koul --- drivers/dma/imx-dma.c | 57 +++++++++++-------------------------------- 1 file changed, 14 insertions(+), 43 deletions(-) diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index 04a2c1446dc2..dcb2c70aec7a 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -260,41 +260,6 @@ static inline int imxdma_sg_next(struct imxdma_desc *d, struct scatterlist *sg) return now; } -static int -imxdma_setup_mem2mem_hw(struct imxdma_channel *imxdmac, dma_addr_t dma_address, - unsigned int dma_length, unsigned int dev_addr) -{ - int channel = imxdmac->channel; - - imxdmac->internal.sg = NULL; - - if (!dma_address) { - printk(KERN_ERR "imxdma%d: imx_dma_setup_single null address\n", - channel); - return -EINVAL; - } - - if (!dma_length) { - printk(KERN_ERR "imxdma%d: imx_dma_setup_single zero length\n", - channel); - return -EINVAL; - } - - pr_debug("imxdma%d: %s dma_addressg=0x%08x dma_length=%d " - "dev_addr=0x%08x for write\n", - channel, __func__, (unsigned int)dma_address, - dma_length, dev_addr); - - imx_dmav1_writel(dma_address, DMA_SAR(channel)); - imx_dmav1_writel(dev_addr, DMA_DAR(channel)); - imx_dmav1_writel(imxdmac->internal.ccr_to_device, - DMA_CCR(channel)); - - imx_dmav1_writel(dma_length, DMA_CNTR(channel)); - - return 0; -} - static void imxdma_enable_hw(struct imxdma_desc *d) { struct imxdma_channel *imxdmac = to_imxdma_chan(d->desc.chan); @@ -586,20 +551,26 @@ static irqreturn_t dma_irq_handler(int irq, void *dev_id) static int imxdma_xfer_desc(struct imxdma_desc *d) { struct imxdma_channel *imxdmac = to_imxdma_chan(d->desc.chan); + struct imxdma_engine *imxdma = imxdmac->imxdma; int ret; /* Configure and enable */ switch (d->type) { case IMXDMA_DESC_MEMCPY: - ret = imxdma_config_channel_hw(imxdmac, - d->config_port, d->config_mem, 0, 0); - if (ret < 0) - return ret; - ret = imxdma_setup_mem2mem_hw(imxdmac, d->src, d->len, d->dest); - if (ret < 0) - return ret; - break; + imxdmac->internal.sg = NULL; + imx_dmav1_writel(d->src, DMA_SAR(imxdmac->channel)); + imx_dmav1_writel(d->dest, DMA_DAR(imxdmac->channel)); + imx_dmav1_writel(d->config_mem | (d->config_port << 2), + DMA_CCR(imxdmac->channel)); + + imx_dmav1_writel(d->len, DMA_CNTR(imxdmac->channel)); + + dev_dbg(imxdma->dev, "%s channel: %d dest=0x%08x src=0x%08x " + "dma_length=%d\n", __func__, imxdmac->channel, + d->dest, d->src, d->len); + + break; /* Cyclic transfer is the same as slave_sg with special sg configuration. */ case IMXDMA_DESC_CYCLIC: case IMXDMA_DESC_SLAVE_SG: From bdc0c7534c80c479b2336aed3e4016f4743f4853 Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Thu, 22 Mar 2012 14:54:05 +0100 Subject: [PATCH 56/67] dmaengine: imx-dma: remove 'imxdma_config_channel_hw' function. This function is only used once in the driver and uses some intermediary variables that are not needed anymore. For this reason it's been merged with 'imxdma_control'. Signed-off-by: Javier Martin Acked-by: Sascha Hauer Signed-off-by: Vinod Koul --- drivers/dma/imx-dma.c | 46 ++++++++++++------------------------------- 1 file changed, 13 insertions(+), 33 deletions(-) diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index dcb2c70aec7a..25b4108f6224 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -313,32 +313,6 @@ static void imxdma_disable_hw(struct imxdma_channel *imxdmac) local_irq_restore(flags); } -static int -imxdma_config_channel_hw(struct imxdma_channel *imxdmac, unsigned int config_port, - unsigned int config_mem, unsigned int dmareq, int hw_chaining) -{ - int channel = imxdmac->channel; - u32 dreq = 0; - - imxdmac->internal.hw_chaining = 0; - - if (hw_chaining) { - imxdmac->internal.hw_chaining = 1; - if (!imxdma_hw_chain(&imxdmac->internal)) - return -EINVAL; - } - - if (dmareq) - dreq = CCR_REN; - - imxdmac->internal.ccr_from_device = config_port | (config_mem << 2) | dreq; - imxdmac->internal.ccr_to_device = config_mem | (config_port << 2) | dreq; - - imx_dmav1_writel(dmareq, DMA_RSSR(channel)); - - return 0; -} - static int imxdma_setup_sg_hw(struct imxdma_desc *d, struct scatterlist *sg, unsigned int sgcount, @@ -628,7 +602,6 @@ static int imxdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, { struct imxdma_channel *imxdmac = to_imxdma_chan(chan); struct dma_slave_config *dmaengine_cfg = (void *)arg; - int ret; unsigned long flags; unsigned int mode = 0; @@ -664,13 +637,20 @@ static int imxdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, mode = IMX_DMA_MEMSIZE_32; break; } - ret = imxdma_config_channel_hw(imxdmac, - mode | IMX_DMA_TYPE_FIFO, - IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR, - imxdmac->dma_request, 1); - if (ret) - return ret; + imxdmac->internal.hw_chaining = 1; + if (!imxdma_hw_chain(&imxdmac->internal)) + return -EINVAL; + imxdmac->internal.ccr_from_device = + (mode | IMX_DMA_TYPE_FIFO) | + ((IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR) << 2) | + CCR_REN; + imxdmac->internal.ccr_to_device = + (IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR) | + ((mode | IMX_DMA_TYPE_FIFO) << 2) | CCR_REN; + imx_dmav1_writel(imxdmac->dma_request, + DMA_RSSR(imxdmac->channel)); + /* Set burst length */ imx_dmav1_writel(imxdmac->watermark_level * imxdmac->word_size, DMA_BLR(imxdmac->channel)); From 359291a1a095a8a402405cd9c4bab46684e7bcfe Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Thu, 22 Mar 2012 14:54:06 +0100 Subject: [PATCH 57/67] dmaengine: imx-dma: remove 'imxdma_setup_sg_hw' function. Removing this function allows moving 'ccr_to_device' and 'ccr_from_device' from internal struct to channel struct. This repesents a step forward towards removing auxiliary 'internal' structure. Signed-off-by: Javier Martin Acked-by: Sascha Hauer Signed-off-by: Vinod Koul --- drivers/dma/imx-dma.c | 98 ++++++++++++++++--------------------------- 1 file changed, 35 insertions(+), 63 deletions(-) diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index 25b4108f6224..484f35365902 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -134,9 +134,6 @@ struct imxdma_channel_internal { int in_use; - u32 ccr_from_device; - u32 ccr_to_device; - struct timer_list watchdog; int hw_chaining; @@ -182,6 +179,8 @@ struct imxdma_channel { enum dma_status status; int dma_request; struct scatterlist *sg_list; + u32 ccr_from_device; + u32 ccr_to_device; }; struct imxdma_engine { @@ -313,58 +312,6 @@ static void imxdma_disable_hw(struct imxdma_channel *imxdmac) local_irq_restore(flags); } -static int -imxdma_setup_sg_hw(struct imxdma_desc *d, - struct scatterlist *sg, unsigned int sgcount, - unsigned int dma_length, unsigned int dev_addr, - enum dma_transfer_direction direction) -{ - struct imxdma_channel *imxdmac = to_imxdma_chan(d->desc.chan); - int channel = imxdmac->channel; - - if (imxdmac->internal.in_use) - return -EBUSY; - - imxdmac->internal.sg = sg; - imxdmac->internal.resbytes = dma_length; - - if (!sg || !sgcount) { - printk(KERN_ERR "imxdma%d: imx_dma_setup_sg empty sg list\n", - channel); - return -EINVAL; - } - - if (!sg->length) { - printk(KERN_ERR "imxdma%d: imx_dma_setup_sg zero length\n", - channel); - return -EINVAL; - } - - if (direction == DMA_DEV_TO_MEM) { - pr_debug("imxdma%d: %s sg=%p sgcount=%d total length=%d " - "dev_addr=0x%08x for read\n", - channel, __func__, sg, sgcount, dma_length, dev_addr); - - imx_dmav1_writel(dev_addr, DMA_SAR(channel)); - imx_dmav1_writel(imxdmac->internal.ccr_from_device, DMA_CCR(channel)); - } else if (direction == DMA_MEM_TO_DEV) { - pr_debug("imxdma%d: %s sg=%p sgcount=%d total length=%d " - "dev_addr=0x%08x for write\n", - channel, __func__, sg, sgcount, dma_length, dev_addr); - - imx_dmav1_writel(dev_addr, DMA_DAR(channel)); - imx_dmav1_writel(imxdmac->internal.ccr_to_device, DMA_CCR(channel)); - } else { - printk(KERN_ERR "imxdma%d: imx_dma_setup_sg bad dmamode\n", - channel); - return -EINVAL; - } - - imxdma_sg_next(d, sg); - - return 0; -} - static void imxdma_watchdog(unsigned long data) { struct imxdma_channel *imxdmac = (struct imxdma_channel *)data; @@ -526,7 +473,6 @@ static int imxdma_xfer_desc(struct imxdma_desc *d) { struct imxdma_channel *imxdmac = to_imxdma_chan(d->desc.chan); struct imxdma_engine *imxdma = imxdmac->imxdma; - int ret; /* Configure and enable */ switch (d->type) { @@ -548,10 +494,37 @@ static int imxdma_xfer_desc(struct imxdma_desc *d) /* Cyclic transfer is the same as slave_sg with special sg configuration. */ case IMXDMA_DESC_CYCLIC: case IMXDMA_DESC_SLAVE_SG: - ret = imxdma_setup_sg_hw(d, d->sg, d->sgcount, d->len, - imxdmac->per_address, d->direction); - if (ret < 0) - return ret; + imxdmac->internal.sg = d->sg; + imxdmac->internal.resbytes = d->len; + + if (d->direction == DMA_DEV_TO_MEM) { + imx_dmav1_writel(imxdmac->per_address, + DMA_SAR(imxdmac->channel)); + imx_dmav1_writel(imxdmac->ccr_from_device, + DMA_CCR(imxdmac->channel)); + + dev_dbg(imxdma->dev, "%s channel: %d sg=%p sgcount=%d " + "total length=%d dev_addr=0x%08x (dev2mem)\n", + __func__, imxdmac->channel, d->sg, d->sgcount, + d->len, imxdmac->per_address); + } else if (d->direction == DMA_MEM_TO_DEV) { + imx_dmav1_writel(imxdmac->per_address, + DMA_DAR(imxdmac->channel)); + imx_dmav1_writel(imxdmac->ccr_to_device, + DMA_CCR(imxdmac->channel)); + + dev_dbg(imxdma->dev, "%s channel: %d sg=%p sgcount=%d " + "total length=%d dev_addr=0x%08x (mem2dev)\n", + __func__, imxdmac->channel, d->sg, d->sgcount, + d->len, imxdmac->per_address); + } else { + dev_err(imxdma->dev, "%s channel: %d bad dma mode\n", + __func__, imxdmac->channel); + return -EINVAL; + } + + imxdma_sg_next(d, d->sg); + break; default: return -EINVAL; @@ -641,11 +614,10 @@ static int imxdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, imxdmac->internal.hw_chaining = 1; if (!imxdma_hw_chain(&imxdmac->internal)) return -EINVAL; - imxdmac->internal.ccr_from_device = - (mode | IMX_DMA_TYPE_FIFO) | + imxdmac->ccr_from_device = (mode | IMX_DMA_TYPE_FIFO) | ((IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR) << 2) | CCR_REN; - imxdmac->internal.ccr_to_device = + imxdmac->ccr_to_device = (IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR) | ((mode | IMX_DMA_TYPE_FIFO) << 2) | CCR_REN; imx_dmav1_writel(imxdmac->dma_request, From 833bc03bf14ef6d3f82d86845c29aa1f7e2037e3 Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Thu, 22 Mar 2012 14:54:07 +0100 Subject: [PATCH 58/67] dmaengine: imx-dma: remove sg member from internal structure. This member is redundant, because it is already present in descriptor structure. Removing it will make further removing of 'internal' structure easier. Signed-off-by: Javier Martin Acked-by: Sascha Hauer Signed-off-by: Vinod Koul --- drivers/dma/imx-dma.c | 44 +++++++++++++++++++------------------------ 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index 484f35365902..82d4099bd8f4 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -129,7 +129,6 @@ enum imxdma_prep_type { */ struct imxdma_channel_internal { - struct scatterlist *sg; unsigned int resbytes; int in_use; @@ -278,11 +277,11 @@ static void imxdma_enable_hw(struct imxdma_desc *d) CCR_ACRPT, DMA_CCR(channel)); if ((cpu_is_mx21() || cpu_is_mx27()) && - imxdmac->internal.sg && imxdma_hw_chain(&imxdmac->internal)) { - imxdmac->internal.sg = sg_next(imxdmac->internal.sg); - if (imxdmac->internal.sg) { + d->sg && imxdma_hw_chain(&imxdmac->internal)) { + d->sg = sg_next(d->sg); + if (d->sg) { u32 tmp; - imxdma_sg_next(d, imxdmac->internal.sg); + imxdma_sg_next(d, d->sg); tmp = imx_dmav1_readl(DMA_CCR(channel)); imx_dmav1_writel(tmp | CCR_RPT | CCR_ACRPT, DMA_CCR(channel)); @@ -319,7 +318,6 @@ static void imxdma_watchdog(unsigned long data) imx_dmav1_writel(0, DMA_CCR(channel)); imxdmac->internal.in_use = 0; - imxdmac->internal.sg = NULL; /* Tasklet watchdog error handler */ tasklet_schedule(&imxdmac->dma_tasklet); @@ -387,24 +385,23 @@ static void dma_irq_handle_channel(struct imxdma_channel *imxdmac) int chno = imxdmac->channel; struct imxdma_desc *desc; - if (imxdma->sg) { + spin_lock(&imxdmac->lock); + if (list_empty(&imxdmac->ld_active)) { + spin_unlock(&imxdmac->lock); + goto out; + } + + desc = list_first_entry(&imxdmac->ld_active, + struct imxdma_desc, + node); + spin_unlock(&imxdmac->lock); + + if (desc->sg) { u32 tmp; - imxdma->sg = sg_next(imxdma->sg); + desc->sg = sg_next(desc->sg); - if (imxdma->sg) { - - spin_lock(&imxdmac->lock); - if (list_empty(&imxdmac->ld_active)) { - spin_unlock(&imxdmac->lock); - goto out; - } - - desc = list_first_entry(&imxdmac->ld_active, - struct imxdma_desc, - node); - spin_unlock(&imxdmac->lock); - - imxdma_sg_next(desc, imxdma->sg); + if (desc->sg) { + imxdma_sg_next(desc, desc->sg); tmp = imx_dmav1_readl(DMA_CCR(chno)); @@ -477,8 +474,6 @@ static int imxdma_xfer_desc(struct imxdma_desc *d) /* Configure and enable */ switch (d->type) { case IMXDMA_DESC_MEMCPY: - imxdmac->internal.sg = NULL; - imx_dmav1_writel(d->src, DMA_SAR(imxdmac->channel)); imx_dmav1_writel(d->dest, DMA_DAR(imxdmac->channel)); imx_dmav1_writel(d->config_mem | (d->config_port << 2), @@ -494,7 +489,6 @@ static int imxdma_xfer_desc(struct imxdma_desc *d) /* Cyclic transfer is the same as slave_sg with special sg configuration. */ case IMXDMA_DESC_CYCLIC: case IMXDMA_DESC_SLAVE_SG: - imxdmac->internal.sg = d->sg; imxdmac->internal.resbytes = d->len; if (d->direction == DMA_DEV_TO_MEM) { From e4756b5e068d866239b6880a7030c9d31400b254 Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Thu, 22 Mar 2012 14:54:08 +0100 Subject: [PATCH 59/67] dmaengine: imx-dma: remove 'in_use' field of 'internal' structure. It makes no sense keeping an 'in_use' flag when the multiple descriptor mechanism already prevents a new DMA transfer to be issued when another one is in course. Signed-off-by: Javier Martin Acked-by: Sascha Hauer Signed-off-by: Vinod Koul --- drivers/dma/imx-dma.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index 82d4099bd8f4..d7309e44c0df 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -131,8 +131,6 @@ enum imxdma_prep_type { struct imxdma_channel_internal { unsigned int resbytes; - int in_use; - struct timer_list watchdog; int hw_chaining; @@ -266,9 +264,6 @@ static void imxdma_enable_hw(struct imxdma_desc *d) pr_debug("imxdma%d: imx_dma_enable\n", channel); - if (imxdmac->internal.in_use) - return; - local_irq_save(flags); imx_dmav1_writel(1 << channel, DMA_DISR); @@ -287,7 +282,6 @@ static void imxdma_enable_hw(struct imxdma_desc *d) DMA_CCR(channel)); } } - imxdmac->internal.in_use = 1; local_irq_restore(flags); } @@ -307,7 +301,6 @@ static void imxdma_disable_hw(struct imxdma_channel *imxdmac) imx_dmav1_writel(imx_dmav1_readl(DMA_CCR(channel)) & ~CCR_CEN, DMA_CCR(channel)); imx_dmav1_writel(1 << channel, DMA_DISR); - imxdmac->internal.in_use = 0; local_irq_restore(flags); } @@ -317,7 +310,6 @@ static void imxdma_watchdog(unsigned long data) int channel = imxdmac->channel; imx_dmav1_writel(0, DMA_CCR(channel)); - imxdmac->internal.in_use = 0; /* Tasklet watchdog error handler */ tasklet_schedule(&imxdmac->dma_tasklet); @@ -436,7 +428,6 @@ static void dma_irq_handle_channel(struct imxdma_channel *imxdmac) out: imx_dmav1_writel(0, DMA_CCR(chno)); - imxdma->in_use = 0; /* Tasklet irq */ tasklet_schedule(&imxdmac->dma_tasklet); } From 6b0e2f55e3ebc7089abf5e4770f03fb264b6d2ea Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Thu, 22 Mar 2012 14:54:09 +0100 Subject: [PATCH 60/67] dmaengine: imx-dma: remove 'resbytes' field of 'internal' structure. Use per-descriptor 'len' field to keep track of the remaining bytes instead. This goes on the direction of eventually removing the 'internal' structure. Signed-off-by: Javier Martin Acked-by: Sascha Hauer Signed-off-by: Vinod Koul --- drivers/dma/imx-dma.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index d7309e44c0df..6e03f928ca81 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -129,8 +129,6 @@ enum imxdma_prep_type { */ struct imxdma_channel_internal { - unsigned int resbytes; - struct timer_list watchdog; int hw_chaining; @@ -233,12 +231,11 @@ static int imxdma_hw_chain(struct imxdma_channel_internal *imxdma) static inline int imxdma_sg_next(struct imxdma_desc *d, struct scatterlist *sg) { struct imxdma_channel *imxdmac = to_imxdma_chan(d->desc.chan); - struct imxdma_channel_internal *imxdma = &imxdmac->internal; unsigned long now; - now = min(imxdma->resbytes, sg->length); - if (imxdma->resbytes != IMX_DMA_LENGTH_LOOP) - imxdma->resbytes -= now; + now = min(d->len, sg->length); + if (d->len != IMX_DMA_LENGTH_LOOP) + d->len -= now; if (d->direction == DMA_DEV_TO_MEM) imx_dmav1_writel(sg->dma_address, DMA_DAR(imxdmac->channel)); @@ -480,8 +477,6 @@ static int imxdma_xfer_desc(struct imxdma_desc *d) /* Cyclic transfer is the same as slave_sg with special sg configuration. */ case IMXDMA_DESC_CYCLIC: case IMXDMA_DESC_SLAVE_SG: - imxdmac->internal.resbytes = d->len; - if (d->direction == DMA_DEV_TO_MEM) { imx_dmav1_writel(imxdmac->per_address, DMA_SAR(imxdmac->channel)); From 2d9c2fc59a74e625eff795a788cacc65648290d6 Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Thu, 22 Mar 2012 14:54:10 +0100 Subject: [PATCH 61/67] dmaengine: imx-dma: remove internal structure. This structure was created to allow an smoothless merge but was meant to be removed. Remaining members 'hw_chaining' and 'watchdog' are moved to the channel structure. Signed-off-by: Javier Martin Acked-by: Sascha Hauer Signed-off-by: Vinod Koul --- drivers/dma/imx-dma.c | 64 ++++++++++++------------------------------- 1 file changed, 18 insertions(+), 46 deletions(-) diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index 6e03f928ca81..629be353c63a 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -111,29 +111,6 @@ enum imxdma_prep_type { IMXDMA_DESC_CYCLIC, }; -/* - * struct imxdma_channel_internal - i.MX specific DMA extension - * @name: name specified by DMA client - * @irq_handler: client callback for end of transfer - * @err_handler: client callback for error condition - * @data: clients context data for callbacks - * @dma_mode: direction of the transfer %DMA_MODE_READ or %DMA_MODE_WRITE - * @sg: pointer to the actual read/written chunk for scatter-gather emulation - * @resbytes: total residual number of bytes to transfer - * (it can be lower or same as sum of SG mapped chunk sizes) - * @sgcount: number of chunks to be read/written - * - * Structure is used for IMX DMA processing. It would be probably good - * @struct dma_struct in the future for external interfacing and use - * @struct imxdma_channel_internal only as extension to it. - */ - -struct imxdma_channel_internal { - struct timer_list watchdog; - - int hw_chaining; -}; - struct imxdma_desc { struct list_head node; struct dma_async_tx_descriptor desc; @@ -156,7 +133,8 @@ struct imxdma_desc { }; struct imxdma_channel { - struct imxdma_channel_internal internal; + int hw_chaining; + struct timer_list watchdog; struct imxdma_engine *imxdma; unsigned int channel; @@ -217,10 +195,10 @@ static unsigned imx_dmav1_readl(unsigned offset) return __raw_readl(imx_dmav1_baseaddr + offset); } -static int imxdma_hw_chain(struct imxdma_channel_internal *imxdma) +static int imxdma_hw_chain(struct imxdma_channel *imxdmac) { if (cpu_is_mx27()) - return imxdma->hw_chaining; + return imxdmac->hw_chaining; else return 0; } @@ -269,7 +247,7 @@ static void imxdma_enable_hw(struct imxdma_desc *d) CCR_ACRPT, DMA_CCR(channel)); if ((cpu_is_mx21() || cpu_is_mx27()) && - d->sg && imxdma_hw_chain(&imxdmac->internal)) { + d->sg && imxdma_hw_chain(imxdmac)) { d->sg = sg_next(d->sg); if (d->sg) { u32 tmp; @@ -290,8 +268,8 @@ static void imxdma_disable_hw(struct imxdma_channel *imxdmac) pr_debug("imxdma%d: imx_dma_disable\n", channel); - if (imxdma_hw_chain(&imxdmac->internal)) - del_timer(&imxdmac->internal.watchdog); + if (imxdma_hw_chain(imxdmac)) + del_timer(&imxdmac->watchdog); local_irq_save(flags); imx_dmav1_writel(imx_dmav1_readl(DMA_DIMR) | (1 << channel), DMA_DIMR); @@ -316,7 +294,6 @@ static void imxdma_watchdog(unsigned long data) static irqreturn_t imxdma_err_handler(int irq, void *dev_id) { struct imxdma_engine *imxdma = dev_id; - struct imxdma_channel_internal *internal; unsigned int err_mask; int i, disr; int errcode; @@ -336,7 +313,6 @@ static irqreturn_t imxdma_err_handler(int irq, void *dev_id) for (i = 0; i < IMX_DMA_CHANNELS; i++) { if (!(err_mask & (1 << i))) continue; - internal = &imxdma->channel[i].internal; errcode = 0; if (imx_dmav1_readl(DMA_DBTOSR) & (1 << i)) { @@ -370,7 +346,6 @@ static irqreturn_t imxdma_err_handler(int irq, void *dev_id) static void dma_irq_handle_channel(struct imxdma_channel *imxdmac) { - struct imxdma_channel_internal *imxdma = &imxdmac->internal; int chno = imxdmac->channel; struct imxdma_desc *desc; @@ -394,11 +369,11 @@ static void dma_irq_handle_channel(struct imxdma_channel *imxdmac) tmp = imx_dmav1_readl(DMA_CCR(chno)); - if (imxdma_hw_chain(imxdma)) { + if (imxdma_hw_chain(imxdmac)) { /* FIXME: The timeout should probably be * configurable */ - mod_timer(&imxdma->watchdog, + mod_timer(&imxdmac->watchdog, jiffies + msecs_to_jiffies(500)); tmp |= CCR_CEN | CCR_RPT | CCR_ACRPT; @@ -417,8 +392,8 @@ static void dma_irq_handle_channel(struct imxdma_channel *imxdmac) return; } - if (imxdma_hw_chain(imxdma)) { - del_timer(&imxdma->watchdog); + if (imxdma_hw_chain(imxdmac)) { + del_timer(&imxdmac->watchdog); return; } } @@ -432,7 +407,6 @@ out: static irqreturn_t dma_irq_handler(int irq, void *dev_id) { struct imxdma_engine *imxdma = dev_id; - struct imxdma_channel_internal *internal; int i, disr; if (cpu_is_mx21() || cpu_is_mx27()) @@ -445,10 +419,8 @@ static irqreturn_t dma_irq_handler(int irq, void *dev_id) imx_dmav1_writel(disr, DMA_DISR); for (i = 0; i < IMX_DMA_CHANNELS; i++) { - if (disr & (1 << i)) { - internal = &imxdma->channel[i].internal; + if (disr & (1 << i)) dma_irq_handle_channel(&imxdma->channel[i]); - } } return IRQ_HANDLED; @@ -591,8 +563,8 @@ static int imxdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, break; } - imxdmac->internal.hw_chaining = 1; - if (!imxdma_hw_chain(&imxdmac->internal)) + imxdmac->hw_chaining = 1; + if (!imxdma_hw_chain(imxdmac)) return -EINVAL; imxdmac->ccr_from_device = (mode | IMX_DMA_TYPE_FIFO) | ((IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR) << 2) | @@ -917,7 +889,7 @@ static int __init imxdma_probe(struct platform_device *pdev) /* Initialize channel parameters */ for (i = 0; i < IMX_DMA_CHANNELS; i++) { struct imxdma_channel *imxdmac = &imxdma->channel[i]; - memset(&imxdmac->internal, 0, sizeof(imxdmac->internal)); + if (cpu_is_mx21() || cpu_is_mx27()) { ret = request_irq(MX2x_INT_DMACH0 + i, dma_irq_handler, 0, "DMA", imxdma); @@ -926,9 +898,9 @@ static int __init imxdma_probe(struct platform_device *pdev) MX2x_INT_DMACH0 + i, i); goto err_init; } - init_timer(&imxdmac->internal.watchdog); - imxdmac->internal.watchdog.function = &imxdma_watchdog; - imxdmac->internal.watchdog.data = (unsigned long)imxdmac; + init_timer(&imxdmac->watchdog); + imxdmac->watchdog.function = &imxdma_watchdog; + imxdmac->watchdog.data = (unsigned long)imxdmac; } imxdmac->imxdma = imxdma; From a6cbb2d87d20817e555a6ffa3131bfa1cdd9ab73 Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Thu, 22 Mar 2012 14:54:11 +0100 Subject: [PATCH 62/67] dmaengine: imx-dma: remove unused arg of imxdma_sg_next. Since this function is always used with 'desc' as first argument and 'desc->sg' as second argument, the latter is clearly redundant and can be removed. Signed-off-by: Javier Martin Acked-by: Sascha Hauer Signed-off-by: Vinod Koul --- drivers/dma/imx-dma.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index 629be353c63a..628b0f61ab38 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -206,9 +206,10 @@ static int imxdma_hw_chain(struct imxdma_channel *imxdmac) /* * imxdma_sg_next - prepare next chunk for scatter-gather DMA emulation */ -static inline int imxdma_sg_next(struct imxdma_desc *d, struct scatterlist *sg) +static inline int imxdma_sg_next(struct imxdma_desc *d) { struct imxdma_channel *imxdmac = to_imxdma_chan(d->desc.chan); + struct scatterlist *sg = d->sg; unsigned long now; now = min(d->len, sg->length); @@ -251,7 +252,7 @@ static void imxdma_enable_hw(struct imxdma_desc *d) d->sg = sg_next(d->sg); if (d->sg) { u32 tmp; - imxdma_sg_next(d, d->sg); + imxdma_sg_next(d); tmp = imx_dmav1_readl(DMA_CCR(channel)); imx_dmav1_writel(tmp | CCR_RPT | CCR_ACRPT, DMA_CCR(channel)); @@ -365,7 +366,7 @@ static void dma_irq_handle_channel(struct imxdma_channel *imxdmac) desc->sg = sg_next(desc->sg); if (desc->sg) { - imxdma_sg_next(desc, desc->sg); + imxdma_sg_next(desc); tmp = imx_dmav1_readl(DMA_CCR(chno)); @@ -475,7 +476,7 @@ static int imxdma_xfer_desc(struct imxdma_desc *d) return -EINVAL; } - imxdma_sg_next(d, d->sg); + imxdma_sg_next(d); break; default: From cd5cf9da020293118800864641e09b71e23ba41c Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Thu, 22 Mar 2012 14:54:12 +0100 Subject: [PATCH 63/67] dmaengine: imx-dma: remove 'imx_dmav1_baseaddr' and 'dma_clk'. These global variables are integrated into the dmaengine structure. Signed-off-by: Javier Martin Acked-by: Sascha Hauer Signed-off-by: Vinod Koul --- drivers/dma/imx-dma.c | 171 +++++++++++++++++++++++------------------- 1 file changed, 94 insertions(+), 77 deletions(-) diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index 628b0f61ab38..cdca95a5666c 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -160,6 +160,8 @@ struct imxdma_engine { struct device *dev; struct device_dma_parameters dma_parms; struct dma_device dma_device; + void __iomem *base; + struct clk *dma_clk; struct imxdma_channel channel[IMX_DMA_CHANNELS]; }; @@ -181,18 +183,17 @@ static inline bool imxdma_chan_is_doing_cyclic(struct imxdma_channel *imxdmac) return false; } -/* TODO: put this inside any struct */ -static void __iomem *imx_dmav1_baseaddr; -static struct clk *dma_clk; -static void imx_dmav1_writel(unsigned val, unsigned offset) + +static void imx_dmav1_writel(struct imxdma_engine *imxdma, unsigned val, + unsigned offset) { - __raw_writel(val, imx_dmav1_baseaddr + offset); + __raw_writel(val, imxdma->base + offset); } -static unsigned imx_dmav1_readl(unsigned offset) +static unsigned imx_dmav1_readl(struct imxdma_engine *imxdma, unsigned offset) { - return __raw_readl(imx_dmav1_baseaddr + offset); + return __raw_readl(imxdma->base + offset); } static int imxdma_hw_chain(struct imxdma_channel *imxdmac) @@ -209,6 +210,7 @@ static int imxdma_hw_chain(struct imxdma_channel *imxdmac) static inline int imxdma_sg_next(struct imxdma_desc *d) { struct imxdma_channel *imxdmac = to_imxdma_chan(d->desc.chan); + struct imxdma_engine *imxdma = imxdmac->imxdma; struct scatterlist *sg = d->sg; unsigned long now; @@ -217,17 +219,19 @@ static inline int imxdma_sg_next(struct imxdma_desc *d) d->len -= now; if (d->direction == DMA_DEV_TO_MEM) - imx_dmav1_writel(sg->dma_address, DMA_DAR(imxdmac->channel)); + imx_dmav1_writel(imxdma, sg->dma_address, + DMA_DAR(imxdmac->channel)); else - imx_dmav1_writel(sg->dma_address, DMA_SAR(imxdmac->channel)); + imx_dmav1_writel(imxdma, sg->dma_address, + DMA_SAR(imxdmac->channel)); - imx_dmav1_writel(now, DMA_CNTR(imxdmac->channel)); + imx_dmav1_writel(imxdma, now, DMA_CNTR(imxdmac->channel)); pr_debug("imxdma%d: next sg chunk dst 0x%08x, src 0x%08x, " "size 0x%08x\n", imxdmac->channel, - imx_dmav1_readl(DMA_DAR(imxdmac->channel)), - imx_dmav1_readl(DMA_SAR(imxdmac->channel)), - imx_dmav1_readl(DMA_CNTR(imxdmac->channel))); + imx_dmav1_readl(imxdma, DMA_DAR(imxdmac->channel)), + imx_dmav1_readl(imxdma, DMA_SAR(imxdmac->channel)), + imx_dmav1_readl(imxdma, DMA_CNTR(imxdmac->channel))); return now; } @@ -235,6 +239,7 @@ static inline int imxdma_sg_next(struct imxdma_desc *d) static void imxdma_enable_hw(struct imxdma_desc *d) { struct imxdma_channel *imxdmac = to_imxdma_chan(d->desc.chan); + struct imxdma_engine *imxdma = imxdmac->imxdma; int channel = imxdmac->channel; unsigned long flags; @@ -242,10 +247,11 @@ static void imxdma_enable_hw(struct imxdma_desc *d) local_irq_save(flags); - imx_dmav1_writel(1 << channel, DMA_DISR); - imx_dmav1_writel(imx_dmav1_readl(DMA_DIMR) & ~(1 << channel), DMA_DIMR); - imx_dmav1_writel(imx_dmav1_readl(DMA_CCR(channel)) | CCR_CEN | - CCR_ACRPT, DMA_CCR(channel)); + imx_dmav1_writel(imxdma, 1 << channel, DMA_DISR); + imx_dmav1_writel(imxdma, imx_dmav1_readl(imxdma, DMA_DIMR) & + ~(1 << channel), DMA_DIMR); + imx_dmav1_writel(imxdma, imx_dmav1_readl(imxdma, DMA_CCR(channel)) | + CCR_CEN | CCR_ACRPT, DMA_CCR(channel)); if ((cpu_is_mx21() || cpu_is_mx27()) && d->sg && imxdma_hw_chain(imxdmac)) { @@ -253,9 +259,9 @@ static void imxdma_enable_hw(struct imxdma_desc *d) if (d->sg) { u32 tmp; imxdma_sg_next(d); - tmp = imx_dmav1_readl(DMA_CCR(channel)); - imx_dmav1_writel(tmp | CCR_RPT | CCR_ACRPT, - DMA_CCR(channel)); + tmp = imx_dmav1_readl(imxdma, DMA_CCR(channel)); + imx_dmav1_writel(imxdma, tmp | CCR_RPT | CCR_ACRPT, + DMA_CCR(channel)); } } @@ -264,6 +270,7 @@ static void imxdma_enable_hw(struct imxdma_desc *d) static void imxdma_disable_hw(struct imxdma_channel *imxdmac) { + struct imxdma_engine *imxdma = imxdmac->imxdma; int channel = imxdmac->channel; unsigned long flags; @@ -273,19 +280,21 @@ static void imxdma_disable_hw(struct imxdma_channel *imxdmac) del_timer(&imxdmac->watchdog); local_irq_save(flags); - imx_dmav1_writel(imx_dmav1_readl(DMA_DIMR) | (1 << channel), DMA_DIMR); - imx_dmav1_writel(imx_dmav1_readl(DMA_CCR(channel)) & ~CCR_CEN, - DMA_CCR(channel)); - imx_dmav1_writel(1 << channel, DMA_DISR); + imx_dmav1_writel(imxdma, imx_dmav1_readl(imxdma, DMA_DIMR) | + (1 << channel), DMA_DIMR); + imx_dmav1_writel(imxdma, imx_dmav1_readl(imxdma, DMA_CCR(channel)) & + ~CCR_CEN, DMA_CCR(channel)); + imx_dmav1_writel(imxdma, 1 << channel, DMA_DISR); local_irq_restore(flags); } static void imxdma_watchdog(unsigned long data) { struct imxdma_channel *imxdmac = (struct imxdma_channel *)data; + struct imxdma_engine *imxdma = imxdmac->imxdma; int channel = imxdmac->channel; - imx_dmav1_writel(0, DMA_CCR(channel)); + imx_dmav1_writel(imxdma, 0, DMA_CCR(channel)); /* Tasklet watchdog error handler */ tasklet_schedule(&imxdmac->dma_tasklet); @@ -299,37 +308,37 @@ static irqreturn_t imxdma_err_handler(int irq, void *dev_id) int i, disr; int errcode; - disr = imx_dmav1_readl(DMA_DISR); + disr = imx_dmav1_readl(imxdma, DMA_DISR); - err_mask = imx_dmav1_readl(DMA_DBTOSR) | - imx_dmav1_readl(DMA_DRTOSR) | - imx_dmav1_readl(DMA_DSESR) | - imx_dmav1_readl(DMA_DBOSR); + err_mask = imx_dmav1_readl(imxdma, DMA_DBTOSR) | + imx_dmav1_readl(imxdma, DMA_DRTOSR) | + imx_dmav1_readl(imxdma, DMA_DSESR) | + imx_dmav1_readl(imxdma, DMA_DBOSR); if (!err_mask) return IRQ_HANDLED; - imx_dmav1_writel(disr & err_mask, DMA_DISR); + imx_dmav1_writel(imxdma, disr & err_mask, DMA_DISR); for (i = 0; i < IMX_DMA_CHANNELS; i++) { if (!(err_mask & (1 << i))) continue; errcode = 0; - if (imx_dmav1_readl(DMA_DBTOSR) & (1 << i)) { - imx_dmav1_writel(1 << i, DMA_DBTOSR); + if (imx_dmav1_readl(imxdma, DMA_DBTOSR) & (1 << i)) { + imx_dmav1_writel(imxdma, 1 << i, DMA_DBTOSR); errcode |= IMX_DMA_ERR_BURST; } - if (imx_dmav1_readl(DMA_DRTOSR) & (1 << i)) { - imx_dmav1_writel(1 << i, DMA_DRTOSR); + if (imx_dmav1_readl(imxdma, DMA_DRTOSR) & (1 << i)) { + imx_dmav1_writel(imxdma, 1 << i, DMA_DRTOSR); errcode |= IMX_DMA_ERR_REQUEST; } - if (imx_dmav1_readl(DMA_DSESR) & (1 << i)) { - imx_dmav1_writel(1 << i, DMA_DSESR); + if (imx_dmav1_readl(imxdma, DMA_DSESR) & (1 << i)) { + imx_dmav1_writel(imxdma, 1 << i, DMA_DSESR); errcode |= IMX_DMA_ERR_TRANSFER; } - if (imx_dmav1_readl(DMA_DBOSR) & (1 << i)) { - imx_dmav1_writel(1 << i, DMA_DBOSR); + if (imx_dmav1_readl(imxdma, DMA_DBOSR) & (1 << i)) { + imx_dmav1_writel(imxdma, 1 << i, DMA_DBOSR); errcode |= IMX_DMA_ERR_BUFFER; } /* Tasklet error handler */ @@ -347,6 +356,7 @@ static irqreturn_t imxdma_err_handler(int irq, void *dev_id) static void dma_irq_handle_channel(struct imxdma_channel *imxdmac) { + struct imxdma_engine *imxdma = imxdmac->imxdma; int chno = imxdmac->channel; struct imxdma_desc *desc; @@ -368,7 +378,7 @@ static void dma_irq_handle_channel(struct imxdma_channel *imxdmac) if (desc->sg) { imxdma_sg_next(desc); - tmp = imx_dmav1_readl(DMA_CCR(chno)); + tmp = imx_dmav1_readl(imxdma, DMA_CCR(chno)); if (imxdma_hw_chain(imxdmac)) { /* FIXME: The timeout should probably be @@ -378,13 +388,14 @@ static void dma_irq_handle_channel(struct imxdma_channel *imxdmac) jiffies + msecs_to_jiffies(500)); tmp |= CCR_CEN | CCR_RPT | CCR_ACRPT; - imx_dmav1_writel(tmp, DMA_CCR(chno)); + imx_dmav1_writel(imxdma, tmp, DMA_CCR(chno)); } else { - imx_dmav1_writel(tmp & ~CCR_CEN, DMA_CCR(chno)); + imx_dmav1_writel(imxdma, tmp & ~CCR_CEN, + DMA_CCR(chno)); tmp |= CCR_CEN; } - imx_dmav1_writel(tmp, DMA_CCR(chno)); + imx_dmav1_writel(imxdma, tmp, DMA_CCR(chno)); if (imxdma_chan_is_doing_cyclic(imxdmac)) /* Tasklet progression */ @@ -400,7 +411,7 @@ static void dma_irq_handle_channel(struct imxdma_channel *imxdmac) } out: - imx_dmav1_writel(0, DMA_CCR(chno)); + imx_dmav1_writel(imxdma, 0, DMA_CCR(chno)); /* Tasklet irq */ tasklet_schedule(&imxdmac->dma_tasklet); } @@ -413,12 +424,12 @@ static irqreturn_t dma_irq_handler(int irq, void *dev_id) if (cpu_is_mx21() || cpu_is_mx27()) imxdma_err_handler(irq, dev_id); - disr = imx_dmav1_readl(DMA_DISR); + disr = imx_dmav1_readl(imxdma, DMA_DISR); pr_debug("imxdma: dma_irq_handler called, disr=0x%08x\n", disr); - imx_dmav1_writel(disr, DMA_DISR); + imx_dmav1_writel(imxdma, disr, DMA_DISR); for (i = 0; i < IMX_DMA_CHANNELS; i++) { if (disr & (1 << i)) dma_irq_handle_channel(&imxdma->channel[i]); @@ -435,12 +446,12 @@ static int imxdma_xfer_desc(struct imxdma_desc *d) /* Configure and enable */ switch (d->type) { case IMXDMA_DESC_MEMCPY: - imx_dmav1_writel(d->src, DMA_SAR(imxdmac->channel)); - imx_dmav1_writel(d->dest, DMA_DAR(imxdmac->channel)); - imx_dmav1_writel(d->config_mem | (d->config_port << 2), + imx_dmav1_writel(imxdma, d->src, DMA_SAR(imxdmac->channel)); + imx_dmav1_writel(imxdma, d->dest, DMA_DAR(imxdmac->channel)); + imx_dmav1_writel(imxdma, d->config_mem | (d->config_port << 2), DMA_CCR(imxdmac->channel)); - imx_dmav1_writel(d->len, DMA_CNTR(imxdmac->channel)); + imx_dmav1_writel(imxdma, d->len, DMA_CNTR(imxdmac->channel)); dev_dbg(imxdma->dev, "%s channel: %d dest=0x%08x src=0x%08x " "dma_length=%d\n", __func__, imxdmac->channel, @@ -451,9 +462,9 @@ static int imxdma_xfer_desc(struct imxdma_desc *d) case IMXDMA_DESC_CYCLIC: case IMXDMA_DESC_SLAVE_SG: if (d->direction == DMA_DEV_TO_MEM) { - imx_dmav1_writel(imxdmac->per_address, + imx_dmav1_writel(imxdma, imxdmac->per_address, DMA_SAR(imxdmac->channel)); - imx_dmav1_writel(imxdmac->ccr_from_device, + imx_dmav1_writel(imxdma, imxdmac->ccr_from_device, DMA_CCR(imxdmac->channel)); dev_dbg(imxdma->dev, "%s channel: %d sg=%p sgcount=%d " @@ -461,9 +472,9 @@ static int imxdma_xfer_desc(struct imxdma_desc *d) __func__, imxdmac->channel, d->sg, d->sgcount, d->len, imxdmac->per_address); } else if (d->direction == DMA_MEM_TO_DEV) { - imx_dmav1_writel(imxdmac->per_address, + imx_dmav1_writel(imxdma, imxdmac->per_address, DMA_DAR(imxdmac->channel)); - imx_dmav1_writel(imxdmac->ccr_to_device, + imx_dmav1_writel(imxdma, imxdmac->ccr_to_device, DMA_CCR(imxdmac->channel)); dev_dbg(imxdma->dev, "%s channel: %d sg=%p sgcount=%d " @@ -528,6 +539,7 @@ static int imxdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, { struct imxdma_channel *imxdmac = to_imxdma_chan(chan); struct dma_slave_config *dmaengine_cfg = (void *)arg; + struct imxdma_engine *imxdma = imxdmac->imxdma; unsigned long flags; unsigned int mode = 0; @@ -573,12 +585,12 @@ static int imxdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, imxdmac->ccr_to_device = (IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR) | ((mode | IMX_DMA_TYPE_FIFO) << 2) | CCR_REN; - imx_dmav1_writel(imxdmac->dma_request, + imx_dmav1_writel(imxdma, imxdmac->dma_request, DMA_RSSR(imxdmac->channel)); /* Set burst length */ - imx_dmav1_writel(imxdmac->watermark_level * imxdmac->word_size, - DMA_BLR(imxdmac->channel)); + imx_dmav1_writel(imxdma, imxdmac->watermark_level * + imxdmac->word_size, DMA_BLR(imxdmac->channel)); return 0; default: @@ -836,27 +848,35 @@ static int __init imxdma_probe(struct platform_device *pdev) struct imxdma_engine *imxdma; int ret, i; - if (cpu_is_mx1()) - imx_dmav1_baseaddr = MX1_IO_ADDRESS(MX1_DMA_BASE_ADDR); - else if (cpu_is_mx21()) - imx_dmav1_baseaddr = MX21_IO_ADDRESS(MX21_DMA_BASE_ADDR); - else if (cpu_is_mx27()) - imx_dmav1_baseaddr = MX27_IO_ADDRESS(MX27_DMA_BASE_ADDR); - else - return 0; - dma_clk = clk_get(NULL, "dma"); - if (IS_ERR(dma_clk)) - return PTR_ERR(dma_clk); - clk_enable(dma_clk); + imxdma = kzalloc(sizeof(*imxdma), GFP_KERNEL); + if (!imxdma) + return -ENOMEM; + + if (cpu_is_mx1()) { + imxdma->base = MX1_IO_ADDRESS(MX1_DMA_BASE_ADDR); + } else if (cpu_is_mx21()) { + imxdma->base = MX21_IO_ADDRESS(MX21_DMA_BASE_ADDR); + } else if (cpu_is_mx27()) { + imxdma->base = MX27_IO_ADDRESS(MX27_DMA_BASE_ADDR); + } else { + kfree(imxdma); + return 0; + } + + imxdma->dma_clk = clk_get(NULL, "dma"); + if (IS_ERR(imxdma->dma_clk)) + return PTR_ERR(imxdma->dma_clk); + clk_enable(imxdma->dma_clk); /* reset DMA module */ - imx_dmav1_writel(DCR_DRST, DMA_DCR); + imx_dmav1_writel(imxdma, DCR_DRST, DMA_DCR); if (cpu_is_mx1()) { ret = request_irq(MX1_DMA_INT, dma_irq_handler, 0, "DMA", imxdma); if (ret) { pr_crit("Can't register IRQ for DMA\n"); + kfree(imxdma); return ret; } @@ -864,22 +884,19 @@ static int __init imxdma_probe(struct platform_device *pdev) if (ret) { pr_crit("Can't register ERRIRQ for DMA\n"); free_irq(MX1_DMA_INT, NULL); + kfree(imxdma); return ret; } } /* enable DMA module */ - imx_dmav1_writel(DCR_DEN, DMA_DCR); + imx_dmav1_writel(imxdma, DCR_DEN, DMA_DCR); /* clear all interrupts */ - imx_dmav1_writel((1 << IMX_DMA_CHANNELS) - 1, DMA_DISR); + imx_dmav1_writel(imxdma, (1 << IMX_DMA_CHANNELS) - 1, DMA_DISR); /* disable interrupts */ - imx_dmav1_writel((1 << IMX_DMA_CHANNELS) - 1, DMA_DIMR); - - imxdma = kzalloc(sizeof(*imxdma), GFP_KERNEL); - if (!imxdma) - return -ENOMEM; + imx_dmav1_writel(imxdma, (1 << IMX_DMA_CHANNELS) - 1, DMA_DIMR); INIT_LIST_HEAD(&imxdma->dma_device.channels); From f9b283a6e41be584f4b1f4c6634625f41ff0c728 Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Thu, 22 Mar 2012 14:54:13 +0100 Subject: [PATCH 64/67] dmaengine: imx-dma: use 'dev_dbg' and 'dev_warn' for messages. There were some 'pr_crit' and 'pr_debug' messages due to the initial merge. Replace them by 'dev_dbg' and 'dev_warn' to be consistent. Signed-off-by: Javier Martin Acked-by: Sascha Hauer Signed-off-by: Vinod Koul --- drivers/dma/imx-dma.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index cdca95a5666c..307cd142f06a 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -227,8 +227,8 @@ static inline int imxdma_sg_next(struct imxdma_desc *d) imx_dmav1_writel(imxdma, now, DMA_CNTR(imxdmac->channel)); - pr_debug("imxdma%d: next sg chunk dst 0x%08x, src 0x%08x, " - "size 0x%08x\n", imxdmac->channel, + dev_dbg(imxdma->dev, " %s channel: %d dst 0x%08x, src 0x%08x, " + "size 0x%08x\n", __func__, imxdmac->channel, imx_dmav1_readl(imxdma, DMA_DAR(imxdmac->channel)), imx_dmav1_readl(imxdma, DMA_SAR(imxdmac->channel)), imx_dmav1_readl(imxdma, DMA_CNTR(imxdmac->channel))); @@ -243,7 +243,7 @@ static void imxdma_enable_hw(struct imxdma_desc *d) int channel = imxdmac->channel; unsigned long flags; - pr_debug("imxdma%d: imx_dma_enable\n", channel); + dev_dbg(imxdma->dev, "%s channel %d\n", __func__, channel); local_irq_save(flags); @@ -274,7 +274,7 @@ static void imxdma_disable_hw(struct imxdma_channel *imxdmac) int channel = imxdmac->channel; unsigned long flags; - pr_debug("imxdma%d: imx_dma_disable\n", channel); + dev_dbg(imxdma->dev, "%s channel %d\n", __func__, channel); if (imxdma_hw_chain(imxdmac)) del_timer(&imxdmac->watchdog); @@ -298,7 +298,8 @@ static void imxdma_watchdog(unsigned long data) /* Tasklet watchdog error handler */ tasklet_schedule(&imxdmac->dma_tasklet); - pr_debug("imxdma%d: watchdog timeout!\n", imxdmac->channel); + dev_dbg(imxdma->dev, "channel %d: watchdog timeout!\n", + imxdmac->channel); } static irqreturn_t imxdma_err_handler(int irq, void *dev_id) @@ -426,8 +427,7 @@ static irqreturn_t dma_irq_handler(int irq, void *dev_id) disr = imx_dmav1_readl(imxdma, DMA_DISR); - pr_debug("imxdma: dma_irq_handler called, disr=0x%08x\n", - disr); + dev_dbg(imxdma->dev, "%s called, disr=0x%08x\n", __func__, disr); imx_dmav1_writel(imxdma, disr, DMA_DISR); for (i = 0; i < IMX_DMA_CHANNELS; i++) { @@ -875,14 +875,14 @@ static int __init imxdma_probe(struct platform_device *pdev) if (cpu_is_mx1()) { ret = request_irq(MX1_DMA_INT, dma_irq_handler, 0, "DMA", imxdma); if (ret) { - pr_crit("Can't register IRQ for DMA\n"); + dev_warn(imxdma->dev, "Can't register IRQ for DMA\n"); kfree(imxdma); return ret; } ret = request_irq(MX1_DMA_ERR, imxdma_err_handler, 0, "DMA", imxdma); if (ret) { - pr_crit("Can't register ERRIRQ for DMA\n"); + dev_warn(imxdma->dev, "Can't register ERRIRQ for DMA\n"); free_irq(MX1_DMA_INT, NULL); kfree(imxdma); return ret; @@ -912,8 +912,9 @@ static int __init imxdma_probe(struct platform_device *pdev) ret = request_irq(MX2x_INT_DMACH0 + i, dma_irq_handler, 0, "DMA", imxdma); if (ret) { - pr_crit("Can't register IRQ %d for DMA channel %d\n", - MX2x_INT_DMACH0 + i, i); + dev_warn(imxdma->dev, "Can't register IRQ %d " + "for DMA channel %d\n", + MX2x_INT_DMACH0 + i, i); goto err_init; } init_timer(&imxdmac->watchdog); From f606ab897b6d7f35b57c7474424676e30457520b Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Thu, 22 Mar 2012 14:54:14 +0100 Subject: [PATCH 65/67] dmaengine: i.MX: Add support for interleaved transfers. i.MX2 and i.MX1 chips have the possibility to do interleaved transfers with two constraints: - Only one chunk can be used (i.e. only 2D transfers are allowed). - Only 2 interleaved configurations can be applied at the same time for all channels. Since this patch adds a new resource 'slots_2d' which is shared by all the DMA channels and to avoid disgustin locking BUGs, the 'lock' member has been moved to the global 'imxdma_engine' structure. Signed-off-by: Javier Martin Acked-by: Sascha Hauer Signed-off-by: Vinod Koul --- drivers/dma/imx-dma.c | 146 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 131 insertions(+), 15 deletions(-) diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index 307cd142f06a..0f698f883cca 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -36,6 +36,10 @@ #define IMXDMA_MAX_CHAN_DESCRIPTORS 16 #define IMX_DMA_CHANNELS 16 +#define IMX_DMA_2D_SLOTS 2 +#define IMX_DMA_2D_SLOT_A 0 +#define IMX_DMA_2D_SLOT_B 1 + #define IMX_DMA_LENGTH_LOOP ((unsigned int)-1) #define IMX_DMA_MEMSIZE_32 (0 << 4) #define IMX_DMA_MEMSIZE_8 (1 << 4) @@ -111,6 +115,13 @@ enum imxdma_prep_type { IMXDMA_DESC_CYCLIC, }; +struct imx_dma_2d_config { + u16 xsr; + u16 ysr; + u16 wsr; + int count; +}; + struct imxdma_desc { struct list_head node; struct dma_async_tx_descriptor desc; @@ -147,13 +158,14 @@ struct imxdma_channel { dma_addr_t per_address; u32 watermark_level; struct dma_chan chan; - spinlock_t lock; struct dma_async_tx_descriptor desc; enum dma_status status; int dma_request; struct scatterlist *sg_list; u32 ccr_from_device; u32 ccr_to_device; + bool enabled_2d; + int slot_2d; }; struct imxdma_engine { @@ -162,6 +174,8 @@ struct imxdma_engine { struct dma_device dma_device; void __iomem *base; struct clk *dma_clk; + spinlock_t lock; + struct imx_dma_2d_config slots_2d[IMX_DMA_2D_SLOTS]; struct imxdma_channel channel[IMX_DMA_CHANNELS]; }; @@ -361,16 +375,16 @@ static void dma_irq_handle_channel(struct imxdma_channel *imxdmac) int chno = imxdmac->channel; struct imxdma_desc *desc; - spin_lock(&imxdmac->lock); + spin_lock(&imxdma->lock); if (list_empty(&imxdmac->ld_active)) { - spin_unlock(&imxdmac->lock); + spin_unlock(&imxdma->lock); goto out; } desc = list_first_entry(&imxdmac->ld_active, struct imxdma_desc, node); - spin_unlock(&imxdmac->lock); + spin_unlock(&imxdma->lock); if (desc->sg) { u32 tmp; @@ -442,9 +456,53 @@ static int imxdma_xfer_desc(struct imxdma_desc *d) { struct imxdma_channel *imxdmac = to_imxdma_chan(d->desc.chan); struct imxdma_engine *imxdma = imxdmac->imxdma; + unsigned long flags; + int slot = -1; + int i; /* Configure and enable */ switch (d->type) { + case IMXDMA_DESC_INTERLEAVED: + /* Try to get a free 2D slot */ + spin_lock_irqsave(&imxdma->lock, flags); + for (i = 0; i < IMX_DMA_2D_SLOTS; i++) { + if ((imxdma->slots_2d[i].count > 0) && + ((imxdma->slots_2d[i].xsr != d->x) || + (imxdma->slots_2d[i].ysr != d->y) || + (imxdma->slots_2d[i].wsr != d->w))) + continue; + slot = i; + break; + } + if (slot < 0) + return -EBUSY; + + imxdma->slots_2d[slot].xsr = d->x; + imxdma->slots_2d[slot].ysr = d->y; + imxdma->slots_2d[slot].wsr = d->w; + imxdma->slots_2d[slot].count++; + + imxdmac->slot_2d = slot; + imxdmac->enabled_2d = true; + spin_unlock_irqrestore(&imxdma->lock, flags); + + if (slot == IMX_DMA_2D_SLOT_A) { + d->config_mem &= ~CCR_MSEL_B; + d->config_port &= ~CCR_MSEL_B; + imx_dmav1_writel(imxdma, d->x, DMA_XSRA); + imx_dmav1_writel(imxdma, d->y, DMA_YSRA); + imx_dmav1_writel(imxdma, d->w, DMA_WSRA); + } else { + d->config_mem |= CCR_MSEL_B; + d->config_port |= CCR_MSEL_B; + imx_dmav1_writel(imxdma, d->x, DMA_XSRB); + imx_dmav1_writel(imxdma, d->y, DMA_YSRB); + imx_dmav1_writel(imxdma, d->w, DMA_WSRB); + } + /* + * We fall-through here intentionally, since a 2D transfer is + * similar to MEMCPY just adding the 2D slot configuration. + */ case IMXDMA_DESC_MEMCPY: imx_dmav1_writel(imxdma, d->src, DMA_SAR(imxdmac->channel)); imx_dmav1_writel(imxdma, d->dest, DMA_DAR(imxdmac->channel)); @@ -503,7 +561,7 @@ static void imxdma_tasklet(unsigned long data) struct imxdma_engine *imxdma = imxdmac->imxdma; struct imxdma_desc *desc; - spin_lock(&imxdmac->lock); + spin_lock(&imxdma->lock); if (list_empty(&imxdmac->ld_active)) { /* Someone might have called terminate all */ @@ -520,6 +578,12 @@ static void imxdma_tasklet(unsigned long data) if (imxdma_chan_is_doing_cyclic(imxdmac)) goto out; + /* Free 2D slot if it was an interleaved transfer */ + if (imxdmac->enabled_2d) { + imxdma->slots_2d[imxdmac->slot_2d].count--; + imxdmac->enabled_2d = false; + } + list_move_tail(imxdmac->ld_active.next, &imxdmac->ld_free); if (!list_empty(&imxdmac->ld_queue)) { @@ -531,7 +595,7 @@ static void imxdma_tasklet(unsigned long data) __func__, imxdmac->channel); } out: - spin_unlock(&imxdmac->lock); + spin_unlock(&imxdma->lock); } static int imxdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, @@ -547,10 +611,10 @@ static int imxdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, case DMA_TERMINATE_ALL: imxdma_disable_hw(imxdmac); - spin_lock_irqsave(&imxdmac->lock, flags); + spin_lock_irqsave(&imxdma->lock, flags); list_splice_tail_init(&imxdmac->ld_active, &imxdmac->ld_free); list_splice_tail_init(&imxdmac->ld_queue, &imxdmac->ld_free); - spin_unlock_irqrestore(&imxdmac->lock, flags); + spin_unlock_irqrestore(&imxdma->lock, flags); return 0; case DMA_SLAVE_CONFIG: if (dmaengine_cfg->direction == DMA_DEV_TO_MEM) { @@ -610,12 +674,13 @@ static enum dma_status imxdma_tx_status(struct dma_chan *chan, static dma_cookie_t imxdma_tx_submit(struct dma_async_tx_descriptor *tx) { struct imxdma_channel *imxdmac = to_imxdma_chan(tx->chan); + struct imxdma_engine *imxdma = imxdmac->imxdma; dma_cookie_t cookie; unsigned long flags; - spin_lock_irqsave(&imxdmac->lock, flags); + spin_lock_irqsave(&imxdma->lock, flags); cookie = dma_cookie_assign(tx); - spin_unlock_irqrestore(&imxdmac->lock, flags); + spin_unlock_irqrestore(&imxdma->lock, flags); return cookie; } @@ -654,16 +719,17 @@ static int imxdma_alloc_chan_resources(struct dma_chan *chan) static void imxdma_free_chan_resources(struct dma_chan *chan) { struct imxdma_channel *imxdmac = to_imxdma_chan(chan); + struct imxdma_engine *imxdma = imxdmac->imxdma; struct imxdma_desc *desc, *_desc; unsigned long flags; - spin_lock_irqsave(&imxdmac->lock, flags); + spin_lock_irqsave(&imxdma->lock, flags); imxdma_disable_hw(imxdmac); list_splice_tail_init(&imxdmac->ld_active, &imxdmac->ld_free); list_splice_tail_init(&imxdmac->ld_queue, &imxdmac->ld_free); - spin_unlock_irqrestore(&imxdmac->lock, flags); + spin_unlock_irqrestore(&imxdma->lock, flags); list_for_each_entry_safe(desc, _desc, &imxdmac->ld_free, node) { kfree(desc); @@ -818,6 +884,49 @@ static struct dma_async_tx_descriptor *imxdma_prep_dma_memcpy( return &desc->desc; } +static struct dma_async_tx_descriptor *imxdma_prep_dma_interleaved( + struct dma_chan *chan, struct dma_interleaved_template *xt, + unsigned long flags) +{ + struct imxdma_channel *imxdmac = to_imxdma_chan(chan); + struct imxdma_engine *imxdma = imxdmac->imxdma; + struct imxdma_desc *desc; + + dev_dbg(imxdma->dev, "%s channel: %d src_start=0x%x dst_start=0x%x\n" + " src_sgl=%s dst_sgl=%s numf=%d frame_size=%d\n", __func__, + imxdmac->channel, xt->src_start, xt->dst_start, + xt->src_sgl ? "true" : "false", xt->dst_sgl ? "true" : "false", + xt->numf, xt->frame_size); + + if (list_empty(&imxdmac->ld_free) || + imxdma_chan_is_doing_cyclic(imxdmac)) + return NULL; + + if (xt->frame_size != 1 || xt->numf <= 0 || xt->dir != DMA_MEM_TO_MEM) + return NULL; + + desc = list_first_entry(&imxdmac->ld_free, struct imxdma_desc, node); + + desc->type = IMXDMA_DESC_INTERLEAVED; + desc->src = xt->src_start; + desc->dest = xt->dst_start; + desc->x = xt->sgl[0].size; + desc->y = xt->numf; + desc->w = xt->sgl[0].icg + desc->x; + desc->len = desc->x * desc->y; + desc->direction = DMA_MEM_TO_MEM; + desc->config_port = IMX_DMA_MEMSIZE_32; + desc->config_mem = IMX_DMA_MEMSIZE_32; + if (xt->src_sgl) + desc->config_mem |= IMX_DMA_TYPE_2D; + if (xt->dst_sgl) + desc->config_port |= IMX_DMA_TYPE_2D; + desc->desc.callback = NULL; + desc->desc.callback_param = NULL; + + return &desc->desc; +} + static void imxdma_issue_pending(struct dma_chan *chan) { struct imxdma_channel *imxdmac = to_imxdma_chan(chan); @@ -825,7 +934,7 @@ static void imxdma_issue_pending(struct dma_chan *chan) struct imxdma_desc *desc; unsigned long flags; - spin_lock_irqsave(&imxdmac->lock, flags); + spin_lock_irqsave(&imxdma->lock, flags); if (list_empty(&imxdmac->ld_active) && !list_empty(&imxdmac->ld_queue)) { desc = list_first_entry(&imxdmac->ld_queue, @@ -840,7 +949,7 @@ static void imxdma_issue_pending(struct dma_chan *chan) &imxdmac->ld_active); } } - spin_unlock_irqrestore(&imxdmac->lock, flags); + spin_unlock_irqrestore(&imxdma->lock, flags); } static int __init imxdma_probe(struct platform_device *pdev) @@ -903,6 +1012,13 @@ static int __init imxdma_probe(struct platform_device *pdev) dma_cap_set(DMA_SLAVE, imxdma->dma_device.cap_mask); dma_cap_set(DMA_CYCLIC, imxdma->dma_device.cap_mask); dma_cap_set(DMA_MEMCPY, imxdma->dma_device.cap_mask); + dma_cap_set(DMA_INTERLEAVE, imxdma->dma_device.cap_mask); + + /* Initialize 2D global parameters */ + for (i = 0; i < IMX_DMA_2D_SLOTS; i++) + imxdma->slots_2d[i].count = 0; + + spin_lock_init(&imxdma->lock); /* Initialize channel parameters */ for (i = 0; i < IMX_DMA_CHANNELS; i++) { @@ -923,7 +1039,6 @@ static int __init imxdma_probe(struct platform_device *pdev) } imxdmac->imxdma = imxdma; - spin_lock_init(&imxdmac->lock); INIT_LIST_HEAD(&imxdmac->ld_queue); INIT_LIST_HEAD(&imxdmac->ld_free); @@ -949,6 +1064,7 @@ static int __init imxdma_probe(struct platform_device *pdev) imxdma->dma_device.device_prep_slave_sg = imxdma_prep_slave_sg; imxdma->dma_device.device_prep_dma_cyclic = imxdma_prep_dma_cyclic; imxdma->dma_device.device_prep_dma_memcpy = imxdma_prep_dma_memcpy; + imxdma->dma_device.device_prep_interleaved_dma = imxdma_prep_dma_interleaved; imxdma->dma_device.device_control = imxdma_control; imxdma->dma_device.device_issue_pending = imxdma_issue_pending; From 660cd0dd94eba5201c69cd10f2d2fefb52807fa8 Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Thu, 22 Mar 2012 14:54:15 +0100 Subject: [PATCH 66/67] dmaengine: i.MX: Fix merge of cookie branch. When merging DMA cookie changes a small chunk of code was dropped. This broke imx-dma driver. This patch adds this chunk again and fixes the problem. Signed-off-by: Javier Martin Signed-off-by: Vinod Koul --- drivers/dma/imx-dma.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index 0f698f883cca..569b0a29fa8c 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -679,6 +679,7 @@ static dma_cookie_t imxdma_tx_submit(struct dma_async_tx_descriptor *tx) unsigned long flags; spin_lock_irqsave(&imxdma->lock, flags); + list_move_tail(imxdmac->ld_free.next, &imxdmac->ld_queue); cookie = dma_cookie_assign(tx); spin_unlock_irqrestore(&imxdma->lock, flags); From 5b2e02e401deb44e7f5befe19404d8b2688efea4 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Tue, 27 Mar 2012 13:53:00 +0530 Subject: [PATCH 67/67] dmaengine: imx: fix the build failure on x86_64 commit 6bd081277ea0 "dmaengine: imx-dma: merge old dma-v1.c with imx-dma.c" removed the dependency in config for the imx dma driver, whereas it should depend on ARCH_MXS Signed-off-by: Vinod Koul --- drivers/dma/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index b4a53a0eeaf2..34e48d905c27 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -230,6 +230,7 @@ config IMX_SDMA config IMX_DMA tristate "i.MX DMA support" + depends on ARCH_MXC select DMA_ENGINE help Support the i.MX DMA engine. This engine is integrated into