mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v2 0/6] dmaengine: pl330: Updates and logical changes for peripheral usecases
@ 2023-05-12 14:14 Joy Chakraborty
  2023-05-12 14:14 ` [PATCH v2 1/6] dmaengine: pl330: Separate SRC and DST burst size and len Joy Chakraborty
                   ` (5 more replies)
  0 siblings, 6 replies; 7+ messages in thread
From: Joy Chakraborty @ 2023-05-12 14:14 UTC (permalink / raw)
  To: Vinod Koul
  Cc: dmaengine, linux-kernel, devicetree, manugautam, danielmentz,
	sjadavani, Joy Chakraborty

This patch series makes some initial minor and cosmetic changes:
    -Add variables and logic to handle separate source and destination
    AxSize and AxLen.
    -Use __ffs to calculate AxSize for consistency in the driver
    -Use switch-case in prep_slave_sg() for consistency
    -Change args get_burst_len() to remove redundant "len" and add
    burst_size so that it can be used in multiple places.

to majorly enable addition of 2 logical changes in the last 2 patches:
    -Allow transactions towards memory to use the maximum possible
    bus width (AxSize) during a memory to peripheral dma usage or
    vise-versa.
    -Add logic to copy left over data after executing bursts to
    the peripheral in singles instead of bursts.
---
V1->V2 Changes : Remove Quirk logic and update description texts.
---

Joy Chakraborty (6):
  dmaengine: pl330: Separate SRC and DST burst size and len
  dmaengine: pl330: Use FFS to calculate burst size
  dmaengine: pl330: Change if-else to switch-case for consistency
  dmaengine: pl330: Change unused arg "len" from get_burst_len()
  dmaengine: pl330: Optimize AxSize for peripheral usecases
  dmaengine: pl330: Use dma singles for peripheral _dregs

 drivers/dma/pl330.c | 223 ++++++++++++++++++++++++++++++++++++--------
 1 file changed, 185 insertions(+), 38 deletions(-)

-- 
2.40.1.606.ga4b1b128d6-goog


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v2 1/6] dmaengine: pl330: Separate SRC and DST burst size and len
  2023-05-12 14:14 [PATCH v2 0/6] dmaengine: pl330: Updates and logical changes for peripheral usecases Joy Chakraborty
@ 2023-05-12 14:14 ` Joy Chakraborty
  2023-05-12 14:14 ` [PATCH v2 2/6] dmaengine: pl330: Use FFS to calculate burst size Joy Chakraborty
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Joy Chakraborty @ 2023-05-12 14:14 UTC (permalink / raw)
  To: Vinod Koul
  Cc: dmaengine, linux-kernel, devicetree, manugautam, danielmentz,
	sjadavani, Joy Chakraborty

Add new variables in request configuration to handle source and
destination AxSize and AxLen separately and allow them to have different
values.

This allows further patches to configure different AxSize and AxLen for
optimum bus utilisation.

Signed-off-by: Joy Chakraborty <joychakr@google.com>
---
 drivers/dma/pl330.c | 71 +++++++++++++++++++++++++++++----------------
 1 file changed, 46 insertions(+), 25 deletions(-)

diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index 0d9257fbdfb0..c006e481b4c5 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -240,6 +240,12 @@ enum pl330_byteswap {
 #define BYTE_TO_BURST(b, ccr)	((b) / BRST_SIZE(ccr) / BRST_LEN(ccr))
 #define BURST_TO_BYTE(c, ccr)	((c) * BRST_SIZE(ccr) * BRST_LEN(ccr))
 
+#define SRC_BRST_SIZE(ccr)	BRST_SIZE(ccr)
+#define DST_BRST_SIZE(ccr)	(1 << (((ccr) >> CC_DSTBRSTSIZE_SHFT) & 0x7))
+
+#define SRC_BRST_LEN(ccr)	BRST_LEN(ccr)
+#define DST_BRST_LEN(ccr)	((((ccr) >> CC_DSTBRSTLEN_SHFT) & 0xf) + 1)
+
 /*
  * With 256 bytes, we can do more than 2.5MB and 5MB xfers per req
  * at 1byte/burst for P<->M and M<->M respectively.
@@ -305,8 +311,10 @@ struct pl330_reqcfg {
 	bool nonsecure;
 	bool privileged;
 	bool insnaccess;
-	unsigned brst_len:5;
-	unsigned brst_size:3; /* in power of 2 */
+	unsigned src_brst_size : 3; /* in power of 2 */
+	unsigned src_brst_len:5;
+	unsigned dst_brst_size : 3; /* in power of 2 */
+	unsigned dst_brst_len:5;
 
 	enum pl330_cachectrl dcctl;
 	enum pl330_cachectrl scctl;
@@ -1204,7 +1212,10 @@ static int _bursts(struct pl330_dmac *pl330, unsigned dry_run, u8 buf[],
 		const struct _xfer_spec *pxs, int cyc)
 {
 	int off = 0;
-	enum pl330_cond cond = BRST_LEN(pxs->ccr) > 1 ? BURST : SINGLE;
+	enum pl330_cond cond = SINGLE;
+
+	if (SRC_BRST_LEN(pxs->ccr) > 1 || DST_BRST_LEN(pxs->ccr) > 1)
+		cond = BURST;
 
 	if (pl330->quirks & PL330_QUIRK_PERIPH_BURST)
 		cond = BURST;
@@ -1235,12 +1246,12 @@ static int _bursts(struct pl330_dmac *pl330, unsigned dry_run, u8 buf[],
  * for mem-to-mem, mem-to-dev or dev-to-mem.
  */
 static int _dregs(struct pl330_dmac *pl330, unsigned int dry_run, u8 buf[],
-		const struct _xfer_spec *pxs, int transfer_length)
+		const struct _xfer_spec *pxs, int src_length, int dst_length)
 {
 	int off = 0;
 	int dregs_ccr;
 
-	if (transfer_length == 0)
+	if (src_length == 0 || dst_length == 0)
 		return off;
 
 	/*
@@ -1253,9 +1264,9 @@ static int _dregs(struct pl330_dmac *pl330, unsigned int dry_run, u8 buf[],
 	dregs_ccr = pxs->ccr;
 	dregs_ccr &= ~((0xf << CC_SRCBRSTLEN_SHFT) |
 		(0xf << CC_DSTBRSTLEN_SHFT));
-	dregs_ccr |= (((transfer_length - 1) & 0xf) <<
+	dregs_ccr |= (((src_length - 1) & 0xf) <<
 		CC_SRCBRSTLEN_SHFT);
-	dregs_ccr |= (((transfer_length - 1) & 0xf) <<
+	dregs_ccr |= (((dst_length - 1) & 0xf) <<
 		CC_DSTBRSTLEN_SHFT);
 
 	switch (pxs->desc->rqtype) {
@@ -1369,16 +1380,18 @@ static inline int _setup_loops(struct pl330_dmac *pl330,
 	struct pl330_xfer *x = &pxs->desc->px;
 	u32 ccr = pxs->ccr;
 	unsigned long c, bursts = BYTE_TO_BURST(x->bytes, ccr);
-	int num_dregs = (x->bytes - BURST_TO_BYTE(bursts, ccr)) /
-		BRST_SIZE(ccr);
-	int off = 0;
+	int num_dreg_bytes = x->bytes - BURST_TO_BYTE(bursts, ccr);
+	int num_src_dregs, num_dst_dregs, off = 0;
+
+	num_src_dregs = num_dreg_bytes / SRC_BRST_SIZE(ccr);
+	num_dst_dregs = num_dreg_bytes / DST_BRST_SIZE(ccr);
 
 	while (bursts) {
 		c = bursts;
 		off += _loop(pl330, dry_run, &buf[off], &c, pxs);
 		bursts -= c;
 	}
-	off += _dregs(pl330, dry_run, &buf[off], pxs, num_dregs);
+	off += _dregs(pl330, dry_run, &buf[off], pxs, num_src_dregs, num_dst_dregs);
 
 	return off;
 }
@@ -1446,11 +1459,11 @@ static inline u32 _prepare_ccr(const struct pl330_reqcfg *rqc)
 	if (rqc->insnaccess)
 		ccr |= CC_SRCIA | CC_DSTIA;
 
-	ccr |= (((rqc->brst_len - 1) & 0xf) << CC_SRCBRSTLEN_SHFT);
-	ccr |= (((rqc->brst_len - 1) & 0xf) << CC_DSTBRSTLEN_SHFT);
+	ccr |= (((rqc->src_brst_len - 1) & 0xf) << CC_SRCBRSTLEN_SHFT);
+	ccr |= (((rqc->dst_brst_len - 1) & 0xf) << CC_DSTBRSTLEN_SHFT);
 
-	ccr |= (rqc->brst_size << CC_SRCBRSTSIZE_SHFT);
-	ccr |= (rqc->brst_size << CC_DSTBRSTSIZE_SHFT);
+	ccr |= (rqc->src_brst_size << CC_SRCBRSTSIZE_SHFT);
+	ccr |= (rqc->dst_brst_size << CC_DSTBRSTSIZE_SHFT);
 
 	ccr |= (rqc->scctl << CC_SRCCCTRL_SHFT);
 	ccr |= (rqc->dcctl << CC_DSTCCTRL_SHFT);
@@ -2656,7 +2669,7 @@ static inline int get_burst_len(struct dma_pl330_desc *desc, size_t len)
 
 	burst_len = pl330->pcfg.data_bus_width / 8;
 	burst_len *= pl330->pcfg.data_buf_dep / pl330->pcfg.num_chan;
-	burst_len >>= desc->rqcfg.brst_size;
+	burst_len >>= desc->rqcfg.src_brst_size;
 
 	/* src/dst_burst_len can't be more than 16 */
 	if (burst_len > PL330_MAX_BURST)
@@ -2735,8 +2748,10 @@ static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic(
 		}
 
 		desc->rqtype = direction;
-		desc->rqcfg.brst_size = pch->burst_sz;
-		desc->rqcfg.brst_len = pch->burst_len;
+		desc->rqcfg.src_brst_size = pch->burst_sz;
+		desc->rqcfg.src_brst_len = pch->burst_len;
+		desc->rqcfg.dst_brst_size = pch->burst_sz;
+		desc->rqcfg.dst_brst_len = pch->burst_len;
 		desc->bytes_requested = period_len;
 		fill_px(&desc->px, dst, src, period_len);
 
@@ -2789,17 +2804,21 @@ pl330_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dst,
 	while ((src | dst | len) & (burst - 1))
 		burst /= 2;
 
-	desc->rqcfg.brst_size = 0;
-	while (burst != (1 << desc->rqcfg.brst_size))
-		desc->rqcfg.brst_size++;
+	desc->rqcfg.src_brst_size = 0;
+	while (burst != (1 << desc->rqcfg.src_brst_size))
+		desc->rqcfg.src_brst_size++;
 
-	desc->rqcfg.brst_len = get_burst_len(desc, len);
+	desc->rqcfg.src_brst_len = get_burst_len(desc, len);
 	/*
 	 * If burst size is smaller than bus width then make sure we only
 	 * transfer one at a time to avoid a burst stradling an MFIFO entry.
 	 */
 	if (burst * 8 < pl330->pcfg.data_bus_width)
-		desc->rqcfg.brst_len = 1;
+		desc->rqcfg.src_brst_len = 1;
+
+	/* For Mem2Mem, set destination AxSize and AxLen same as source*/
+	desc->rqcfg.dst_brst_len = desc->rqcfg.src_brst_len;
+	desc->rqcfg.dst_brst_size = desc->rqcfg.src_brst_size;
 
 	desc->bytes_requested = len;
 
@@ -2879,8 +2898,10 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
 				sg_dma_len(sg));
 		}
 
-		desc->rqcfg.brst_size = pch->burst_sz;
-		desc->rqcfg.brst_len = pch->burst_len;
+		desc->rqcfg.src_brst_size = pch->burst_sz;
+		desc->rqcfg.src_brst_len = pch->burst_len;
+		desc->rqcfg.dst_brst_size = pch->burst_sz;
+		desc->rqcfg.dst_brst_len = pch->burst_len;
 		desc->rqtype = direction;
 		desc->bytes_requested = sg_dma_len(sg);
 	}
-- 
2.40.1.606.ga4b1b128d6-goog


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v2 2/6] dmaengine: pl330: Use FFS to calculate burst size
  2023-05-12 14:14 [PATCH v2 0/6] dmaengine: pl330: Updates and logical changes for peripheral usecases Joy Chakraborty
  2023-05-12 14:14 ` [PATCH v2 1/6] dmaengine: pl330: Separate SRC and DST burst size and len Joy Chakraborty
@ 2023-05-12 14:14 ` Joy Chakraborty
  2023-05-12 14:14 ` [PATCH v2 3/6] dmaengine: pl330: Change if-else to switch-case for consistency Joy Chakraborty
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Joy Chakraborty @ 2023-05-12 14:14 UTC (permalink / raw)
  To: Vinod Koul
  Cc: dmaengine, linux-kernel, devicetree, manugautam, danielmentz,
	sjadavani, Joy Chakraborty

Use __ffs to calculate burst size in pl330_prep_dma_memcpy() for
consistency across the driver as other functions already use __ffs for
the same functionality.

Signed-off-by: Joy Chakraborty <joychakr@google.com>
---
 drivers/dma/pl330.c | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index c006e481b4c5..39a66ff29e27 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -2804,10 +2804,7 @@ pl330_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dst,
 	while ((src | dst | len) & (burst - 1))
 		burst /= 2;
 
-	desc->rqcfg.src_brst_size = 0;
-	while (burst != (1 << desc->rqcfg.src_brst_size))
-		desc->rqcfg.src_brst_size++;
-
+	desc->rqcfg.src_brst_size = __ffs(burst);
 	desc->rqcfg.src_brst_len = get_burst_len(desc, len);
 	/*
 	 * If burst size is smaller than bus width then make sure we only
-- 
2.40.1.606.ga4b1b128d6-goog


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v2 3/6] dmaengine: pl330: Change if-else to switch-case for consistency
  2023-05-12 14:14 [PATCH v2 0/6] dmaengine: pl330: Updates and logical changes for peripheral usecases Joy Chakraborty
  2023-05-12 14:14 ` [PATCH v2 1/6] dmaengine: pl330: Separate SRC and DST burst size and len Joy Chakraborty
  2023-05-12 14:14 ` [PATCH v2 2/6] dmaengine: pl330: Use FFS to calculate burst size Joy Chakraborty
@ 2023-05-12 14:14 ` Joy Chakraborty
  2023-05-12 14:14 ` [PATCH v2 4/6] dmaengine: pl330: Change unused arg "len" from get_burst_len() Joy Chakraborty
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Joy Chakraborty @ 2023-05-12 14:14 UTC (permalink / raw)
  To: Vinod Koul
  Cc: dmaengine, linux-kernel, devicetree, manugautam, danielmentz,
	sjadavani, Joy Chakraborty

Change if-else to switch-case in pl330_prep_slave_sg() function for
consistency with other peripheral transfer functions in the driver.

Signed-off-by: Joy Chakraborty <joychakr@google.com>
---
 drivers/dma/pl330.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index 39a66ff29e27..746da0bbea92 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -2883,16 +2883,21 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
 		else
 			list_add_tail(&desc->node, &first->node);
 
-		if (direction == DMA_MEM_TO_DEV) {
+		switch (direction) {
+		case DMA_MEM_TO_DEV:
 			desc->rqcfg.src_inc = 1;
 			desc->rqcfg.dst_inc = 0;
 			fill_px(&desc->px, pch->fifo_dma, sg_dma_address(sg),
 				sg_dma_len(sg));
-		} else {
+			break;
+		case DMA_DEV_TO_MEM:
 			desc->rqcfg.src_inc = 0;
 			desc->rqcfg.dst_inc = 1;
 			fill_px(&desc->px, sg_dma_address(sg), pch->fifo_dma,
 				sg_dma_len(sg));
+			break;
+		default:
+			break;
 		}
 
 		desc->rqcfg.src_brst_size = pch->burst_sz;
-- 
2.40.1.606.ga4b1b128d6-goog


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v2 4/6] dmaengine: pl330: Change unused arg "len" from get_burst_len()
  2023-05-12 14:14 [PATCH v2 0/6] dmaengine: pl330: Updates and logical changes for peripheral usecases Joy Chakraborty
                   ` (2 preceding siblings ...)
  2023-05-12 14:14 ` [PATCH v2 3/6] dmaengine: pl330: Change if-else to switch-case for consistency Joy Chakraborty
@ 2023-05-12 14:14 ` Joy Chakraborty
  2023-05-12 14:14 ` [PATCH v2 5/6] dmaengine: pl330: Optimize AxSize for peripheral usecases Joy Chakraborty
  2023-05-12 14:14 ` [PATCH v2 6/6] dmaengine: pl330: Use dma singles for peripheral _dregs Joy Chakraborty
  5 siblings, 0 replies; 7+ messages in thread
