Merge tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso...
[linux-2.6-microblaze.git] / drivers / dma / dw / core.c
index a1b56f5..4700f2e 100644 (file)
@@ -786,6 +786,11 @@ static int dwc_config(struct dma_chan *chan, struct dma_slave_config *sconfig)
 
        memcpy(&dwc->dma_sconfig, sconfig, sizeof(*sconfig));
 
+       dwc->dma_sconfig.src_maxburst =
+               clamp(dwc->dma_sconfig.src_maxburst, 0U, dwc->max_burst);
+       dwc->dma_sconfig.dst_maxburst =
+               clamp(dwc->dma_sconfig.dst_maxburst, 0U, dwc->max_burst);
+
        dw->encode_maxburst(dwc, &dwc->dma_sconfig.src_maxburst);
        dw->encode_maxburst(dwc, &dwc->dma_sconfig.dst_maxburst);
 
@@ -1037,6 +1042,25 @@ static void dwc_free_chan_resources(struct dma_chan *chan)
        dev_vdbg(chan2dev(chan), "%s: done\n", __func__);
 }
 
+static void dwc_caps(struct dma_chan *chan, struct dma_slave_caps *caps)
+{
+       struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
+
+       caps->max_burst = dwc->max_burst;
+
+       /*
+        * It might be crucial for some devices to have the hardware
+        * accelerated multi-block transfers supported, aka LLPs in DW DMAC
+        * notation. So if LLPs are supported then max_sg_burst is set to
+        * zero which means unlimited number of SG entries can be handled in a
+        * single DMA transaction, otherwise it's just one SG entry.
+        */
+       if (dwc->nollp)
+               caps->max_sg_burst = 1;
+       else
+               caps->max_sg_burst = 0;
+}
+
 int do_dma_probe(struct dw_dma_chip *chip)
 {
        struct dw_dma *dw = chip->dw;
@@ -1166,11 +1190,23 @@ int do_dma_probe(struct dw_dma_chip *chip)
                         */
                        dwc->block_size =
                                (4 << ((pdata->block_size >> 4 * i) & 0xf)) - 1;
+
+                       /*
+                        * According to the DW DMA databook the true scatter-
+                        * gether LLPs aren't available if either multi-block
+                        * config is disabled (CHx_MULTI_BLK_EN == 0) or the
+                        * LLP register is hard-coded to zeros
+                        * (CHx_HC_LLP == 1).
+                        */
                        dwc->nollp =
-                               (dwc_params >> DWC_PARAMS_MBLK_EN & 0x1) == 0;
+                               (dwc_params >> DWC_PARAMS_MBLK_EN & 0x1) == 0 ||
+                               (dwc_params >> DWC_PARAMS_HC_LLP & 0x1) == 1;
+                       dwc->max_burst =
+                               (0x4 << (dwc_params >> DWC_PARAMS_MSIZE & 0x7));
                } else {
                        dwc->block_size = pdata->block_size;
                        dwc->nollp = !pdata->multi_block[i];
+                       dwc->max_burst = pdata->max_burst[i] ?: DW_DMA_MAX_BURST;
                }
        }
 
@@ -1193,6 +1229,7 @@ int do_dma_probe(struct dw_dma_chip *chip)
        dw->dma.device_prep_dma_memcpy = dwc_prep_dma_memcpy;
        dw->dma.device_prep_slave_sg = dwc_prep_slave_sg;
 
+       dw->dma.device_caps = dwc_caps;
        dw->dma.device_config = dwc_config;
        dw->dma.device_pause = dwc_pause;
        dw->dma.device_resume = dwc_resume;
@@ -1202,12 +1239,21 @@ int do_dma_probe(struct dw_dma_chip *chip)
        dw->dma.device_issue_pending = dwc_issue_pending;
 
        /* DMA capabilities */
+       dw->dma.min_burst = DW_DMA_MIN_BURST;
+       dw->dma.max_burst = DW_DMA_MAX_BURST;
        dw->dma.src_addr_widths = DW_DMA_BUSWIDTHS;
        dw->dma.dst_addr_widths = DW_DMA_BUSWIDTHS;
        dw->dma.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV) |
                             BIT(DMA_MEM_TO_MEM);
        dw->dma.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
 
+       /*
+        * For now there is no hardware with non uniform maximum block size
+        * across all of the device channels, so we set the maximum segment
+        * size as the block size found for the very first channel.
+        */
+       dma_set_max_seg_size(dw->dma.dev, dw->chan[0].block_size);
+
        err = dma_async_device_register(&dw->dma);
        if (err)
                goto err_dma_register;