Merge tag 'iio-for-4.4a' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio...
[firefly-linux-kernel-4.4.55.git] / drivers / dma / tegra20-apb-dma.c
index 11edcca7619bb4f0cd600a26e73a6f863065af40..c8f79dcaaee8089c1a031c2e76fbbda2c4465488 100644 (file)
@@ -219,6 +219,13 @@ struct tegra_dma {
        void __iomem                    *base_addr;
        const struct tegra_dma_chip_data *chip_data;
 
+       /*
+        * Counter for managing global pausing of the DMA controller.
+        * Only applicable for devices that don't support individual
+        * channel pausing.
+        */
+       u32                             global_pause_count;
+
        /* Some register need to be cache before suspend */
        u32                             reg_gen;
 
@@ -358,16 +365,32 @@ static void tegra_dma_global_pause(struct tegra_dma_channel *tdc,
        struct tegra_dma *tdma = tdc->tdma;
 
        spin_lock(&tdma->global_lock);
-       tdma_write(tdma, TEGRA_APBDMA_GENERAL, 0);
-       if (wait_for_burst_complete)
-               udelay(TEGRA_APBDMA_BURST_COMPLETE_TIME);
+
+       if (tdc->tdma->global_pause_count == 0) {
+               tdma_write(tdma, TEGRA_APBDMA_GENERAL, 0);
+               if (wait_for_burst_complete)
+                       udelay(TEGRA_APBDMA_BURST_COMPLETE_TIME);
+       }
+
+       tdc->tdma->global_pause_count++;
+
+       spin_unlock(&tdma->global_lock);
 }
 
 static void tegra_dma_global_resume(struct tegra_dma_channel *tdc)
 {
        struct tegra_dma *tdma = tdc->tdma;
 
-       tdma_write(tdma, TEGRA_APBDMA_GENERAL, TEGRA_APBDMA_GENERAL_ENABLE);
+       spin_lock(&tdma->global_lock);
+
+       if (WARN_ON(tdc->tdma->global_pause_count == 0))
+               goto out;
+
+       if (--tdc->tdma->global_pause_count == 0)
+               tdma_write(tdma, TEGRA_APBDMA_GENERAL,
+                          TEGRA_APBDMA_GENERAL_ENABLE);
+
+out:
        spin_unlock(&tdma->global_lock);
 }
 
@@ -598,7 +621,6 @@ static void handle_once_dma_done(struct tegra_dma_channel *tdc,
                return;
 
        tdc_start_head_req(tdc);
-       return;
 }
 
 static void handle_cont_sngl_cycle_dma_done(struct tegra_dma_channel *tdc,
@@ -625,7 +647,6 @@ static void handle_cont_sngl_cycle_dma_done(struct tegra_dma_channel *tdc,
                if (!st)
                        dma_desc->dma_status = DMA_ERROR;
        }
-       return;
 }
 
 static void tegra_dma_tasklet(unsigned long data)
@@ -717,7 +738,6 @@ static void tegra_dma_issue_pending(struct dma_chan *dc)
        }
 end:
        spin_unlock_irqrestore(&tdc->lock, flags);
-       return;
 }
 
 static int tegra_dma_terminate_all(struct dma_chan *dc)
@@ -929,7 +949,6 @@ static struct dma_async_tx_descriptor *tegra_dma_prep_slave_sg(
        struct tegra_dma_sg_req  *sg_req = NULL;
        u32 burst_size;
        enum dma_slave_buswidth slave_bw;
-       int ret;
 
        if (!tdc->config_init) {
                dev_err(tdc2dev(tdc), "dma channel is not configured\n");
@@ -940,9 +959,8 @@ static struct dma_async_tx_descriptor *tegra_dma_prep_slave_sg(
                return NULL;
        }
 
-       ret = get_transfer_param(tdc, direction, &apb_ptr, &apb_seq, &csr,
-                               &burst_size, &slave_bw);
-       if (ret < 0)
+       if (get_transfer_param(tdc, direction, &apb_ptr, &apb_seq, &csr,
+                               &burst_size, &slave_bw) < 0)
                return NULL;
 
        INIT_LIST_HEAD(&req_list);
@@ -1045,7 +1063,6 @@ static struct dma_async_tx_descriptor *tegra_dma_prep_dma_cyclic(
        dma_addr_t mem = buf_addr;
        u32 burst_size;
        enum dma_slave_buswidth slave_bw;
-       int ret;
 
        if (!buf_len || !period_len) {
                dev_err(tdc2dev(tdc), "Invalid buffer/period len\n");
@@ -1084,12 +1101,10 @@ static struct dma_async_tx_descriptor *tegra_dma_prep_dma_cyclic(
                return NULL;
        }
 
-       ret = get_transfer_param(tdc, direction, &apb_ptr, &apb_seq, &csr,
-                               &burst_size, &slave_bw);
-       if (ret < 0)
+       if (get_transfer_param(tdc, direction, &apb_ptr, &apb_seq, &csr,
+                               &burst_size, &slave_bw) < 0)
                return NULL;
 
-
        ahb_seq = TEGRA_APBDMA_AHBSEQ_INTR_ENB;
        ahb_seq |= TEGRA_APBDMA_AHBSEQ_WRAP_NONE <<
                                        TEGRA_APBDMA_AHBSEQ_WRAP_SHIFT;
@@ -1415,6 +1430,7 @@ static int tegra_dma_probe(struct platform_device *pdev)
        dma_cap_set(DMA_PRIVATE, tdma->dma_dev.cap_mask);
        dma_cap_set(DMA_CYCLIC, tdma->dma_dev.cap_mask);
 
+       tdma->global_pause_count = 0;
        tdma->dma_dev.dev = &pdev->dev;
        tdma->dma_dev.device_alloc_chan_resources =
                                        tegra_dma_alloc_chan_resources;