From: Joy Chakraborty @ 2023-05-12 14:14 UTC (permalink / raw)
  To: Vinod Koul
  Cc: dmaengine, linux-kernel, devicetree, manugautam, danielmentz,
	sjadavani, Joy Chakraborty

Remove unused length argument from get_burst_len() and add burst size as
an argument to allow usage of this function in other places as source and
destination burst sizes are handled separately.

Signed-off-by: Joy Chakraborty <joychakr@google.com>
---
 drivers/dma/pl330.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index 746da0bbea92..e5e610c91f18 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -2660,8 +2660,7 @@ __pl330_prep_dma_memcpy(struct dma_pl330_chan *pch, dma_addr_t dst,
 	return desc;
 }
 
-/* Call after fixing burst size */
-static inline int get_burst_len(struct dma_pl330_desc *desc, size_t len)
+static inline int get_burst_len(struct dma_pl330_desc *desc, unsigned int brst_size)
 {
 	struct dma_pl330_chan *pch = desc->pchan;
 	struct pl330_dmac *pl330 = pch->dmac;
@@ -2669,7 +2668,7 @@ static inline int get_burst_len(struct dma_pl330_desc *desc, size_t len)
 
 	burst_len = pl330->pcfg.data_bus_width / 8;
 	burst_len *= pl330->pcfg.data_buf_dep / pl330->pcfg.num_chan;
-	burst_len >>= desc->rqcfg.src_brst_size;
+	burst_len >>= brst_size;
 
 	/* src/dst_burst_len can't be more than 16 */
 	if (burst_len > PL330_MAX_BURST)
@@ -2805,7 +2804,7 @@ pl330_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dst,
 		burst /= 2;
 
 	desc->rqcfg.src_brst_size = __ffs(burst);
-	desc->rqcfg.src_brst_len = get_burst_len(desc, len);
+	desc->rqcfg.src_brst_len = get_burst_len(desc, desc->rqcfg.src_brst_size);
 	/*
 	 * If burst size is smaller than bus width then make sure we only
 	 * transfer one at a time to avoid a burst stradling an MFIFO entry.
-- 
2.40.1.606.ga4b1b128d6-goog


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v2 5/6] dmaengine: pl330: Optimize AxSize for peripheral usecases
  2023-05-12 14:14 [PATCH v2 0/6] dmaengine: pl330: Updates and logical changes for peripheral usecases Joy Chakraborty
                   ` (3 preceding siblings ...)
  2023-05-12 14:14 ` [PATCH v2 4/6] dmaengine: pl330: Change unused arg "len" from get_burst_len() Joy Chakraborty
@ 2023-05-12 14:14 ` Joy Chakraborty
  2023-05-12 14:14 ` [PATCH v2 6/6] dmaengine: pl330: Use dma singles for peripheral _dregs Joy Chakraborty
  5 siblings, 0 replies; 7+ messages in thread
From: Joy Chakraborty @ 2023-05-12 14:14 UTC (permalink / raw)
  To: Vinod Koul
  Cc: dmaengine, linux-kernel, devicetree, manugautam, danielmentz,
	sjadavani, Joy Chakraborty

Add logic to choose the maximum possible AxSize for transactions towards
memory during usecases which copy data between memory and peripherals.

Currently PL330 driver chooses equal AxLen and AxSize for both loads and
stores to/from memory and peripherals which is inefficient towards
memory as the whole bus width is not used for transfers as a peripheral
might be limited to use only a narrow size of the buswidth available.

Example scenario:
    A peripheral might require data byte by byte which would  make AxSize
    = 1 byte and AxLen = 16 for both load from memory and store to
    Peripheral.
    This can be optimized for memory by using maximum AxSize (say
    16bytes) then load from memory can be done with AxSize = 16byte,
    AxLen = 1 and store to peripheral with AxSize = 1byte, AxLen =
    16 beats.

Instruction setup post changes :
    512bytes copy from Memory(16bytes * 4beats) to Peripheral(4bytes *
    16 beats)
    ---
    DMAMOV CCR 0xbd0239
    DMAMOV SAR 0xffffe000
    DMAMOV DAR 0xffffc860
    DMALP_1 7
    DMAFLUSHP 0
    DMAWFPB 0
    DMALDB
    DMASTPB 0
    DMALPENDA_1 bjmpto_7
    DMASEV 3
    DMAEND
    ---

Signed-off-by: Joy Chakraborty <joychakr@google.com>
---
 drivers/dma/pl330.c | 92 ++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 78 insertions(+), 14 deletions(-)

diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index e5e610c91f18..46e254fd4007 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -2677,6 +2677,52 @@ static inline int get_burst_len(struct dma_pl330_desc *desc, unsigned int brst_s
 	return burst_len;
 }
 
+/*
+ * Returns burst size to be used to copy data from/to memory during a
+ * peripheral transfer
+ */
+static unsigned int get_periph_mem_brst_sz(dma_addr_t addr, size_t len,
+					   struct dma_pl330_chan *pch)
+{
+	unsigned int burst;
+
+	/* Select max possible burst size */
+	burst = pch->dmac->pcfg.data_bus_width / 8;
+
+	/*
+	 * Make sure we use a burst size that aligns with the memory and length.
+	 */
+	while ((addr | len) & (burst - 1))
+		burst /= 2;
+
+	return __ffs(burst);
+}
+
+/*
+ * Returns burst length to be used to copy data from/to memory during a
+ * peripheral transfer
+ */
+static unsigned int get_periph_mem_brst_len(struct dma_pl330_desc *desc,
+					    struct dma_pl330_chan *pch,
+					    unsigned int burst_size)
+{
+	unsigned int burst_len = pch->burst_len;
+
+	if (burst_size != pch->burst_sz) {
+		/* Select max possible burst len */
+		burst_len = get_burst_len(desc, burst_size);
+
+		/*
+		 * Adjust AxLen to keep number of bytes same in Load/Store
+		 */
+		if (burst_size > pch->burst_sz)
+			burst_len = pch->burst_len >> (burst_size - pch->burst_sz);
+		else
+			pch->burst_len = burst_len >> (pch->burst_sz - burst_size);
+	}
+	return burst_len;
+}
+
 static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic(
 		struct dma_chan *chan, dma_addr_t dma_addr, size_t len,
 		size_t period_len, enum dma_transfer_direction direction,
@@ -2684,8 +2730,8 @@ static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic(
 {
 	struct dma_pl330_desc *desc = NULL, *first = NULL;
 	struct dma_pl330_chan *pch = to_pchan(chan);
+	unsigned int i, burst_size, burst_len;
 	struct pl330_dmac *pl330 = pch->dmac;
-	unsigned int i;
 	dma_addr_t dst;
 	dma_addr_t src;
 
@@ -2729,28 +2775,35 @@ static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic(
 			return NULL;
 		}
 
+		burst_size = get_periph_mem_brst_sz(dma_addr, period_len, pch);
+		burst_len = get_periph_mem_brst_len(desc, pch, burst_size);
+
 		switch (direction) {
 		case DMA_MEM_TO_DEV:
 			desc->rqcfg.src_inc = 1;
 			desc->rqcfg.dst_inc = 0;
 			src = dma_addr;
 			dst = pch->fifo_dma;
+			desc->rqcfg.src_brst_size = burst_size;
+			desc->rqcfg.src_brst_len = burst_len;
+			desc->rqcfg.dst_brst_size = pch->burst_sz;
+			desc->rqcfg.dst_brst_len = pch->burst_len;
 			break;
 		case DMA_DEV_TO_MEM:
 			desc->rqcfg.src_inc = 0;
 			desc->rqcfg.dst_inc = 1;
 			src = pch->fifo_dma;
 			dst = dma_addr;
+			desc->rqcfg.src_brst_size = pch->burst_sz;
+			desc->rqcfg.src_brst_len = pch->burst_len;
+			desc->rqcfg.dst_brst_size = burst_size;
+			desc->rqcfg.dst_brst_len = burst_len;
 			break;
 		default:
 			break;
 		}
 
 		desc->rqtype = direction;
-		desc->rqcfg.src_brst_size = pch->burst_sz;
-		desc->rqcfg.src_brst_len = pch->burst_len;
-		desc->rqcfg.dst_brst_size = pch->burst_sz;
-		desc->rqcfg.dst_brst_len = pch->burst_len;
 		desc->bytes_requested = period_len;
 		fill_px(&desc->px, dst, src, period_len);
 
@@ -2850,7 +2903,10 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
 {
 	struct dma_pl330_desc *first, *desc = NULL;
 	struct dma_pl330_chan *pch = to_pchan(chan);
+	unsigned int burst_size, burst_len;
 	struct scatterlist *sg;
+	dma_addr_t mem_addr;
+	size_t len;
 	int i;
 
 	if (unlikely(!pch || !sgl || !sg_len))
@@ -2882,29 +2938,37 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
 		else
 			list_add_tail(&desc->node, &first->node);
 
+		mem_addr = sg_dma_address(sg);
+		len = sg_dma_len(sg);
+
+		burst_size = get_periph_mem_brst_sz(mem_addr, len, pch);
+		burst_len = get_periph_mem_brst_len(desc, pch, burst_size);
+
 		switch (direction) {
 		case DMA_MEM_TO_DEV:
 			desc->rqcfg.src_inc = 1;
 			desc->rqcfg.dst_inc = 0;
-			fill_px(&desc->px, pch->fifo_dma, sg_dma_address(sg),
-				sg_dma_len(sg));
+			desc->rqcfg.src_brst_size = burst_size;
+			desc->rqcfg.src_brst_len = burst_len;
+			desc->rqcfg.dst_brst_size = pch->burst_sz;
+			desc->rqcfg.dst_brst_len = pch->burst_len;
+			fill_px(&desc->px, pch->fifo_dma, mem_addr, len);
 			break;
 		case DMA_DEV_TO_MEM:
 			desc->rqcfg.src_inc = 0;
 			desc->rqcfg.dst_inc = 1;
-			fill_px(&desc->px, sg_dma_address(sg), pch->fifo_dma,
-				sg_dma_len(sg));
+			desc->rqcfg.src_brst_size = pch->burst_sz;
+			desc->rqcfg.src_brst_len = pch->burst_len;
+			desc->rqcfg.dst_brst_size = burst_size;
+			desc->rqcfg.dst_brst_len = burst_len;
+			fill_px(&desc->px, mem_addr, pch->fifo_dma, len);
 			break;
 		default:
 			break;
 		}
 
-		desc->rqcfg.src_brst_size = pch->burst_sz;
-		desc->rqcfg.src_brst_len = pch->burst_len;
-		desc->rqcfg.dst_brst_size = pch->burst_sz;
-		desc->rqcfg.dst_brst_len = pch->burst_len;
 		desc->rqtype = direction;
-		desc->bytes_requested = sg_dma_len(sg);
+		desc->bytes_requested = len;
 	}
 
 	/* Return the last desc in the chain */
-- 
2.40.1.606.ga4b1b128d6-goog


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v2 6/6] dmaengine: pl330: Use dma singles for peripheral _dregs
  2023-05-12 14:14 [PATCH v2 0/6] dmaengine: pl330: Updates and logical changes for peripheral usecases Joy Chakraborty
                   ` (4 preceding siblings ...)
  2023-05-12 14:14 ` [PATCH v2 5/6] dmaengine: pl330: Optimize AxSize for peripheral usecases Joy Chakraborty
@ 2023-05-12 14:14 ` Joy Chakraborty
  5 siblings, 0 replies; 7+ messages in thread
From: Joy Chakraborty @ 2023-05-12 14:14 UTC (permalink / raw)
  To: Vinod Koul
  Cc: dmaengine, linux-kernel, devicetree, manugautam, danielmentz,
	sjadavani, Joy Chakraborty

Use DMA singles in a loop to load/store data to the peripheral for
the remaining bytes left after chucks of bursts are done, which is
handled by the _dregs() function.

If the transfer length is not a multiple of (AxLen*AxSize) then the
_dregs function takes care of setting up CCR with residual burst
required to complete the transaction. It does so by changing the
AxLen in CCR and 1 burst of Load and Store.
But some peripherals might not set the burst request signal to the DMA
controller since the number of bytes to transfer is less then the
initial size of burst requested i.e. AxLen*AxSize leading to a forever
wait.

Example of such a case :
    Considering a peripheral having an RX FIFO of n bytes and a sw
    configurable threshold level logic which drives the RX burst req
    signal to PL330 i.e. when data in the RX fifo crosses the threshold
    value the peripheral asserts the RX burst request to PL330 to copy
    data from the fifo in bursts.
    Taking an example say the Rx Fifo is 256 bytes in depth, the max
    AxLen is 16, max AxSize is 4bytes and 304 bytes had to copied from
    peripheral to memory.
    In this case the peripheral SW driver would configure the threshold
    to the maximum possible burst size (AxLen*AxSize) i.e. 64 bytes and
    pass the same to pl330 driver using src/dst_maxburst variable.
    PL330 would copy the first 256 bytes with 4 burst transactions and
    the 48 remaining bytes would be handled by _dregs().
    Currently _dregs() would setup a burst for AxLen=3 and AxSize=16 to
    copy the 48bytes but since 48bytes is below the threshold configured
    at the peripheral the Rx burst request signal would not get set
    leading to a forever wait and timeout.
    This logic will copy the remaining 48bytes using single transactions
    of 4bytes each which would not depend on the burst req signal from
    the peripheral.

Instructions generated for above example with logic change:
    DMAMOV CCR 0xbd0239
    DMAMOV SAR 0xffffe000
    DMAMOV DAR 0xffffc860
    DMALP_1 3
    DMAFLUSHP 0
    DMAWFPB 0
    DMALDB
    DMASTPB 0
    DMALPENDA_1 bjmpto_7
    DMAMOV CCR 0xad0229
    DMALDA
    DMALP_0 11
    DMAFLUSHP 0
    DMAWFPS 0
    DMASTPS 0
    DMALPENDA_0 bjmpto_6
    DMASEV 3
    DMAEND

Signed-off-by: Joy Chakraborty <joychakr@google.com>
---
 drivers/dma/pl330.c | 65 +++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 63 insertions(+), 2 deletions(-)

diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index 46e254fd4007..2145f601939e 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -1208,6 +1208,67 @@ static inline int _ldst_peripheral(struct pl330_dmac *pl330,
 	return off;
 }
 
+/*
+ * Sets up transfers to peripheral using DMA Singles instead of Bursts.
+ * Data is moved between fifo and memory in bursts following which it is
+ * loaded/stored to peripheral using Loops of DMA singles based on
+ * transfer direction.
+ */
+static inline int _ldst_periph_single_dregs(struct pl330_dmac *pl330,
+					    unsigned int dry_run, u8 buf[],
+					    const struct _xfer_spec *pxs,
+					    int src_length, int dst_length)
+{
+	int off = 0;
+	unsigned int ljmp, lpcnt;
+	struct _arg_LPEND lpend;
+	enum dma_transfer_direction direction = pxs->desc->rqtype;
+
+	if (direction == DMA_MEM_TO_DEV) {
+		off += _emit_load(dry_run, &buf[off], ALWAYS, direction,
+				  pxs->desc->peri);
+		lpcnt = dst_length;
+	} else {
+		lpcnt = src_length;
+	}
+
+	/*
+	 * Use Loop Cnt 0 to load/store from/to peripheral in single transactions
+	 * since Burst Req might not be set as pending transfer length maybe less
+	 * size of bytes to burst (AxSize * AxLen).
+	 */
+	off += _emit_LP(dry_run, &buf[off], 0, lpcnt);
+	ljmp = off;
+
+	/*
+	 * do FLUSHP at beginning to clear any stale dma requests before the
+	 * first WFP.
+	 */
+	if (!(pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP))
+		off += _emit_FLUSHP(dry_run, &buf[off], pxs->desc->peri);
+
+	off += _emit_WFP(dry_run, &buf[off], SINGLE, pxs->desc->peri);
+
+	if (direction == DMA_MEM_TO_DEV)
+		off += _emit_store(dry_run, &buf[off], SINGLE, direction,
+				   pxs->desc->peri);
+	else
+		off += _emit_load(dry_run, &buf[off], SINGLE, direction,
+				  pxs->desc->peri);
+
+	lpend.cond = ALWAYS;
+	lpend.forever = false;
+	lpend.loop = 0;
+	lpend.bjump = off - ljmp;
+	off += _emit_LPEND(dry_run, &buf[off], &lpend);
+
+	if (direction == DMA_DEV_TO_MEM)
+		off += _emit_store(dry_run, &buf[off], ALWAYS, direction,
+				   pxs->desc->peri);
+
+	return off;
+}
+
 static int _bursts(struct pl330_dmac *pl330, unsigned dry_run, u8 buf[],
 		const struct _xfer_spec *pxs, int cyc)
 {
@@ -1273,8 +1334,8 @@ static int _dregs(struct pl330_dmac *pl330, unsigned int dry_run, u8 buf[],
 	case DMA_MEM_TO_DEV:
 	case DMA_DEV_TO_MEM:
 		off += _emit_MOV(dry_run, &buf[off], CCR, dregs_ccr);
-		off += _ldst_peripheral(pl330, dry_run, &buf[off], pxs, 1,
-					BURST);
+		off += _ldst_periph_single_dregs(pl330, dry_run, &buf[off],
+							 pxs, src_length, dst_length);
 		break;
 
 	case DMA_MEM_TO_MEM:
-- 
2.40.1.606.ga4b1b128d6-goog


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2023-05-12 14:16 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-05-12 14:14 [PATCH v2 0/6] dmaengine: pl330: Updates and logical changes for peripheral usecases Joy Chakraborty
2023-05-12 14:14 ` [PATCH v2 1/6] dmaengine: pl330: Separate SRC and DST burst size and len Joy Chakraborty
2023-05-12 14:14 ` [PATCH v2 2/6] dmaengine: pl330: Use FFS to calculate burst size Joy Chakraborty
2023-05-12 14:14 ` [PATCH v2 3/6] dmaengine: pl330: Change if-else to switch-case for consistency Joy Chakraborty
2023-05-12 14:14 ` [PATCH v2 4/6] dmaengine: pl330: Change unused arg "len" from get_burst_len() Joy Chakraborty
2023-05-12 14:14 ` [PATCH v2 5/6] dmaengine: pl330: Optimize AxSize for peripheral usecases Joy Chakraborty
2023-05-12 14:14 ` [PATCH v2 6/6] dmaengine: pl330: Use dma singles for peripheral _dregs Joy Chakraborty

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®