From ee95e6a2f3be169a9221515496be0252758a57d4 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Mon, 13 Oct 2025 21:43:41 +0100 Subject: [PATCH 1/3] fixup! dmaengine: dw-axi-dmac: Add DMA channel selection Simplify and improve the channel selection scheme by removing the concept of excluded channels. Requesting more channels than are available is a configuration error, but otherwise it is better to get a non-ideal channel than no channel at all. Give each channel a score based on how well it matches the client's requirements, then attempt to claim them in order of decreasing score. A channel which possesses a required flag is scored more highly than one that doesn't, but not having a trait that isn't wanted also contributes to a channel's score, just less so. By walking the list until the end, even channels that don't match at all will be allocated if there is enough demand. Signed-off-by: Phil Elwell --- .../dma/dw-axi-dmac/dw-axi-dmac-platform.c | 80 ++++++++++++------- drivers/dma/dw-axi-dmac/dw-axi-dmac.h | 3 +- 2 files changed, 54 insertions(+), 29 deletions(-) diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c index 2e86ee44469081..a82f5ce5e39ff7 100644 --- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c +++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c @@ -1499,33 +1499,62 @@ static struct dma_chan *dw_axi_dma_of_xlate(struct of_phandle_args *dma_spec, { struct dw_axi_dma *dw = ofdma->of_dma_data; struct axi_dma_chan *chan; + uint32_t chan_flags_all; + uint32_t busy_channels; struct dma_chan *dchan; - uint32_t chan_mask = 0; - uint32_t chan_sel; dma_cap_mask_t mask; + uint32_t chan_mask; + uint32_t chan_sel; + int max_score; + int score; int i; - /* - * Walk through all channels looking for the best match. - * Starting from 0, choose the first available slave channel which isn't precluded. - */ - chan_sel = dma_spec->args[0]; - - for (i = 0; i < dw->hdata->nr_channels; i++) { - if (((dw->sel_precluded[i] & chan_sel) == 0) && - ((dw->sel_required[i] & chan_sel) == dw->sel_required[i])) - chan_mask |= (1 << i); - } + for (i = 0; i < dw->hdata->nr_channels; i++) + chan_flags_all |= dw->chan_flags[i]; dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); - dchan = __dma_request_channel(&mask, dw_axi_dma_filter_fn, &chan_mask, ofdma->of_node); - if (!dchan) - return NULL; + chan_sel = dma_spec->args[0]; + busy_channels = 0; + dchan = NULL; + + while (1) { + max_score = 0; + chan_mask = 0; + + for (i = 0; i < dw->hdata->nr_channels; i++) { + if (busy_channels & (1 << i)) + continue; + /* + * Positive matches (wanted flags that match) score twice that of + * negetive matches (not wanted flags that are not present). + */ + score = 2 * hweight32(chan_sel & dw->chan_flags[i]) + + 1 * hweight32(~chan_sel & ~dw->chan_flags[i] & chan_flags_all); + if (score > max_score) { + max_score = score; + chan_mask = (1 << i); + } else if (score == max_score) { + chan_mask |= (1 << i); + } + } + + if (!chan_mask) + return NULL; + + dchan = __dma_request_channel(&mask, dw_axi_dma_filter_fn, + &chan_mask, ofdma->of_node); + if (dchan) + break; + + /* Repeat, after first marking this group of channels as busy */ + busy_channels |= chan_mask; + } chan = dchan_to_axi_dma_chan(dchan); chan->hw_handshake_num = (u8)chan_sel; + return dchan; } @@ -1607,17 +1636,14 @@ static int parse_device_properties(struct axi_dma_chip *chip) } } - /* sel-require is optional */ - memset(chip->dw->sel_required, 0, sizeof(chip->dw->sel_required)); - device_property_read_u32_array(dev, "snps,sel-require", - chip->dw->sel_required, - chip->dw->hdata->nr_channels); - - /* sel-preclude is optional */ - memset(chip->dw->sel_precluded, 0, sizeof(chip->dw->sel_precluded)); - device_property_read_u32_array(dev, "snps,sel-preclude", - chip->dw->sel_precluded, - chip->dw->hdata->nr_channels); + /* snps,chan-flags is optional */ + memset(chip->dw->chan_flags, 0, sizeof(chip->dw->chan_flags)); + if (device_property_read_u32_array(dev, "snps,chan-flags", + chip->dw->chan_flags, + chip->dw->hdata->nr_channels) < 0) + device_property_read_u32_array(dev, "snps,sel-require", + chip->dw->chan_flags, + chip->dw->hdata->nr_channels); return 0; } diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac.h b/drivers/dma/dw-axi-dmac/dw-axi-dmac.h index 5bf8bc890e6cb8..ab868c16d6b5a5 100644 --- a/drivers/dma/dw-axi-dmac/dw-axi-dmac.h +++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac.h @@ -58,8 +58,7 @@ struct dw_axi_dma { struct dma_device dma; struct dw_axi_dma_hcfg *hdata; struct device_dma_parameters dma_parms; - u32 sel_required[DMAC_MAX_CHANNELS]; - u32 sel_precluded[DMAC_MAX_CHANNELS]; + u32 chan_flags[DMAC_MAX_CHANNELS]; /* channels */ struct axi_dma_chan *chan; From 9129853479ffbb4fb32f742c9f524206a0b36b85 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Mon, 13 Oct 2025 21:50:49 +0100 Subject: [PATCH 2/3] fixup! dts: bcm2712-rpi: Give PIO the "heavy" DMA channels Use the simplified channel property declaration scheme, where the DMA driver finds the best match based on positive matches with no hard exclusions. N.B. The new scheme should give the same results with old and new DTB, with the old property name still be accepted for backwards-compatibility. Signed-off-by: Phil Elwell --- arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi b/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi index 2f6e4a569b3e59..52452d0bec59ec 100644 --- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi +++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi @@ -2,8 +2,7 @@ #include -#define DMA_SEL_WANTHEAVY (1 << 8) -#define DMA_SEL_ONLYHEAVY (1 << 9) +#define DMA_FLAG_HEAVY (1 << 8) &soc { firmware: firmware { @@ -95,20 +94,18 @@ }; &rp1_dma { - snps,sel-require = ; - snps,sel-preclude = <0 0 DMA_SEL_ONLYHEAVY DMA_SEL_ONLYHEAVY DMA_SEL_ONLYHEAVY - DMA_SEL_ONLYHEAVY DMA_SEL_ONLYHEAVY DMA_SEL_ONLYHEAVY>; + snps,chan-flags = ; }; pio: &rp1_pio { - dmas = <&rp1_dma (RP1_DMA_PIO_CH0_TX | DMA_SEL_WANTHEAVY)>, - <&rp1_dma (RP1_DMA_PIO_CH0_RX | DMA_SEL_WANTHEAVY)>, - <&rp1_dma (RP1_DMA_PIO_CH1_TX | DMA_SEL_WANTHEAVY)>, - <&rp1_dma (RP1_DMA_PIO_CH1_RX | DMA_SEL_WANTHEAVY)>, - <&rp1_dma (RP1_DMA_PIO_CH2_TX | DMA_SEL_WANTHEAVY)>, - <&rp1_dma (RP1_DMA_PIO_CH2_RX | DMA_SEL_WANTHEAVY)>, - <&rp1_dma (RP1_DMA_PIO_CH3_TX | DMA_SEL_WANTHEAVY)>, - <&rp1_dma (RP1_DMA_PIO_CH3_RX | DMA_SEL_WANTHEAVY)>; + dmas = <&rp1_dma (RP1_DMA_PIO_CH0_TX | DMA_FLAG_HEAVY)>, + <&rp1_dma (RP1_DMA_PIO_CH0_RX | DMA_FLAG_HEAVY)>, + <&rp1_dma (RP1_DMA_PIO_CH1_TX | DMA_FLAG_HEAVY)>, + <&rp1_dma (RP1_DMA_PIO_CH1_RX | DMA_FLAG_HEAVY)>, + <&rp1_dma (RP1_DMA_PIO_CH2_TX | DMA_FLAG_HEAVY)>, + <&rp1_dma (RP1_DMA_PIO_CH2_RX | DMA_FLAG_HEAVY)>, + <&rp1_dma (RP1_DMA_PIO_CH3_TX | DMA_FLAG_HEAVY)>, + <&rp1_dma (RP1_DMA_PIO_CH3_RX | DMA_FLAG_HEAVY)>; status = "okay"; }; From 12dfd6cdd4b28604e82f01ced5e22a2c78fedac8 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Tue, 14 Oct 2025 09:16:46 +0100 Subject: [PATCH 3/3] misc: rp1-pio: Get burst size from DMA capabilities Although the PIO throughput benefits from larger burst sizes, only the first two DMA channels support a burst size of 8 - the others are capped at 4. To avoid misconfiguring the PIO hardware, retrieve the actual max_burst value from the DMA channel's capabilities. Signed-off-by: Phil Elwell --- drivers/misc/rp1-pio.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/misc/rp1-pio.c b/drivers/misc/rp1-pio.c index 206539fa5f4e06..5758647fc7971c 100644 --- a/drivers/misc/rp1-pio.c +++ b/drivers/misc/rp1-pio.c @@ -611,6 +611,7 @@ static int rp1_pio_sm_config_xfer_internal(struct rp1_pio_client *client, uint s struct platform_device *pdev = pio->pdev; struct device *dev = &pdev->dev; struct dma_slave_config config = {}; + struct dma_slave_caps dma_caps; phys_addr_t fifo_addr; struct dma_info *dma; uint32_t dma_mask; @@ -676,10 +677,12 @@ static int rp1_pio_sm_config_xfer_internal(struct rp1_pio_client *client, uint s config.src_addr = fifo_addr; config.dst_addr = fifo_addr; config.direction = (dir == RP1_PIO_DIR_TO_SM) ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; + dma_caps.max_burst = 4; + dma_get_slave_caps(dma->chan, &dma_caps); if (dir == RP1_PIO_DIR_TO_SM) - config.dst_maxburst = 8; + config.dst_maxburst = dma_caps.max_burst; else - config.src_maxburst = 8; + config.src_maxburst = dma_caps.max_burst; ret = dmaengine_slave_config(dma->chan, &config); if (ret)