Hi All
This series initially cleans up the BCM2835 DMA driver in preparation for
supporting the 40bit version. It then fixes up the incorrect mapping behaviour
we've had to date.
The cleanups are based on Stefan Wahren's RFC [1], with a couple of minor bugs
fixed, but stopping before actually adding the 40bit support. If we can sort
the mapping issue, it avoids having to have workarounds in the 40bit support.
The mapping issues were discussed in [2].
Up until this point all DMA users have been passing in dma addresses rather than
CPU physical addresses, and the DMA driver has been using those directly rather
than using dma_map_resource() to map them.
The DT has also been missing some of the required mappings in "dma-ranges", but
they have been present in "ranges". I've therefore duplicated the minimum amount
of of_dma_get_range and translate_phys_to_dma to be able to use "ranges" as
discussed in that thread. I'm assuming that sort of code is not desirable in the
core code as it shouldn't be necessary, so keeping it contained within a driver
is the better solution.
When Andrea posted our downstream patches in [3], Robin Murphy stated that
dma_map_resource is the correct API, but as it currently doesn't check the
dma_range_map we need Sergey Semin's patch [4].
There seemed to be no follow up over the implications of it. I've therefore
included it in the series at least for discussion. If it's not acceptable then
I'm not sure of the route forward in fixing this mapping issue.
I'm expecting there to be some discussion, but also acknowledge that merging this
will need to be phased with the patches 1-13 needing to be merged before any of
14-17, and then 18 merged last to remove the workaround. I suspect that's the
least of my worries though.
I will apologise in advance if I don't respond immediately to comments - I'm
out of the office for the next week, but do appreciate any feedback.
Thanks
Dave
[1] https://lore.kernel.org/linux-arm-kernel/[email protected]/T/
[2] https://lore.kernel.org/linux-arm-kernel/CAPY8ntBua=wPVUj+SM0WGcUL0fT56uEHo8YZUTMB8Z54X_aPRw@mail.gmail.com/T/
[3] https://lore.kernel.org/lkml/[email protected]/T/
[4] https://lore.kernel.org/linux-iommu/[email protected]/
Dave Stevenson (7):
ARM: dts: bcm283x: Update to use dma-channel-mask
dmaengine: bcm2835: Add function to handle DMA mapping
dmaengine: bcm2835: Add backwards compatible handling until clients
updated
dmaengine: bcm2835: Use dma_map_resource to map addresses
dmaengine: bcm2835: Read ranges if dma-ranges aren't mapped
arm: dt: Add dma-ranges to the bcm283x platforms
dmaengine: bcm2835: Revert the workaround for DMA addresses
Phil Elwell (4):
mmc: bcm2835: Use phys addresses for slave DMA config
spi: bcm2835: Use phys addresses for slave DMA config
drm/vc4: Use phys addresses for slave DMA config
ASoC: bcm2835-i2s: Use phys addresses for DAI DMA
Serge Semin (1):
dma-direct: take dma-ranges/offsets into account in resource mapping
Stefan Wahren (6):
dmaengine: bcm2835: Support common dma-channel-mask
dmaengine: bcm2835: move CB info generation into separate function
dmaengine: bcm2835: move CB final extra info generation into function
dmaengine: bcm2835: make address increment platform independent
dmaengine: bcm2385: drop info parameters
dmaengine: bcm2835: pass dma_chan to generic functions
arch/arm/boot/dts/broadcom/bcm2711.dtsi | 14 +-
.../arm/boot/dts/broadcom/bcm2835-common.dtsi | 2 +-
arch/arm/boot/dts/broadcom/bcm2835.dtsi | 3 +-
arch/arm/boot/dts/broadcom/bcm2836.dtsi | 3 +-
arch/arm/boot/dts/broadcom/bcm2837.dtsi | 3 +-
drivers/dma/bcm2835-dma.c | 432 ++++++++++++++----
drivers/gpu/drm/vc4/vc4_hdmi.c | 15 +-
drivers/mmc/host/bcm2835.c | 17 +-
drivers/spi/spi-bcm2835.c | 23 +-
kernel/dma/direct.c | 2 +-
sound/soc/bcm/bcm2835-i2s.c | 18 +-
11 files changed, 383 insertions(+), 149 deletions(-)
--
2.34.1
From: Stefan Wahren <[email protected]>
Nowadays there is a generic property for dma-channel-mask in the DMA
controller binding. So prefer this one instead of the old vendor specific
one. Print a warning in case the old one is used. Btw use the result of
of_property_read_u32() as return code in error case.
Signed-off-by: Stefan Wahren <[email protected]>
Signed-off-by: Dave Stevenson <[email protected]>
---
drivers/dma/bcm2835-dma.c | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)
diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
index 9d74fe97452e..528c4593b45a 100644
--- a/drivers/dma/bcm2835-dma.c
+++ b/drivers/dma/bcm2835-dma.c
@@ -941,12 +941,19 @@ static int bcm2835_dma_probe(struct platform_device *pdev)
}
/* Request DMA channel mask from device tree */
- if (of_property_read_u32(pdev->dev.of_node,
- "brcm,dma-channel-mask",
- &chans_available)) {
- dev_err(&pdev->dev, "Failed to get channel mask\n");
- rc = -EINVAL;
- goto err_no_dma;
+ rc = of_property_read_u32(pdev->dev.of_node, "dma-channel-mask",
+ &chans_available);
+
+ if (rc) {
+ /* Try deprecated property */
+ if (of_property_read_u32(pdev->dev.of_node,
+ "brcm,dma-channel-mask",
+ &chans_available)) {
+ dev_err(&pdev->dev, "Failed to get channel mask\n");
+ goto err_no_dma;
+ }
+
+ dev_warn(&pdev->dev, "brcm,dma-channel-mask deprecated - please update DT\n");
}
/* get irqs for each channel that we support */
--
2.34.1
Now the driver looks for the common dma-channel-mask property
rather than the vendor-specific brcm,dma-channel-mask, update
the dt files to follow suit.
Signed-off-by: Dave Stevenson <[email protected]>
---
arch/arm/boot/dts/broadcom/bcm2711.dtsi | 2 +-
arch/arm/boot/dts/broadcom/bcm2835-common.dtsi | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/arm/boot/dts/broadcom/bcm2711.dtsi b/arch/arm/boot/dts/broadcom/bcm2711.dtsi
index e4e42af21ef3..d64bf098b697 100644
--- a/arch/arm/boot/dts/broadcom/bcm2711.dtsi
+++ b/arch/arm/boot/dts/broadcom/bcm2711.dtsi
@@ -103,7 +103,7 @@ dma: dma-controller@7e007000 {
"dma9",
"dma10";
#dma-cells = <1>;
- brcm,dma-channel-mask = <0x07f5>;
+ dma-channel-mask = <0x07f5>;
};
pm: watchdog@7e100000 {
diff --git a/arch/arm/boot/dts/broadcom/bcm2835-common.dtsi b/arch/arm/boot/dts/broadcom/bcm2835-common.dtsi
index 9261b67dbee1..3ba8db8eed0f 100644
--- a/arch/arm/boot/dts/broadcom/bcm2835-common.dtsi
+++ b/arch/arm/boot/dts/broadcom/bcm2835-common.dtsi
@@ -46,7 +46,7 @@ dma: dma-controller@7e007000 {
"dma14",
"dma-shared-all";
#dma-cells = <1>;
- brcm,dma-channel-mask = <0x7f35>;
+ dma-channel-mask = <0x7f35>;
};
intc: interrupt-controller@7e00b200 {
--
2.34.1
From: Serge Semin <[email protected]>
A basic device-specific linear memory mapping was introduced back in
commit ("dma: Take into account dma_pfn_offset") as a single-valued offset
preserved in the device.dma_pfn_offset field, which was initialized for
instance by means of the "dma-ranges" DT property. Afterwards the
functionality was extended to support more than one device-specific region
defined in the device.dma_range_map list of maps. But all of these
improvements concerned a single pointer, page or sg DMA-mapping methods,
while the system resource mapping function turned to miss the
corresponding modification. Thus the dma_direct_map_resource() method now
just casts the CPU physical address to the device DMA address with no
dma-ranges-based mapping taking into account, which is obviously wrong.
Let's fix it by using the phys_to_dma_direct() method to get the
device-specific bus address from the passed memory resource for the case
of the directly mapped DMA.
Fixes: 25f1e1887088 ("dma: Take into account dma_pfn_offset")
Signed-off-by: Serge Semin <[email protected]>
Signed-off-by: Dave Stevenson <[email protected]>
---
kernel/dma/direct.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c
index 4d543b1e9d57..916a16959575 100644
--- a/kernel/dma/direct.c
+++ b/kernel/dma/direct.c
@@ -509,7 +509,7 @@ int dma_direct_map_sg(struct device *dev, struct scatterlist *sgl, int nents,
dma_addr_t dma_direct_map_resource(struct device *dev, phys_addr_t paddr,
size_t size, enum dma_data_direction dir, unsigned long attrs)
{
- dma_addr_t dma_addr = paddr;
+ dma_addr_t dma_addr = phys_to_dma_direct(dev, paddr);
if (unlikely(!dma_capable(dev, dma_addr, size, false))) {
dev_err_once(dev,
--
2.34.1
From: Stefan Wahren <[email protected]>
Similar to the info generation, generate the final extra info with a
separate function. This is necessary to introduce other platforms
with different info bits.
Signed-off-by: Stefan Wahren <[email protected]>
Signed-off-by: Dave Stevenson <[email protected]>
---
drivers/dma/bcm2835-dma.c | 34 ++++++++++++++++++++++++++++------
1 file changed, 28 insertions(+), 6 deletions(-)
diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
index 7cef7ff89575..ef452ebb3c15 100644
--- a/drivers/dma/bcm2835-dma.c
+++ b/drivers/dma/bcm2835-dma.c
@@ -229,6 +229,29 @@ static u32 bcm2835_dma_prepare_cb_info(struct bcm2835_chan *c,
return result;
}
+static u32 bcm2835_dma_prepare_cb_extra(struct bcm2835_chan *c,
+ enum dma_transfer_direction direction,
+ bool cyclic, bool final,
+ unsigned long flags)
+{
+ u32 result = 0;
+
+ if (cyclic) {
+ if (flags & DMA_PREP_INTERRUPT)
+ result |= BCM2835_DMA_INT_EN;
+ } else {
+ if (!final)
+ return 0;
+
+ result |= BCM2835_DMA_INT_EN;
+
+ if (direction == DMA_MEM_TO_MEM)
+ result |= BCM2835_DMA_WAIT_RESP;
+ }
+
+ return result;
+}
+
static void bcm2835_dma_free_cb_chain(struct bcm2835_desc *desc)
{
size_t i;
@@ -644,7 +667,8 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_memcpy(
struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
struct bcm2835_desc *d;
u32 info = bcm2835_dma_prepare_cb_info(c, DMA_MEM_TO_MEM, false);
- u32 extra = BCM2835_DMA_INT_EN | BCM2835_DMA_WAIT_RESP;
+ u32 extra = bcm2835_dma_prepare_cb_extra(c, DMA_MEM_TO_MEM, false,
+ true, 0);
size_t max_len = bcm2835_dma_max_frame_length(c);
size_t frames;
@@ -675,7 +699,7 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_slave_sg(
struct bcm2835_desc *d;
dma_addr_t src = 0, dst = 0;
u32 info = bcm2835_dma_prepare_cb_info(c, direction, false);
- u32 extra = BCM2835_DMA_INT_EN;
+ u32 extra = bcm2835_dma_prepare_cb_extra(c, direction, false, true, 0);
size_t frames;
if (!is_slave_direction(direction)) {
@@ -723,7 +747,7 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic(
dma_addr_t src, dst;
u32 info = bcm2835_dma_prepare_cb_info(c, direction,
buf_addr == od->zero_page);
- u32 extra = 0;
+ u32 extra = bcm2835_dma_prepare_cb_extra(c, direction, true, true, 0);
size_t max_len = bcm2835_dma_max_frame_length(c);
size_t frames;
@@ -739,9 +763,7 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic(
return NULL;
}
- if (flags & DMA_PREP_INTERRUPT)
- extra |= BCM2835_DMA_INT_EN;
- else
+ if (!(flags & DMA_PREP_INTERRUPT))
period_len = buf_len;
/*
--
2.34.1
There is a need to account for dma-ranges and iommus in the
dma mapping process, and the public API for handling that is
dma_map_resource.
Add support for mapping addresses to the DMA driver.
Signed-off-by: Dave Stevenson <[email protected]>
---
drivers/dma/bcm2835-dma.c | 46 ++++++++++++++++++++++++++++++++++-----
1 file changed, 41 insertions(+), 5 deletions(-)
diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
index 9531c0b82071..e48008b06716 100644
--- a/drivers/dma/bcm2835-dma.c
+++ b/drivers/dma/bcm2835-dma.c
@@ -67,6 +67,10 @@ struct bcm2835_cb_entry {
struct bcm2835_dma_chan_map {
dma_addr_t addr;
+ enum dma_data_direction dir;
+
+ phys_addr_t slave_addr;
+ unsigned int xfer_size;
};
struct bcm2835_chan {
@@ -294,12 +298,44 @@ static int bcm2835_dma_map_slave_addr(struct dma_chan *chan,
return 0;
}
- /*
- * This path will be updated to handle new clients, but currently should
- * never be used.
- */
+ if (dev_size != DMA_SLAVE_BUSWIDTH_4_BYTES)
+ return -EIO;
+
+ /* Reuse current map if possible. */
+ if (dev_addr == map->slave_addr &&
+ dev_size == map->xfer_size &&
+ dev_dir == map->dir)
+ return 0;
+
+ /* Remove old mapping if present. */
+ if (map->xfer_size) {
+ dev_dbg(chan->device->dev, "chan: unmap %zx@%pap to %pad dir: %s\n",
+ dev_size, &dev_addr, &map->addr,
+ dev_dir == DMA_TO_DEVICE ? "DMA_TO_DEVICE" : "DMA_FROM_DEVICE");
+ dma_unmap_resource(chan->device->dev, map->addr,
+ map->xfer_size, map->dir, 0);
+ }
+ map->xfer_size = 0;
- return -EINVAL;
+ /* Create new slave address map. */
+ map->addr = dma_map_resource(chan->device->dev, dev_addr, dev_size,
+ dev_dir, 0);
+
+ if (dma_mapping_error(chan->device->dev, map->addr)) {
+ dev_err(chan->device->dev, "chan: failed to map %zx@%pap",
+ dev_size, &dev_addr);
+ return -EIO;
+ }
+
+ dev_dbg(chan->device->dev, "chan: map %zx@%pap to %pad dir: %s\n",
+ dev_size, &dev_addr, &map->addr,
+ dev_dir == DMA_TO_DEVICE ? "DMA_TO_DEVICE" : "DMA_FROM_DEVICE");
+
+ map->slave_addr = dev_addr;
+ map->xfer_size = dev_size;
+ map->dir = dev_dir;
+
+ return 0;
}
static void bcm2835_dma_free_cb_chain(struct bcm2835_desc *desc)
--
2.34.1
The code handling DMA mapping is currently incorrect and
needs a sequence of fixups.
Move the mapping out into a separate function and structure
to allow for those fixes to be applied more cleanly.
Signed-off-by: Dave Stevenson <[email protected]>
---
drivers/dma/bcm2835-dma.c | 46 ++++++++++++++++++++++++++++++++-------
1 file changed, 38 insertions(+), 8 deletions(-)
diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
index aefaa1f01d7f..ef1d95bae84e 100644
--- a/drivers/dma/bcm2835-dma.c
+++ b/drivers/dma/bcm2835-dma.c
@@ -65,6 +65,10 @@ struct bcm2835_cb_entry {
dma_addr_t paddr;
};
+struct bcm2835_dma_chan_map {
+ dma_addr_t addr;
+};
+
struct bcm2835_chan {
struct virt_dma_chan vc;
@@ -74,6 +78,7 @@ struct bcm2835_chan {
int ch;
struct bcm2835_desc *desc;
struct dma_pool *cb_pool;
+ struct bcm2835_dma_chan_map map;
void __iomem *chan_base;
int irq_number;
@@ -268,6 +273,19 @@ static inline bool need_dst_incr(enum dma_transfer_direction direction)
}
return false;
+};
+
+static int bcm2835_dma_map_slave_addr(struct dma_chan *chan,
+ phys_addr_t dev_addr,
+ size_t dev_size,
+ enum dma_data_direction dev_dir)
+{
+ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
+ struct bcm2835_dma_chan_map *map = &c->map;
+
+ map->addr = dev_addr;
+
+ return 0;
}
static void bcm2835_dma_free_cb_chain(struct bcm2835_desc *desc)
@@ -734,13 +752,19 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_slave_sg(
}
if (direction == DMA_DEV_TO_MEM) {
- if (c->cfg.src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES)
+ if (bcm2835_dma_map_slave_addr(chan, c->cfg.src_addr,
+ c->cfg.src_addr_width,
+ DMA_TO_DEVICE))
return NULL;
- src = c->cfg.src_addr;
+
+ src = c->map.addr;
} else {
- if (c->cfg.dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES)
+ if (bcm2835_dma_map_slave_addr(chan, c->cfg.dst_addr,
+ c->cfg.dst_addr_width,
+ DMA_FROM_DEVICE))
return NULL;
- dst = c->cfg.dst_addr;
+
+ dst = c->map.addr;
}
/* count frames in sg list */
@@ -795,14 +819,20 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic(
__func__, buf_len, period_len);
if (direction == DMA_DEV_TO_MEM) {
- if (c->cfg.src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES)
+ if (bcm2835_dma_map_slave_addr(chan, c->cfg.src_addr,
+ c->cfg.src_addr_width,
+ DMA_TO_DEVICE))
return NULL;
- src = c->cfg.src_addr;
+
+ src = c->map.addr;
dst = buf_addr;
} else {
- if (c->cfg.dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES)
+ if (bcm2835_dma_map_slave_addr(chan, c->cfg.dst_addr,
+ c->cfg.dst_addr_width,
+ DMA_FROM_DEVICE))
return NULL;
- dst = c->cfg.dst_addr;
+
+ dst = c->map.addr;
src = buf_addr;
}
--
2.34.1
In order to use the dma_map_resource for mappings, add the
dma-ranges to the relevant DT files.
Signed-off-by: Dave Stevenson <[email protected]>
---
arch/arm/boot/dts/broadcom/bcm2711.dtsi | 12 ++++++++++--
arch/arm/boot/dts/broadcom/bcm2835.dtsi | 3 ++-
arch/arm/boot/dts/broadcom/bcm2836.dtsi | 3 ++-
arch/arm/boot/dts/broadcom/bcm2837.dtsi | 3 ++-
4 files changed, 16 insertions(+), 5 deletions(-)
diff --git a/arch/arm/boot/dts/broadcom/bcm2711.dtsi b/arch/arm/boot/dts/broadcom/bcm2711.dtsi
index d64bf098b697..d6f32d32b456 100644
--- a/arch/arm/boot/dts/broadcom/bcm2711.dtsi
+++ b/arch/arm/boot/dts/broadcom/bcm2711.dtsi
@@ -42,7 +42,8 @@ soc {
<0x7c000000 0x0 0xfc000000 0x02000000>,
<0x40000000 0x0 0xff800000 0x00800000>;
/* Emulate a contiguous 30-bit address range for DMA */
- dma-ranges = <0xc0000000 0x0 0x00000000 0x40000000>;
+ dma-ranges = <0xc0000000 0x0 0x00000000 0x40000000>,
+ <0x7c000000 0x0 0xfc000000 0x03800000>;
/*
* This node is the provider for the enable-method for
@@ -550,7 +551,14 @@ scb {
#size-cells = <1>;
ranges = <0x0 0x7c000000 0x0 0xfc000000 0x03800000>,
- <0x6 0x00000000 0x6 0x00000000 0x40000000>;
+ <0x0 0x40000000 0x0 0xff800000 0x00800000>,
+ <0x6 0x00000000 0x6 0x00000000 0x40000000>,
+ <0x0 0x00000000 0x0 0x00000000 0xfc000000>;
+ dma-ranges = <0x4 0x7c000000 0x0 0xfc000000 0x03800000>,
+ <0x0 0x00000000 0x0 0x00000000 0x80000000>,
+ <0x0 0x80000000 0x0 0x80000000 0x80000000>,
+ <0x1 0x00000000 0x1 0x00000000 0x80000000>,
+ <0x1 0x80000000 0x1 0x80000000 0x80000000>;
pcie0: pcie@7d500000 {
compatible = "brcm,bcm2711-pcie";
diff --git a/arch/arm/boot/dts/broadcom/bcm2835.dtsi b/arch/arm/boot/dts/broadcom/bcm2835.dtsi
index 15cb331febbb..480e12fd8a17 100644
--- a/arch/arm/boot/dts/broadcom/bcm2835.dtsi
+++ b/arch/arm/boot/dts/broadcom/bcm2835.dtsi
@@ -35,7 +35,8 @@ cpu@0 {
soc {
ranges = <0x7e000000 0x20000000 0x02000000>;
- dma-ranges = <0x40000000 0x00000000 0x20000000>;
+ dma-ranges = <0x80000000 0x00000000 0x20000000>,
+ <0x7e000000 0x20000000 0x02000000>;
};
arm-pmu {
diff --git a/arch/arm/boot/dts/broadcom/bcm2836.dtsi b/arch/arm/boot/dts/broadcom/bcm2836.dtsi
index 783fe624ba68..4ab7769c056a 100644
--- a/arch/arm/boot/dts/broadcom/bcm2836.dtsi
+++ b/arch/arm/boot/dts/broadcom/bcm2836.dtsi
@@ -8,7 +8,8 @@ / {
soc {
ranges = <0x7e000000 0x3f000000 0x1000000>,
<0x40000000 0x40000000 0x00001000>;
- dma-ranges = <0xc0000000 0x00000000 0x3f000000>;
+ dma-ranges = <0xc0000000 0x00000000 0x3f000000>,
+ <0x7e000000 0x3f000000 0x01000000>;
local_intc: interrupt-controller@40000000 {
compatible = "brcm,bcm2836-l1-intc";
diff --git a/arch/arm/boot/dts/broadcom/bcm2837.dtsi b/arch/arm/boot/dts/broadcom/bcm2837.dtsi
index 84c08b46519d..d034d6a8caad 100644
--- a/arch/arm/boot/dts/broadcom/bcm2837.dtsi
+++ b/arch/arm/boot/dts/broadcom/bcm2837.dtsi
@@ -7,7 +7,8 @@ / {
soc {
ranges = <0x7e000000 0x3f000000 0x1000000>,
<0x40000000 0x40000000 0x00001000>;
- dma-ranges = <0xc0000000 0x00000000 0x3f000000>;
+ dma-ranges = <0xc0000000 0x00000000 0x3f000000>,
+ <0x7e000000 0x3f000000 0x01000000>;
local_intc: local_intc@40000000 {
compatible = "brcm,bcm2836-l1-intc";
--
2.34.1
From: Stefan Wahren <[email protected]>
Actually the criteria to increment source & destination address doesn't
based on platform specific bits. It's just the DMA transfer direction which
is translated into the info bits. So introduce two new helper functions
and get the rid of these platform specifics.
Signed-off-by: Stefan Wahren <[email protected]>
Signed-off-by: Dave Stevenson <[email protected]>
---
drivers/dma/bcm2835-dma.c | 28 ++++++++++++++++++++++------
1 file changed, 22 insertions(+), 6 deletions(-)
diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
index ef452ebb3c15..d6c5a2762a46 100644
--- a/drivers/dma/bcm2835-dma.c
+++ b/drivers/dma/bcm2835-dma.c
@@ -252,6 +252,24 @@ static u32 bcm2835_dma_prepare_cb_extra(struct bcm2835_chan *c,
return result;
}
+static inline bool need_src_incr(enum dma_transfer_direction direction)
+{
+ return direction != DMA_DEV_TO_MEM;
+}
+
+static inline bool need_dst_incr(enum dma_transfer_direction direction)
+{
+ switch (direction) {
+ case DMA_MEM_TO_MEM:
+ case DMA_DEV_TO_MEM:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
static void bcm2835_dma_free_cb_chain(struct bcm2835_desc *desc)
{
size_t i;
@@ -336,10 +354,8 @@ static inline size_t bcm2835_dma_count_frames_for_sg(
* @cyclic: it is a cyclic transfer
* @info: the default info bits to apply per controlblock
* @frames: number of controlblocks to allocate
- * @src: the src address to assign (if the S_INC bit is set
- * in @info, then it gets incremented)
- * @dst: the dst address to assign (if the D_INC bit is set
- * in @info, then it gets incremented)
+ * @src: the src address to assign
+ * @dst: the dst address to assign
* @buf_len: the full buffer length (may also be 0)
* @period_len: the period length when to apply @finalextrainfo
* in addition to the last transfer
@@ -408,9 +424,9 @@ static struct bcm2835_desc *bcm2835_dma_create_cb_chain(
d->cb_list[frame - 1].cb->next = cb_entry->paddr;
/* update src and dst and length */
- if (src && (info & BCM2835_DMA_S_INC))
+ if (src && need_src_incr(direction))
src += control_block->length;
- if (dst && (info & BCM2835_DMA_D_INC))
+ if (dst && need_dst_incr(direction))
dst += control_block->length;
/* Length of total transfer */
--
2.34.1
From: Phil Elwell <[email protected]>
Contrary to what struct snd_dmaengine_dai_dma_data suggests, the
configuration of addresses of DMA slave interfaces should be done in
CPU physical addresses.
Signed-off-by: Phil Elwell <[email protected]>
Signed-off-by: Dave Stevenson <[email protected]>
---
drivers/spi/spi-bcm2835.c | 23 ++++++++---------------
1 file changed, 8 insertions(+), 15 deletions(-)
diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c
index e1b9b1235787..e8242e0c4246 100644
--- a/drivers/spi/spi-bcm2835.c
+++ b/drivers/spi/spi-bcm2835.c
@@ -119,6 +119,7 @@ MODULE_PARM_DESC(polling_limit_us,
*/
struct bcm2835_spi {
void __iomem *regs;
+ phys_addr_t phys_addr;
struct clk *clk;
struct gpio_desc *cs_gpio;
unsigned long clk_hz;
@@ -891,19 +892,8 @@ static int bcm2835_dma_init(struct spi_controller *ctlr, struct device *dev,
struct bcm2835_spi *bs)
{
struct dma_slave_config slave_config;
- const __be32 *addr;
- dma_addr_t dma_reg_base;
int ret;
- /* base address in dma-space */
- addr = of_get_address(ctlr->dev.of_node, 0, NULL, NULL);
- if (!addr) {
- dev_err(dev, "could not get DMA-register address - not using dma mode\n");
- /* Fall back to interrupt mode */
- return 0;
- }
- dma_reg_base = be32_to_cpup(addr);
-
/* get tx/rx dma */
ctlr->dma_tx = dma_request_chan(dev, "tx");
if (IS_ERR(ctlr->dma_tx)) {
@@ -925,7 +915,7 @@ static int bcm2835_dma_init(struct spi_controller *ctlr, struct device *dev,
* or, in case of an RX-only transfer, cyclically copies from the zero
* page to the FIFO using a preallocated, reusable descriptor.
*/
- slave_config.dst_addr = (u32)(dma_reg_base + BCM2835_SPI_FIFO);
+ slave_config.dst_addr = bs->phys_addr + BCM2835_SPI_FIFO;
slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
ret = dmaengine_slave_config(ctlr->dma_tx, &slave_config);
@@ -964,9 +954,9 @@ static int bcm2835_dma_init(struct spi_controller *ctlr, struct device *dev,
* RX FIFO or, in case of a TX-only transfer, cyclically writes a
* precalculated value to the CS register to clear the RX FIFO.
*/
- slave_config.src_addr = (u32)(dma_reg_base + BCM2835_SPI_FIFO);
+ slave_config.src_addr = bs->phys_addr + BCM2835_SPI_FIFO;
slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
- slave_config.dst_addr = (u32)(dma_reg_base + BCM2835_SPI_CS);
+ slave_config.dst_addr = bs->phys_addr + BCM2835_SPI_CS;
slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
ret = dmaengine_slave_config(ctlr->dma_rx, &slave_config);
@@ -1336,6 +1326,7 @@ static int bcm2835_spi_probe(struct platform_device *pdev)
{
struct spi_controller *ctlr;
struct bcm2835_spi *bs;
+ struct resource *iomem;
int err;
ctlr = devm_spi_alloc_host(&pdev->dev, sizeof(*bs));
@@ -1359,10 +1350,12 @@ static int bcm2835_spi_probe(struct platform_device *pdev)
bs = spi_controller_get_devdata(ctlr);
bs->ctlr = ctlr;
- bs->regs = devm_platform_ioremap_resource(pdev, 0);
+ bs->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &iomem);
if (IS_ERR(bs->regs))
return PTR_ERR(bs->regs);
+ bs->phys_addr = iomem->start;
+
bs->clk = devm_clk_get_enabled(&pdev->dev, NULL);
if (IS_ERR(bs->clk))
return dev_err_probe(&pdev->dev, PTR_ERR(bs->clk),
--
2.34.1
bcm2835-dma has been (incorrectly) expecting dma addresses to be
passed in, not CPU physical addresses.
In order to fix this up, add temporary handling of clients still
passing in dma addresses until they are fixed up.
This will be reverted once all clients have been fixed.
Signed-off-by: Dave Stevenson <[email protected]>
---
drivers/dma/bcm2835-dma.c | 18 ++++++++++++++++--
1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
index ef1d95bae84e..9531c0b82071 100644
--- a/drivers/dma/bcm2835-dma.c
+++ b/drivers/dma/bcm2835-dma.c
@@ -283,9 +283,23 @@ static int bcm2835_dma_map_slave_addr(struct dma_chan *chan,
struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
struct bcm2835_dma_chan_map *map = &c->map;
- map->addr = dev_addr;
+ if ((dev_addr & 0xfe000000ULL) == 0x7e000000ULL) {
+ /*
+ * Address is already in the 0x7e... peripherals range.
+ * Assume this is an old client that hasn't been updated to
+ * correctly pass a cpu phys_addr to the DMA subsystem.
+ */
+ map->addr = dev_addr;
- return 0;
+ return 0;
+ }
+
+ /*
+ * This path will be updated to handle new clients, but currently should
+ * never be used.
+ */
+
+ return -EINVAL;
}
static void bcm2835_dma_free_cb_chain(struct bcm2835_desc *desc)
--
2.34.1
From: Stefan Wahren <[email protected]>
The parameters info and finalextrainfo are platform specific. So drop
them by generating them within bcm2835_dma_create_cb_chain().
Signed-off-by: Stefan Wahren <[email protected]>
Signed-off-by: Dave Stevenson <[email protected]>
---
drivers/dma/bcm2835-dma.c | 83 +++++++++++++++++++--------------------
1 file changed, 40 insertions(+), 43 deletions(-)
diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
index d6c5a2762a46..e2f9c8692e6b 100644
--- a/drivers/dma/bcm2835-dma.c
+++ b/drivers/dma/bcm2835-dma.c
@@ -287,13 +287,11 @@ static void bcm2835_dma_desc_free(struct virt_dma_desc *vd)
container_of(vd, struct bcm2835_desc, vd));
}
-static void bcm2835_dma_create_cb_set_length(
- struct bcm2835_chan *chan,
- struct bcm2835_dma_cb *control_block,
- size_t len,
- size_t period_len,
- size_t *total_len,
- u32 finalextrainfo)
+static bool
+bcm2835_dma_create_cb_set_length(struct bcm2835_chan *chan,
+ struct bcm2835_dma_cb *control_block,
+ size_t len, size_t period_len,
+ size_t *total_len)
{
size_t max_len = bcm2835_dma_max_frame_length(chan);
@@ -302,7 +300,7 @@ static void bcm2835_dma_create_cb_set_length(
/* finished if we have no period_length */
if (!period_len)
- return;
+ return false;
/*
* period_len means: that we need to generate
@@ -316,7 +314,7 @@ static void bcm2835_dma_create_cb_set_length(
if (*total_len + control_block->length < period_len) {
/* update number of bytes in this period so far */
*total_len += control_block->length;
- return;
+ return false;
}
/* calculate the length that remains to reach period_length */
@@ -325,8 +323,7 @@ static void bcm2835_dma_create_cb_set_length(
/* reset total_length for next period */
*total_len = 0;
- /* add extrainfo bits in info */
- control_block->info |= finalextrainfo;
+ return true;
}
static inline size_t bcm2835_dma_count_frames_for_sg(
@@ -352,7 +349,6 @@ static inline size_t bcm2835_dma_count_frames_for_sg(
* @chan: the @dma_chan for which we run this
* @direction: the direction in which we transfer
* @cyclic: it is a cyclic transfer
- * @info: the default info bits to apply per controlblock
* @frames: number of controlblocks to allocate
* @src: the src address to assign
* @dst: the dst address to assign
@@ -360,22 +356,24 @@ static inline size_t bcm2835_dma_count_frames_for_sg(
* @period_len: the period length when to apply @finalextrainfo
* in addition to the last transfer
* this will also break some control-blocks early
- * @finalextrainfo: additional bits in last controlblock
- * (or when period_len is reached in case of cyclic)
* @gfp: the GFP flag to use for allocation
+ * @flags
*/
static struct bcm2835_desc *bcm2835_dma_create_cb_chain(
struct dma_chan *chan, enum dma_transfer_direction direction,
- bool cyclic, u32 info, u32 finalextrainfo, size_t frames,
- dma_addr_t src, dma_addr_t dst, size_t buf_len,
- size_t period_len, gfp_t gfp)
+ bool cyclic, size_t frames, dma_addr_t src, dma_addr_t dst,
+ size_t buf_len, size_t period_len, gfp_t gfp, unsigned long flags)
{
+ struct bcm2835_dmadev *od = to_bcm2835_dma_dev(chan->device);
struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
size_t len = buf_len, total_len;
size_t frame;
struct bcm2835_desc *d;
struct bcm2835_cb_entry *cb_entry;
struct bcm2835_dma_cb *control_block;
+ u32 extrainfo = bcm2835_dma_prepare_cb_extra(c, direction, cyclic,
+ false, flags);
+ bool zero_page = false;
if (!frames)
return NULL;
@@ -389,6 +387,14 @@ static struct bcm2835_desc *bcm2835_dma_create_cb_chain(
d->dir = direction;
d->cyclic = cyclic;
+ switch (direction) {
+ case DMA_MEM_TO_MEM:
+ case DMA_DEV_TO_MEM:
+ break;
+ default:
+ zero_page = src == od->zero_page;
+ }
+
/*
* Iterate over all frames, create a control block
* for each frame and link them together.
@@ -402,7 +408,8 @@ static struct bcm2835_desc *bcm2835_dma_create_cb_chain(
/* fill in the control block */
control_block = cb_entry->cb;
- control_block->info = info;
+ control_block->info = bcm2835_dma_prepare_cb_info(c, direction,
+ zero_page);
control_block->src = src;
control_block->dst = dst;
control_block->stride = 0;
@@ -410,10 +417,12 @@ static struct bcm2835_desc *bcm2835_dma_create_cb_chain(
/* set up length in control_block if requested */
if (buf_len) {
/* calculate length honoring period_length */
- bcm2835_dma_create_cb_set_length(
- c, control_block,
- len, period_len, &total_len,
- cyclic ? finalextrainfo : 0);
+ if (bcm2835_dma_create_cb_set_length(c, control_block,
+ len, period_len,
+ &total_len)) {
+ /* add extrainfo bits in info */
+ control_block->info |= extrainfo;
+ }
/* calculate new remaining length */
len -= control_block->length;
@@ -434,7 +443,9 @@ static struct bcm2835_desc *bcm2835_dma_create_cb_chain(
}
/* the last frame requires extra flags */
- d->cb_list[d->frames - 1].cb->info |= finalextrainfo;
+ extrainfo = bcm2835_dma_prepare_cb_extra(c, direction, cyclic, true,
+ flags);
+ d->cb_list[d->frames - 1].cb->info |= extrainfo;
/* detect a size missmatch */
if (buf_len && (d->size != buf_len))
@@ -682,9 +693,6 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_memcpy(
{
struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
struct bcm2835_desc *d;
- u32 info = bcm2835_dma_prepare_cb_info(c, DMA_MEM_TO_MEM, false);
- u32 extra = bcm2835_dma_prepare_cb_extra(c, DMA_MEM_TO_MEM, false,
- true, 0);
size_t max_len = bcm2835_dma_max_frame_length(c);
size_t frames;
@@ -696,9 +704,8 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_memcpy(
frames = bcm2835_dma_frames_for_length(len, max_len);
/* allocate the CB chain - this also fills in the pointers */
- d = bcm2835_dma_create_cb_chain(chan, DMA_MEM_TO_MEM, false,
- info, extra, frames,
- src, dst, len, 0, GFP_KERNEL);
+ d = bcm2835_dma_create_cb_chain(chan, DMA_MEM_TO_MEM, false, frames,
+ src, dst, len, 0, GFP_KERNEL, 0);
if (!d)
return NULL;
@@ -714,8 +721,6 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_slave_sg(
struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
struct bcm2835_desc *d;
dma_addr_t src = 0, dst = 0;
- u32 info = bcm2835_dma_prepare_cb_info(c, direction, false);
- u32 extra = bcm2835_dma_prepare_cb_extra(c, direction, false, true, 0);
size_t frames;
if (!is_slave_direction(direction)) {
@@ -738,10 +743,8 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_slave_sg(
frames = bcm2835_dma_count_frames_for_sg(c, sgl, sg_len);
/* allocate the CB chain */
- d = bcm2835_dma_create_cb_chain(chan, direction, false,
- info, extra,
- frames, src, dst, 0, 0,
- GFP_NOWAIT);
+ d = bcm2835_dma_create_cb_chain(chan, direction, false, frames, src,
+ dst, 0, 0, GFP_NOWAIT, 0);
if (!d)
return NULL;
@@ -757,13 +760,9 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic(
size_t period_len, enum dma_transfer_direction direction,
unsigned long flags)
{
- struct bcm2835_dmadev *od = to_bcm2835_dma_dev(chan->device);
struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
struct bcm2835_desc *d;
dma_addr_t src, dst;
- u32 info = bcm2835_dma_prepare_cb_info(c, direction,
- buf_addr == od->zero_page);
- u32 extra = bcm2835_dma_prepare_cb_extra(c, direction, true, true, 0);
size_t max_len = bcm2835_dma_max_frame_length(c);
size_t frames;
@@ -814,10 +813,8 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic(
* note that we need to use GFP_NOWAIT, as the ALSA i2s dmaengine
* implementation calls prep_dma_cyclic with interrupts disabled.
*/
- d = bcm2835_dma_create_cb_chain(chan, direction, true,
- info, extra,
- frames, src, dst, buf_len,
- period_len, GFP_NOWAIT);
+ d = bcm2835_dma_create_cb_chain(chan, direction, true, frames, src, dst,
+ buf_len, period_len, GFP_NOWAIT, flags);
if (!d)
return NULL;
--
2.34.1
From: Phil Elwell <[email protected]>
Slave addresses for DMA are meant to be supplied as physical addresses
(contrary to what struct snd_dmaengine_dai_dma_data does).
Signed-off-by: Phil Elwell <[email protected]>
Signed-off-by: Dave Stevenson <[email protected]>
---
drivers/gpu/drm/vc4/vc4_hdmi.c | 15 ++++-----------
1 file changed, 4 insertions(+), 11 deletions(-)
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index d30f8e8e8967..c2afd72bd96e 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -2696,7 +2696,7 @@ static int vc4_hdmi_audio_init(struct vc4_hdmi *vc4_hdmi)
struct snd_soc_card *card = &vc4_hdmi->audio.card;
struct device *dev = &vc4_hdmi->pdev->dev;
struct platform_device *codec_pdev;
- const __be32 *addr;
+ struct resource *iomem;
int index, len;
int ret;
@@ -2732,22 +2732,15 @@ static int vc4_hdmi_audio_init(struct vc4_hdmi *vc4_hdmi)
}
/*
- * Get the physical address of VC4_HD_MAI_DATA. We need to retrieve
- * the bus address specified in the DT, because the physical address
- * (the one returned by platform_get_resource()) is not appropriate
- * for DMA transfers.
- * This VC/MMU should probably be exposed to avoid this kind of hacks.
+ * Get the physical address of VC4_HD_MAI_DATA.
*/
index = of_property_match_string(dev->of_node, "reg-names", "hd");
/* Before BCM2711, we don't have a named register range */
if (index < 0)
index = 1;
+ iomem = platform_get_resource(vc4_hdmi->pdev, IORESOURCE_MEM, index);
- addr = of_get_address(dev->of_node, index, NULL, NULL);
- if (!addr)
- return -EINVAL;
-
- vc4_hdmi->audio.dma_data.addr = be32_to_cpup(addr) + mai_data->offset;
+ vc4_hdmi->audio.dma_data.addr = iomem->start + mai_data->offset;
vc4_hdmi->audio.dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
vc4_hdmi->audio.dma_data.maxburst = 2;
--
2.34.1
From: Stefan Wahren <[email protected]>
In preparation to support more platforms pass the dma_chan to the
generic functions. This provides access to the DMA device and possible
platform specific data.
Signed-off-by: Stefan Wahren <[email protected]>
Signed-off-by: Dave Stevenson <[email protected]>
---
drivers/dma/bcm2835-dma.c | 24 ++++++++++++++----------
1 file changed, 14 insertions(+), 10 deletions(-)
diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
index e2f9c8692e6b..aefaa1f01d7f 100644
--- a/drivers/dma/bcm2835-dma.c
+++ b/drivers/dma/bcm2835-dma.c
@@ -288,12 +288,13 @@ static void bcm2835_dma_desc_free(struct virt_dma_desc *vd)
}
static bool
-bcm2835_dma_create_cb_set_length(struct bcm2835_chan *chan,
+bcm2835_dma_create_cb_set_length(struct dma_chan *chan,
struct bcm2835_dma_cb *control_block,
size_t len, size_t period_len,
size_t *total_len)
{
- size_t max_len = bcm2835_dma_max_frame_length(chan);
+ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
+ size_t max_len = bcm2835_dma_max_frame_length(c);
/* set the length taking lite-channel limitations into account */
control_block->length = min_t(u32, len, max_len);
@@ -417,7 +418,7 @@ static struct bcm2835_desc *bcm2835_dma_create_cb_chain(
/* set up length in control_block if requested */
if (buf_len) {
/* calculate length honoring period_length */
- if (bcm2835_dma_create_cb_set_length(c, control_block,
+ if (bcm2835_dma_create_cb_set_length(chan, control_block,
len, period_len,
&total_len)) {
/* add extrainfo bits in info */
@@ -485,8 +486,9 @@ static void bcm2835_dma_fill_cb_chain_with_sg(
}
}
-static void bcm2835_dma_abort(struct bcm2835_chan *c)
+static void bcm2835_dma_abort(struct dma_chan *chan)
{
+ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
void __iomem *chan_base = c->chan_base;
long int timeout = 10000;
@@ -513,8 +515,9 @@ static void bcm2835_dma_abort(struct bcm2835_chan *c)
writel(BCM2835_DMA_RESET, chan_base + BCM2835_DMA_CS);
}
-static void bcm2835_dma_start_desc(struct bcm2835_chan *c)
+static void bcm2835_dma_start_desc(struct dma_chan *chan)
{
+ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
struct virt_dma_desc *vd = vchan_next_desc(&c->vc);
struct bcm2835_desc *d;
@@ -533,7 +536,8 @@ static void bcm2835_dma_start_desc(struct bcm2835_chan *c)
static irqreturn_t bcm2835_dma_callback(int irq, void *data)
{
- struct bcm2835_chan *c = data;
+ struct dma_chan *chan = data;
+ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
struct bcm2835_desc *d;
unsigned long flags;
@@ -566,7 +570,7 @@ static irqreturn_t bcm2835_dma_callback(int irq, void *data)
vchan_cyclic_callback(&d->vd);
} else if (!readl(c->chan_base + BCM2835_DMA_ADDR)) {
vchan_cookie_complete(&c->desc->vd);
- bcm2835_dma_start_desc(c);
+ bcm2835_dma_start_desc(chan);
}
}
@@ -594,7 +598,7 @@ static int bcm2835_dma_alloc_chan_resources(struct dma_chan *chan)
}
return request_irq(c->irq_number, bcm2835_dma_callback,
- c->irq_flags, "DMA IRQ", c);
+ c->irq_flags, "DMA IRQ", chan);
}
static void bcm2835_dma_free_chan_resources(struct dma_chan *chan)
@@ -682,7 +686,7 @@ static void bcm2835_dma_issue_pending(struct dma_chan *chan)
spin_lock_irqsave(&c->vc.lock, flags);
if (vchan_issue_pending(&c->vc) && !c->desc)
- bcm2835_dma_start_desc(c);
+ bcm2835_dma_start_desc(chan);
spin_unlock_irqrestore(&c->vc.lock, flags);
}
@@ -846,7 +850,7 @@ static int bcm2835_dma_terminate_all(struct dma_chan *chan)
if (c->desc) {
vchan_terminate_vdesc(&c->desc->vd);
c->desc = NULL;
- bcm2835_dma_abort(c);
+ bcm2835_dma_abort(chan);
}
vchan_get_all_descriptors(&c->vc, &head);
--
2.34.1
We have a historical error in the DT files that don't define
the dma-ranges fully, and DMA users have been passing in
DMA addresses instead of CPU physical addresses.
As DT is ABI, we have to be able to work with old DT but new
kernel, which means handling this missing dma-range mapping
somehow.
The "ranges" property has always been defined correctly, so
abuse that in the event that dma-ranges are missing.
There appears to be no easy route to access "ranges", so
duplicate the functions for handling "dma-ranges" here to
keep the hack contained.
Signed-off-by: Dave Stevenson <[email protected]>
---
drivers/dma/bcm2835-dma.c | 139 ++++++++++++++++++++++++++++++++++++--
1 file changed, 134 insertions(+), 5 deletions(-)
diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
index e48008b06716..06407691ef28 100644
--- a/drivers/dma/bcm2835-dma.c
+++ b/drivers/dma/bcm2835-dma.c
@@ -25,6 +25,7 @@
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/module.h>
+#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/io.h>
@@ -37,6 +38,12 @@
#define BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED 14
#define BCM2835_DMA_CHAN_NAME_SIZE 8
+struct bcm2835_bus_dma_region {
+ phys_addr_t cpu_start;
+ dma_addr_t dma_start;
+ u64 size;
+};
+
/**
* struct bcm2835_dmadev - BCM2835 DMA controller
* @ddev: DMA device
@@ -48,6 +55,8 @@ struct bcm2835_dmadev {
struct dma_device ddev;
void __iomem *base;
dma_addr_t zero_page;
+ bool ranges_initialised;
+ struct bcm2835_bus_dma_region *ranges_map;
};
struct bcm2835_dma_cb {
@@ -71,6 +80,7 @@ struct bcm2835_dma_chan_map {
phys_addr_t slave_addr;
unsigned int xfer_size;
+ bool ranges;
};
struct bcm2835_chan {
@@ -279,6 +289,114 @@ static inline bool need_dst_incr(enum dma_transfer_direction direction)
return false;
};
+static int bcm2835_dma_init_ranges(struct dma_chan *chan)
+{
+ struct bcm2835_dmadev *od = to_bcm2835_dma_dev(chan->device);
+ struct device *dev = chan->device->dev;
+ struct device_node *node = of_node_get(dev->of_node);
+ const __be32 *ranges = NULL;
+ bool found_ranges = false;
+ struct of_range_parser parser;
+ struct of_range range;
+ struct bcm2835_bus_dma_region *r;
+ int len, num_ranges = 0;
+ int ret = 0;
+
+ while (node) {
+ ranges = of_get_property(node, "ranges", &len);
+
+ /* Ignore empty ranges, they imply no translation required */
+ if (ranges && len > 0)
+ break;
+
+ /* Once we find 'dma-ranges', then a missing one is an error */
+ if (found_ranges && !ranges) {
+ ret = -ENODEV;
+ goto out;
+ }
+ found_ranges = true;
+
+ node = of_get_next_parent(node);
+ }
+
+ if (!node || !ranges) {
+ pr_debug("no ranges found for node(%pOF)\n", dev->of_node);
+ ret = -ENODEV;
+ goto out;
+ }
+
+ of_pci_range_parser_init(&parser, node);
+ for_each_of_range(&parser, &range) {
+ if (range.cpu_addr == OF_BAD_ADDR) {
+ pr_err("translation of DMA address(%llx) to CPU address failed node(%pOF)\n",
+ range.bus_addr, node);
+ continue;
+ }
+ num_ranges++;
+ }
+
+ if (!num_ranges) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ r = kcalloc(num_ranges + 1, sizeof(*r), GFP_KERNEL);
+ if (!r) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /*
+ * Record all info in the generic DMA ranges array for struct device,
+ * returning an error if we don't find any parsable ranges.
+ */
+ od->ranges_map = r;
+ of_pci_range_parser_init(&parser, node);
+ for_each_of_range(&parser, &range) {
+ pr_debug("dma_addr(%llx) cpu_addr(%llx) size(%llx)\n",
+ range.bus_addr, range.cpu_addr, range.size);
+ if (range.cpu_addr == OF_BAD_ADDR)
+ continue;
+ r->cpu_start = range.cpu_addr;
+ r->dma_start = range.bus_addr;
+ r->size = range.size;
+ r++;
+ }
+out:
+ of_node_put(node);
+ return ret;
+}
+
+static dma_addr_t bcm2835_translate_phys_to_dma(struct bcm2835_dmadev *od,
+ phys_addr_t paddr)
+{
+ const struct bcm2835_bus_dma_region *m;
+
+ for (m = od->ranges_map; m && m->size; m++) {
+ u64 offset = paddr - m->cpu_start;
+
+ if (paddr >= m->cpu_start && offset < m->size)
+ return m->dma_start + offset;
+ }
+
+ /* make sure dma_capable fails when no translation is available */
+ return DMA_MAPPING_ERROR;
+}
+
+static dma_addr_t
+bcm2835_dma_map_using_range(struct dma_chan *chan, phys_addr_t phys_addr,
+ size_t size, enum dma_data_direction dir)
+{
+ struct bcm2835_dmadev *od = to_bcm2835_dma_dev(chan->device);
+
+ if (!od->ranges_initialised) {
+ bcm2835_dma_init_ranges(chan);
+ od->ranges_initialised = true;
+ }
+
+ return bcm2835_translate_phys_to_dma(od, phys_addr);
+}
+
static int bcm2835_dma_map_slave_addr(struct dma_chan *chan,
phys_addr_t dev_addr,
size_t dev_size,
@@ -307,8 +425,11 @@ static int bcm2835_dma_map_slave_addr(struct dma_chan *chan,
dev_dir == map->dir)
return 0;
- /* Remove old mapping if present. */
- if (map->xfer_size) {
+ /*
+ * Remove old mapping if present and we haven't used our own "ranges"
+ * mapping (which has no unmap)
+ */
+ if (map->xfer_size && !map->ranges) {
dev_dbg(chan->device->dev, "chan: unmap %zx@%pap to %pad dir: %s\n",
dev_size, &dev_addr, &map->addr,
dev_dir == DMA_TO_DEVICE ? "DMA_TO_DEVICE" : "DMA_FROM_DEVICE");
@@ -322,9 +443,17 @@ static int bcm2835_dma_map_slave_addr(struct dma_chan *chan,
dev_dir, 0);
if (dma_mapping_error(chan->device->dev, map->addr)) {
- dev_err(chan->device->dev, "chan: failed to map %zx@%pap",
- dev_size, &dev_addr);
- return -EIO;
+ map->addr = bcm2835_dma_map_using_range(chan, dev_addr, dev_size,
+ dev_dir);
+ if (dma_mapping_error(chan->device->dev, map->addr)) {
+ dev_err(chan->device->dev, "chan: failed to map %zx@%pap",
+ dev_size, &dev_addr);
+
+ return -EIO;
+ }
+ map->ranges = true;
+ } else {
+ map->ranges = false;
}
dev_dbg(chan->device->dev, "chan: map %zx@%pap to %pad dir: %s\n",
--
2.34.1
From: Stefan Wahren <[email protected]>
Actually the generation of the Control Block info follows some simple
rules. So handle this with a separate function to avoid open coding
for every DMA operation. Another advantage is that we can easier
introduce other platforms with different info bits.
Signed-off-by: Stefan Wahren <[email protected]>
Signed-off-by: Dave Stevenson <[email protected]>
---
drivers/dma/bcm2835-dma.c | 50 +++++++++++++++++++++++++--------------
1 file changed, 32 insertions(+), 18 deletions(-)
diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
index 528c4593b45a..7cef7ff89575 100644
--- a/drivers/dma/bcm2835-dma.c
+++ b/drivers/dma/bcm2835-dma.c
@@ -201,6 +201,34 @@ static inline struct bcm2835_desc *to_bcm2835_dma_desc(
return container_of(t, struct bcm2835_desc, vd.tx);
}
+static u32 bcm2835_dma_prepare_cb_info(struct bcm2835_chan *c,
+ enum dma_transfer_direction direction,
+ bool zero_page)
+{
+ u32 result;
+
+ if (direction == DMA_MEM_TO_MEM)
+ return BCM2835_DMA_D_INC | BCM2835_DMA_S_INC;
+
+ result = BCM2835_DMA_WAIT_RESP;
+
+ /* Setup DREQ channel */
+ if (c->dreq != 0)
+ result |= BCM2835_DMA_PER_MAP(c->dreq);
+
+ if (direction == DMA_DEV_TO_MEM) {
+ result |= BCM2835_DMA_S_DREQ | BCM2835_DMA_D_INC;
+ } else {
+ result |= BCM2835_DMA_D_DREQ | BCM2835_DMA_S_INC;
+
+ /* non-lite channels can write zeroes w/o accessing memory */
+ if (zero_page && !c->is_lite_channel)
+ result |= BCM2835_DMA_S_IGNORE;
+ }
+
+ return result;
+}
+
static void bcm2835_dma_free_cb_chain(struct bcm2835_desc *desc)
{
size_t i;
@@ -615,7 +643,7 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_memcpy(
{
struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
struct bcm2835_desc *d;
- u32 info = BCM2835_DMA_D_INC | BCM2835_DMA_S_INC;
+ u32 info = bcm2835_dma_prepare_cb_info(c, DMA_MEM_TO_MEM, false);
u32 extra = BCM2835_DMA_INT_EN | BCM2835_DMA_WAIT_RESP;
size_t max_len = bcm2835_dma_max_frame_length(c);
size_t frames;
@@ -646,7 +674,7 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_slave_sg(
struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
struct bcm2835_desc *d;
dma_addr_t src = 0, dst = 0;
- u32 info = BCM2835_DMA_WAIT_RESP;
+ u32 info = bcm2835_dma_prepare_cb_info(c, direction, false);
u32 extra = BCM2835_DMA_INT_EN;
size_t frames;
@@ -656,19 +684,14 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_slave_sg(
return NULL;
}
- if (c->dreq != 0)
- info |= BCM2835_DMA_PER_MAP(c->dreq);
-
if (direction == DMA_DEV_TO_MEM) {
if (c->cfg.src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES)
return NULL;
src = c->cfg.src_addr;
- info |= BCM2835_DMA_S_DREQ | BCM2835_DMA_D_INC;
} else {
if (c->cfg.dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES)
return NULL;
dst = c->cfg.dst_addr;
- info |= BCM2835_DMA_D_DREQ | BCM2835_DMA_S_INC;
}
/* count frames in sg list */
@@ -698,7 +721,8 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic(
struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
struct bcm2835_desc *d;
dma_addr_t src, dst;
- u32 info = BCM2835_DMA_WAIT_RESP;
+ u32 info = bcm2835_dma_prepare_cb_info(c, direction,
+ buf_addr == od->zero_page);
u32 extra = 0;
size_t max_len = bcm2835_dma_max_frame_length(c);
size_t frames;
@@ -729,26 +753,16 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic(
"%s: buffer_length (%zd) is not a multiple of period_len (%zd)\n",
__func__, buf_len, period_len);
- /* Setup DREQ channel */
- if (c->dreq != 0)
- info |= BCM2835_DMA_PER_MAP(c->dreq);
-
if (direction == DMA_DEV_TO_MEM) {
if (c->cfg.src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES)
return NULL;
src = c->cfg.src_addr;
dst = buf_addr;
- info |= BCM2835_DMA_S_DREQ | BCM2835_DMA_D_INC;
} else {
if (c->cfg.dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES)
return NULL;
dst = c->cfg.dst_addr;
src = buf_addr;
- info |= BCM2835_DMA_D_DREQ | BCM2835_DMA_S_INC;
-
- /* non-lite channels can write zeroes w/o accessing memory */
- if (buf_addr == od->zero_page && !c->is_lite_channel)
- info |= BCM2835_DMA_S_IGNORE;
}
/* calculate number of frames */
--
2.34.1
From: Phil Elwell <[email protected]>
Contrary to what struct snd_dmaengine_dai_dma_data suggests, the
configuration of addresses of DMA slave interfaces should be done in
CPU physical addresses.
Signed-off-by: Phil Elwell <[email protected]>
Signed-off-by: Dave Stevenson <[email protected]>
---
drivers/mmc/host/bcm2835.c | 17 +++--------------
1 file changed, 3 insertions(+), 14 deletions(-)
diff --git a/drivers/mmc/host/bcm2835.c b/drivers/mmc/host/bcm2835.c
index 35d8fdea668b..746a60fac0f0 100644
--- a/drivers/mmc/host/bcm2835.c
+++ b/drivers/mmc/host/bcm2835.c
@@ -38,7 +38,6 @@
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/module.h>
-#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/scatterlist.h>
@@ -1347,8 +1346,8 @@ static int bcm2835_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct clk *clk;
struct bcm2835_host *host;
+ struct resource *iomem;
struct mmc_host *mmc;
- const __be32 *regaddr_p;
int ret;
dev_dbg(dev, "%s\n", __func__);
@@ -1361,23 +1360,13 @@ static int bcm2835_probe(struct platform_device *pdev)
host->pdev = pdev;
spin_lock_init(&host->lock);
- host->ioaddr = devm_platform_ioremap_resource(pdev, 0);
+ host->ioaddr = devm_platform_get_and_ioremap_resource(pdev, 0, &iomem);
if (IS_ERR(host->ioaddr)) {
ret = PTR_ERR(host->ioaddr);
goto err;
}
- /* Parse OF address directly to get the physical address for
- * DMA to our registers.
- */
- regaddr_p = of_get_address(pdev->dev.of_node, 0, NULL, NULL);
- if (!regaddr_p) {
- dev_err(dev, "Can't get phys address\n");
- ret = -EINVAL;
- goto err;
- }
-
- host->phys_addr = be32_to_cpup(regaddr_p);
+ host->phys_addr = iomem->start;
host->dma_chan = NULL;
host->dma_desc = NULL;
--
2.34.1
From: Phil Elwell <[email protected]>
Contrary to what struct snd_dmaengine_dai_dma_data suggests, the
configuration of addresses of DMA slave interfaces should be done in
CPU physical addresses.
Signed-off-by: Phil Elwell <[email protected]>
Signed-off-by: Dave Stevenson <[email protected]>
---
sound/soc/bcm/bcm2835-i2s.c | 18 ++++--------------
1 file changed, 4 insertions(+), 14 deletions(-)
diff --git a/sound/soc/bcm/bcm2835-i2s.c b/sound/soc/bcm/bcm2835-i2s.c
index 9bda6499e66e..2d0fe53245f0 100644
--- a/sound/soc/bcm/bcm2835-i2s.c
+++ b/sound/soc/bcm/bcm2835-i2s.c
@@ -30,7 +30,6 @@
#include <linux/init.h>
#include <linux/io.h>
#include <linux/module.h>
-#include <linux/of_address.h>
#include <linux/slab.h>
#include <sound/core.h>
@@ -830,8 +829,7 @@ static int bcm2835_i2s_probe(struct platform_device *pdev)
struct bcm2835_i2s_dev *dev;
int ret;
void __iomem *base;
- const __be32 *addr;
- dma_addr_t dma_base;
+ struct resource *res;
dev = devm_kzalloc(&pdev->dev, sizeof(*dev),
GFP_KERNEL);
@@ -846,7 +844,7 @@ static int bcm2835_i2s_probe(struct platform_device *pdev)
"could not get clk\n");
/* Request ioarea */
- base = devm_platform_ioremap_resource(pdev, 0);
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(base))
return PTR_ERR(base);
@@ -855,19 +853,11 @@ static int bcm2835_i2s_probe(struct platform_device *pdev)
if (IS_ERR(dev->i2s_regmap))
return PTR_ERR(dev->i2s_regmap);
- /* Set the DMA address - we have to parse DT ourselves */
- addr = of_get_address(pdev->dev.of_node, 0, NULL, NULL);
- if (!addr) {
- dev_err(&pdev->dev, "could not get DMA-register address\n");
- return -EINVAL;
- }
- dma_base = be32_to_cpup(addr);
-
dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr =
- dma_base + BCM2835_I2S_FIFO_A_REG;
+ res->start + BCM2835_I2S_FIFO_A_REG;
dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr =
- dma_base + BCM2835_I2S_FIFO_A_REG;
+ res->start + BCM2835_I2S_FIFO_A_REG;
/* Set the bus width */
dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr_width =
--
2.34.1
Now that all DMA clients are passing in CPU addresses, drop
the workaround that would accept those and not try mapping
them.
Signed-off-by: Dave Stevenson <[email protected]>
---
drivers/dma/bcm2835-dma.c | 11 -----------
1 file changed, 11 deletions(-)
diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
index 06407691ef28..181f2c291109 100644
--- a/drivers/dma/bcm2835-dma.c
+++ b/drivers/dma/bcm2835-dma.c
@@ -405,17 +405,6 @@ static int bcm2835_dma_map_slave_addr(struct dma_chan *chan,
struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
struct bcm2835_dma_chan_map *map = &c->map;
- if ((dev_addr & 0xfe000000ULL) == 0x7e000000ULL) {
- /*
- * Address is already in the 0x7e... peripherals range.
- * Assume this is an old client that hasn't been updated to
- * correctly pass a cpu phys_addr to the DMA subsystem.
- */
- map->addr = dev_addr;
-
- return 0;
- }
-
if (dev_size != DMA_SLAVE_BUSWIDTH_4_BYTES)
return -EIO;
--
2.34.1
On Fri, May 24, 2024 at 07:26:45PM +0100, Dave Stevenson wrote:
> From: Serge Semin <[email protected]>
>
> A basic device-specific linear memory mapping was introduced back in
> commit ("dma: Take into account dma_pfn_offset") as a single-valued offset
> preserved in the device.dma_pfn_offset field, which was initialized for
> instance by means of the "dma-ranges" DT property. Afterwards the
> functionality was extended to support more than one device-specific region
> defined in the device.dma_range_map list of maps. But all of these
> improvements concerned a single pointer, page or sg DMA-mapping methods,
> while the system resource mapping function turned to miss the
> corresponding modification. Thus the dma_direct_map_resource() method now
> just casts the CPU physical address to the device DMA address with no
> dma-ranges-based mapping taking into account, which is obviously wrong.
> Let's fix it by using the phys_to_dma_direct() method to get the
> device-specific bus address from the passed memory resource for the case
> of the directly mapped DMA.
My memory is getting a little bad, but as dma_direct_map_resource is
mostly used for (non-PCIe) peer to peer transfers, any kind of mapping
from the host address should be excluded.
(dma_direct_map_resource in general is a horrible interface and I'd
prefer everyone to switch to the map_sg based P2P support, but we
have plenty of users for it unfortunately)
On 5/24/2024 8:26 PM, Dave Stevenson wrote:
> Now the driver looks for the common dma-channel-mask property
> rather than the vendor-specific brcm,dma-channel-mask, update
> the dt files to follow suit.
>
> Signed-off-by: Dave Stevenson <[email protected]>
Acked-by: Florian Fainelli <[email protected]>
--
Florian
On 5/24/2024 8:26 PM, 'Dave Stevenson' via BCM-KERNEL-FEEDBACK-LIST,PDL
wrote:
> In order to use the dma_map_resource for mappings, add the
> dma-ranges to the relevant DT files.
Subject should be "arm: dts: " prefixed to be consistent with patch #2
and prior submissions to those files. With that:
Acked-by: Florian Fainelli <[email protected]>
--
Florian
On Fri, May 24, 2024 at 07:26:46PM +0100, Dave Stevenson wrote:
> From: Stefan Wahren <[email protected]>
>
> Nowadays there is a generic property for dma-channel-mask in the DMA
> controller binding. So prefer this one instead of the old vendor specific
> one. Print a warning in case the old one is used. Btw use the result of
> of_property_read_u32() as return code in error case.
Use generic 'dma-channel-mask' property. Print a warning in case the
old brcm,dma-channel-mask is used.
Did you update binding doc?
>
> Signed-off-by: Stefan Wahren <[email protected]>
> Signed-off-by: Dave Stevenson <[email protected]>
> ---
> drivers/dma/bcm2835-dma.c | 19 +++++++++++++------
> 1 file changed, 13 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
> index 9d74fe97452e..528c4593b45a 100644
> --- a/drivers/dma/bcm2835-dma.c
> +++ b/drivers/dma/bcm2835-dma.c
> @@ -941,12 +941,19 @@ static int bcm2835_dma_probe(struct platform_device *pdev)
> }
>
> /* Request DMA channel mask from device tree */
> - if (of_property_read_u32(pdev->dev.of_node,
> - "brcm,dma-channel-mask",
> - &chans_available)) {
> - dev_err(&pdev->dev, "Failed to get channel mask\n");
> - rc = -EINVAL;
> - goto err_no_dma;
> + rc = of_property_read_u32(pdev->dev.of_node, "dma-channel-mask",
> + &chans_available);
> +
> + if (rc) {
> + /* Try deprecated property */
> + if (of_property_read_u32(pdev->dev.of_node,
> + "brcm,dma-channel-mask",
> + &chans_available)) {
> + dev_err(&pdev->dev, "Failed to get channel mask\n");
> + goto err_no_dma;
> + }
> +
> + dev_warn(&pdev->dev, "brcm,dma-channel-mask deprecated - please update DT\n");
> }
>
> /* get irqs for each channel that we support */
> --
> 2.34.1
>
On Fri, May 24, 2024 at 07:26:48PM +0100, Dave Stevenson wrote:
> From: Stefan Wahren <[email protected]>
>
> Actually the generation of the Control Block info follows some simple
> rules. So handle this with a separate function to avoid open coding
> for every DMA operation. Another advantage is that we can easier
> introduce other platforms with different info bits.
May simple said as:
Introduce common help funtion to prepare Control Block Info to avoid
dupicate code in every DMA operation.
>
> Signed-off-by: Stefan Wahren <[email protected]>
> Signed-off-by: Dave Stevenson <[email protected]>
> ---
> drivers/dma/bcm2835-dma.c | 50 +++++++++++++++++++++++++--------------
> 1 file changed, 32 insertions(+), 18 deletions(-)
>
> diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
> index 528c4593b45a..7cef7ff89575 100644
> --- a/drivers/dma/bcm2835-dma.c
> +++ b/drivers/dma/bcm2835-dma.c
> @@ -201,6 +201,34 @@ static inline struct bcm2835_desc *to_bcm2835_dma_desc(
> return container_of(t, struct bcm2835_desc, vd.tx);
> }
>
> +static u32 bcm2835_dma_prepare_cb_info(struct bcm2835_chan *c,
> + enum dma_transfer_direction direction,
> + bool zero_page)
> +{
> + u32 result;
> +
> + if (direction == DMA_MEM_TO_MEM)
> + return BCM2835_DMA_D_INC | BCM2835_DMA_S_INC;
> +
> + result = BCM2835_DMA_WAIT_RESP;
> +
> + /* Setup DREQ channel */
> + if (c->dreq != 0)
> + result |= BCM2835_DMA_PER_MAP(c->dreq);
> +
> + if (direction == DMA_DEV_TO_MEM) {
> + result |= BCM2835_DMA_S_DREQ | BCM2835_DMA_D_INC;
> + } else {
> + result |= BCM2835_DMA_D_DREQ | BCM2835_DMA_S_INC;
> +
> + /* non-lite channels can write zeroes w/o accessing memory */
> + if (zero_page && !c->is_lite_channel)
> + result |= BCM2835_DMA_S_IGNORE;
> + }
> +
> + return result;
> +}
> +
> static void bcm2835_dma_free_cb_chain(struct bcm2835_desc *desc)
> {
> size_t i;
> @@ -615,7 +643,7 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_memcpy(
> {
> struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
> struct bcm2835_desc *d;
> - u32 info = BCM2835_DMA_D_INC | BCM2835_DMA_S_INC;
> + u32 info = bcm2835_dma_prepare_cb_info(c, DMA_MEM_TO_MEM, false);
> u32 extra = BCM2835_DMA_INT_EN | BCM2835_DMA_WAIT_RESP;
> size_t max_len = bcm2835_dma_max_frame_length(c);
> size_t frames;
> @@ -646,7 +674,7 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_slave_sg(
> struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
> struct bcm2835_desc *d;
> dma_addr_t src = 0, dst = 0;
> - u32 info = BCM2835_DMA_WAIT_RESP;
> + u32 info = bcm2835_dma_prepare_cb_info(c, direction, false);
> u32 extra = BCM2835_DMA_INT_EN;
> size_t frames;
>
> @@ -656,19 +684,14 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_slave_sg(
> return NULL;
> }
>
> - if (c->dreq != 0)
> - info |= BCM2835_DMA_PER_MAP(c->dreq);
> -
> if (direction == DMA_DEV_TO_MEM) {
> if (c->cfg.src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES)
> return NULL;
> src = c->cfg.src_addr;
> - info |= BCM2835_DMA_S_DREQ | BCM2835_DMA_D_INC;
> } else {
> if (c->cfg.dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES)
> return NULL;
> dst = c->cfg.dst_addr;
> - info |= BCM2835_DMA_D_DREQ | BCM2835_DMA_S_INC;
> }
>
> /* count frames in sg list */
> @@ -698,7 +721,8 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic(
> struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
> struct bcm2835_desc *d;
> dma_addr_t src, dst;
> - u32 info = BCM2835_DMA_WAIT_RESP;
> + u32 info = bcm2835_dma_prepare_cb_info(c, direction,
> + buf_addr == od->zero_page);
> u32 extra = 0;
> size_t max_len = bcm2835_dma_max_frame_length(c);
> size_t frames;
> @@ -729,26 +753,16 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic(
> "%s: buffer_length (%zd) is not a multiple of period_len (%zd)\n",
> __func__, buf_len, period_len);
>
> - /* Setup DREQ channel */
> - if (c->dreq != 0)
> - info |= BCM2835_DMA_PER_MAP(c->dreq);
> -
> if (direction == DMA_DEV_TO_MEM) {
> if (c->cfg.src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES)
> return NULL;
> src = c->cfg.src_addr;
> dst = buf_addr;
> - info |= BCM2835_DMA_S_DREQ | BCM2835_DMA_D_INC;
> } else {
> if (c->cfg.dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES)
> return NULL;
> dst = c->cfg.dst_addr;
> src = buf_addr;
> - info |= BCM2835_DMA_D_DREQ | BCM2835_DMA_S_INC;
> -
> - /* non-lite channels can write zeroes w/o accessing memory */
> - if (buf_addr == od->zero_page && !c->is_lite_channel)
> - info |= BCM2835_DMA_S_IGNORE;
> }
>
> /* calculate number of frames */
> --
> 2.34.1
>
On Fri, May 24, 2024 at 07:26:49PM +0100, Dave Stevenson wrote:
> From: Stefan Wahren <[email protected]>
>
> Similar to the info generation, generate the final extra info with a
> separate function. This is necessary to introduce other platforms
> with different info bits.
Each patch commit is independent.
Introduce common help function to generate the final extra info to reduce
duplicate codes in each DMA operation.
>
> Signed-off-by: Stefan Wahren <[email protected]>
> Signed-off-by: Dave Stevenson <[email protected]>
> ---
> drivers/dma/bcm2835-dma.c | 34 ++++++++++++++++++++++++++++------
> 1 file changed, 28 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
> index 7cef7ff89575..ef452ebb3c15 100644
> --- a/drivers/dma/bcm2835-dma.c
> +++ b/drivers/dma/bcm2835-dma.c
> @@ -229,6 +229,29 @@ static u32 bcm2835_dma_prepare_cb_info(struct bcm2835_chan *c,
> return result;
> }
>
> +static u32 bcm2835_dma_prepare_cb_extra(struct bcm2835_chan *c,
> + enum dma_transfer_direction direction,
> + bool cyclic, bool final,
> + unsigned long flags)
> +{
> + u32 result = 0;
> +
> + if (cyclic) {
> + if (flags & DMA_PREP_INTERRUPT)
> + result |= BCM2835_DMA_INT_EN;
> + } else {
> + if (!final)
> + return 0;
> +
> + result |= BCM2835_DMA_INT_EN;
> +
> + if (direction == DMA_MEM_TO_MEM)
> + result |= BCM2835_DMA_WAIT_RESP;
> + }
move if (direction == DMA_MEM_TO_MEM) outof else branch.
DMA_MEM_TO_MEM is impossible for cyclic. Reduce if level can help
easy to follow up.
if (cyclic)
...
else
...
if (direction == DMA_MEM_TO_MEM)
result |= BCM2835_DMA_WAIT_RESP;
> +
> + return result;
> +}
> +
> static void bcm2835_dma_free_cb_chain(struct bcm2835_desc *desc)
> {
> size_t i;
> @@ -644,7 +667,8 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_memcpy(
> struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
> struct bcm2835_desc *d;
> u32 info = bcm2835_dma_prepare_cb_info(c, DMA_MEM_TO_MEM, false);
> - u32 extra = BCM2835_DMA_INT_EN | BCM2835_DMA_WAIT_RESP;
> + u32 extra = bcm2835_dma_prepare_cb_extra(c, DMA_MEM_TO_MEM, false,
> + true, 0);
> size_t max_len = bcm2835_dma_max_frame_length(c);
> size_t frames;
>
> @@ -675,7 +699,7 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_slave_sg(
> struct bcm2835_desc *d;
> dma_addr_t src = 0, dst = 0;
> u32 info = bcm2835_dma_prepare_cb_info(c, direction, false);
> - u32 extra = BCM2835_DMA_INT_EN;
> + u32 extra = bcm2835_dma_prepare_cb_extra(c, direction, false, true, 0);
> size_t frames;
>
> if (!is_slave_direction(direction)) {
> @@ -723,7 +747,7 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic(
> dma_addr_t src, dst;
> u32 info = bcm2835_dma_prepare_cb_info(c, direction,
> buf_addr == od->zero_page);
> - u32 extra = 0;
> + u32 extra = bcm2835_dma_prepare_cb_extra(c, direction, true, true, 0);
> size_t max_len = bcm2835_dma_max_frame_length(c);
> size_t frames;
>
> @@ -739,9 +763,7 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic(
> return NULL;
> }
>
> - if (flags & DMA_PREP_INTERRUPT)
> - extra |= BCM2835_DMA_INT_EN;
> - else
> + if (!(flags & DMA_PREP_INTERRUPT))
> period_len = buf_len;
>
> /*
> --
> 2.34.1
>
On Fri, May 24, 2024 at 07:26:50PM +0100, Dave Stevenson wrote:
> From: Stefan Wahren <[email protected]>
>
> Actually the criteria to increment source & destination address doesn't
> based on platform specific bits. It's just the DMA transfer direction which
> is translated into the info bits. So introduce two new helper functions
> and get the rid of these platform specifics.
>
Fix increment source & destination address depend on the platform drvdata.
It should be depend on dma_transfer_direction.
look like it is bug fixes. Can you add fixes tag.
> Signed-off-by: Stefan Wahren <[email protected]>
> Signed-off-by: Dave Stevenson <[email protected]>
> ---
> drivers/dma/bcm2835-dma.c | 28 ++++++++++++++++++++++------
> 1 file changed, 22 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
> index ef452ebb3c15..d6c5a2762a46 100644
> --- a/drivers/dma/bcm2835-dma.c
> +++ b/drivers/dma/bcm2835-dma.c
> @@ -252,6 +252,24 @@ static u32 bcm2835_dma_prepare_cb_extra(struct bcm2835_chan *c,
> return result;
> }
>
> +static inline bool need_src_incr(enum dma_transfer_direction direction)
> +{
> + return direction != DMA_DEV_TO_MEM;
> +}
> +
> +static inline bool need_dst_incr(enum dma_transfer_direction direction)
> +{
> + switch (direction) {
> + case DMA_MEM_TO_MEM:
> + case DMA_DEV_TO_MEM:
> + return true;
> + default:
> + break;
> + }
> +
> + return false;
> +}
> +
> static void bcm2835_dma_free_cb_chain(struct bcm2835_desc *desc)
> {
> size_t i;
> @@ -336,10 +354,8 @@ static inline size_t bcm2835_dma_count_frames_for_sg(
> * @cyclic: it is a cyclic transfer
> * @info: the default info bits to apply per controlblock
> * @frames: number of controlblocks to allocate
> - * @src: the src address to assign (if the S_INC bit is set
> - * in @info, then it gets incremented)
> - * @dst: the dst address to assign (if the D_INC bit is set
> - * in @info, then it gets incremented)
> + * @src: the src address to assign
> + * @dst: the dst address to assign
> * @buf_len: the full buffer length (may also be 0)
> * @period_len: the period length when to apply @finalextrainfo
> * in addition to the last transfer
> @@ -408,9 +424,9 @@ static struct bcm2835_desc *bcm2835_dma_create_cb_chain(
> d->cb_list[frame - 1].cb->next = cb_entry->paddr;
>
> /* update src and dst and length */
> - if (src && (info & BCM2835_DMA_S_INC))
> + if (src && need_src_incr(direction))
> src += control_block->length;
> - if (dst && (info & BCM2835_DMA_D_INC))
> + if (dst && need_dst_incr(direction))
> dst += control_block->length;
>
> /* Length of total transfer */
> --
> 2.34.1
>
On Fri, May 24, 2024 at 07:26:51PM +0100, Dave Stevenson wrote:
> From: Stefan Wahren <[email protected]>
>
> The parameters info and finalextrainfo are platform specific. So drop
> them by generating them within bcm2835_dma_create_cb_chain().
Drop 'info' and 'finalextrainfo' because these can be generated by
bcm2835_dma_create_cb_chain().
>
> Signed-off-by: Stefan Wahren <[email protected]>
> Signed-off-by: Dave Stevenson <[email protected]>
> ---
> drivers/dma/bcm2835-dma.c | 83 +++++++++++++++++++--------------------
> 1 file changed, 40 insertions(+), 43 deletions(-)
>
> diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
> index d6c5a2762a46..e2f9c8692e6b 100644
> --- a/drivers/dma/bcm2835-dma.c
> +++ b/drivers/dma/bcm2835-dma.c
> @@ -287,13 +287,11 @@ static void bcm2835_dma_desc_free(struct virt_dma_desc *vd)
> container_of(vd, struct bcm2835_desc, vd));
> }
>
> -static void bcm2835_dma_create_cb_set_length(
> - struct bcm2835_chan *chan,
> - struct bcm2835_dma_cb *control_block,
> - size_t len,
> - size_t period_len,
> - size_t *total_len,
> - u32 finalextrainfo)
> +static bool
> +bcm2835_dma_create_cb_set_length(struct bcm2835_chan *chan,
> + struct bcm2835_dma_cb *control_block,
> + size_t len, size_t period_len,
> + size_t *total_len)
Can you document this function, what's return value means? look like if
need extrainfo?
> {
> size_t max_len = bcm2835_dma_max_frame_length(chan);
>
> @@ -302,7 +300,7 @@ static void bcm2835_dma_create_cb_set_length(
>
> /* finished if we have no period_length */
> if (!period_len)
> - return;
> + return false;
>
> /*
> * period_len means: that we need to generate
> @@ -316,7 +314,7 @@ static void bcm2835_dma_create_cb_set_length(
> if (*total_len + control_block->length < period_len) {
> /* update number of bytes in this period so far */
> *total_len += control_block->length;
> - return;
> + return false;
> }
>
> /* calculate the length that remains to reach period_length */
> @@ -325,8 +323,7 @@ static void bcm2835_dma_create_cb_set_length(
> /* reset total_length for next period */
> *total_len = 0;
>
> - /* add extrainfo bits in info */
> - control_block->info |= finalextrainfo;
> + return true;
> }
>
> static inline size_t bcm2835_dma_count_frames_for_sg(
> @@ -352,7 +349,6 @@ static inline size_t bcm2835_dma_count_frames_for_sg(
> * @chan: the @dma_chan for which we run this
> * @direction: the direction in which we transfer
> * @cyclic: it is a cyclic transfer
> - * @info: the default info bits to apply per controlblock
> * @frames: number of controlblocks to allocate
> * @src: the src address to assign
> * @dst: the dst address to assign
> @@ -360,22 +356,24 @@ static inline size_t bcm2835_dma_count_frames_for_sg(
> * @period_len: the period length when to apply @finalextrainfo
> * in addition to the last transfer
> * this will also break some control-blocks early
> - * @finalextrainfo: additional bits in last controlblock
> - * (or when period_len is reached in case of cyclic)
> * @gfp: the GFP flag to use for allocation
> + * @flags
> */
> static struct bcm2835_desc *bcm2835_dma_create_cb_chain(
> struct dma_chan *chan, enum dma_transfer_direction direction,
> - bool cyclic, u32 info, u32 finalextrainfo, size_t frames,
> - dma_addr_t src, dma_addr_t dst, size_t buf_len,
> - size_t period_len, gfp_t gfp)
> + bool cyclic, size_t frames, dma_addr_t src, dma_addr_t dst,
> + size_t buf_len, size_t period_len, gfp_t gfp, unsigned long flags)
> {
> + struct bcm2835_dmadev *od = to_bcm2835_dma_dev(chan->device);
> struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
> size_t len = buf_len, total_len;
> size_t frame;
> struct bcm2835_desc *d;
> struct bcm2835_cb_entry *cb_entry;
> struct bcm2835_dma_cb *control_block;
> + u32 extrainfo = bcm2835_dma_prepare_cb_extra(c, direction, cyclic,
> + false, flags);
> + bool zero_page = false;
>
> if (!frames)
> return NULL;
> @@ -389,6 +387,14 @@ static struct bcm2835_desc *bcm2835_dma_create_cb_chain(
> d->dir = direction;
> d->cyclic = cyclic;
>
> + switch (direction) {
> + case DMA_MEM_TO_MEM:
> + case DMA_DEV_TO_MEM:
> + break;
> + default:
> + zero_page = src == od->zero_page;
> + }
> +
> /*
> * Iterate over all frames, create a control block
> * for each frame and link them together.
> @@ -402,7 +408,8 @@ static struct bcm2835_desc *bcm2835_dma_create_cb_chain(
>
> /* fill in the control block */
> control_block = cb_entry->cb;
> - control_block->info = info;
> + control_block->info = bcm2835_dma_prepare_cb_info(c, direction,
> + zero_page);
> control_block->src = src;
> control_block->dst = dst;
> control_block->stride = 0;
> @@ -410,10 +417,12 @@ static struct bcm2835_desc *bcm2835_dma_create_cb_chain(
> /* set up length in control_block if requested */
> if (buf_len) {
> /* calculate length honoring period_length */
> - bcm2835_dma_create_cb_set_length(
> - c, control_block,
> - len, period_len, &total_len,
> - cyclic ? finalextrainfo : 0);
> + if (bcm2835_dma_create_cb_set_length(c, control_block,
> + len, period_len,
> + &total_len)) {
> + /* add extrainfo bits in info */
> + control_block->info |= extrainfo;
> + }
>
> /* calculate new remaining length */
> len -= control_block->length;
> @@ -434,7 +443,9 @@ static struct bcm2835_desc *bcm2835_dma_create_cb_chain(
> }
>
> /* the last frame requires extra flags */
> - d->cb_list[d->frames - 1].cb->info |= finalextrainfo;
> + extrainfo = bcm2835_dma_prepare_cb_extra(c, direction, cyclic, true,
> + flags);
> + d->cb_list[d->frames - 1].cb->info |= extrainfo;
>
> /* detect a size missmatch */
> if (buf_len && (d->size != buf_len))
> @@ -682,9 +693,6 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_memcpy(
> {
> struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
> struct bcm2835_desc *d;
> - u32 info = bcm2835_dma_prepare_cb_info(c, DMA_MEM_TO_MEM, false);
> - u32 extra = bcm2835_dma_prepare_cb_extra(c, DMA_MEM_TO_MEM, false,
> - true, 0);
> size_t max_len = bcm2835_dma_max_frame_length(c);
> size_t frames;
>
> @@ -696,9 +704,8 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_memcpy(
> frames = bcm2835_dma_frames_for_length(len, max_len);
>
> /* allocate the CB chain - this also fills in the pointers */
> - d = bcm2835_dma_create_cb_chain(chan, DMA_MEM_TO_MEM, false,
> - info, extra, frames,
> - src, dst, len, 0, GFP_KERNEL);
> + d = bcm2835_dma_create_cb_chain(chan, DMA_MEM_TO_MEM, false, frames,
> + src, dst, len, 0, GFP_KERNEL, 0);
> if (!d)
> return NULL;
>
> @@ -714,8 +721,6 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_slave_sg(
> struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
> struct bcm2835_desc *d;
> dma_addr_t src = 0, dst = 0;
> - u32 info = bcm2835_dma_prepare_cb_info(c, direction, false);
> - u32 extra = bcm2835_dma_prepare_cb_extra(c, direction, false, true, 0);
> size_t frames;
>
> if (!is_slave_direction(direction)) {
> @@ -738,10 +743,8 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_slave_sg(
> frames = bcm2835_dma_count_frames_for_sg(c, sgl, sg_len);
>
> /* allocate the CB chain */
> - d = bcm2835_dma_create_cb_chain(chan, direction, false,
> - info, extra,
> - frames, src, dst, 0, 0,
> - GFP_NOWAIT);
> + d = bcm2835_dma_create_cb_chain(chan, direction, false, frames, src,
> + dst, 0, 0, GFP_NOWAIT, 0);
> if (!d)
> return NULL;
>
> @@ -757,13 +760,9 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic(
> size_t period_len, enum dma_transfer_direction direction,
> unsigned long flags)
> {
> - struct bcm2835_dmadev *od = to_bcm2835_dma_dev(chan->device);
> struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
> struct bcm2835_desc *d;
> dma_addr_t src, dst;
> - u32 info = bcm2835_dma_prepare_cb_info(c, direction,
> - buf_addr == od->zero_page);
> - u32 extra = bcm2835_dma_prepare_cb_extra(c, direction, true, true, 0);
> size_t max_len = bcm2835_dma_max_frame_length(c);
> size_t frames;
>
> @@ -814,10 +813,8 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic(
> * note that we need to use GFP_NOWAIT, as the ALSA i2s dmaengine
> * implementation calls prep_dma_cyclic with interrupts disabled.
> */
> - d = bcm2835_dma_create_cb_chain(chan, direction, true,
> - info, extra,
> - frames, src, dst, buf_len,
> - period_len, GFP_NOWAIT);
> + d = bcm2835_dma_create_cb_chain(chan, direction, true, frames, src, dst,
> + buf_len, period_len, GFP_NOWAIT, flags);
> if (!d)
> return NULL;
>
> --
> 2.34.1
>
On 5/24/2024 8:26 PM, Dave Stevenson wrote:
> Hi All
>
> This series initially cleans up the BCM2835 DMA driver in preparation for
> supporting the 40bit version. It then fixes up the incorrect mapping behaviour
> we've had to date.
>
> The cleanups are based on Stefan Wahren's RFC [1], with a couple of minor bugs
> fixed, but stopping before actually adding the 40bit support. If we can sort
> the mapping issue, it avoids having to have workarounds in the 40bit support.
>
> The mapping issues were discussed in [2].
> Up until this point all DMA users have been passing in dma addresses rather than
> CPU physical addresses, and the DMA driver has been using those directly rather
> than using dma_map_resource() to map them.
> The DT has also been missing some of the required mappings in "dma-ranges", but
> they have been present in "ranges". I've therefore duplicated the minimum amount
> of of_dma_get_range and translate_phys_to_dma to be able to use "ranges" as
> discussed in that thread. I'm assuming that sort of code is not desirable in the
> core code as it shouldn't be necessary, so keeping it contained within a driver
> is the better solution.
>
> When Andrea posted our downstream patches in [3], Robin Murphy stated that
> dma_map_resource is the correct API, but as it currently doesn't check the
> dma_range_map we need Sergey Semin's patch [4].
> There seemed to be no follow up over the implications of it. I've therefore
> included it in the series at least for discussion. If it's not acceptable then
> I'm not sure of the route forward in fixing this mapping issue.
>
> I'm expecting there to be some discussion, but also acknowledge that merging this
> will need to be phased with the patches 1-13 needing to be merged before any of
> 14-17, and then 18 merged last to remove the workaround. I suspect that's the
> least of my worries though.
>
>
> I will apologise in advance if I don't respond immediately to comments - I'm
> out of the office for the next week, but do appreciate any feedback.
Those patches should be routed via the dmaengine tree, including the DTS
files to minimize the possibility of introducing regressions if people
happen to bisect changes. I don't expect conflicts when these changes
reach linux-next.
--
Florian
On Fri, May 24, 2024 at 07:26:53PM +0100, Dave Stevenson wrote:
> The code handling DMA mapping is currently incorrect and
> needs a sequence of fixups.
Can you descript what incorrect here?
> Move the mapping out into a separate function and structure
> to allow for those fixes to be applied more cleanly.
>
> Signed-off-by: Dave Stevenson <[email protected]>
> ---
> drivers/dma/bcm2835-dma.c | 46 ++++++++++++++++++++++++++++++++-------
> 1 file changed, 38 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
> index aefaa1f01d7f..ef1d95bae84e 100644
> --- a/drivers/dma/bcm2835-dma.c
> +++ b/drivers/dma/bcm2835-dma.c
> @@ -65,6 +65,10 @@ struct bcm2835_cb_entry {
> dma_addr_t paddr;
> };
>
> +struct bcm2835_dma_chan_map {
> + dma_addr_t addr;
> +};
> +
> struct bcm2835_chan {
> struct virt_dma_chan vc;
>
> @@ -74,6 +78,7 @@ struct bcm2835_chan {
> int ch;
> struct bcm2835_desc *desc;
> struct dma_pool *cb_pool;
> + struct bcm2835_dma_chan_map map;
I suppose map should in bcm2835_desc. if put in chan, how about client
driver create two desc by bcm2835_dma_prep_slave_sg()?
prep_slave_sg()
submit()
prep_savle_sg()
submit()
issue_pending()
Frank
>
> void __iomem *chan_base;
> int irq_number;
> @@ -268,6 +273,19 @@ static inline bool need_dst_incr(enum dma_transfer_direction direction)
> }
>
> return false;
> +};
> +
> +static int bcm2835_dma_map_slave_addr(struct dma_chan *chan,
> + phys_addr_t dev_addr,
> + size_t dev_size,
> + enum dma_data_direction dev_dir)
> +{
> + struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
> + struct bcm2835_dma_chan_map *map = &c->map;
> +
> + map->addr = dev_addr;
> +
> + return 0;
> }
>
> static void bcm2835_dma_free_cb_chain(struct bcm2835_desc *desc)
> @@ -734,13 +752,19 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_slave_sg(
> }
>
> if (direction == DMA_DEV_TO_MEM) {
> - if (c->cfg.src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES)
> + if (bcm2835_dma_map_slave_addr(chan, c->cfg.src_addr,
> + c->cfg.src_addr_width,
> + DMA_TO_DEVICE))
> return NULL;
> - src = c->cfg.src_addr;
> +
> + src = c->map.addr;
> } else {
> - if (c->cfg.dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES)
> + if (bcm2835_dma_map_slave_addr(chan, c->cfg.dst_addr,
> + c->cfg.dst_addr_width,
> + DMA_FROM_DEVICE))
> return NULL;
> - dst = c->cfg.dst_addr;
> +
> + dst = c->map.addr;
> }
>
> /* count frames in sg list */
> @@ -795,14 +819,20 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic(
> __func__, buf_len, period_len);
>
> if (direction == DMA_DEV_TO_MEM) {
> - if (c->cfg.src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES)
> + if (bcm2835_dma_map_slave_addr(chan, c->cfg.src_addr,
> + c->cfg.src_addr_width,
> + DMA_TO_DEVICE))
> return NULL;
> - src = c->cfg.src_addr;
> +
> + src = c->map.addr;
> dst = buf_addr;
> } else {
> - if (c->cfg.dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES)
> + if (bcm2835_dma_map_slave_addr(chan, c->cfg.dst_addr,
> + c->cfg.dst_addr_width,
> + DMA_FROM_DEVICE))
> return NULL;
> - dst = c->cfg.dst_addr;
> +
> + dst = c->map.addr;
> src = buf_addr;
> }
>
> --
> 2.34.1
>
On Fri, May 24, 2024 at 07:26:52PM +0100, Dave Stevenson wrote:
> From: Stefan Wahren <[email protected]>
>
> In preparation to support more platforms pass the dma_chan to the
> generic functions. This provides access to the DMA device and possible
> platform specific data.
why need this change? you can easy convert between dma_chan and
bcm2835_chan.
>
> Signed-off-by: Stefan Wahren <[email protected]>
> Signed-off-by: Dave Stevenson <[email protected]>
> ---
> drivers/dma/bcm2835-dma.c | 24 ++++++++++++++----------
> 1 file changed, 14 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
> index e2f9c8692e6b..aefaa1f01d7f 100644
> --- a/drivers/dma/bcm2835-dma.c
> +++ b/drivers/dma/bcm2835-dma.c
> @@ -288,12 +288,13 @@ static void bcm2835_dma_desc_free(struct virt_dma_desc *vd)
> }
>
> static bool
> -bcm2835_dma_create_cb_set_length(struct bcm2835_chan *chan,
> +bcm2835_dma_create_cb_set_length(struct dma_chan *chan,
> struct bcm2835_dma_cb *control_block,
> size_t len, size_t period_len,
> size_t *total_len)
> {
> - size_t max_len = bcm2835_dma_max_frame_length(chan);
> + struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
> + size_t max_len = bcm2835_dma_max_frame_length(c);
>
> /* set the length taking lite-channel limitations into account */
> control_block->length = min_t(u32, len, max_len);
> @@ -417,7 +418,7 @@ static struct bcm2835_desc *bcm2835_dma_create_cb_chain(
> /* set up length in control_block if requested */
> if (buf_len) {
> /* calculate length honoring period_length */
> - if (bcm2835_dma_create_cb_set_length(c, control_block,
> + if (bcm2835_dma_create_cb_set_length(chan, control_block,
> len, period_len,
> &total_len)) {
> /* add extrainfo bits in info */
> @@ -485,8 +486,9 @@ static void bcm2835_dma_fill_cb_chain_with_sg(
> }
> }
>
> -static void bcm2835_dma_abort(struct bcm2835_chan *c)
> +static void bcm2835_dma_abort(struct dma_chan *chan)
> {
> + struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
> void __iomem *chan_base = c->chan_base;
> long int timeout = 10000;
>
> @@ -513,8 +515,9 @@ static void bcm2835_dma_abort(struct bcm2835_chan *c)
> writel(BCM2835_DMA_RESET, chan_base + BCM2835_DMA_CS);
> }
>
> -static void bcm2835_dma_start_desc(struct bcm2835_chan *c)
> +static void bcm2835_dma_start_desc(struct dma_chan *chan)
> {
> + struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
> struct virt_dma_desc *vd = vchan_next_desc(&c->vc);
> struct bcm2835_desc *d;
>
> @@ -533,7 +536,8 @@ static void bcm2835_dma_start_desc(struct bcm2835_chan *c)
>
> static irqreturn_t bcm2835_dma_callback(int irq, void *data)
> {
> - struct bcm2835_chan *c = data;
> + struct dma_chan *chan = data;
> + struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
> struct bcm2835_desc *d;
> unsigned long flags;
>
> @@ -566,7 +570,7 @@ static irqreturn_t bcm2835_dma_callback(int irq, void *data)
> vchan_cyclic_callback(&d->vd);
> } else if (!readl(c->chan_base + BCM2835_DMA_ADDR)) {
> vchan_cookie_complete(&c->desc->vd);
> - bcm2835_dma_start_desc(c);
> + bcm2835_dma_start_desc(chan);
> }
> }
>
> @@ -594,7 +598,7 @@ static int bcm2835_dma_alloc_chan_resources(struct dma_chan *chan)
> }
>
> return request_irq(c->irq_number, bcm2835_dma_callback,
> - c->irq_flags, "DMA IRQ", c);
> + c->irq_flags, "DMA IRQ", chan);
> }
>
> static void bcm2835_dma_free_chan_resources(struct dma_chan *chan)
> @@ -682,7 +686,7 @@ static void bcm2835_dma_issue_pending(struct dma_chan *chan)
>
> spin_lock_irqsave(&c->vc.lock, flags);
> if (vchan_issue_pending(&c->vc) && !c->desc)
> - bcm2835_dma_start_desc(c);
> + bcm2835_dma_start_desc(chan);
>
> spin_unlock_irqrestore(&c->vc.lock, flags);
> }
> @@ -846,7 +850,7 @@ static int bcm2835_dma_terminate_all(struct dma_chan *chan)
> if (c->desc) {
> vchan_terminate_vdesc(&c->desc->vd);
> c->desc = NULL;
> - bcm2835_dma_abort(c);
> + bcm2835_dma_abort(chan);
> }
>
> vchan_get_all_descriptors(&c->vc, &head);
> --
> 2.34.1
>
On Fri, May 24, 2024 at 07:26:55PM +0100, Dave Stevenson wrote:
> There is a need to account for dma-ranges and iommus in the
> dma mapping process, and the public API for handling that is
> dma_map_resource.
what's means?
>
> Add support for mapping addresses to the DMA driver.
>
> Signed-off-by: Dave Stevenson <[email protected]>
> ---
> drivers/dma/bcm2835-dma.c | 46 ++++++++++++++++++++++++++++++++++-----
> 1 file changed, 41 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
> index 9531c0b82071..e48008b06716 100644
> --- a/drivers/dma/bcm2835-dma.c
> +++ b/drivers/dma/bcm2835-dma.c
> @@ -67,6 +67,10 @@ struct bcm2835_cb_entry {
>
> struct bcm2835_dma_chan_map {
> dma_addr_t addr;
> + enum dma_data_direction dir;
> +
> + phys_addr_t slave_addr;
> + unsigned int xfer_size;
> };
>
> struct bcm2835_chan {
> @@ -294,12 +298,44 @@ static int bcm2835_dma_map_slave_addr(struct dma_chan *chan,
> return 0;
> }
where umap function? I suppose you should unmap all when terminate chan
or free chan by client driver.
>
> - /*
> - * This path will be updated to handle new clients, but currently should
> - * never be used.
> - */
> + if (dev_size != DMA_SLAVE_BUSWIDTH_4_BYTES)
> + return -EIO;
> +
> + /* Reuse current map if possible. */
> + if (dev_addr == map->slave_addr &&
> + dev_size == map->xfer_size &&
> + dev_dir == map->dir)
> + return 0;
> +
> + /* Remove old mapping if present. */
> + if (map->xfer_size) {
> + dev_dbg(chan->device->dev, "chan: unmap %zx@%pap to %pad dir: %s\n",
> + dev_size, &dev_addr, &map->addr,
> + dev_dir == DMA_TO_DEVICE ? "DMA_TO_DEVICE" : "DMA_FROM_DEVICE");
> + dma_unmap_resource(chan->device->dev, map->addr,
> + map->xfer_size, map->dir, 0);
> + }
> + map->xfer_size = 0;
>
> - return -EINVAL;
> + /* Create new slave address map. */
> + map->addr = dma_map_resource(chan->device->dev, dev_addr, dev_size,
> + dev_dir, 0);
> +
> + if (dma_mapping_error(chan->device->dev, map->addr)) {
> + dev_err(chan->device->dev, "chan: failed to map %zx@%pap",
> + dev_size, &dev_addr);
> + return -EIO;
> + }
> +
> + dev_dbg(chan->device->dev, "chan: map %zx@%pap to %pad dir: %s\n",
> + dev_size, &dev_addr, &map->addr,
> + dev_dir == DMA_TO_DEVICE ? "DMA_TO_DEVICE" : "DMA_FROM_DEVICE");
> +
> + map->slave_addr = dev_addr;
> + map->xfer_size = dev_size;
> + map->dir = dev_dir;
> +
> + return 0;
> }
>
> static void bcm2835_dma_free_cb_chain(struct bcm2835_desc *desc)
> --
> 2.34.1
>
On Fri, May 24, 2024 at 07:27:00PM +0100, Dave Stevenson wrote:
> From: Phil Elwell <[email protected]>
>
> Slave addresses for DMA are meant to be supplied as physical addresses
> (contrary to what struct snd_dmaengine_dai_dma_data does).
Can you use the same content for patch 14-17?
Frank
>
> Signed-off-by: Phil Elwell <[email protected]>
> Signed-off-by: Dave Stevenson <[email protected]>
> ---
> drivers/gpu/drm/vc4/vc4_hdmi.c | 15 ++++-----------
> 1 file changed, 4 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
> index d30f8e8e8967..c2afd72bd96e 100644
> --- a/drivers/gpu/drm/vc4/vc4_hdmi.c
> +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
> @@ -2696,7 +2696,7 @@ static int vc4_hdmi_audio_init(struct vc4_hdmi *vc4_hdmi)
> struct snd_soc_card *card = &vc4_hdmi->audio.card;
> struct device *dev = &vc4_hdmi->pdev->dev;
> struct platform_device *codec_pdev;
> - const __be32 *addr;
> + struct resource *iomem;
> int index, len;
> int ret;
>
> @@ -2732,22 +2732,15 @@ static int vc4_hdmi_audio_init(struct vc4_hdmi *vc4_hdmi)
> }
>
> /*
> - * Get the physical address of VC4_HD_MAI_DATA. We need to retrieve
> - * the bus address specified in the DT, because the physical address
> - * (the one returned by platform_get_resource()) is not appropriate
> - * for DMA transfers.
> - * This VC/MMU should probably be exposed to avoid this kind of hacks.
> + * Get the physical address of VC4_HD_MAI_DATA.
> */
> index = of_property_match_string(dev->of_node, "reg-names", "hd");
> /* Before BCM2711, we don't have a named register range */
> if (index < 0)
> index = 1;
> + iomem = platform_get_resource(vc4_hdmi->pdev, IORESOURCE_MEM, index);
>
> - addr = of_get_address(dev->of_node, index, NULL, NULL);
> - if (!addr)
> - return -EINVAL;
> -
> - vc4_hdmi->audio.dma_data.addr = be32_to_cpup(addr) + mai_data->offset;
> + vc4_hdmi->audio.dma_data.addr = iomem->start + mai_data->offset;
> vc4_hdmi->audio.dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
> vc4_hdmi->audio.dma_data.maxburst = 2;
>
> --
> 2.34.1
>