2024-03-13 14:08:59

by Andrea della Porta

[permalink] [raw]
Subject: [PATCH v2 00/15] dmaengine: bcm2835: add BCM2711 40-bit DMA support

The BCM2711 has 4 DMA channels with a 40-bit address range, allowing them
to access the full 4GB of memory on a Pi 4.
This patchset aims to update the dma engine for BCM* chipset with respect
to current advancements in downstream vendor tree. In particular, it
supports the BCM2711 DMA engine in terms of extended DMA addressing to 40 bit.

Changes with respect to the first version (see [1]) of this patchset:

* dropped support of the new BCM2712. It will be the focus of a subsequent
patch.

* merged patchset from Stefan Wahren [2] to support newer chipset with a
platform agnostic design, while also retaining the new features added
from downstream [1], as follows:

- patches from 1 to 5 are preparatory, adding some features and bugfix
common to all chipsets.
- patches from 6 to 12 add hw abstraction
- patches 13 to 15 eventually add 40 bit and BCM2711 support

* fixed a couple of bugs from [2] relative to address shifting on 40 bit
support specific code

* added the relevant entries in the dts and DT binding that was missing
in the first patch

* used FIELD_PREP() wherever appropriate as advised in [3]

* of_match_node() has been replaced by the more generic device_get_match_data(),
as per [4]

* fixed several errors and warnings from checkpatch


