Patch "dmaengine: dw: Add peripheral bus width verification" has been added to the 5.15-stable tree

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This is a note to let you know that I've just added the patch titled

    dmaengine: dw: Add peripheral bus width verification

to the 5.15-stable tree which can be found at:
    http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary

The filename of the patch is:
     dmaengine-dw-add-peripheral-bus-width-verification.patch
and it can be found in the queue-5.15 subdirectory.

If you, or anyone else, feels it should not be added to the stable tree,
please let <stable@xxxxxxxxxxxxxxx> know about it.



commit cdbfafbb4160d91a70466e88aa26e7612ba19e01
Author: Serge Semin <fancer.lancer@xxxxxxxxx>
Date:   Fri Aug 2 10:50:46 2024 +0300

    dmaengine: dw: Add peripheral bus width verification
    
    [ Upstream commit b336268dde75cb09bd795cb24893d52152a9191f ]
    
    Currently the src_addr_width and dst_addr_width fields of the
    dma_slave_config structure are mapped to the CTLx.SRC_TR_WIDTH and
    CTLx.DST_TR_WIDTH fields of the peripheral bus side in order to have the
    properly aligned data passed to the target device. It's done just by
    converting the passed peripheral bus width to the encoded value using the
    __ffs() function. This implementation has several problematic sides:
    
    1. __ffs() is undefined if no bit exist in the passed value. Thus if the
    specified addr-width is DMA_SLAVE_BUSWIDTH_UNDEFINED, __ffs() may return
    unexpected value depending on the platform-specific implementation.
    
    2. DW AHB DMA-engine permits having the power-of-2 transfer width limited
    by the DMAH_Mk_HDATA_WIDTH IP-core synthesize parameter. Specifying
    bus-width out of that constraints scope will definitely cause unexpected
    result since the destination reg will be only partly touched than the
    client driver implied.
    
    Let's fix all of that by adding the peripheral bus width verification
    method and calling it in dwc_config() which is supposed to be executed
    before preparing any transfer. The new method will make sure that the
    passed source or destination address width is valid and if undefined then
    the driver will just fallback to the 1-byte width transfer.
    
    Fixes: 029a40e97d0d ("dmaengine: dw: provide DMA capabilities")
    Signed-off-by: Serge Semin <fancer.lancer@xxxxxxxxx>
    Acked-by: Andy Shevchenko <andy@xxxxxxxxxx>
    Link: https://lore.kernel.org/r/20240802075100.6475-2-fancer.lancer@xxxxxxxxx
    Signed-off-by: Vinod Koul <vkoul@xxxxxxxxxx>
    Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>

diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c
index 7ab83fe601ede..128c194d65b6d 100644
--- a/drivers/dma/dw/core.c
+++ b/drivers/dma/dw/core.c
@@ -16,6 +16,7 @@
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
+#include <linux/log2.h>
 #include <linux/mm.h>
 #include <linux/module.h>
 #include <linux/slab.h>
@@ -783,10 +784,43 @@ bool dw_dma_filter(struct dma_chan *chan, void *param)
 }
 EXPORT_SYMBOL_GPL(dw_dma_filter);
 
+static int dwc_verify_p_buswidth(struct dma_chan *chan)
+{
+	struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
+	struct dw_dma *dw = to_dw_dma(chan->device);
+	u32 reg_width, max_width;
+
+	if (dwc->dma_sconfig.direction == DMA_MEM_TO_DEV)
+		reg_width = dwc->dma_sconfig.dst_addr_width;
+	else if (dwc->dma_sconfig.direction == DMA_DEV_TO_MEM)
+		reg_width = dwc->dma_sconfig.src_addr_width;
+	else /* DMA_MEM_TO_MEM */
+		return 0;
+
+	max_width = dw->pdata->data_width[dwc->dws.p_master];
+
+	/* Fall-back to 1-byte transfer width if undefined */
+	if (reg_width == DMA_SLAVE_BUSWIDTH_UNDEFINED)
+		reg_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+	else if (!is_power_of_2(reg_width) || reg_width > max_width)
+		return -EINVAL;
+	else /* bus width is valid */
+		return 0;
+
+	/* Update undefined addr width value */
+	if (dwc->dma_sconfig.direction == DMA_MEM_TO_DEV)
+		dwc->dma_sconfig.dst_addr_width = reg_width;
+	else /* DMA_DEV_TO_MEM */
+		dwc->dma_sconfig.src_addr_width = reg_width;
+
+	return 0;
+}
+
 static int dwc_config(struct dma_chan *chan, struct dma_slave_config *sconfig)
 {
 	struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
 	struct dw_dma *dw = to_dw_dma(chan->device);
+	int ret;
 
 	memcpy(&dwc->dma_sconfig, sconfig, sizeof(*sconfig));
 
@@ -795,6 +829,10 @@ static int dwc_config(struct dma_chan *chan, struct dma_slave_config *sconfig)
 	dwc->dma_sconfig.dst_maxburst =
 		clamp(dwc->dma_sconfig.dst_maxburst, 0U, dwc->max_burst);
 
+	ret = dwc_verify_p_buswidth(chan);
+	if (ret)
+		return ret;
+
 	dw->encode_maxburst(dwc, &dwc->dma_sconfig.src_maxburst);
 	dw->encode_maxburst(dwc, &dwc->dma_sconfig.dst_maxburst);
 




[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux