]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
dmaengine: sun6i: Choose appropriate burst length under maxburst
authorChen-Yu Tsai <wens@kernel.org>
Sun, 21 Dec 2025 08:04:48 +0000 (16:04 +0800)
committerVinod Koul <vkoul@kernel.org>
Tue, 23 Dec 2025 07:00:16 +0000 (12:30 +0530)
maxburst, as provided by the client, specifies the largest amount of
data that is allowed to be transferred in one burst. This limit is
normally provided to avoid a data burst overflowing the target FIFO.
It does not mean that the DMA engine can only do bursts in that size.

Let the driver pick the largest supported burst length within the
given limit. This lets the driver work correctly with some clients that
give a large maxburst value. In particular, the 8250_dw driver will give
a quarter of the UART's FIFO size as maxburst. On some systems the FIFO
size is 256 bytes, giving a maxburst of 64 bytes, while the hardware
only supports bursts of up to 16 bytes.

Signed-off-by: Chen-Yu Tsai <wens@kernel.org>
Reviewed-by: Jernej Skrabec <jernej.skrabec@gmail.com>
Link: https://patch.msgid.link/20251221080450.1813479-1-wens@kernel.org
Signed-off-by: Vinod Koul <vkoul@kernel.org>
drivers/dma/sun6i-dma.c

index 2215ff877bf7d024d1885c37c98187b519c75980..f9d876deb1f05c44fd572789adfb4f79b6328eff 100644 (file)
@@ -583,6 +583,22 @@ static irqreturn_t sun6i_dma_interrupt(int irq, void *dev_id)
        return ret;
 }
 
+static u32 find_burst_size(const u32 burst_lengths, u32 maxburst)
+{
+       if (!maxburst)
+               return 1;
+
+       if (BIT(maxburst) & burst_lengths)
+               return maxburst;
+
+       /* Hardware only does power-of-two bursts. */
+       for (u32 burst = rounddown_pow_of_two(maxburst); burst > 0; burst /= 2)
+               if (BIT(burst) & burst_lengths)
+                       return burst;
+
+       return 1;
+}
+
 static int set_config(struct sun6i_dma_dev *sdev,
                        struct dma_slave_config *sconfig,
                        enum dma_transfer_direction direction,
@@ -616,15 +632,13 @@ static int set_config(struct sun6i_dma_dev *sdev,
                return -EINVAL;
        if (!(BIT(dst_addr_width) & sdev->slave.dst_addr_widths))
                return -EINVAL;
-       if (!(BIT(src_maxburst) & sdev->cfg->src_burst_lengths))
-               return -EINVAL;
-       if (!(BIT(dst_maxburst) & sdev->cfg->dst_burst_lengths))
-               return -EINVAL;
 
        src_width = convert_buswidth(src_addr_width);
        dst_width = convert_buswidth(dst_addr_width);
-       dst_burst = convert_burst(dst_maxburst);
-       src_burst = convert_burst(src_maxburst);
+       src_burst = find_burst_size(sdev->cfg->src_burst_lengths, src_maxburst);
+       dst_burst = find_burst_size(sdev->cfg->dst_burst_lengths, dst_maxburst);
+       dst_burst = convert_burst(dst_burst);
+       src_burst = convert_burst(src_burst);
 
        *p_cfg = DMA_CHAN_CFG_SRC_WIDTH(src_width) |
                DMA_CHAN_CFG_DST_WIDTH(dst_width);