Please note that there is still a pending discussion around here [5]:
this patch still use the current approach (used in both downstream
code and in Stefan's redesigned patchset) of getting the address as it is
(dma_addr_t) and just add the relevant offset when needed (on 40 bit
channel, see .addr_offset in struct bcm2835_dma_cfg). This is not
optimal but still deemed as less hacky than using DMA internals (see
[6]). As soon as there will be guidelines for [5] or dma_map_resource()
will take care of dma_ranges, a subsequent patch will adjust accordingly.

Since there is an ongoing effort from Dave Stevenson to upstream a
patchset with similar goals, I'm adding him to the email loop in order
seek for collaboration.

NOTE: I apologize to any of you that received an incomplete patchset: unfortunately
I had some issues with internal imap server that prevent correct mail delivery.
I'm resending the entire patchset again to be sure everyone is getting a copy.
Again, apologize for the inconvenince.

Many thanks,

Andrea

Links:
[1] https://lore.kernel.org/linux-arm-kernel/[email protected]/
[2] https://lore.kernel.org/linux-arm-kernel/[email protected]/T/
[3] https://lore.kernel.org/linux-arm-kernel/YguMW8n1q0ZV5tKH@matsya/
[4] https://lore.kernel.org/linux-arm-kernel/[email protected]/
[5] https://lore.kernel.org/all/CAPY8ntByJYzSv0kTAc1kY0Dp=vwrzcA0oWiPpyg7x7_BQwGSnA@mail.gmail.com/
[6] https://lkml.org/lkml/2024/2/5/1161

Andrea della Porta (11):
dmaengine: bcm2835: Fix several spellos
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
dmaengine: bcm2835: introduce multi platform support
dt-bindings: dma: Added bcm2711-dma
dmaengine: bcm2835: Add BCM2711 40-bit DMA support
ARM: dts: bcm2711: add bcm2711-dma node

Dom Cobley (2):
dmaengine: bcm2835: Support dma flags for multi-beat burst
dmaengine: bcm2835: Fixes for dma_abort

Phil Elwell (2):
dmaengine: bcm2835: Add support for per-channel flags
dmaengine: bcm2835: Add NO_WAIT_RESP, DMA_WIDE_SOURCE and
DMA_WIDE_DEST flag

.../bindings/dma/brcm,bcm2835-dma.yaml | 4 +-
arch/arm/boot/dts/broadcom/bcm2711.dtsi | 16 +
drivers/dma/bcm2835-dma.c | 1084 +++++++++++++----
3 files changed, 892 insertions(+), 212 deletions(-)

--
2.35.3



2024-03-13 14:09:14

by Andrea della Porta

[permalink] [raw]
Subject: [PATCH v2 01/15] dmaengine: bcm2835: Fix several spellos

Fixed Codespell reported warnings about spelling and coding convention
violations, among which there are also a couple potential operator
precedence issue in macroes.

Signed-off-by: Andrea della Porta <[email protected]>
---
drivers/dma/bcm2835-dma.c | 96 +++++++++++++++++++--------------------
1 file changed, 48 insertions(+), 48 deletions(-)

diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
index 9d74fe97452e..428253b468ac 100644
--- a/drivers/dma/bcm2835-dma.c
+++ b/drivers/dma/bcm2835-dma.c
@@ -51,13 +51,13 @@ struct bcm2835_dmadev {
};

struct bcm2835_dma_cb {
- uint32_t info;
- uint32_t src;
- uint32_t dst;
- uint32_t length;
- uint32_t stride;
- uint32_t next;
- uint32_t pad[2];
+ u32 info;
+ u32 src;
+ u32 dst;
+ u32 length;
+ u32 stride;
+ u32 next;
+ u32 pad[2];
};

struct bcm2835_cb_entry {
@@ -116,8 +116,8 @@ struct bcm2835_desc {
* AXI-write to ack
*/
#define BCM2835_DMA_ERR BIT(8)
-#define BCM2835_DMA_PRIORITY(x) ((x & 15) << 16) /* AXI priority */
-#define BCM2835_DMA_PANIC_PRIORITY(x) ((x & 15) << 20) /* panic priority */
+#define BCM2835_DMA_PRIORITY(x) (((x) & 15) << 16) /* AXI priority */
+#define BCM2835_DMA_PANIC_PRIORITY(x) (((x) & 15) << 20) /* panic priority */
/* current value of TI.BCM2835_DMA_WAIT_RESP */
#define BCM2835_DMA_WAIT_FOR_WRITES BIT(28)
#define BCM2835_DMA_DIS_DEBUG BIT(29) /* disable debug pause signal */
@@ -136,9 +136,9 @@ struct bcm2835_desc {
#define BCM2835_DMA_S_WIDTH BIT(9) /* 128bit writes if set */
#define BCM2835_DMA_S_DREQ BIT(10) /* enable SREQ for source */
#define BCM2835_DMA_S_IGNORE BIT(11) /* ignore source reads - read 0 */
-#define BCM2835_DMA_BURST_LENGTH(x) ((x & 15) << 12)
-#define BCM2835_DMA_PER_MAP(x) ((x & 31) << 16) /* REQ source */
-#define BCM2835_DMA_WAIT(x) ((x & 31) << 21) /* add DMA-wait cycles */
+#define BCM2835_DMA_BURST_LENGTH(x) (((x) & 15) << 12)
+#define BCM2835_DMA_PER_MAP(x) (((x) & 31) << 16) /* REQ source */
+#define BCM2835_DMA_WAIT(x) (((x) & 31) << 21) /* add DMA-wait cycles */
#define BCM2835_DMA_NO_WIDE_BURSTS BIT(26) /* no 2 beat write bursts */

/* debug register bits */
@@ -214,17 +214,15 @@ static void bcm2835_dma_free_cb_chain(struct bcm2835_desc *desc)

static void bcm2835_dma_desc_free(struct virt_dma_desc *vd)
{
- bcm2835_dma_free_cb_chain(
- container_of(vd, struct bcm2835_desc, vd));
+ bcm2835_dma_free_cb_chain(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 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)
{
size_t max_len = bcm2835_dma_max_frame_length(chan);

@@ -260,10 +258,9 @@ static void bcm2835_dma_create_cb_set_length(
control_block->info |= finalextrainfo;
}

-static inline size_t bcm2835_dma_count_frames_for_sg(
- struct bcm2835_chan *c,
- struct scatterlist *sgl,
- unsigned int sg_len)
+static inline size_t bcm2835_dma_count_frames_for_sg(struct bcm2835_chan *c,
+ struct scatterlist *sgl,
+ unsigned int sg_len)
{
size_t frames = 0;
struct scatterlist *sgent;
@@ -271,8 +268,8 @@ static inline size_t bcm2835_dma_count_frames_for_sg(
size_t plength = bcm2835_dma_max_frame_length(c);

for_each_sg(sgl, sgent, sg_len, i)
- frames += bcm2835_dma_frames_for_length(
- sg_dma_len(sgent), plength);
+ frames += bcm2835_dma_frames_for_length(sg_dma_len(sgent),
+ plength);

return frames;
}
@@ -298,10 +295,13 @@ static inline size_t bcm2835_dma_count_frames_for_sg(
* @gfp: the GFP flag to use for allocation
*/
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)
+ 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)
{
struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
size_t len = buf_len, total_len;
@@ -343,10 +343,10 @@ 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);
+ bcm2835_dma_create_cb_set_length(c, control_block,
+ len, period_len,
+ &total_len,
+ cyclic ? finalextrainfo : 0);

/* calculate new remaining length */
len -= control_block->length;
@@ -369,8 +369,8 @@ static struct bcm2835_desc *bcm2835_dma_create_cb_chain(
/* the last frame requires extra flags */
d->cb_list[d->frames - 1].cb->info |= finalextrainfo;

- /* detect a size missmatch */
- if (buf_len && (d->size != buf_len))
+ /* detect a size mismatch */
+ if (buf_len && d->size != buf_len)
goto error_cb;

return d;
@@ -410,7 +410,7 @@ static void bcm2835_dma_fill_cb_chain_with_sg(
static void bcm2835_dma_abort(struct bcm2835_chan *c)
{
void __iomem *chan_base = c->chan_base;
- long int timeout = 10000;
+ long timeout = 10000;

/*
* A zero control block address means the channel is idle.
@@ -438,7 +438,6 @@ static void bcm2835_dma_abort(struct bcm2835_chan *c)
static void bcm2835_dma_start_desc(struct bcm2835_chan *c)
{
struct virt_dma_desc *vd = vchan_next_desc(&c->vc);
- struct bcm2835_desc *d;

if (!vd) {
c->desc = NULL;
@@ -447,9 +446,9 @@ static void bcm2835_dma_start_desc(struct bcm2835_chan *c)

list_del(&vd->node);

- c->desc = d = to_bcm2835_dma_desc(&vd->tx);
+ c->desc = to_bcm2835_dma_desc(&vd->tx);

- writel(d->cb_list[0].paddr, c->chan_base + BCM2835_DMA_ADDR);
+ writel(c->desc->cb_list[0].paddr, c->chan_base + BCM2835_DMA_ADDR);
writel(BCM2835_DMA_ACTIVE, c->chan_base + BCM2835_DMA_CS);
}

@@ -560,7 +559,8 @@ static size_t bcm2835_dma_desc_size_pos(struct bcm2835_desc *d, dma_addr_t addr)
}

static enum dma_status bcm2835_dma_tx_status(struct dma_chan *chan,
- dma_cookie_t cookie, struct dma_tx_state *txstate)
+ dma_cookie_t cookie,
+ struct dma_tx_state *txstate)
{
struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
struct virt_dma_desc *vd;
@@ -860,7 +860,7 @@ static const struct of_device_id bcm2835_dma_of_match[] = {
MODULE_DEVICE_TABLE(of, bcm2835_dma_of_match);

static struct dma_chan *bcm2835_dma_xlate(struct of_phandle_args *spec,
- struct of_dma *ofdma)
+ struct of_dma *ofdma)
{
struct bcm2835_dmadev *d = ofdma->of_dma_data;
struct dma_chan *chan;
@@ -883,7 +883,7 @@ static int bcm2835_dma_probe(struct platform_device *pdev)
int i, j;
int irq[BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED + 1];
int irq_flags;
- uint32_t chans_available;
+ u32 chans_available;
char chan_name[BCM2835_DMA_CHAN_NAME_SIZE];

if (!pdev->dev.dma_mask)
@@ -942,8 +942,8 @@ 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)) {
+ "brcm,dma-channel-mask",
+ &chans_available)) {
dev_err(&pdev->dev, "Failed to get channel mask\n");
rc = -EINVAL;
goto err_no_dma;
@@ -982,7 +982,7 @@ static int bcm2835_dma_probe(struct platform_device *pdev)
/* check if there are other channels that also use this irq */
irq_flags = 0;
for (j = 0; j <= BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED; j++)
- if ((i != j) && (irq[j] == irq[i])) {
+ if (i != j && irq[j] == irq[i]) {
irq_flags = IRQF_SHARED;
break;
}
@@ -997,7 +997,7 @@ static int bcm2835_dma_probe(struct platform_device *pdev)

/* Device-tree DMA controller registration */
rc = of_dma_controller_register(pdev->dev.of_node,
- bcm2835_dma_xlate, od);
+ bcm2835_dma_xlate, od);
if (rc) {
dev_err(&pdev->dev, "Failed to register DMA controller\n");
goto err_no_dma;
--
2.35.3


2024-03-13 14:09:20

by Andrea della Porta

[permalink] [raw]
Subject: [PATCH v2 02/15] dmaengine: bcm2835: Add support for per-channel flags

From: Phil Elwell <[email protected]>

Add the ability to interpret the high bits of the dreq specifier as
flags to be included in the DMA_CS register. The motivation for this
change is the ability to set the DISDEBUG flag for SD card transfers
to avoid corruption when using the VPU debugger.

Signed-off-by: Phil Elwell <[email protected]>
Signed-off-by: Andrea della Porta <[email protected]>
---
drivers/dma/bcm2835-dma.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
index 428253b468ac..3d9973dd041d 100644
--- a/drivers/dma/bcm2835-dma.c
+++ b/drivers/dma/bcm2835-dma.c
@@ -137,6 +137,10 @@ struct bcm2835_desc {
#define BCM2835_DMA_S_DREQ BIT(10) /* enable SREQ for source */
#define BCM2835_DMA_S_IGNORE BIT(11) /* ignore source reads - read 0 */
#define BCM2835_DMA_BURST_LENGTH(x) (((x) & 15) << 12)
+#define BCM2835_DMA_CS_FLAGS(x) ((x) & (BCM2835_DMA_PRIORITY(15) | \
+ BCM2835_DMA_PANIC_PRIORITY(15) | \
+ BCM2835_DMA_WAIT_FOR_WRITES | \
+ BCM2835_DMA_DIS_DEBUG))
#define BCM2835_DMA_PER_MAP(x) (((x) & 31) << 16) /* REQ source */
#define BCM2835_DMA_WAIT(x) (((x) & 31) << 21) /* add DMA-wait cycles */
#define BCM2835_DMA_NO_WIDE_BURSTS BIT(26) /* no 2 beat write bursts */
@@ -449,7 +453,8 @@ static void bcm2835_dma_start_desc(struct bcm2835_chan *c)
c->desc = to_bcm2835_dma_desc(&vd->tx);

writel(c->desc->cb_list[0].paddr, c->chan_base + BCM2835_DMA_ADDR);
- writel(BCM2835_DMA_ACTIVE, c->chan_base + BCM2835_DMA_CS);
+ writel(BCM2835_DMA_ACTIVE | BCM2835_DMA_CS_FLAGS(c->dreq),
+ c->chan_base + BCM2835_DMA_CS);
}

static irqreturn_t bcm2835_dma_callback(int irq, void *data)
@@ -476,7 +481,8 @@ static irqreturn_t bcm2835_dma_callback(int irq, void *data)
* if this IRQ handler is threaded.) If the channel is finished, it
* will remain idle despite the ACTIVE flag being set.
*/
- writel(BCM2835_DMA_INT | BCM2835_DMA_ACTIVE,
+ writel(BCM2835_DMA_INT | BCM2835_DMA_ACTIVE |
+ BCM2835_DMA_CS_FLAGS(c->dreq),
c->chan_base + BCM2835_DMA_CS);

d = c->desc;
--
2.35.3


2024-03-13 14:09:21

by Andrea della Porta

[permalink] [raw]
Subject: [PATCH v2 03/15] dmaengine: bcm2835: Add NO_WAIT_RESP, DMA_WIDE_SOURCE and DMA_WIDE_DEST flag

From: Phil Elwell <[email protected]>

Use bit 27 of the dreq value (the second cell of the DT DMA descriptor)
to request that the WAIT_RESP bit is not set.

Use (reserved) bits 24 and 25 of the dreq value
(the second cell of the DT DMA descriptor) to request
that wide source reads or wide dest writes are required

Originally-by: Dom Cobley <[email protected]>
Originally-by: Phil Elwell <[email protected]>
Signed-off-by: Andrea della Porta <[email protected]>
---
drivers/dma/bcm2835-dma.c | 25 +++++++++++++++++++++----
1 file changed, 21 insertions(+), 4 deletions(-)

diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
index 3d9973dd041d..69a77caf78cc 100644
--- a/drivers/dma/bcm2835-dma.c
+++ b/drivers/dma/bcm2835-dma.c
@@ -145,6 +145,21 @@ struct bcm2835_desc {
#define BCM2835_DMA_WAIT(x) (((x) & 31) << 21) /* add DMA-wait cycles */
#define BCM2835_DMA_NO_WIDE_BURSTS BIT(26) /* no 2 beat write bursts */

+/* A fake bit to request that the driver doesn't set the WAIT_RESP bit. */
+#define BCM2835_DMA_NO_WAIT_RESP BIT(27)
+#define WAIT_RESP(x) (((x) & BCM2835_DMA_NO_WAIT_RESP) ? \
+ 0 : BCM2835_DMA_WAIT_RESP)
+
+/* A fake bit to request that the driver requires wide reads */
+#define BCM2835_DMA_WIDE_SOURCE BIT(24)
+#define WIDE_SOURCE(x) (((x) & BCM2835_DMA_WIDE_SOURCE) ? \
+ BCM2835_DMA_S_WIDTH : 0)
+
+/* A fake bit to request that the driver requires wide writes */
+#define BCM2835_DMA_WIDE_DEST BIT(25)
+#define WIDE_DEST(x) (((x) & BCM2835_DMA_WIDE_DEST) ? \
+ BCM2835_DMA_D_WIDTH : 0)
+
/* debug register bits */
#define BCM2835_DMA_DEBUG_LAST_NOT_SET_ERR BIT(0)
#define BCM2835_DMA_DEBUG_FIFO_ERR BIT(1)
@@ -621,8 +636,9 @@ 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 extra = BCM2835_DMA_INT_EN | BCM2835_DMA_WAIT_RESP;
+ u32 info = BCM2835_DMA_D_INC | BCM2835_DMA_S_INC |
+ WAIT_RESP(c->dreq) | WIDE_SOURCE(c->dreq) | WIDE_DEST(c->dreq);
+ u32 extra = BCM2835_DMA_INT_EN;
size_t max_len = bcm2835_dma_max_frame_length(c);
size_t frames;

@@ -652,7 +668,8 @@ 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 = WAIT_RESP(c->dreq) |
+ WIDE_SOURCE(c->dreq) | WIDE_DEST(c->dreq);
u32 extra = BCM2835_DMA_INT_EN;
size_t frames;

@@ -704,7 +721,7 @@ 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 = WAIT_RESP(c->dreq) | WIDE_SOURCE(c->dreq) | WIDE_DEST(c->dreq);
u32 extra = 0;
size_t max_len = bcm2835_dma_max_frame_length(c);
size_t frames;
--
2.35.3


2024-03-13 14:09:38

by Andrea della Porta

[permalink] [raw]
Subject: [PATCH v2 04/15] dmaengine: bcm2835: Support dma flags for multi-beat burst

From: Dom Cobley <[email protected]>

Add a control bit to enable a multi-beat burst on a DMA.
This improves DMA performance and is required for HDMI audio.

Signed-off-by: Dom Cobley <[email protected]>
Signed-off-by: Andrea della Porta <[email protected]>
---
drivers/dma/bcm2835-dma.c | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
index 69a77caf78cc..d442f8728c05 100644
--- a/drivers/dma/bcm2835-dma.c
+++ b/drivers/dma/bcm2835-dma.c
@@ -137,6 +137,7 @@ struct bcm2835_desc {
#define BCM2835_DMA_S_DREQ BIT(10) /* enable SREQ for source */
#define BCM2835_DMA_S_IGNORE BIT(11) /* ignore source reads - read 0 */
#define BCM2835_DMA_BURST_LENGTH(x) (((x) & 15) << 12)
+#define BCM2835_DMA_GET_BURST_LENGTH(x) (((x) >> 12) & 15)
#define BCM2835_DMA_CS_FLAGS(x) ((x) & (BCM2835_DMA_PRIORITY(15) | \
BCM2835_DMA_PANIC_PRIORITY(15) | \
BCM2835_DMA_WAIT_FOR_WRITES | \
@@ -160,6 +161,11 @@ struct bcm2835_desc {
#define WIDE_DEST(x) (((x) & BCM2835_DMA_WIDE_DEST) ? \
BCM2835_DMA_D_WIDTH : 0)

+/* A fake bit to request that the driver requires multi-beat burst */
+#define BCM2835_DMA_BURST BIT(30)
+#define BURST_LENGTH(x) (((x) & BCM2835_DMA_BURST) ? \
+ BCM2835_DMA_BURST_LENGTH(3) : 0)
+
/* debug register bits */
#define BCM2835_DMA_DEBUG_LAST_NOT_SET_ERR BIT(0)
#define BCM2835_DMA_DEBUG_FIFO_ERR BIT(1)
@@ -637,7 +643,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_D_INC | BCM2835_DMA_S_INC |
- WAIT_RESP(c->dreq) | WIDE_SOURCE(c->dreq) | WIDE_DEST(c->dreq);
+ WAIT_RESP(c->dreq) | WIDE_SOURCE(c->dreq) |
+ WIDE_DEST(c->dreq) | BURST_LENGTH(c->dreq);
u32 extra = BCM2835_DMA_INT_EN;
size_t max_len = bcm2835_dma_max_frame_length(c);
size_t frames;
@@ -668,8 +675,8 @@ 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 = WAIT_RESP(c->dreq) |
- WIDE_SOURCE(c->dreq) | WIDE_DEST(c->dreq);
+ u32 info = WAIT_RESP(c->dreq) | WIDE_SOURCE(c->dreq) |
+ WIDE_DEST(c->dreq) | BURST_LENGTH(c->dreq);
u32 extra = BCM2835_DMA_INT_EN;
size_t frames;

@@ -721,7 +728,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 = WAIT_RESP(c->dreq) | WIDE_SOURCE(c->dreq) | WIDE_DEST(c->dreq);
+ u32 info = WAIT_RESP(c->dreq) | WIDE_SOURCE(c->dreq) |
+ WIDE_DEST(c->dreq) | BURST_LENGTH(c->dreq);
u32 extra = 0;
size_t max_len = bcm2835_dma_max_frame_length(c);
size_t frames;
--
2.35.3


2024-03-13 14:09:54

by Andrea della Porta

[permalink] [raw]
Subject: [PATCH v2 05/15] dmaengine: bcm2835: Fixes for dma_abort

From: Dom Cobley <[email protected]>

There is a problem with the current abort scheme
when dma is blocked on a DREQ which prevents halting.

This is triggered by SPI driver which aborts dma
in this state and so leads to a halt timeout.

We attempt to abort the channel, which will work
if there is no blocked DREQ.

It it times out, we can assume there is no AXI
transfer in progress and reset anyway.

The length of the timeout is observed at ~20us.

Signed-off-by: Dom Cobley <[email protected]>
Signed-off-by: Andrea della Porta <[email protected]>
---
drivers/dma/bcm2835-dma.c | 32 +++++++++++++++++++++++---------
1 file changed, 23 insertions(+), 9 deletions(-)

diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
index d442f8728c05..6b8e7461efaf 100644
--- a/drivers/dma/bcm2835-dma.c
+++ b/drivers/dma/bcm2835-dma.c
@@ -435,7 +435,7 @@ static void bcm2835_dma_fill_cb_chain_with_sg(
static void bcm2835_dma_abort(struct bcm2835_chan *c)
{
void __iomem *chan_base = c->chan_base;
- long timeout = 10000;
+ long timeout = 100;

/*
* A zero control block address means the channel is idle.
@@ -444,19 +444,33 @@ static void bcm2835_dma_abort(struct bcm2835_chan *c)
if (!readl(chan_base + BCM2835_DMA_ADDR))
return;

- /* Write 0 to the active bit - Pause the DMA */
- writel(0, chan_base + BCM2835_DMA_CS);
+ /* We need to clear the next DMA block pending */
+ writel(0, chan_base + BCM2835_DMA_NEXTCB);
+
+ /* Abort the DMA, which needs to be enabled to complete */
+ writel(readl(chan_base + BCM2835_DMA_CS) | BCM2835_DMA_ABORT | BCM2835_DMA_ACTIVE,
+ chan_base + BCM2835_DMA_CS);

- /* Wait for any current AXI transfer to complete */
- while ((readl(chan_base + BCM2835_DMA_CS) &
- BCM2835_DMA_WAITING_FOR_WRITES) && --timeout)
+ /* wait for DMA to be aborted */
+ while ((readl(chan_base + BCM2835_DMA_CS) & BCM2835_DMA_ABORT) && --timeout)
cpu_relax();

- /* Peripheral might be stuck and fail to signal AXI write responses */
- if (!timeout)
+ /* Write 0 to the active bit - Pause the DMA */
+ writel(readl(chan_base + BCM2835_DMA_CS) & ~BCM2835_DMA_ACTIVE,
+ chan_base + BCM2835_DMA_CS);
+
+ /*
+ * Peripheral might be stuck and fail to complete
+ * This is expected when dreqs are enabled but not asserted
+ * so only report error in non dreq case
+ */
+ if (!timeout && !(readl(chan_base + BCM2835_DMA_TI) &
+ (BCM2835_DMA_S_DREQ | BCM2835_DMA_D_DREQ)))
dev_err(c->vc.chan.device->dev,
- "failed to complete outstanding writes\n");
+ "failed to complete pause on dma %d (CS:%08x)\n", c->ch,
+ readl(chan_base + BCM2835_DMA_CS));

+ /* Set CS back to default state and reset the DMA */
writel(BCM2835_DMA_RESET, chan_base + BCM2835_DMA_CS);
}

--
2.35.3


2024-03-13 14:10:10

by Andrea della Porta

[permalink] [raw]
Subject: [PATCH v2 06/15] dmaengine: bcm2835: Support common dma-channel-mask

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: Andrea della Porta <[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 6b8e7461efaf..88aaf7769864 100644
--- a/drivers/dma/bcm2835-dma.c
+++ b/drivers/dma/bcm2835-dma.c
@@ -986,12 +986,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, "Please update DT blob\n");
}

/* get irqs for each channel that we support */
--
2.35.3


2024-03-13 14:10:24

by Andrea della Porta

[permalink] [raw]
Subject: [PATCH v2 07/15] dmaengine: bcm2835: move CB info generation into separate function

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: Andrea della Porta <[email protected]>
---
drivers/dma/bcm2835-dma.c | 54 +++++++++++++++++++++++----------------
1 file changed, 32 insertions(+), 22 deletions(-)

diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
index 88aaf7769864..c651aca363c2 100644
--- a/drivers/dma/bcm2835-dma.c
+++ b/drivers/dma/bcm2835-dma.c
@@ -226,6 +226,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 = WAIT_RESP(c->dreq) | WIDE_SOURCE(c->dreq) |
+ WIDE_DEST(c->dreq) | BURST_LENGTH(c->dreq);
+
+ if (direction == DMA_MEM_TO_MEM)
+ return result | BCM2835_DMA_D_INC | BCM2835_DMA_S_INC;
+
+ /* 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;
@@ -656,9 +684,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 |
- WAIT_RESP(c->dreq) | WIDE_SOURCE(c->dreq) |
- WIDE_DEST(c->dreq) | BURST_LENGTH(c->dreq);
+ u32 info = bcm2835_dma_prepare_cb_info(c, DMA_MEM_TO_MEM, false);
u32 extra = BCM2835_DMA_INT_EN;
size_t max_len = bcm2835_dma_max_frame_length(c);
size_t frames;
@@ -689,8 +715,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 = WAIT_RESP(c->dreq) | WIDE_SOURCE(c->dreq) |
- WIDE_DEST(c->dreq) | BURST_LENGTH(c->dreq);
+ u32 info = bcm2835_dma_prepare_cb_info(c, direction, false);
u32 extra = BCM2835_DMA_INT_EN;
size_t frames;

@@ -700,19 +725,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 */
@@ -742,8 +762,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 = WAIT_RESP(c->dreq) | WIDE_SOURCE(c->dreq) |
- WIDE_DEST(c->dreq) | BURST_LENGTH(c->dreq);
+ 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;
@@ -774,26 +794,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.35.3


2024-03-13 14:10:41

by Andrea della Porta

[permalink] [raw]
Subject: [PATCH v2 09/15] dmaengine: bcm2835: make address increment platform independent

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: Andrea della Porta <[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 b633c40142fe..6f896bb1a4fe 100644
--- a/drivers/dma/bcm2835-dma.c
+++ b/drivers/dma/bcm2835-dma.c
@@ -274,6 +274,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;
@@ -355,10 +373,8 @@ static inline size_t bcm2835_dma_count_frames_for_sg(struct bcm2835_chan *c,
* @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
@@ -430,9 +446,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.35.3


2024-03-13 14:11:05

by Andrea della Porta

[permalink] [raw]
Subject: [PATCH v2 10/15] dmaengine: bcm2385: drop info parameters

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: Andrea della Porta <[email protected]>
---
drivers/dma/bcm2835-dma.c | 80 +++++++++++++++++++--------------------
1 file changed, 39 insertions(+), 41 deletions(-)

diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
index 6f896bb1a4fe..03d97312a3f8 100644
--- a/drivers/dma/bcm2835-dma.c
+++ b/drivers/dma/bcm2835-dma.c
@@ -308,12 +308,11 @@ static void bcm2835_dma_desc_free(struct virt_dma_desc *vd)
bcm2835_dma_free_cb_chain(container_of(vd, struct bcm2835_desc, vd));
}

-static void bcm2835_dma_create_cb_set_length(struct bcm2835_chan *chan,
+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,
- u32 finalextrainfo)
+ size_t *total_len)
{
size_t max_len = bcm2835_dma_max_frame_length(chan);

@@ -322,7 +321,7 @@ static void bcm2835_dma_create_cb_set_length(struct bcm2835_chan *chan,

/* finished if we have no period_length */
if (!period_len)
- return;
+ return false;

/*
* period_len means: that we need to generate
@@ -336,7 +335,7 @@ static void bcm2835_dma_create_cb_set_length(struct bcm2835_chan *chan,
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 */
@@ -345,8 +344,7 @@ static void bcm2835_dma_create_cb_set_length(struct bcm2835_chan *chan,
/* 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(struct bcm2835_chan *c,
@@ -371,7 +369,6 @@ static inline size_t bcm2835_dma_count_frames_for_sg(struct bcm2835_chan *c,
* @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
@@ -379,25 +376,27 @@ static inline size_t bcm2835_dma_count_frames_for_sg(struct bcm2835_chan *c,
* @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;
@@ -411,6 +410,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.
@@ -424,7 +431,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;
@@ -432,10 +440,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;
@@ -456,7 +466,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 mismatch */
if (buf_len && d->size != buf_len)
@@ -720,9 +732,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;

@@ -734,9 +743,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;

@@ -752,8 +760,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)) {
@@ -776,10 +782,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;

@@ -795,13 +799,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;

@@ -852,10 +852,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.35.3


2024-03-13 14:11:09

by Andrea della Porta

[permalink] [raw]
Subject: [PATCH v2 11/15] dmaengine: bcm2835: pass dma_chan to generic functions

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: Andrea della Porta <[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 03d97312a3f8..88ae5d05402e 100644
--- a/drivers/dma/bcm2835-dma.c
+++ b/drivers/dma/bcm2835-dma.c
@@ -308,13 +308,14 @@ static void bcm2835_dma_desc_free(struct virt_dma_desc *vd)
bcm2835_dma_free_cb_chain(container_of(vd, struct bcm2835_desc, vd));
}

-static bool bcm2835_dma_create_cb_set_length(struct bcm2835_chan *chan,
+static bool 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);
@@ -441,7 +442,7 @@ static struct bcm2835_desc *bcm2835_dma_create_cb_chain(
if (buf_len) {
/* calculate length honoring period_length */
if (bcm2835_dma_create_cb_set_length(
- c, control_block,
+ chan, control_block,
len, period_len, &total_len)) {
/* add extrainfo bits in info */
control_block->info |= extrainfo;
@@ -508,8 +509,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 timeout = 100;

@@ -550,8 +552,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);

if (!vd) {
@@ -570,7 +573,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;

@@ -604,7 +608,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);
}
}

@@ -632,7 +636,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)
@@ -721,7 +725,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);
}
@@ -885,7 +889,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.35.3


2024-03-13 14:11:35

by Andrea della Porta

[permalink] [raw]
Subject: [PATCH v2 12/15] dmaengine: bcm2835: introduce multi platform support

This finally moves all platform specific stuff into a separate structure,
which is initialized on the OF compatible during probing. Since the DMA
control block is different on the BCM2711 platform, we introduce a common
control block to reserve the necessary space and adequate methods for
access.

Signed-off-by: Stefan Wahren <[email protected]>
Signed-off-by: Andrea della Porta <[email protected]>
---
drivers/dma/bcm2835-dma.c | 336 +++++++++++++++++++++++++++++---------
1 file changed, 260 insertions(+), 76 deletions(-)

diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
index 88ae5d05402e..b015eae29b08 100644
--- a/drivers/dma/bcm2835-dma.c
+++ b/drivers/dma/bcm2835-dma.c
@@ -48,6 +48,11 @@ struct bcm2835_dmadev {
struct dma_device ddev;
void __iomem *base;
dma_addr_t zero_page;
+ const struct bcm2835_dma_cfg *cfg;
+};
+
+struct bcm_dma_cb {
+ uint32_t rsvd[8];
};

struct bcm2835_dma_cb {
@@ -61,7 +66,7 @@ struct bcm2835_dma_cb {
};

struct bcm2835_cb_entry {
- struct bcm2835_dma_cb *cb;
+ struct bcm_dma_cb *cb;
dma_addr_t paddr;
};

@@ -82,6 +87,44 @@ struct bcm2835_chan {
bool is_lite_channel;
};

+struct bcm2835_dma_cfg {
+ dma_addr_t addr_offset;
+ u32 cs_reg;
+ u32 cb_reg;
+ u32 next_reg;
+ u32 ti_reg;
+
+ u32 wait_mask;
+ u32 reset_mask;
+ u32 int_mask;
+ u32 active_mask;
+ u32 abort_mask;
+ u32 s_dreq_mask;
+ u32 d_dreq_mask;
+
+ u32 (*cb_get_length)(void *data);
+ dma_addr_t (*cb_get_addr)(void *data, enum dma_transfer_direction);
+
+ void (*cb_init)(void *data, struct bcm2835_chan *c,
+ enum dma_transfer_direction, u32 src, u32 dst,
+ bool zero_page);
+ void (*cb_set_src)(void *data, enum dma_transfer_direction, u32 src);
+ void (*cb_set_dst)(void *data, enum dma_transfer_direction, u32 dst);
+ void (*cb_set_next)(void *data, u32 next);
+ void (*cb_set_length)(void *data, u32 length);
+ void (*cb_append_extra)(void *data,
+ struct bcm2835_chan *c,
+ enum dma_transfer_direction direction,
+ bool cyclic, bool final, unsigned long flags);
+
+ dma_addr_t (*to_cb_addr)(dma_addr_t addr);
+
+ void (*chan_plat_init)(struct bcm2835_chan *c);
+ dma_addr_t (*read_addr)(struct bcm2835_chan *c,
+ enum dma_transfer_direction);
+ u32 (*cs_flags)(struct bcm2835_chan *c);
+};
+
struct bcm2835_desc {
struct bcm2835_chan *c;
struct virt_dma_desc vd;
@@ -215,6 +258,13 @@ static inline struct bcm2835_dmadev *to_bcm2835_dma_dev(struct dma_device *d)
return container_of(d, struct bcm2835_dmadev, ddev);
}

+static inline const struct bcm2835_dma_cfg *to_bcm2835_cfg(struct dma_device *d)
+{
+ struct bcm2835_dmadev *od = container_of(d, struct bcm2835_dmadev, ddev);
+
+ return od->cfg;
+}
+
static inline struct bcm2835_chan *to_bcm2835_dma_chan(struct dma_chan *c)
{
return container_of(c, struct bcm2835_chan, vc.chan);
@@ -292,6 +342,109 @@ static inline bool need_dst_incr(enum dma_transfer_direction direction)
return false;
}

+static inline u32 bcm2835_dma_cb_get_length(void *data)
+{
+ struct bcm2835_dma_cb *cb = data;
+
+ return cb->length;
+}
+
+static inline dma_addr_t
+bcm2835_dma_cb_get_addr(void *data, enum dma_transfer_direction direction)
+{
+ struct bcm2835_dma_cb *cb = data;
+
+ if (direction == DMA_DEV_TO_MEM)
+ return cb->dst;
+
+ return cb->src;
+}
+
+static inline void
+bcm2835_dma_cb_init(void *data, struct bcm2835_chan *c,
+ enum dma_transfer_direction direction, u32 src, u32 dst,
+ bool zero_page)
+{
+ struct bcm2835_dma_cb *cb = data;
+
+ cb->info = bcm2835_dma_prepare_cb_info(c, direction, zero_page);
+ cb->src = src;
+ cb->dst = dst;
+ cb->stride = 0;
+ cb->next = 0;
+}
+
+static inline void
+bcm2835_dma_cb_set_src(void *data, enum dma_transfer_direction direction,
+ u32 src)
+{
+ struct bcm2835_dma_cb *cb = data;
+
+ cb->src = src;
+}
+
+static inline void
+bcm2835_dma_cb_set_dst(void *data, enum dma_transfer_direction direction,
+ u32 dst)
+{
+ struct bcm2835_dma_cb *cb = data;
+
+ cb->dst = dst;
+}
+
+static inline void bcm2835_dma_cb_set_next(void *data, u32 next)
+{
+ struct bcm2835_dma_cb *cb = data;
+
+ cb->next = next;
+}
+
+static inline void bcm2835_dma_cb_set_length(void *data, u32 length)
+{
+ struct bcm2835_dma_cb *cb = data;
+
+ cb->length = length;
+}
+
+static inline void
+bcm2835_dma_cb_append_extra(void *data, struct bcm2835_chan *c,
+ enum dma_transfer_direction direction,
+ bool cyclic, bool final, unsigned long flags)
+{
+ struct bcm2835_dma_cb *cb = data;
+
+ cb->info |= bcm2835_dma_prepare_cb_extra(c, direction, cyclic, final,
+ flags);
+}
+
+static inline dma_addr_t bcm2835_dma_to_cb_addr(dma_addr_t addr)
+{
+ return addr;
+}
+
+static void bcm2835_dma_chan_plat_init(struct bcm2835_chan *c)
+{
+ /* check in DEBUG register if this is a LITE channel */
+ if (readl(c->chan_base + BCM2835_DMA_DEBUG) & BCM2835_DMA_DEBUG_LITE)
+ c->is_lite_channel = true;
+}
+
+static dma_addr_t bcm2835_dma_read_addr(struct bcm2835_chan *c,
+ enum dma_transfer_direction direction)
+{
+ if (direction == DMA_MEM_TO_DEV)
+ return readl(c->chan_base + BCM2835_DMA_SOURCE_AD);
+ else if (direction == DMA_DEV_TO_MEM)
+ return readl(c->chan_base + BCM2835_DMA_DEST_AD);
+
+ return 0;
+}
+
+static u32 bcm2835_dma_cs_flags(struct bcm2835_chan *c)
+{
+ return BCM2835_DMA_CS_FLAGS(c->dreq);
+}
+
static void bcm2835_dma_free_cb_chain(struct bcm2835_desc *desc)
{
size_t i;
@@ -309,16 +462,19 @@ static void bcm2835_dma_desc_free(struct virt_dma_desc *vd)
}

static bool bcm2835_dma_create_cb_set_length(struct dma_chan *chan,
- struct bcm2835_dma_cb *control_block,
+ void *data,
size_t len,
size_t period_len,
size_t *total_len)
{
+ const struct bcm2835_dma_cfg *cfg = to_bcm2835_cfg(chan->device);
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);
+ u32 length = min_t(u32, len, max_len);
+
+ cfg->cb_set_length(data, length);

/* finished if we have no period_length */
if (!period_len)
@@ -333,14 +489,14 @@ static bool bcm2835_dma_create_cb_set_length(struct dma_chan *chan,
*/

/* have we filled in period_length yet? */
- if (*total_len + control_block->length < period_len) {
+ if (*total_len + length < period_len) {
/* update number of bytes in this period so far */
- *total_len += control_block->length;
+ *total_len += length;
return false;
}

/* calculate the length that remains to reach period_length */
- control_block->length = period_len - *total_len;
+ cfg->cb_set_length(data, period_len - *total_len);

/* reset total_length for next period */
*total_len = 0;
@@ -388,15 +544,14 @@ static struct bcm2835_desc *bcm2835_dma_create_cb_chain(
size_t buf_len, size_t period_len,
gfp_t gfp, unsigned long flags)
{
+ const struct bcm2835_dma_cfg *cfg = to_bcm2835_cfg(chan->device);
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);
+ struct bcm_dma_cb *control_block;
bool zero_page = false;

if (!frames)
@@ -432,12 +587,7 @@ static struct bcm2835_desc *bcm2835_dma_create_cb_chain(

/* fill in the control block */
control_block = cb_entry->cb;
- control_block->info = bcm2835_dma_prepare_cb_info(c, direction,
- zero_page);
- control_block->src = src;
- control_block->dst = dst;
- control_block->stride = 0;
- control_block->next = 0;
+ cfg->cb_init(control_block, c, src, dst, direction, zero_page);
/* set up length in control_block if requested */
if (buf_len) {
/* calculate length honoring period_length */
@@ -445,31 +595,33 @@ static struct bcm2835_desc *bcm2835_dma_create_cb_chain(
chan, control_block,
len, period_len, &total_len)) {
/* add extrainfo bits in info */
- control_block->info |= extrainfo;
+ bcm2835_dma_cb_append_extra(control_block, c,
+ direction, cyclic,
+ false, flags);
}

/* calculate new remaining length */
- len -= control_block->length;
+ len -= cfg->cb_get_length(control_block);
}

/* link this the last controlblock */
if (frame)
- d->cb_list[frame - 1].cb->next = cb_entry->paddr;
+ cfg->cb_set_next(d->cb_list[frame - 1].cb,
+ cb_entry->paddr);

/* update src and dst and length */
if (src && need_src_incr(direction))
- src += control_block->length;
+ src += cfg->cb_get_length(control_block);
if (dst && need_dst_incr(direction))
- dst += control_block->length;
+ dst += cfg->cb_get_length(control_block);

/* Length of total transfer */
- d->size += control_block->length;
+ d->size += cfg->cb_get_length(control_block);
}

/* the last frame requires extra flags */
- extrainfo = bcm2835_dma_prepare_cb_extra(c, direction, cyclic, true,
- flags);
- d->cb_list[d->frames - 1].cb->info |= extrainfo;
+ cfg->cb_append_extra(d->cb_list[d->frames - 1].cb, c, direction, cyclic,
+ true, flags);

/* detect a size mismatch */
if (buf_len && d->size != buf_len)
@@ -489,6 +641,7 @@ static void bcm2835_dma_fill_cb_chain_with_sg(
struct scatterlist *sgl,
unsigned int sg_len)
{
+ const struct bcm2835_dma_cfg *cfg = to_bcm2835_cfg(chan->device);
struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
size_t len, max_len;
unsigned int i;
@@ -499,18 +652,19 @@ static void bcm2835_dma_fill_cb_chain_with_sg(
for_each_sg(sgl, sgent, sg_len, i) {
for (addr = sg_dma_address(sgent), len = sg_dma_len(sgent);
len > 0;
- addr += cb->cb->length, len -= cb->cb->length, cb++) {
+ addr += cfg->cb_get_length(cb->cb), len -= cfg->cb_get_length(cb->cb), cb++) {
if (direction == DMA_DEV_TO_MEM)
- cb->cb->dst = addr;
+ cfg->cb_set_dst(cb->cb, direction, addr);
else
- cb->cb->src = addr;
- cb->cb->length = min(len, max_len);
+ cfg->cb_set_src(cb->cb, direction, addr);
+ cfg->cb_set_length(cb->cb, min(len, max_len));
}
}
}

static void bcm2835_dma_abort(struct dma_chan *chan)
{
+ const struct bcm2835_dma_cfg *cfg = to_bcm2835_cfg(chan->device);
struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
void __iomem *chan_base = c->chan_base;
long timeout = 100;
@@ -519,41 +673,42 @@ static void bcm2835_dma_abort(struct dma_chan *chan)
* A zero control block address means the channel is idle.
* (The ACTIVE flag in the CS register is not a reliable indicator.)
*/
- if (!readl(chan_base + BCM2835_DMA_ADDR))
+ if (!readl(chan_base + cfg->cb_reg))
return;

/* We need to clear the next DMA block pending */
- writel(0, chan_base + BCM2835_DMA_NEXTCB);
+ writel(0, chan_base + cfg->next_reg);

/* Abort the DMA, which needs to be enabled to complete */
- writel(readl(chan_base + BCM2835_DMA_CS) | BCM2835_DMA_ABORT | BCM2835_DMA_ACTIVE,
- chan_base + BCM2835_DMA_CS);
+ writel(readl(chan_base + cfg->cs_reg) | cfg->abort_mask | cfg->active_mask,
+ chan_base + cfg->cs_reg);

/* wait for DMA to be aborted */
- while ((readl(chan_base + BCM2835_DMA_CS) & BCM2835_DMA_ABORT) && --timeout)
+ while ((readl(chan_base + cfg->cs_reg) & cfg->abort_mask) && --timeout)
cpu_relax();

/* Write 0 to the active bit - Pause the DMA */
- writel(readl(chan_base + BCM2835_DMA_CS) & ~BCM2835_DMA_ACTIVE,
- chan_base + BCM2835_DMA_CS);
+ writel(readl(chan_base + cfg->cs_reg) & ~cfg->active_mask,
+ chan_base + cfg->cs_reg);

/*
* Peripheral might be stuck and fail to complete
* This is expected when dreqs are enabled but not asserted
* so only report error in non dreq case
*/
- if (!timeout && !(readl(chan_base + BCM2835_DMA_TI) &
- (BCM2835_DMA_S_DREQ | BCM2835_DMA_D_DREQ)))
+ if (!timeout && !(readl(chan_base + cfg->ti_reg) &
+ (cfg->s_dreq_mask | cfg->d_dreq_mask)))
dev_err(c->vc.chan.device->dev,
"failed to complete pause on dma %d (CS:%08x)\n", c->ch,
- readl(chan_base + BCM2835_DMA_CS));
+ readl(chan_base + cfg->cs_reg));

/* Set CS back to default state and reset the DMA */
- writel(BCM2835_DMA_RESET, chan_base + BCM2835_DMA_CS);
+ writel(cfg->reset_mask, chan_base + cfg->cs_reg);
}

static void bcm2835_dma_start_desc(struct dma_chan *chan)
{
+ const struct bcm2835_dma_cfg *cfg = to_bcm2835_cfg(chan->device);
struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
struct virt_dma_desc *vd = vchan_next_desc(&c->vc);

@@ -566,14 +721,15 @@ static void bcm2835_dma_start_desc(struct dma_chan *chan)

c->desc = to_bcm2835_dma_desc(&vd->tx);

- writel(c->desc->cb_list[0].paddr, c->chan_base + BCM2835_DMA_ADDR);
- writel(BCM2835_DMA_ACTIVE | BCM2835_DMA_CS_FLAGS(c->dreq),
- c->chan_base + BCM2835_DMA_CS);
+ writel(cfg->to_cb_addr(c->desc->cb_list[0].paddr), c->chan_base + cfg->cb_reg);
+ writel(cfg->active_mask | cfg->cs_flags(c),
+ c->chan_base + cfg->cs_reg);
}

static irqreturn_t bcm2835_dma_callback(int irq, void *data)
{
struct dma_chan *chan = data;
+ const struct bcm2835_dma_cfg *cfg = to_bcm2835_cfg(chan->device);
struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
struct bcm2835_desc *d;
unsigned long flags;
@@ -581,9 +737,9 @@ static irqreturn_t bcm2835_dma_callback(int irq, void *data)
/* check the shared interrupt */
if (c->irq_flags & IRQF_SHARED) {
/* check if the interrupt is enabled */
- flags = readl(c->chan_base + BCM2835_DMA_CS);
+ flags = readl(c->chan_base + cfg->cs_reg);
/* if not set then we are not the reason for the irq */
- if (!(flags & BCM2835_DMA_INT))
+ if (!(flags & cfg->int_mask))
return IRQ_NONE;
}

@@ -596,9 +752,7 @@ static irqreturn_t bcm2835_dma_callback(int irq, void *data)
* if this IRQ handler is threaded.) If the channel is finished, it
* will remain idle despite the ACTIVE flag being set.
*/
- writel(BCM2835_DMA_INT | BCM2835_DMA_ACTIVE |
- BCM2835_DMA_CS_FLAGS(c->dreq),
- c->chan_base + BCM2835_DMA_CS);
+ writel(cfg->int_mask | cfg->active_mask | cfg->cs_flags(c), c->chan_base + cfg->cs_reg);

d = c->desc;

@@ -606,7 +760,7 @@ static irqreturn_t bcm2835_dma_callback(int irq, void *data)
if (d->cyclic) {
/* call the cyclic callback */
vchan_cyclic_callback(&d->vd);
- } else if (!readl(c->chan_base + BCM2835_DMA_ADDR)) {
+ } else if (!readl(c->chan_base + cfg->cb_reg)) {
vchan_cookie_complete(&c->desc->vd);
bcm2835_dma_start_desc(chan);
}
@@ -629,7 +783,7 @@ static int bcm2835_dma_alloc_chan_resources(struct dma_chan *chan)
* (32 byte) aligned address (BCM2835 ARM Peripherals, sec. 4.2.1.1).
*/
c->cb_pool = dma_pool_create(dev_name(dev), dev,
- sizeof(struct bcm2835_dma_cb), 32, 0);
+ sizeof(struct bcm_dma_cb), 32, 0);
if (!c->cb_pool) {
dev_err(dev, "unable to allocate descriptor pool\n");
return -ENOMEM;
@@ -655,20 +809,16 @@ static size_t bcm2835_dma_desc_size(struct bcm2835_desc *d)
return d->size;
}

-static size_t bcm2835_dma_desc_size_pos(struct bcm2835_desc *d, dma_addr_t addr)
+static size_t bcm2835_dma_desc_size_pos(const struct bcm2835_dma_cfg *cfg,
+ struct bcm2835_desc *d, dma_addr_t addr)
{
unsigned int i;
size_t size;

for (size = i = 0; i < d->frames; i++) {
- struct bcm2835_dma_cb *control_block = d->cb_list[i].cb;
- size_t this_size = control_block->length;
- dma_addr_t dma;
-
- if (d->dir == DMA_DEV_TO_MEM)
- dma = control_block->dst;
- else
- dma = control_block->src;
+ struct bcm_dma_cb *control_block = d->cb_list[i].cb;
+ size_t this_size = cfg->cb_get_length(control_block);
+ dma_addr_t dma = cfg->cb_get_addr(control_block, d->dir);

if (size)
size += this_size;
@@ -683,6 +833,7 @@ static enum dma_status bcm2835_dma_tx_status(struct dma_chan *chan,
dma_cookie_t cookie,
struct dma_tx_state *txstate)
{
+ const struct bcm2835_dma_cfg *cfg = to_bcm2835_cfg(chan->device);
struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
struct virt_dma_desc *vd;
enum dma_status ret;
@@ -701,14 +852,8 @@ static enum dma_status bcm2835_dma_tx_status(struct dma_chan *chan,
struct bcm2835_desc *d = c->desc;
dma_addr_t pos;

- if (d->dir == DMA_MEM_TO_DEV)
- pos = readl(c->chan_base + BCM2835_DMA_SOURCE_AD);
- else if (d->dir == DMA_DEV_TO_MEM)
- pos = readl(c->chan_base + BCM2835_DMA_DEST_AD);
- else
- pos = 0;
-
- txstate->residue = bcm2835_dma_desc_size_pos(d, pos);
+ pos = cfg->read_addr(c, d->dir);
+ txstate->residue = bcm2835_dma_desc_size_pos(cfg, d, pos);
} else {
txstate->residue = 0;
}
@@ -761,6 +906,7 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_slave_sg(
enum dma_transfer_direction direction,
unsigned long flags, void *context)
{
+ const struct bcm2835_dma_cfg *cfg = to_bcm2835_cfg(chan->device);
struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
struct bcm2835_desc *d;
dma_addr_t src = 0, dst = 0;
@@ -775,11 +921,11 @@ 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)
return NULL;
- src = c->cfg.src_addr;
+ src = cfg->addr_offset + c->cfg.src_addr;
} else {
if (c->cfg.dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES)
return NULL;
- dst = c->cfg.dst_addr;
+ dst = cfg->addr_offset + c->cfg.dst_addr;
}

/* count frames in sg list */
@@ -803,6 +949,7 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic(
size_t period_len, enum dma_transfer_direction direction,
unsigned long flags)
{
+ const struct bcm2835_dma_cfg *cfg = to_bcm2835_cfg(chan->device);
struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
struct bcm2835_desc *d;
dma_addr_t src, dst;
@@ -836,12 +983,12 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic(
if (direction == DMA_DEV_TO_MEM) {
if (c->cfg.src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES)
return NULL;
- src = c->cfg.src_addr;
+ src = cfg->addr_offset + c->cfg.src_addr;
dst = buf_addr;
} else {
if (c->cfg.dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES)
return NULL;
- dst = c->cfg.dst_addr;
+ dst = cfg->addr_offset + c->cfg.dst_addr;
src = buf_addr;
}

@@ -862,7 +1009,8 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic(
return NULL;

/* wrap around into a loop */
- d->cb_list[d->frames - 1].cb->next = d->cb_list[0].paddr;
+ cfg->cb_set_next(d->cb_list[d->frames - 1].cb,
+ cfg->to_cb_addr(d->cb_list[0].paddr));

return vchan_tx_prep(&c->vc, &d->vd, flags);
}
@@ -923,10 +1071,7 @@ static int bcm2835_dma_chan_init(struct bcm2835_dmadev *d, int chan_id,
c->irq_number = irq;
c->irq_flags = irq_flags;

- /* check in DEBUG register if this is a LITE channel */
- if (readl(c->chan_base + BCM2835_DMA_DEBUG) &
- BCM2835_DMA_DEBUG_LITE)
- c->is_lite_channel = true;
+ d->cfg->chan_plat_init(c);

return 0;
}
@@ -945,8 +1090,40 @@ static void bcm2835_dma_free(struct bcm2835_dmadev *od)
DMA_TO_DEVICE, DMA_ATTR_SKIP_CPU_SYNC);
}

+static const struct bcm2835_dma_cfg bcm2835_data = {
+ .addr_offset = 0,
+
+ .cs_reg = BCM2835_DMA_CS,
+ .cb_reg = BCM2835_DMA_ADDR,
+ .next_reg = BCM2835_DMA_NEXTCB,
+ .ti_reg = BCM2835_DMA_TI,
+
+ .wait_mask = BCM2835_DMA_WAITING_FOR_WRITES,
+ .reset_mask = BCM2835_DMA_RESET,
+ .int_mask = BCM2835_DMA_INT,
+ .active_mask = BCM2835_DMA_ACTIVE,
+ .abort_mask = BCM2835_DMA_ABORT,
+ .s_dreq_mask = BCM2835_DMA_S_DREQ,
+ .d_dreq_mask = BCM2835_DMA_D_DREQ,
+
+ .cb_get_length = bcm2835_dma_cb_get_length,
+ .cb_get_addr = bcm2835_dma_cb_get_addr,
+ .cb_init = bcm2835_dma_cb_init,
+ .cb_set_src = bcm2835_dma_cb_set_src,
+ .cb_set_dst = bcm2835_dma_cb_set_dst,
+ .cb_set_next = bcm2835_dma_cb_set_next,
+ .cb_set_length = bcm2835_dma_cb_set_length,
+ .cb_append_extra = bcm2835_dma_cb_append_extra,
+
+ .to_cb_addr = bcm2835_dma_to_cb_addr,
+
+ .chan_plat_init = bcm2835_dma_chan_plat_init,
+ .read_addr = bcm2835_dma_read_addr,
+ .cs_flags = bcm2835_dma_cs_flags,
+};
+
static const struct of_device_id bcm2835_dma_of_match[] = {
- { .compatible = "brcm,bcm2835-dma", },
+ { .compatible = "brcm,bcm2835-dma", .data = &bcm2835_data },
{},
};
MODULE_DEVICE_TABLE(of, bcm2835_dma_of_match);
@@ -978,6 +1155,12 @@ static int bcm2835_dma_probe(struct platform_device *pdev)
u32 chans_available;
char chan_name[BCM2835_DMA_CHAN_NAME_SIZE];

+ const void *cfg_data = device_get_match_data(&pdev->dev);
+ if (!cfg_data) {
+ dev_err(&pdev->dev, "Failed to match compatible string\n");
+ return -EINVAL;
+ }
+
if (!pdev->dev.dma_mask)
pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;

@@ -998,6 +1181,7 @@ static int bcm2835_dma_probe(struct platform_device *pdev)
return PTR_ERR(base);

od->base = base;
+ od->cfg = cfg_data;

dma_cap_set(DMA_SLAVE, od->ddev.cap_mask);
dma_cap_set(DMA_PRIVATE, od->ddev.cap_mask);
--
2.35.3


2024-03-13 14:11:44

by Andrea della Porta

[permalink] [raw]
Subject: [PATCH v2 08/15] dmaengine: bcm2835: move CB final extra info generation into function

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: Andrea della Porta <[email protected]>
---
drivers/dma/bcm2835-dma.c | 31 +++++++++++++++++++++++++------
1 file changed, 25 insertions(+), 6 deletions(-)

diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
index c651aca363c2..b633c40142fe 100644
--- a/drivers/dma/bcm2835-dma.c
+++ b/drivers/dma/bcm2835-dma.c
@@ -254,6 +254,26 @@ 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;
+ }
+
+ return result;
+}
+
static void bcm2835_dma_free_cb_chain(struct bcm2835_desc *desc)
{
size_t i;
@@ -685,7 +705,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;
+ 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;

@@ -716,7 +737,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)) {
@@ -764,7 +785,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;

@@ -780,9 +801,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.35.3


2024-03-13 14:12:07

by Andrea della Porta

[permalink] [raw]
Subject: [PATCH v2 13/15] dt-bindings: dma: Added bcm2711-dma

Add BCM2711 dma engine compatible.

Signed-off-by: Andrea della Porta <[email protected]>
---
Documentation/devicetree/bindings/dma/brcm,bcm2835-dma.yaml | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/dma/brcm,bcm2835-dma.yaml b/Documentation/devicetree/bindings/dma/brcm,bcm2835-dma.yaml
index c9b9a5490826..4271a6fedf54 100644
--- a/Documentation/devicetree/bindings/dma/brcm,bcm2835-dma.yaml
+++ b/Documentation/devicetree/bindings/dma/brcm,bcm2835-dma.yaml
@@ -20,7 +20,9 @@ allOf:

properties:
compatible:
- const: brcm,bcm2835-dma
+ enum:
+ - brcm,bcm2835-dma
+ - brcm,bcm2711-dma

reg:
maxItems: 1
--
2.35.3


2024-03-13 14:12:14

by Andrea della Porta

[permalink] [raw]
Subject: [PATCH v2 15/15] ARM: dts: bcm2711: add bcm2711-dma node

BCM2711 has 4 DMA channels with a 40-bit address range, allowing them
to access the full 4GB of memory on a Pi 4. Adding a new node to make
use of the DMA channels capable of 40 bit addressing.

Signed-off-by: Stefan Wahren <[email protected]>
Signed-off-by: Andrea della Porta <[email protected]>
---
arch/arm/boot/dts/broadcom/bcm2711.dtsi | 16 ++++++++++++++++
1 file changed, 16 insertions(+)

diff --git a/arch/arm/boot/dts/broadcom/bcm2711.dtsi b/arch/arm/boot/dts/broadcom/bcm2711.dtsi
index 22c7f1561344..d98e3cf0c569 100644
--- a/arch/arm/boot/dts/broadcom/bcm2711.dtsi
+++ b/arch/arm/boot/dts/broadcom/bcm2711.dtsi
@@ -552,6 +552,22 @@ scb {
ranges = <0x0 0x7c000000 0x0 0xfc000000 0x03800000>,
<0x6 0x00000000 0x6 0x00000000 0x40000000>;

+ dma40: dma-controller@7e007b00 {
+ compatible = "brcm,bcm2711-dma";
+ reg = <0x0 0x7e007b00 0x400>;
+ interrupts = <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>, /* dma4 11 */
+ <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>, /* dma4 12 */
+ <GIC_SPI 91 IRQ_TYPE_LEVEL_HIGH>, /* dma4 13 */
+ <GIC_SPI 92 IRQ_TYPE_LEVEL_HIGH>; /* dma4 14 */
+ interrupt-names = "dma11",
+ "dma12",
+ "dma13",
+ "dma14";
+ #dma-cells = <1>;
+ /* The VPU firmware uses DMA channel 11 for VCHIQ */
+ brcm,dma-channel-mask = <0x7000>;
+ };
+
pcie0: pcie@7d500000 {
compatible = "brcm,bcm2711-pcie";
reg = <0x0 0x7d500000 0x9310>;
--
2.35.3


2024-03-13 14:12:20

by Andrea della Porta

[permalink] [raw]
Subject: [PATCH v2 14/15] dmaengine: bcm2835: Add BCM2711 40-bit DMA support

BCM2711 has 4 DMA channels with a 40-bit address range, allowing them
to access the full 4GB of memory on a Pi 4. Assume every channel is capable
of 40-bit address range.

Originally-by: Phil Elwell <[email protected]>
Originally-by: Maxime Ripard <[email protected]>
Originally-by: Stefan Wahren <[email protected]>
Signed-off-by: Andrea della Porta <[email protected]>
---
drivers/dma/bcm2835-dma.c | 553 ++++++++++++++++++++++++++++++++------
1 file changed, 466 insertions(+), 87 deletions(-)

diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
index b015eae29b08..5c8dde8b07cd 100644
--- a/drivers/dma/bcm2835-dma.c
+++ b/drivers/dma/bcm2835-dma.c
@@ -36,13 +36,15 @@

#define BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED 14
#define BCM2835_DMA_CHAN_NAME_SIZE 8
+#define BCM2711_DMA40_PHYS_ADDR 0x400000000ULL
+#define BCM2835_DMA_ABORT_TIMEOUT 100

/**
* struct bcm2835_dmadev - BCM2835 DMA controller
* @ddev: DMA device
* @base: base address of register map
* @zero_page: bus address of zero page (to detect transactions copying from
- * zero page and avoid accessing memory if so)
+ * zero page and avoid accessing memory if so)
*/
struct bcm2835_dmadev {
struct dma_device ddev;
@@ -52,7 +54,7 @@ struct bcm2835_dmadev {
};

struct bcm_dma_cb {
- uint32_t rsvd[8];
+ u32 rsvd[8];
};

struct bcm2835_dma_cb {
@@ -65,6 +67,17 @@ struct bcm2835_dma_cb {
u32 pad[2];
};

+struct bcm2711_dma40_scb {
+ u32 ti;
+ u32 src;
+ u32 srci;
+ u32 dst;
+ u32 dsti;
+ u32 len;
+ u32 next_cb;
+ u32 rsvd;
+};
+
struct bcm2835_cb_entry {
struct bcm_dma_cb *cb;
dma_addr_t paddr;
@@ -102,14 +115,16 @@ struct bcm2835_dma_cfg {
u32 s_dreq_mask;
u32 d_dreq_mask;

+ u64 dma_mask;
+
u32 (*cb_get_length)(void *data);
dma_addr_t (*cb_get_addr)(void *data, enum dma_transfer_direction);

void (*cb_init)(void *data, struct bcm2835_chan *c,
- enum dma_transfer_direction, u32 src, u32 dst,
+ enum dma_transfer_direction, dma_addr_t src, dma_addr_t dst,
bool zero_page);
- void (*cb_set_src)(void *data, enum dma_transfer_direction, u32 src);
- void (*cb_set_dst)(void *data, enum dma_transfer_direction, u32 dst);
+ void (*cb_set_src)(void *data, enum dma_transfer_direction, dma_addr_t src);
+ void (*cb_set_dst)(void *data, enum dma_transfer_direction, dma_addr_t dst);
void (*cb_set_next)(void *data, u32 next);
void (*cb_set_length)(void *data, u32 length);
void (*cb_append_extra)(void *data,
@@ -123,6 +138,7 @@ struct bcm2835_dma_cfg {
dma_addr_t (*read_addr)(struct bcm2835_chan *c,
enum dma_transfer_direction);
u32 (*cs_flags)(struct bcm2835_chan *c);
+ void (*dma_abort)(struct bcm2835_chan *c, const struct bcm2835_dma_cfg *cfg);
};

struct bcm2835_desc {
@@ -233,13 +249,110 @@ struct bcm2835_desc {
#define BCM2835_DMA_DATA_TYPE_S128 16

/* Valid only for channels 0 - 14, 15 has its own base address */
-#define BCM2835_DMA_CHAN(n) ((n) << 8) /* Base address */
+#define BCM2835_DMA_CHAN_SIZE 0x100
+#define BCM2835_DMA_CHAN(n) ((n) * BCM2835_DMA_CHAN_SIZE) /* Base address */
#define BCM2835_DMA_CHANIO(base, n) ((base) + BCM2835_DMA_CHAN(n))

+/* 40-bit DMA support */
+#define BCM2711_DMA40_CS 0x00
+#define BCM2711_DMA40_CB 0x04
+#define BCM2711_DMA40_DEBUG 0x0c
+#define BCM2711_DMA40_TI 0x10
+#define BCM2711_DMA40_SRC 0x14
+#define BCM2711_DMA40_SRCI 0x18
+#define BCM2711_DMA40_DEST 0x1c
+#define BCM2711_DMA40_DESTI 0x20
+#define BCM2711_DMA40_LEN 0x24
+#define BCM2711_DMA40_NEXT_CB 0x28
+#define BCM2711_DMA40_DEBUG2 0x2c
+
+#define BCM2711_DMA40_ACTIVE BIT(0)
+#define BCM2711_DMA40_END BIT(1)
+#define BCM2711_DMA40_INT BIT(2)
+#define BCM2711_DMA40_DREQ BIT(3) /* DREQ state */
+#define BCM2711_DMA40_RD_PAUSED BIT(4) /* Reading is paused */
+#define BCM2711_DMA40_WR_PAUSED BIT(5) /* Writing is paused */
+#define BCM2711_DMA40_DREQ_PAUSED BIT(6) /* Is paused by DREQ flow control */
+#define BCM2711_DMA40_WAITING_FOR_WRITES BIT(7) /* Waiting for last write */
+#define BCM2711_DMA40_ERR BIT(10)
+#define BCM2711_DMA40_QOS(x) FIELD_PREP(GENMASK(19, 16), x)
+#define BCM2711_DMA40_PANIC_QOS(x) FIELD_PREP(GENMASK(23, 20), x)
+#define BCM2711_DMA40_WAIT_FOR_WRITES BIT(28)
+#define BCM2711_DMA40_DISDEBUG BIT(29)
+#define BCM2711_DMA40_ABORT BIT(30)
+#define BCM2711_DMA40_HALT BIT(31)
+// we always want to run in supervisor mode
+#define BCM2711_DMA40_PROT (BIT(8) | BIT(9))
+#define BCM2711_DMA40_TRANSACTIONS BIT(25)
+#define BCM2711_DMA40_CS_FLAGS(x) ((x) & (BCM2711_DMA40_QOS(15) | \
+ BCM2711_DMA40_PANIC_QOS(15) | \
+ BCM2711_DMA40_WAIT_FOR_WRITES | \
+ BCM2711_DMA40_DISDEBUG))
+
+/* Transfer information bits */
+#define BCM2711_DMA40_INTEN BIT(0)
+#define BCM2711_DMA40_TDMODE BIT(1) /* 2D-Mode */
+#define BCM2711_DMA40_WAIT_RESP BIT(2) /* wait for AXI write to be acked */
+#define BCM2711_DMA40_WAIT_RD_RESP BIT(3) /* wait for AXI read to complete */
+#define BCM2711_DMA40_PER_MAP(x) (((x) & 31) << 9) /* REQ source */
+#define BCM2711_DMA40_S_DREQ BIT(14) /* enable SREQ for source */
+#define BCM2711_DMA40_D_DREQ BIT(15) /* enable DREQ for destination */
+#define BCM2711_DMA40_S_WAIT(x) FIELD_PREP(GENMASK(23, 16), x) /* add DMA read-wait cycles */
+#define BCM2711_DMA40_D_WAIT(x) FIELD_PREP(GENMASK(31, 24), x) /* add DMA write-wait cycles */
+
+#define BCM2711_DMA40_INC BIT(12)
+#define BCM2711_DMA40_IGNORE BIT(15)
+
/* the max dma length for different channels */
#define MAX_DMA_LEN SZ_1G
#define MAX_LITE_DMA_LEN (SZ_64K - 4)

+/* debug register bits */
+#define BCM2711_DMA40_DEBUG_WRITE_ERR BIT(0)
+#define BCM2711_DMA40_DEBUG_FIFO_ERR BIT(1)
+#define BCM2711_DMA40_DEBUG_READ_ERR BIT(2)
+#define BCM2711_DMA40_DEBUG_READ_CB_ERR BIT(3)
+#define BCM2711_DMA40_DEBUG_IN_ON_ERR BIT(8)
+#define BCM2711_DMA40_DEBUG_ABORT_ON_ERR BIT(9)
+#define BCM2711_DMA40_DEBUG_HALT_ON_ERR BIT(10)
+#define BCM2711_DMA40_DEBUG_DISABLE_CLK_GATE BIT(11)
+#define BCM2711_DMA40_DEBUG_RSTATE_SHIFT 14
+#define BCM2711_DMA40_DEBUG_RSTATE_BITS 4
+#define BCM2711_DMA40_DEBUG_WSTATE_SHIFT 18
+#define BCM2711_DMA40_DEBUG_WSTATE_BITS 4
+#define BCM2711_DMA40_DEBUG_RESET BIT(23)
+#define BCM2711_DMA40_DEBUG_ID_SHIFT 24
+#define BCM2711_DMA40_DEBUG_ID_BITS 4
+#define BCM2711_DMA40_DEBUG_VERSION_SHIFT 28
+#define BCM2711_DMA40_DEBUG_VERSION_BITS 4
+
+/* Valid only for channels 0 - 3 (11 - 14) */
+#define BCM2711_DMA40_CHAN(n) (((n) + 11) << 8) /* Base address */
+#define BCM2711_DMA40_CHANIO(base, n) ((base) + BCM2711_DMA_CHAN(n))
+
+/* the max dma length for different channels */
+#define MAX_DMA40_LEN SZ_1G
+
+#define BCM2711_DMA40_BURST_LEN(x) (((x) & 15) << 8)
+#define BCM2711_DMA40_INC BIT(12)
+#define BCM2711_DMA40_SIZE_32 (0 << 13)
+#define BCM2711_DMA40_SIZE_64 (1 << 13)
+#define BCM2711_DMA40_SIZE_128 (2 << 13)
+#define BCM2711_DMA40_SIZE_256 (3 << 13)
+#define BCM2711_DMA40_IGNORE BIT(15)
+#define BCM2711_DMA40_STRIDE(x) ((x) << 16) /* For 2D mode */
+
+#define BCM2711_DMA40_MEMCPY_FLAGS \
+ (BCM2711_DMA40_QOS(0) | \
+ BCM2711_DMA40_PANIC_QOS(0) | \
+ BCM2711_DMA40_WAIT_FOR_WRITES | \
+ BCM2711_DMA40_DISDEBUG)
+
+#define BCM2711_DMA40_MEMCPY_XFER_INFO \
+ (BCM2711_DMA40_SIZE_128 | \
+ BCM2711_DMA40_INC | \
+ BCM2711_DMA40_BURST_LEN(16))
+
static inline size_t bcm2835_dma_max_frame_length(struct bcm2835_chan *c)
{
/* lite and normal channels have different max frame length */
@@ -270,8 +383,7 @@ static inline struct bcm2835_chan *to_bcm2835_dma_chan(struct dma_chan *c)
return container_of(c, struct bcm2835_chan, vc.chan);
}

-static inline struct bcm2835_desc *to_bcm2835_dma_desc(
- struct dma_async_tx_descriptor *t)
+static inline struct bcm2835_desc *to_bcm2835_dma_desc(struct dma_async_tx_descriptor *t)
{
return container_of(t, struct bcm2835_desc, vd.tx);
}
@@ -296,9 +408,8 @@ static u32 bcm2835_dma_prepare_cb_info(struct bcm2835_chan *c,
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) {
+ if (zero_page && !c->is_lite_channel)
result |= BCM2835_DMA_S_IGNORE;
- }
}

return result;
@@ -324,6 +435,66 @@ static u32 bcm2835_dma_prepare_cb_extra(struct bcm2835_chan *c,
return result;
}

+static inline uint32_t to_bcm2711_ti(uint32_t info)
+{
+ return ((info & BCM2835_DMA_INT_EN) ? BCM2711_DMA40_INTEN : 0) |
+ ((info & BCM2835_DMA_WAIT_RESP) ? BCM2711_DMA40_WAIT_RESP : 0) |
+ ((info & BCM2835_DMA_S_DREQ) ?
+ (BCM2711_DMA40_S_DREQ | BCM2711_DMA40_WAIT_RD_RESP) : 0) |
+ ((info & BCM2835_DMA_D_DREQ) ? BCM2711_DMA40_D_DREQ : 0) |
+ BCM2711_DMA40_PER_MAP((info >> 16) & 0x1f);
+}
+
+static inline uint32_t to_bcm2711_srci(uint32_t info)
+{
+ return ((info & BCM2835_DMA_S_INC) ? BCM2711_DMA40_INC : 0) |
+ ((info & BCM2835_DMA_S_WIDTH) ? BCM2711_DMA40_SIZE_128 : 0) |
+ BCM2711_DMA40_BURST_LEN(BCM2835_DMA_GET_BURST_LENGTH(info));
+}
+
+static inline uint32_t to_bcm2711_dsti(uint32_t info)
+{
+ return ((info & BCM2835_DMA_D_INC) ? BCM2711_DMA40_INC : 0) |
+ ((info & BCM2835_DMA_D_WIDTH) ? BCM2711_DMA40_SIZE_128 : 0) |
+ BCM2711_DMA40_BURST_LEN(BCM2835_DMA_GET_BURST_LENGTH(info));
+}
+
+static u32 bcm2711_dma_prepare_cb_info(struct bcm2835_chan *c,
+ enum dma_transfer_direction direction,
+ bool zero_page)
+{
+ u32 result = 0;
+ u32 info;
+
+ info = bcm2835_dma_prepare_cb_info(c, direction, zero_page);
+ result = to_bcm2711_ti(info);
+
+ return result;
+}
+
+static u32 bcm2711_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 |= BCM2711_DMA40_INTEN;
+ } else {
+ if (!final)
+ return 0;
+
+ result |= BCM2711_DMA40_INTEN;
+
+ if (direction == DMA_MEM_TO_MEM)
+ result |= BCM2711_DMA40_WAIT_RESP;
+ }
+
+ return result;
+}
+
static inline bool need_src_incr(enum dma_transfer_direction direction)
{
return direction != DMA_DEV_TO_MEM;
@@ -342,6 +513,12 @@ static inline bool need_dst_incr(enum dma_transfer_direction direction)
return false;
}

+static inline uint32_t to_bcm2711_cbaddr(dma_addr_t addr)
+{
+ WARN_ON_ONCE(addr & 0x1f);
+ return (addr >> 5);
+}
+
static inline u32 bcm2835_dma_cb_get_length(void *data)
{
struct bcm2835_dma_cb *cb = data;
@@ -362,7 +539,7 @@ bcm2835_dma_cb_get_addr(void *data, enum dma_transfer_direction direction)

static inline void
bcm2835_dma_cb_init(void *data, struct bcm2835_chan *c,
- enum dma_transfer_direction direction, u32 src, u32 dst,
+ enum dma_transfer_direction direction, dma_addr_t src, dma_addr_t dst,
bool zero_page)
{
struct bcm2835_dma_cb *cb = data;
@@ -376,7 +553,7 @@ bcm2835_dma_cb_init(void *data, struct bcm2835_chan *c,

static inline void
bcm2835_dma_cb_set_src(void *data, enum dma_transfer_direction direction,
- u32 src)
+ dma_addr_t src)
{
struct bcm2835_dma_cb *cb = data;

@@ -385,7 +562,7 @@ bcm2835_dma_cb_set_src(void *data, enum dma_transfer_direction direction,

static inline void
bcm2835_dma_cb_set_dst(void *data, enum dma_transfer_direction direction,
- u32 dst)
+ dma_addr_t dst)
{
struct bcm2835_dma_cb *cb = data;

@@ -445,6 +622,124 @@ static u32 bcm2835_dma_cs_flags(struct bcm2835_chan *c)
return BCM2835_DMA_CS_FLAGS(c->dreq);
}

+static inline u32 bcm2711_dma_cb_get_length(void *data)
+{
+ struct bcm2711_dma40_scb *scb = data;
+
+ return scb->len;
+}
+
+static inline dma_addr_t
+bcm2711_dma_cb_get_addr(void *data, enum dma_transfer_direction direction)
+{
+ struct bcm2711_dma40_scb *scb = data;
+
+ if (direction == DMA_DEV_TO_MEM)
+ return (dma_addr_t)scb->dst + ((dma_addr_t)(scb->dsti & 0xff) << 32);
+
+ return (dma_addr_t)scb->src + ((dma_addr_t)(scb->srci & 0xff) << 32);
+}
+
+static inline void
+bcm2711_dma_cb_init(void *data, struct bcm2835_chan *c,
+ enum dma_transfer_direction direction, dma_addr_t src, dma_addr_t dst,
+ bool zero_page)
+{
+ struct bcm2711_dma40_scb *scb = data;
+ u32 info = bcm2835_dma_prepare_cb_info(c, direction, zero_page);
+
+ scb->ti = bcm2711_dma_prepare_cb_info(c, direction, zero_page);
+
+ scb->src = lower_32_bits(src);
+ scb->srci = upper_32_bits(src);
+ scb->srci |= to_bcm2711_srci(info);
+
+ scb->dst = lower_32_bits(dst);
+ scb->dsti = upper_32_bits(dst);
+ scb->dsti |= to_bcm2711_dsti(info);
+
+ scb->next_cb = 0;
+}
+
+static inline void
+bcm2711_dma_cb_set_src(void *data, enum dma_transfer_direction direction,
+ dma_addr_t src)
+{
+ struct bcm2711_dma40_scb *scb = data;
+
+ scb->src = lower_32_bits(src);
+ scb->srci = upper_32_bits(src);
+
+ if (need_src_incr(direction))
+ scb->srci |= BCM2711_DMA40_INC;
+}
+
+static inline void
+bcm2711_dma_cb_set_dst(void *data, enum dma_transfer_direction direction,
+ dma_addr_t dst)
+{
+ struct bcm2711_dma40_scb *scb = data;
+
+ scb->dst = lower_32_bits(dst);
+ scb->dsti = upper_32_bits(dst);
+
+ if (need_dst_incr(direction))
+ scb->dsti |= BCM2711_DMA40_INC;
+}
+
+static inline void bcm2711_dma_cb_set_next(void *data, u32 next)
+{
+ struct bcm2711_dma40_scb *scb = data;
+
+ scb->next_cb = next;
+}
+
+static inline void bcm2711_dma_cb_set_length(void *data, u32 length)
+{
+ struct bcm2711_dma40_scb *scb = data;
+
+ scb->len = length;
+}
+
+static inline void
+bcm2711_dma_cb_append_extra(void *data, struct bcm2835_chan *c,
+ enum dma_transfer_direction direction,
+ bool cyclic, bool final, unsigned long flags)
+{
+ struct bcm2711_dma40_scb *scb = data;
+
+ scb->ti |= bcm2711_dma_prepare_cb_extra(c, direction, cyclic, final,
+ flags);
+}
+
+static inline dma_addr_t bcm2711_dma_to_cb_addr(dma_addr_t addr)
+{
+ WARN_ON_ONCE(addr & 0x1f);
+ return (addr >> 5);
+}
+
+static void bcm2711_dma_chan_plat_init(struct bcm2835_chan *c)
+{
+}
+
+static dma_addr_t bcm2711_dma_read_addr(struct bcm2835_chan *c,
+ enum dma_transfer_direction direction)
+{
+ if (direction == DMA_MEM_TO_DEV)
+ return (dma_addr_t)readl(c->chan_base + BCM2711_DMA40_SRC) +
+ ((dma_addr_t)(readl(c->chan_base + BCM2711_DMA40_SRCI) & 0xff) << 32);
+ else if (direction == DMA_DEV_TO_MEM)
+ return (dma_addr_t)readl(c->chan_base + BCM2711_DMA40_DEST) +
+ ((dma_addr_t)(readl(c->chan_base + BCM2711_DMA40_DESTI) & 0xff) << 32);
+
+ return 0;
+}
+
+static u32 bcm2711_dma_cs_flags(struct bcm2835_chan *c)
+{
+ return BCM2711_DMA40_CS_FLAGS(c->dreq) | BCM2711_DMA40_PROT;
+}
+
static void bcm2835_dma_free_cb_chain(struct bcm2835_desc *desc)
{
size_t i;
@@ -470,36 +765,34 @@ static bool bcm2835_dma_create_cb_set_length(struct dma_chan *chan,
const struct bcm2835_dma_cfg *cfg = to_bcm2835_cfg(chan->device);
struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
size_t max_len = bcm2835_dma_max_frame_length(c);
+ u32 cb_len;

/* set the length taking lite-channel limitations into account */
- u32 length = min_t(u32, len, max_len);
+ cb_len = min_t(u32, len, max_len);

- cfg->cb_set_length(data, length);
+ if (period_len) {
+ /*
+ * period_len means: that we need to generate
+ * transfers that are terminating at every
+ * multiple of period_len - this is typically
+ * used to set the interrupt flag in info
+ * which is required during cyclic transfers
+ */

- /* finished if we have no period_length */
- if (!period_len)
- return false;
+ /* have we filled in period_length yet? */
+ if (*total_len + cb_len < period_len) {
+ /* update number of bytes in this period so far */
+ *total_len += cb_len;
+ } else {
+ /* calculate the length that remains to reach period_len */
+ cb_len = period_len - *total_len;

- /*
- * period_len means: that we need to generate
- * transfers that are terminating at every
- * multiple of period_len - this is typically
- * used to set the interrupt flag in info
- * which is required during cyclic transfers
- */
-
- /* have we filled in period_length yet? */
- if (*total_len + length < period_len) {
- /* update number of bytes in this period so far */
- *total_len += length;
- return false;
+ /* reset total_length for next period */
+ *total_len = 0;
+ }
}

- /* calculate the length that remains to reach period_length */
- cfg->cb_set_length(data, period_len - *total_len);
-
- /* reset total_length for next period */
- *total_len = 0;
+ cfg->cb_set_length(data, cb_len);

return true;
}
@@ -523,7 +816,7 @@ static inline size_t bcm2835_dma_count_frames_for_sg(struct bcm2835_chan *c,
/**
* bcm2835_dma_create_cb_chain - create a control block and fills data in
*
- * @chan: the @dma_chan for which we run this
+ * @c: the @bcm2835_chan for which we run this
* @direction: the direction in which we transfer
* @cyclic: it is a cyclic transfer
* @frames: number of controlblocks to allocate
@@ -587,17 +880,19 @@ static struct bcm2835_desc *bcm2835_dma_create_cb_chain(

/* fill in the control block */
control_block = cb_entry->cb;
- cfg->cb_init(control_block, c, src, dst, direction, zero_page);
+
+ cfg->cb_init(control_block, c, direction, src, dst, zero_page);
+
/* set up length in control_block if requested */
if (buf_len) {
/* calculate length honoring period_length */
- if (bcm2835_dma_create_cb_set_length(
- chan, control_block,
- len, period_len, &total_len)) {
- /* add extrainfo bits in info */
- bcm2835_dma_cb_append_extra(control_block, c,
- direction, cyclic,
- false, flags);
+ if (bcm2835_dma_create_cb_set_length(chan, control_block,
+ len, period_len,
+ &total_len)) {
+ /* add extrainfo bits in info */
+ bcm2835_dma_cb_append_extra(control_block, c,
+ direction, cyclic,
+ false, flags);
}

/* calculate new remaining length */
@@ -607,11 +902,12 @@ static struct bcm2835_desc *bcm2835_dma_create_cb_chain(
/* link this the last controlblock */
if (frame)
cfg->cb_set_next(d->cb_list[frame - 1].cb,
- cb_entry->paddr);
+ cfg->to_cb_addr(cb_entry->paddr));

/* update src and dst and length */
if (src && need_src_incr(direction))
src += cfg->cb_get_length(control_block);
+
if (dst && need_dst_incr(direction))
dst += cfg->cb_get_length(control_block);

@@ -621,7 +917,7 @@ static struct bcm2835_desc *bcm2835_dma_create_cb_chain(

/* the last frame requires extra flags */
cfg->cb_append_extra(d->cb_list[d->frames - 1].cb, c, direction, cyclic,
- true, flags);
+ true, flags);

/* detect a size mismatch */
if (buf_len && d->size != buf_len)
@@ -650,7 +946,8 @@ static void bcm2835_dma_fill_cb_chain_with_sg(

max_len = bcm2835_dma_max_frame_length(c);
for_each_sg(sgl, sgent, sg_len, i) {
- for (addr = sg_dma_address(sgent), len = sg_dma_len(sgent);
+ for (addr = sg_dma_address(sgent),
+ len = sg_dma_len(sgent);
len > 0;
addr += cfg->cb_get_length(cb->cb), len -= cfg->cb_get_length(cb->cb), cb++) {
if (direction == DMA_DEV_TO_MEM)
@@ -662,48 +959,81 @@ static void bcm2835_dma_fill_cb_chain_with_sg(
}
}

-static void bcm2835_dma_abort(struct dma_chan *chan)
+static void bcm2835_abort(struct bcm2835_chan *c, const struct bcm2835_dma_cfg *cfg)
{
- const struct bcm2835_dma_cfg *cfg = to_bcm2835_cfg(chan->device);
- struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
- void __iomem *chan_base = c->chan_base;
- long timeout = 100;
+ long timeout = BCM2835_DMA_ABORT_TIMEOUT;

- /*
- * A zero control block address means the channel is idle.
- * (The ACTIVE flag in the CS register is not a reliable indicator.)
- */
- if (!readl(chan_base + cfg->cb_reg))
- return;
+ /* Pause the current DMA */
+ writel(readl(c->chan_base + cfg->cs_reg) & ~cfg->active_mask,
+ c->chan_base + cfg->cs_reg);
+
+ /* wait for outstanding transactions to complete */
+ while ((readl(c->chan_base + cfg->cs_reg) & BCM2711_DMA40_TRANSACTIONS) &&
+ --timeout)
+ cpu_relax();
+
+ /* Peripheral might be stuck and fail to complete */
+ if (!timeout)
+ dev_err(c->vc.chan.device->dev,
+ "failed to complete pause on dma %d (CS:%08x)\n", c->ch,
+ readl(c->chan_base + cfg->cs_reg));
+
+ /* Set CS back to default state */
+ writel(BCM2711_DMA40_PROT, c->chan_base + cfg->cs_reg);
+
+ /* Reset the DMA */
+ writel(readl(c->chan_base + BCM2711_DMA40_DEBUG) | BCM2711_DMA40_DEBUG_RESET,
+ c->chan_base + BCM2711_DMA40_DEBUG);
+}
+
+static void bcm2711_abort(struct bcm2835_chan *c, const struct bcm2835_dma_cfg *cfg)
+{
+ long timeout = BCM2835_DMA_ABORT_TIMEOUT;

/* We need to clear the next DMA block pending */
- writel(0, chan_base + cfg->next_reg);
+ writel(0, c->chan_base + cfg->next_reg);

/* Abort the DMA, which needs to be enabled to complete */
- writel(readl(chan_base + cfg->cs_reg) | cfg->abort_mask | cfg->active_mask,
- chan_base + cfg->cs_reg);
+ writel(readl(c->chan_base + cfg->cs_reg) | cfg->abort_mask | cfg->active_mask,
+ c->chan_base + cfg->cs_reg);

/* wait for DMA to be aborted */
- while ((readl(chan_base + cfg->cs_reg) & cfg->abort_mask) && --timeout)
+ while ((readl(c->chan_base + cfg->cs_reg) & cfg->abort_mask) && --timeout)
cpu_relax();

/* Write 0 to the active bit - Pause the DMA */
- writel(readl(chan_base + cfg->cs_reg) & ~cfg->active_mask,
- chan_base + cfg->cs_reg);
+ writel(readl(c->chan_base + cfg->cs_reg) & ~cfg->active_mask,
+ c->chan_base + cfg->cs_reg);

/*
* Peripheral might be stuck and fail to complete
* This is expected when dreqs are enabled but not asserted
* so only report error in non dreq case
*/
- if (!timeout && !(readl(chan_base + cfg->ti_reg) &
+ if (!timeout && !(readl(c->chan_base + cfg->ti_reg) &
(cfg->s_dreq_mask | cfg->d_dreq_mask)))
dev_err(c->vc.chan.device->dev,
"failed to complete pause on dma %d (CS:%08x)\n", c->ch,
- readl(chan_base + cfg->cs_reg));
+ readl(c->chan_base + cfg->cs_reg));

/* Set CS back to default state and reset the DMA */
- writel(cfg->reset_mask, chan_base + cfg->cs_reg);
+ writel(cfg->reset_mask, c->chan_base + cfg->cs_reg);
+}
+
+static void bcm2835_dma_abort(struct dma_chan *chan)
+{
+ const struct bcm2835_dma_cfg *cfg = to_bcm2835_cfg(chan->device);
+ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
+ void __iomem *chan_base = c->chan_base;
+
+ /*
+ * A zero control block address means the channel is idle.
+ * (The ACTIVE flag in the CS register is not a reliable indicator.)
+ */
+ if (!readl(chan_base + cfg->cb_reg))
+ return;
+
+ cfg->dma_abort(c, cfg);
}

static void bcm2835_dma_start_desc(struct dma_chan *chan)
@@ -722,8 +1052,7 @@ static void bcm2835_dma_start_desc(struct dma_chan *chan)
c->desc = to_bcm2835_dma_desc(&vd->tx);

writel(cfg->to_cb_addr(c->desc->cb_list[0].paddr), c->chan_base + cfg->cb_reg);
- writel(cfg->active_mask | cfg->cs_flags(c),
- c->chan_base + cfg->cs_reg);
+ writel(cfg->active_mask | cfg->cs_flags(c), c->chan_base + cfg->cs_reg);
}

static irqreturn_t bcm2835_dma_callback(int irq, void *data)
@@ -1106,6 +1435,8 @@ static const struct bcm2835_dma_cfg bcm2835_data = {
.s_dreq_mask = BCM2835_DMA_S_DREQ,
.d_dreq_mask = BCM2835_DMA_D_DREQ,

+ .dma_mask = DMA_BIT_MASK(32),
+
.cb_get_length = bcm2835_dma_cb_get_length,
.cb_get_addr = bcm2835_dma_cb_get_addr,
.cb_init = bcm2835_dma_cb_init,
@@ -1120,10 +1451,47 @@ static const struct bcm2835_dma_cfg bcm2835_data = {
.chan_plat_init = bcm2835_dma_chan_plat_init,
.read_addr = bcm2835_dma_read_addr,
.cs_flags = bcm2835_dma_cs_flags,
+ .dma_abort = bcm2835_abort,
+};
+
+static const struct bcm2835_dma_cfg bcm2711_data = {
+ .addr_offset = BCM2711_DMA40_PHYS_ADDR,
+
+ .cs_reg = BCM2711_DMA40_CS,
+ .cb_reg = BCM2711_DMA40_CB,
+ .next_reg = BCM2711_DMA40_NEXT_CB,
+ .ti_reg = BCM2711_DMA40_TI,
+
+ .wait_mask = BCM2711_DMA40_WAITING_FOR_WRITES,
+ .reset_mask = BCM2711_DMA40_HALT,
+ .int_mask = BCM2711_DMA40_INTEN,
+ .active_mask = BCM2711_DMA40_ACTIVE,
+ .abort_mask = BCM2711_DMA40_ABORT,
+ .s_dreq_mask = BCM2711_DMA40_S_DREQ,
+ .d_dreq_mask = BCM2711_DMA40_D_DREQ,
+
+ .dma_mask = DMA_BIT_MASK(36),
+
+ .cb_get_length = bcm2711_dma_cb_get_length,
+ .cb_get_addr = bcm2711_dma_cb_get_addr,
+ .cb_init = bcm2711_dma_cb_init,
+ .cb_set_src = bcm2711_dma_cb_set_src,
+ .cb_set_dst = bcm2711_dma_cb_set_dst,
+ .cb_set_next = bcm2711_dma_cb_set_next,
+ .cb_set_length = bcm2711_dma_cb_set_length,
+ .cb_append_extra = bcm2711_dma_cb_append_extra,
+
+ .to_cb_addr = bcm2711_dma_to_cb_addr,
+
+ .chan_plat_init = bcm2711_dma_chan_plat_init,
+ .read_addr = bcm2711_dma_read_addr,
+ .cs_flags = bcm2711_dma_cs_flags,
+ .dma_abort = bcm2711_abort,
};

static const struct of_device_id bcm2835_dma_of_match[] = {
{ .compatible = "brcm,bcm2835-dma", .data = &bcm2835_data },
+ { .compatible = "brcm,bcm2711-dma", .data = &bcm2711_data },
{},
};
MODULE_DEVICE_TABLE(of, bcm2835_dma_of_match);
@@ -1147,6 +1515,7 @@ static struct dma_chan *bcm2835_dma_xlate(struct of_phandle_args *spec,
static int bcm2835_dma_probe(struct platform_device *pdev)
{
struct bcm2835_dmadev *od;
+ struct resource *res;
void __iomem *base;
int rc;
int i, j;
@@ -1154,34 +1523,38 @@ static int bcm2835_dma_probe(struct platform_device *pdev)
int irq_flags;
u32 chans_available;
char chan_name[BCM2835_DMA_CHAN_NAME_SIZE];
+ int chan_count, chan_start, chan_end;

- const void *cfg_data = device_get_match_data(&pdev->dev);
- if (!cfg_data) {
- dev_err(&pdev->dev, "Failed to match compatible string\n");
- return -EINVAL;
- }
+ od = devm_kzalloc(&pdev->dev, sizeof(*od), GFP_KERNEL);
+ if (!od)
+ return -ENOMEM;

- if (!pdev->dev.dma_mask)
- pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
+ od->cfg = device_get_match_data(&pdev->dev);
+ if (!od->cfg) {
+ dev_err(&pdev->dev, "Failed to match compatible string\n");
+ return -EINVAL;
+ }

- rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ rc = dma_set_mask_and_coherent(&pdev->dev, od->cfg->dma_mask);
if (rc) {
dev_err(&pdev->dev, "Unable to set DMA mask\n");
return rc;
}

- od = devm_kzalloc(&pdev->dev, sizeof(*od), GFP_KERNEL);
- if (!od)
- return -ENOMEM;
-
dma_set_max_seg_size(&pdev->dev, 0x3FFFFFFF);

- 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);

+ /* The set of channels can be split across multiple instances. */
+ chan_start = ((u32)(uintptr_t)base / BCM2835_DMA_CHAN_SIZE) & 0xf;
+ base -= BCM2835_DMA_CHAN(chan_start);
+ chan_count = resource_size(res) / BCM2835_DMA_CHAN_SIZE;
+ chan_end = min(chan_start + chan_count,
+ BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED + 1);
+
od->base = base;
- od->cfg = cfg_data;

dma_cap_set(DMA_SLAVE, od->ddev.cap_mask);
dma_cap_set(DMA_PRIVATE, od->ddev.cap_mask);
@@ -1233,7 +1606,7 @@ static int bcm2835_dma_probe(struct platform_device *pdev)
}

/* get irqs for each channel that we support */
- for (i = 0; i <= BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED; i++) {
+ for (i = chan_start; i < chan_end; i++) {
/* skip masked out channels */
if (!(chans_available & (1 << i))) {
irq[i] = -1;
@@ -1256,13 +1629,18 @@ static int bcm2835_dma_probe(struct platform_device *pdev)
irq[i] = platform_get_irq(pdev, i < 11 ? i : 11);
}

+ chan_count = 0;
+
/* get irqs for each channel */
- for (i = 0; i <= BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED; i++) {
+ for (i = chan_start; i < chan_end; i++) {
/* skip channels without irq */
if (irq[i] < 0)
continue;

/* check if there are other channels that also use this irq */
+ /* FIXME: This will fail if interrupts are shared across
+ * instances
+ */
irq_flags = 0;
for (j = 0; j <= BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED; j++)
if (i != j && irq[j] == irq[i]) {
@@ -1274,9 +1652,10 @@ static int bcm2835_dma_probe(struct platform_device *pdev)
rc = bcm2835_dma_chan_init(od, i, irq[i], irq_flags);
if (rc)
goto err_no_dma;
+ chan_count++;
}

- dev_dbg(&pdev->dev, "Initialized %i DMA channels\n", i);
+ dev_dbg(&pdev->dev, "Initialized %i DMA channels\n", chan_count);

/* Device-tree DMA controller registration */
rc = of_dma_controller_register(pdev->dev.of_node,
--
2.35.3


2024-03-13 15:26:13

by Andrea della Porta

[permalink] [raw]
Subject: Re: [PATCH v2 01/15] dmaengine: bcm2835: Fix several spellos

On 08:00 Wed 13 Mar , Florian Fainelli wrote:
>
>
> On 3/13/2024 7:08 AM, Andrea della Porta wrote:
> > Fixed Codespell reported warnings about spelling and coding convention
> > violations, among which there are also a couple potential operator
> > precedence issue in macroes.
> >
> > Signed-off-by: Andrea della Porta <[email protected]>
>
> There are no spelling errors being fixed in this commit, this is purely
> stylistic and conforming to the Linux coding style guidelines.

- /* detect a size missmatch */
- if (buf_len && (d->size != buf_len))
+ /* detect a size mismatch */
+ if (buf_len && d->size != buf_len)

Isn't 'missmatch' a spelling error? Maybe I can drop the word 'several', since it's
indeed only one...

> --
> Florian

Many thanks,
Andrea

2024-03-13 18:17:06

by Andrea della Porta

[permalink] [raw]
Subject: Re: [PATCH v2 01/15] dmaengine: bcm2835: Fix several spellos

On 09:38 Wed 13 Mar , Florian Fainelli wrote:
> On 3/13/24 08:26, Andrea della Porta wrote:
> > On 08:00 Wed 13 Mar , Florian Fainelli wrote:
> > >
> > >
> > > On 3/13/2024 7:08 AM, Andrea della Porta wrote:
> > > > Fixed Codespell reported warnings about spelling and coding convention
> > > > violations, among which there are also a couple potential operator
> > > > precedence issue in macroes.
>
> and s/macroes/macros/

right, sorry about that...

>
> > > >
> > > > Signed-off-by: Andrea della Porta <[email protected]>
> > >
> > > There are no spelling errors being fixed in this commit, this is purely
> > > stylistic and conforming to the Linux coding style guidelines.
> >
> > - /* detect a size missmatch */
> > - if (buf_len && (d->size != buf_len))
> > + /* detect a size mismatch */
> > + if (buf_len && d->size != buf_len)
> >
> > Isn't 'missmatch' a spelling error? Maybe I can drop the word 'several', since it's
> > indeed only one...
>
> Can we agree this was easy to miss when 99% of the changes are stylistic?
> The summary is that the commit subject and the message are not describing
> what this patch is about.
> --
> Florian
>

I agree with you that I could've added '...and coding style fixes' to the subject
(while also dropping 'several'). The comment seems fine enough though. Maybe
you're suggesting to separate the patches into two, one dealing with spelling
errors and the other with coding conventions?

Many thanks,
Andrea

2024-03-13 19:52:31

by Florian Fainelli

[permalink] [raw]
Subject: Re: [PATCH v2 01/15] dmaengine: bcm2835: Fix several spellos



On 3/13/2024 7:08 AM, Andrea della Porta wrote:
> Fixed Codespell reported warnings about spelling and coding convention
> violations, among which there are also a couple potential operator
> precedence issue in macroes.
>
> Signed-off-by: Andrea della Porta <[email protected]>

There are no spelling errors being fixed in this commit, this is purely
stylistic and conforming to the Linux coding style guidelines.
--
Florian


Attachments:
smime.p7s (4.12 kB)
S/MIME Cryptographic Signature

2024-03-13 20:51:01

by Florian Fainelli

[permalink] [raw]
Subject: Re: [PATCH v2 01/15] dmaengine: bcm2835: Fix several spellos

On 3/13/24 08:26, Andrea della Porta wrote:
> On 08:00 Wed 13 Mar , Florian Fainelli wrote:
>>
>>
>> On 3/13/2024 7:08 AM, Andrea della Porta wrote:
>>> Fixed Codespell reported warnings about spelling and coding convention
>>> violations, among which there are also a couple potential operator
>>> precedence issue in macroes.

and s/macroes/macros/

>>>
>>> Signed-off-by: Andrea della Porta <[email protected]>
>>
>> There are no spelling errors being fixed in this commit, this is purely
>> stylistic and conforming to the Linux coding style guidelines.
>
> - /* detect a size missmatch */
> - if (buf_len && (d->size != buf_len))
> + /* detect a size mismatch */
> + if (buf_len && d->size != buf_len)
>
> Isn't 'missmatch' a spelling error? Maybe I can drop the word 'several', since it's
> indeed only one...

Can we agree this was easy to miss when 99% of the changes are
stylistic? The summary is that the commit subject and the message are
not describing what this patch is about.
--
Florian


Attachments:
smime.p7s (4.12 kB)
S/MIME Cryptographic Signature

2024-03-13 22:01:22

by Florian Fainelli

[permalink] [raw]
Subject: Re: [PATCH v2 01/15] dmaengine: bcm2835: Fix several spellos

On 3/13/24 09:49, Andrea della Porta wrote:
> On 09:38 Wed 13 Mar , Florian Fainelli wrote:
>> On 3/13/24 08:26, Andrea della Porta wrote:
>>> On 08:00 Wed 13 Mar , Florian Fainelli wrote:
>>>>
>>>>
>>>> On 3/13/2024 7:08 AM, Andrea della Porta wrote:
>>>>> Fixed Codespell reported warnings about spelling and coding convention
>>>>> violations, among which there are also a couple potential operator
>>>>> precedence issue in macroes.
>>
>> and s/macroes/macros/
>
> right, sorry about that...
>
>>
>>>>>
>>>>> Signed-off-by: Andrea della Porta <[email protected]>
>>>>
>>>> There are no spelling errors being fixed in this commit, this is purely
>>>> stylistic and conforming to the Linux coding style guidelines.
>>>
>>> - /* detect a size missmatch */
>>> - if (buf_len && (d->size != buf_len))
>>> + /* detect a size mismatch */
>>> + if (buf_len && d->size != buf_len)
>>>
>>> Isn't 'missmatch' a spelling error? Maybe I can drop the word 'several', since it's
>>> indeed only one...
>>
>> Can we agree this was easy to miss when 99% of the changes are stylistic?
>> The summary is that the commit subject and the message are not describing
>> what this patch is about.
>> --
>> Florian
>>
>
> I agree with you that I could've added '...and coding style fixes' to the subject
> (while also dropping 'several'). The comment seems fine enough though. Maybe
> you're suggesting to separate the patches into two, one dealing with spelling
> errors and the other with coding conventions?

What I am suggesting is to be somewhat accurate about what you do in
your patch. If there are 99% stylistic issues, then say so, and in the
commit subject, say: by the way, typographic errors are also fixed. Or
any variation of that. Anyway, there are others things to be sorted
about this patch series than this specific one, it just happened to be
the first I looked at.
--
Florian


Attachments:
smime.p7s (4.12 kB)
S/MIME Cryptographic Signature

2024-03-17 12:38:58

by Stefan Wahren

[permalink] [raw]
Subject: Re: [PATCH v2 14/15] dmaengine: bcm2835: Add BCM2711 40-bit DMA support

Hi Andrea,

Am 13.03.24 um 15:08 schrieb Andrea della Porta:
> BCM2711 has 4 DMA channels with a 40-bit address range, allowing them
> to access the full 4GB of memory on a Pi 4. Assume every channel is capable
> of 40-bit address range.
>
> Originally-by: Phil Elwell <[email protected]>
> Originally-by: Maxime Ripard <[email protected]>
> Originally-by: Stefan Wahren <[email protected]>
> Signed-off-by: Andrea della Porta <[email protected]>
> ---
> drivers/dma/bcm2835-dma.c | 553 ++++++++++++++++++++++++++++++++------
> 1 file changed, 466 insertions(+), 87 deletions(-)

this patch contains a lot of changes (including style fixes). It would
be better to split this more for a better understanding.

2024-03-17 12:56:06

by Stefan Wahren

[permalink] [raw]
Subject: Re: [PATCH v2 02/15] dmaengine: bcm2835: Add support for per-channel flags

Hi Andrea,

Am 13.03.24 um 15:08 schrieb Andrea della Porta:
> From: Phil Elwell <[email protected]>
>
> Add the ability to interpret the high bits of the dreq specifier as
> flags to be included in the DMA_CS register. The motivation for this
> change is the ability to set the DISDEBUG flag for SD card transfers
> to avoid corruption when using the VPU debugger.

AFAIK this and the following 2 patches also requires modification on the
DT side. So either they must be included in the series or we better
leave them out completely. I'm not sure which one are really necessary
for 40 bit support.

Regards

>
> Signed-off-by: Phil Elwell <[email protected]>
> Signed-off-by: Andrea della Porta <[email protected]>
> ---
> drivers/dma/bcm2835-dma.c | 10 ++++++++--
> 1 file changed, 8 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
> index 428253b468ac..3d9973dd041d 100644
> --- a/drivers/dma/bcm2835-dma.c
> +++ b/drivers/dma/bcm2835-dma.c
> @@ -137,6 +137,10 @@ struct bcm2835_desc {
> #define BCM2835_DMA_S_DREQ BIT(10) /* enable SREQ for source */
> #define BCM2835_DMA_S_IGNORE BIT(11) /* ignore source reads - read 0 */
> #define BCM2835_DMA_BURST_LENGTH(x) (((x) & 15) << 12)
> +#define BCM2835_DMA_CS_FLAGS(x) ((x) & (BCM2835_DMA_PRIORITY(15) | \
> + BCM2835_DMA_PANIC_PRIORITY(15) | \
> + BCM2835_DMA_WAIT_FOR_WRITES | \
> + BCM2835_DMA_DIS_DEBUG))
> #define BCM2835_DMA_PER_MAP(x) (((x) & 31) << 16) /* REQ source */
> #define BCM2835_DMA_WAIT(x) (((x) & 31) << 21) /* add DMA-wait cycles */
> #define BCM2835_DMA_NO_WIDE_BURSTS BIT(26) /* no 2 beat write bursts */
> @@ -449,7 +453,8 @@ static void bcm2835_dma_start_desc(struct bcm2835_chan *c)
> c->desc = to_bcm2835_dma_desc(&vd->tx);
>
> writel(c->desc->cb_list[0].paddr, c->chan_base + BCM2835_DMA_ADDR);
> - writel(BCM2835_DMA_ACTIVE, c->chan_base + BCM2835_DMA_CS);
> + writel(BCM2835_DMA_ACTIVE | BCM2835_DMA_CS_FLAGS(c->dreq),
> + c->chan_base + BCM2835_DMA_CS);
> }
>
> static irqreturn_t bcm2835_dma_callback(int irq, void *data)
> @@ -476,7 +481,8 @@ static irqreturn_t bcm2835_dma_callback(int irq, void *data)
> * if this IRQ handler is threaded.) If the channel is finished, it
> * will remain idle despite the ACTIVE flag being set.
> */
> - writel(BCM2835_DMA_INT | BCM2835_DMA_ACTIVE,
> + writel(BCM2835_DMA_INT | BCM2835_DMA_ACTIVE |
> + BCM2835_DMA_CS_FLAGS(c->dreq),
> c->chan_base + BCM2835_DMA_CS);
>
> d = c->desc;

2024-04-14 06:27:03

by Krzysztof Kozlowski

[permalink] [raw]
Subject: Re: [PATCH v2 15/15] ARM: dts: bcm2711: add bcm2711-dma node

On 12/03/2024 10:12, Andrea della Porta wrote:
> BCM2711 has 4 DMA channels with a 40-bit address range, allowing them
> to access the full 4GB of memory on a Pi 4. Adding a new node to make
> use of the DMA channels capable of 40 bit addressing.
>
> Signed-off-by: Stefan Wahren <[email protected]>
> Signed-off-by: Andrea della Porta <[email protected]>
> ---
> arch/arm/boot/dts/broadcom/bcm2711.dtsi | 16 ++++++++++++++++
> 1 file changed, 16 insertions(+)
>
> diff --git a/arch/arm/boot/dts/broadcom/bcm2711.dtsi b/arch/arm/boot/dts/broadcom/bcm2711.dtsi
> index 22c7f1561344..d98e3cf0c569 100644
> --- a/arch/arm/boot/dts/broadcom/bcm2711.dtsi
> +++ b/arch/arm/boot/dts/broadcom/bcm2711.dtsi
> @@ -552,6 +552,22 @@ scb {
> ranges = <0x0 0x7c000000 0x0 0xfc000000 0x03800000>,
> <0x6 0x00000000 0x6 0x00000000 0x40000000>;
>
> + dma40: dma-controller@7e007b00 {
> + compatible = "brcm,bcm2711-dma";
> + reg = <0x0 0x7e007b00 0x400>;
> + interrupts = <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>, /* dma4 11 */
> + <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>, /* dma4 12 */
> + <GIC_SPI 91 IRQ_TYPE_LEVEL_HIGH>, /* dma4 13 */
> + <GIC_SPI 92 IRQ_TYPE_LEVEL_HIGH>; /* dma4 14 */
> + interrupt-names = "dma11",
> + "dma12",
> + "dma13",
> + "dma14";
> + #dma-cells = <1>;
> + /* The VPU firmware uses DMA channel 11 for VCHIQ */
> + brcm,dma-channel-mask = <0x7000>;

Isn't one of your commits saying - this property is replaced?

Best regards,
Krzysztof


2024-04-29 14:14:00

by Andrea della Porta

[permalink] [raw]
Subject: Re: [PATCH v2 15/15] ARM: dts: bcm2711: add bcm2711-dma node

On 08:26 Sun 14 Apr , Krzysztof Kozlowski wrote:
> On 12/03/2024 10:12, Andrea della Porta wrote:
> > BCM2711 has 4 DMA channels with a 40-bit address range, allowing them
> > to access the full 4GB of memory on a Pi 4. Adding a new node to make
> > use of the DMA channels capable of 40 bit addressing.
> >
> > Signed-off-by: Stefan Wahren <[email protected]>
> > Signed-off-by: Andrea della Porta <[email protected]>
> > ---
> > arch/arm/boot/dts/broadcom/bcm2711.dtsi | 16 ++++++++++++++++
> > 1 file changed, 16 insertions(+)
> >
> > diff --git a/arch/arm/boot/dts/broadcom/bcm2711.dtsi b/arch/arm/boot/dts/broadcom/bcm2711.dtsi
> > index 22c7f1561344..d98e3cf0c569 100644
> > --- a/arch/arm/boot/dts/broadcom/bcm2711.dtsi
> > +++ b/arch/arm/boot/dts/broadcom/bcm2711.dtsi
> > @@ -552,6 +552,22 @@ scb {
> > ranges = <0x0 0x7c000000 0x0 0xfc000000 0x03800000>,
> > <0x6 0x00000000 0x6 0x00000000 0x40000000>;
> >
> > + dma40: dma-controller@7e007b00 {
> > + compatible = "brcm,bcm2711-dma";
> > + reg = <0x0 0x7e007b00 0x400>;
> > + interrupts = <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>, /* dma4 11 */
> > + <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>, /* dma4 12 */
> > + <GIC_SPI 91 IRQ_TYPE_LEVEL_HIGH>, /* dma4 13 */
> > + <GIC_SPI 92 IRQ_TYPE_LEVEL_HIGH>; /* dma4 14 */
> > + interrupt-names = "dma11",
> > + "dma12",
> > + "dma13",
> > + "dma14";
> > + #dma-cells = <1>;
> > + /* The VPU firmware uses DMA channel 11 for VCHIQ */
> > + brcm,dma-channel-mask = <0x7000>;
>
> Isn't one of your commits saying - this property is replaced?

True. The next patchset revision will drop 'brcm,' prefix.
Many thanks for pointing that out.

Andrea

>
> Best regards,
> Krzysztof
>