dmaengine: dw: Introduce max burst length hw config
authorSerge Semin <Sergey.Semin@baikalelectronics.ru>
Thu, 23 Jul 2020 00:58:47 +0000 (03:58 +0300)
committerVinod Koul <vkoul@kernel.org>
Mon, 27 Jul 2020 09:00:55 +0000 (14:30 +0530)
IP core of the DW DMA controller may be synthesized with different
max burst length of the transfers per each channel. According to Synopsis
having the fixed maximum burst transactions length may provide some
performance gain. At the same time setting up the source and destination
multi size exceeding the max burst length limitation may cause a serious
problems. In our case the DMA transaction just hangs up. In order to fix
this lets introduce the max burst length platform config of the DW DMA
controller device and don't let the DMA channels configuration code
exceed the burst length hardware limitation.

Note the maximum burst length parameter can be detected either in runtime
from the DWC parameter registers or from the dedicated DT property.
Depending on the IP core configuration the maximum value can vary from
channel to channel so by overriding the channel slave max_burst capability
we make sure a DMA consumer will get the channel-specific max burst
length.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Link: https://lore.kernel.org/r/20200723005848.31907-10-Sergey.Semin@baikalelectronics.ru
Signed-off-by: Vinod Koul <vkoul@kernel.org>
drivers/dma/dw/core.c
drivers/dma/dw/of.c
drivers/dma/dw/regs.h
include/linux/platform_data/dma-dw.h

index afe5a2e..588b9ba 100644 (file)
@@ -791,6 +791,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);
 
@@ -1049,6 +1054,13 @@ 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;
+}
+
 int do_dma_probe(struct dw_dma_chip *chip)
 {
        struct dw_dma *dw = chip->dw;
@@ -1189,9 +1201,12 @@ int do_dma_probe(struct dw_dma_chip *chip)
                        dwc->nollp =
                                (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;
                }
        }
 
@@ -1214,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;
index 9e27831..1474b38 100644 (file)
@@ -98,6 +98,11 @@ struct dw_dma_platform_data *dw_dma_parse_dt(struct platform_device *pdev)
                        pdata->multi_block[tmp] = 1;
        }
 
+       if (of_property_read_u32_array(np, "snps,max-burst-len", pdata->max_burst,
+                                      nr_channels)) {
+               memset32(pdata->max_burst, DW_DMA_MAX_BURST, nr_channels);
+       }
+
        if (!of_property_read_u32(np, "snps,dma-protection-control", &tmp)) {
                if (tmp > CHAN_PROTCTL_MASK)
                        return NULL;
index 1ab840b..76654bd 100644 (file)
@@ -126,6 +126,7 @@ struct dw_dma_regs {
 /* Bitfields in DWC_PARAMS */
 #define DWC_PARAMS_MBLK_EN     11              /* multi block transfer */
 #define DWC_PARAMS_HC_LLP      13              /* set LLP register to zero */
+#define DWC_PARAMS_MSIZE       16              /* max group transaction size */
 
 /* bursts size */
 enum dw_dma_msize {
@@ -284,6 +285,7 @@ struct dw_dma_chan {
        /* hardware configuration */
        unsigned int            block_size;
        bool                    nollp;
+       u32                     max_burst;
 
        /* custom slave configuration */
        struct dw_dma_slave     dws;
index 369e41e..4f681df 100644 (file)
@@ -44,6 +44,8 @@ struct dw_dma_slave {
  * @data_width: Maximum data width supported by hardware per AHB master
  *             (in bytes, power of 2)
  * @multi_block: Multi block transfers supported by hardware per channel.
+ * @max_burst: Maximum value of burst transaction size supported by hardware
+ *            per channel (in units of CTL.SRC_TR_WIDTH/CTL.DST_TR_WIDTH).
  * @protctl: Protection control signals setting per channel.
  */
 struct dw_dma_platform_data {
@@ -58,6 +60,7 @@ struct dw_dma_platform_data {
        unsigned char   nr_masters;
        unsigned char   data_width[DW_DMA_MAX_NR_MASTERS];
        unsigned char   multi_block[DW_DMA_MAX_NR_CHANNELS];
+       u32             max_burst[DW_DMA_MAX_NR_CHANNELS];
 #define CHAN_PROTCTL_PRIVILEGED                BIT(0)
 #define CHAN_PROTCTL_BUFFERABLE                BIT(1)
 #define CHAN_PROTCTL_CACHEABLE         BIT(2)