summaryrefslogtreecommitdiff
path: root/drivers/dma
diff options
context:
space:
mode:
authorAndy Shevchenko <andriy.shevchenko@linux.intel.com>2014-09-23 18:18:14 +0400
committerVinod Koul <vinod.koul@intel.com>2014-10-15 19:01:05 +0400
commit99d9bf4ed27c63d5559e31d112f71af655c7182b (patch)
treef0fc2e9d1053d98b265f38453e01cba00d1b7bd2 /drivers/dma
parent2540f74b187e3ec0fe106b7427c4a84c955dc140 (diff)
downloadlinux-99d9bf4ed27c63d5559e31d112f71af655c7182b.tar.xz
dmaengine: dw: enable and disable controller when needed
Enable controller automatically whenever first user requires for a channel and disable it when the last user gone. Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Signed-off-by: Vinod Koul <vinod.koul@intel.com>
Diffstat (limited to 'drivers/dma')
-rw-r--r--drivers/dma/dw/core.c60
-rw-r--r--drivers/dma/dw/regs.h1
2 files changed, 36 insertions, 25 deletions
diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c
index 48126380e036..244722170410 100644
--- a/drivers/dma/dw/core.c
+++ b/drivers/dma/dw/core.c
@@ -1094,6 +1094,31 @@ static void dwc_issue_pending(struct dma_chan *chan)
spin_unlock_irqrestore(&dwc->lock, flags);
}
+/*----------------------------------------------------------------------*/
+
+static void dw_dma_off(struct dw_dma *dw)
+{
+ int i;
+
+ dma_writel(dw, CFG, 0);
+
+ channel_clear_bit(dw, MASK.XFER, 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);
+
+ while (dma_readl(dw, CFG) & DW_CFG_DMA_EN)
+ cpu_relax();
+
+ for (i = 0; i < dw->dma.chancnt; i++)
+ dw->chan[i].initialized = false;
+}
+
+static void dw_dma_on(struct dw_dma *dw)
+{
+ dma_writel(dw, CFG, DW_CFG_DMA_EN);
+}
+
static int dwc_alloc_chan_resources(struct dma_chan *chan)
{
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
@@ -1118,6 +1143,11 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan)
* doesn't mean what you think it means), and status writeback.
*/
+ /* Enable controller here if needed */
+ if (!dw->in_use)
+ dw_dma_on(dw);
+ dw->in_use |= dwc->mask;
+
spin_lock_irqsave(&dwc->lock, flags);
i = dwc->descs_allocated;
while (dwc->descs_allocated < NR_DESCS_PER_CHANNEL) {
@@ -1182,6 +1212,11 @@ static void dwc_free_chan_resources(struct dma_chan *chan)
spin_unlock_irqrestore(&dwc->lock, flags);
+ /* Disable controller in case it was a last user */
+ dw->in_use &= ~dwc->mask;
+ if (!dw->in_use)
+ dw_dma_off(dw);
+
list_for_each_entry_safe(desc, _desc, &list, desc_node) {
dev_vdbg(chan2dev(chan), " freeing descriptor %p\n", desc);
dma_pool_free(dw->desc_pool, desc, desc->txd.phys);
@@ -1452,29 +1487,6 @@ EXPORT_SYMBOL(dw_dma_cyclic_free);
/*----------------------------------------------------------------------*/
-static void dw_dma_off(struct dw_dma *dw)
-{
- int i;
-
- dma_writel(dw, CFG, 0);
-
- channel_clear_bit(dw, MASK.XFER, 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);
-
- while (dma_readl(dw, CFG) & DW_CFG_DMA_EN)
- cpu_relax();
-
- for (i = 0; i < dw->dma.chancnt; i++)
- dw->chan[i].initialized = false;
-}
-
-static void dw_dma_on(struct dw_dma *dw)
-{
- dma_writel(dw, CFG, DW_CFG_DMA_EN);
-}
-
int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata)
{
struct dw_dma *dw;
@@ -1648,8 +1660,6 @@ int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata)
dw->dma.device_tx_status = dwc_tx_status;
dw->dma.device_issue_pending = dwc_issue_pending;
- dw_dma_on(dw);
-
err = dma_async_device_register(&dw->dma);
if (err)
goto err_dma_register;
diff --git a/drivers/dma/dw/regs.h b/drivers/dma/dw/regs.h
index e8f92b28ffc2..848e232f7cc7 100644
--- a/drivers/dma/dw/regs.h
+++ b/drivers/dma/dw/regs.h
@@ -281,6 +281,7 @@ struct dw_dma {
/* channels */
struct dw_dma_chan *chan;
u8 all_chan_mask;
+ u8 in_use;
/* hardware configuration */
unsigned char nr_masters;