Skip to contentSkip to conversation

dmaengine: dw-edma: Add support for native HDMA

PATCH Applied Cai Huoqing 4 participants 11 messages May 20, 2023 – Jun 7, 2023

Subsystems (5)
lore
Share & tools
lore
#0 lore raw

v11 cover dmaengine: dw-edma: Add support for native HDMA

Add support for HDMA NATIVE, as long the IP design has set the compatible register map parameter-HDMA_NATIVE, which allows compatibility for native HDMA register configuration.

The HDMA Hyper-DMA IP is an enhancement of the eDMA embedded-DMA IP. And the native HDMA registers are different from eDMA,
so this patch add support for HDMA NATIVE mode.

HDMA write and read channels operate independently to maximize the performance of the HDMA read and write data transfer over the link When you configure the HDMA with multiple read channels, then it uses a round robin (RR) arbitration scheme to select the next read channel to be serviced.The same applies when youhave multiple write channels.

The native HDMA driver also supports a maximum of 16 independent channels (8 write + 8 read), which can run simultaneously. Both SAR (Source Address Register) and DAR (Destination Address Register) are aligned to byte.

Cai Huoqing (4):
dmaengine: dw-edma: Rename dw_edma_core_ops structure to
dw_edma_plat_ops
dmaengine: dw-edma: Create a new dw_edma_core_ops structure to
abstract controller operation
dmaengine: dw-edma: Add support for native HDMA
dmaengine: dw-edma: Add HDMA DebugFS support

v10->v11: Using single name in commit log.

v10 link:
https://lore.kernel.org/lkml/[email protected]/

 drivers/dma/dw-edma/Makefile                 |   8 +-
 drivers/dma/dw-edma/dw-edma-core.c           |  86 ++----
 drivers/dma/dw-edma/dw-edma-core.h           |  58 ++++
 drivers/dma/dw-edma/dw-edma-pcie.c           |   4 +-
 drivers/dma/dw-edma/dw-edma-v0-core.c        |  85 +++++-
 drivers/dma/dw-edma/dw-edma-v0-core.h        |  14 +-
 drivers/dma/dw-edma/dw-hdma-v0-core.c        | 296 +++++++++++++++++++
 drivers/dma/dw-edma/dw-hdma-v0-core.h        |  17 ++
 drivers/dma/dw-edma/dw-hdma-v0-debugfs.c     | 170 +++++++++++
 drivers/dma/dw-edma/dw-hdma-v0-debugfs.h     |  22 ++
 drivers/dma/dw-edma/dw-hdma-v0-regs.h        | 129 ++++++++
 drivers/pci/controller/dwc/pcie-designware.c |   2 +-
 include/linux/dma/edma.h                     |   7 +-
 13 files changed, 807 insertions(+), 91 deletions(-)
 create mode 100644 drivers/dma/dw-edma/dw-hdma-v0-core.c
 create mode 100644 drivers/dma/dw-edma/dw-hdma-v0-core.h
 create mode 100644 drivers/dma/dw-edma/dw-hdma-v0-debugfs.c
 create mode 100644 drivers/dma/dw-edma/dw-hdma-v0-debugfs.h
 create mode 100644 drivers/dma/dw-edma/dw-hdma-v0-regs.h
Signature · 1 line
2.34.1
#1 lore raw

PATCH v11 1/4 dmaengine: dw-edma: Rename dw_edma_core_ops structure to dw_edma_plat_ops

The dw_edma_core_ops structure contains a set of the operations:
device IRQ numbers getter, CPU/PCI address translation. Based on the functions semantics the structure name "dw_edma_plat_ops" looks more descriptive since indeed the operations are platform-specific. The "dw_edma_core_ops" name shall be used for a structure with the IP-core specific set of callbacks in order to abstract out DW eDMA and DW HDMA setups. Such structure will be added in one of the next commit in the framework of the set of changes adding the DW HDMA device support.

Anyway the renaming was necessary to distinguish two types of the implementation callbacks:
1. DW eDMA/hDMA IP-core specific operations: device-specific CSR setups in one or another aspect of the DMA-engine initialization.
2. DW eDMA/hDMA platform specific operations: the DMA device environment configs like IRQs, address translation, etc.

---
v10->v11: Using single name in commit log.

v10 link:
https://lore.kernel.org/lkml/[email protected]/

 drivers/dma/dw-edma/dw-edma-pcie.c           | 4 ++--
 drivers/pci/controller/dwc/pcie-designware.c | 2 +-
 include/linux/dma/edma.h                     | 4 ++--
 3 files changed, 5 insertions(+), 5 deletions(-)
drivers/dma/dw-edma/dw-edma-pcie.c +2 -2
index 2b40f2b44f5e..1c6043751dc9 100644
@@ -109,7 +109,7 @@ static u64 dw_edma_pcie_address(struct device *dev, phys_addr_t cpu_addr)
 	return region.start;
 }
 
-static const struct dw_edma_core_ops dw_edma_pcie_core_ops = {
+static const struct dw_edma_plat_ops dw_edma_pcie_plat_ops = {
 	.irq_vector = dw_edma_pcie_irq_vector,
 	.pci_address = dw_edma_pcie_address,
 };
@@ -225,7 +225,7 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
 
 	chip->mf = vsec_data.mf;
 	chip->nr_irqs = nr_irqs;
-	chip->ops = &dw_edma_pcie_core_ops;
+	chip->ops = &dw_edma_pcie_plat_ops;
 
 	chip->ll_wr_cnt = vsec_data.wr_ch_cnt;
 	chip->ll_rd_cnt = vsec_data.rd_ch_cnt;
drivers/pci/controller/dwc/pcie-designware.c +1 -1
index 8e33e6e59e68..1f2ee71da4da 100644
@@ -828,7 +828,7 @@ static int dw_pcie_edma_irq_vector(struct device *dev, unsigned int nr)
 	return platform_get_irq_byname_optional(pdev, name);
 }
 
-static struct dw_edma_core_ops dw_pcie_edma_ops = {
+static struct dw_edma_plat_ops dw_pcie_edma_ops = {
 	.irq_vector = dw_pcie_edma_irq_vector,
 };
 
include/linux/dma/edma.h +2 -2
index d2638d9259dc..ed401c965a87 100644
@@ -40,7 +40,7 @@ struct dw_edma_region {
  *			iATU windows. That will be done by the controller
  *			automatically.
  */
-struct dw_edma_core_ops {
+struct dw_edma_plat_ops {
 	int (*irq_vector)(struct device *dev, unsigned int nr);
 	u64 (*pci_address)(struct device *dev, phys_addr_t cpu_addr);
 };
@@ -80,7 +80,7 @@ enum dw_edma_chip_flags {
 struct dw_edma_chip {
 	struct device		*dev;
 	int			nr_irqs;
-	const struct dw_edma_core_ops   *ops;
+	const struct dw_edma_plat_ops	*ops;
 	u32			flags;
 
 	void __iomem		*reg_base;
Signature · 1 line
2.34.1
#2 lore raw

PATCH v11 2/4 dmaengine: dw-edma: Create a new dw_edma_core_ops structure to abstract controller operation

In reply to Cai Huoqing

The structure dw_edma_core_ops has a set of the pointers abstracting out the DW eDMA vX and DW HDMA Native controllers. And use dw_edma_v0_core_register to set up operation.

---
v10->v11: Using single name in commit log.

v10 link:
https://lore.kernel.org/lkml/[email protected]/

 drivers/dma/dw-edma/dw-edma-core.c    | 82 ++++++++------------------
 drivers/dma/dw-edma/dw-edma-core.h    | 58 ++++++++++++++++++
 drivers/dma/dw-edma/dw-edma-v0-core.c | 85 +++++++++++++++++++++++----
 drivers/dma/dw-edma/dw-edma-v0-core.h | 14 +----
 4 files changed, 157 insertions(+), 82 deletions(-)
drivers/dma/dw-edma/dw-edma-core.c +25 -57
index 7d2b73ef0872..f17207c66c19 100644
@@ -183,6 +183,7 @@ static void vchan_free_desc(struct virt_dma_desc *vdesc)
 
 static int dw_edma_start_transfer(struct dw_edma_chan *chan)
 {
+	struct dw_edma *dw = chan->dw;
 	struct dw_edma_chunk *child;
 	struct dw_edma_desc *desc;
 	struct virt_dma_desc *vd;
@@ -200,7 +201,7 @@ static int dw_edma_start_transfer(struct dw_edma_chan *chan)
 	if (!child)
 		return 0;
 
-	dw_edma_v0_core_start(child, !desc->xfer_sz);
+	dw_edma_core_start(dw, child, !desc->xfer_sz);
 	desc->xfer_sz += child->ll_region.sz;
 	dw_edma_free_burst(child);
 	list_del(&child->list);
@@ -287,7 +288,7 @@ static int dw_edma_device_terminate_all(struct dma_chan *dchan)
 		chan->configured = false;
 	} else if (chan->status == EDMA_ST_IDLE) {
 		chan->configured = false;
-	} else if (dw_edma_v0_core_ch_status(chan) == DMA_COMPLETE) {
+	} else if (dw_edma_core_ch_status(chan) == DMA_COMPLETE) {
 		/*
 		 * The channel is in a false BUSY state, probably didn't
 		 * receive or lost an interrupt
@@ -599,8 +600,6 @@ static void dw_edma_done_interrupt(struct dw_edma_chan *chan)
 	struct virt_dma_desc *vd;
 	unsigned long flags;
 
-	dw_edma_v0_core_clear_done_int(chan);
-
 	spin_lock_irqsave(&chan->vc.lock, flags);
 	vd = vchan_next_desc(&chan->vc);
 	if (vd) {
@@ -641,8 +640,6 @@ static void dw_edma_abort_interrupt(struct dw_edma_chan *chan)
 	struct virt_dma_desc *vd;
 	unsigned long flags;
 
-	dw_edma_v0_core_clear_abort_int(chan);
-
 	spin_lock_irqsave(&chan->vc.lock, flags);
 	vd = vchan_next_desc(&chan->vc);
 	if (vd) {
@@ -654,63 +651,32 @@ static void dw_edma_abort_interrupt(struct dw_edma_chan *chan)
 	chan->status = EDMA_ST_IDLE;
 }
 
-static irqreturn_t dw_edma_interrupt(int irq, void *data, bool write)
+static inline irqreturn_t dw_edma_interrupt_write(int irq, void *data)
 {
 	struct dw_edma_irq *dw_irq = data;
-	struct dw_edma *dw = dw_irq->dw;
-	unsigned long total, pos, val;
-	unsigned long off;
-	u32 mask;
-
-	if (write) {
-		total = dw->wr_ch_cnt;
-		off = 0;
-		mask = dw_irq->wr_mask;
-	} else {
-		total = dw->rd_ch_cnt;
-		off = dw->wr_ch_cnt;
-		mask = dw_irq->rd_mask;
-	}
-
-	val = dw_edma_v0_core_status_done_int(dw, write ?
-							  EDMA_DIR_WRITE :
-							  EDMA_DIR_READ);
-	val &= mask;
-	for_each_set_bit(pos, &val, total) {
-		struct dw_edma_chan *chan = &dw->chan[pos + off];
-
-		dw_edma_done_interrupt(chan);
-	}
-
-	val = dw_edma_v0_core_status_abort_int(dw, write ?
-							   EDMA_DIR_WRITE :
-							   EDMA_DIR_READ);
-	val &= mask;
-	for_each_set_bit(pos, &val, total) {
-		struct dw_edma_chan *chan = &dw->chan[pos + off];
-
-		dw_edma_abort_interrupt(chan);
-	}
-
-	return IRQ_HANDLED;
-}
 
-static inline irqreturn_t dw_edma_interrupt_write(int irq, void *data)
-{
-	return dw_edma_interrupt(irq, data, true);
+	return dw_edma_core_handle_int(dw_irq, EDMA_DIR_WRITE,
+				       dw_edma_done_interrupt,
+				       dw_edma_abort_interrupt);
 }
 
 static inline irqreturn_t dw_edma_interrupt_read(int irq, void *data)
 {
-	return dw_edma_interrupt(irq, data, false);
+	struct dw_edma_irq *dw_irq = data;
+
+	return dw_edma_core_handle_int(dw_irq, EDMA_DIR_READ,
+				       dw_edma_done_interrupt,
+				       dw_edma_abort_interrupt);
 }
 
 static irqreturn_t dw_edma_interrupt_common(int irq, void *data)
 {
-	dw_edma_interrupt(irq, data, true);
-	dw_edma_interrupt(irq, data, false);
+	irqreturn_t ret = IRQ_NONE;
+
+	ret |= dw_edma_interrupt_write(irq, data);
+	ret |= dw_edma_interrupt_read(irq, data);
 
-	return IRQ_HANDLED;
+	return ret;
 }
 
 static int dw_edma_alloc_chan_resources(struct dma_chan *dchan)
@@ -811,7 +777,7 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc)
 
 		vchan_init(&chan->vc, dma);
 
-		dw_edma_v0_core_device_config(chan);
+		dw_edma_core_ch_config(chan);
 	}
 
 	/* Set DMA channel capabilities */
@@ -956,14 +922,16 @@ int dw_edma_probe(struct dw_edma_chip *chip)
 
 	dw->chip = chip;
 
+	dw_edma_v0_core_register(dw);
+
 	raw_spin_lock_init(&dw->lock);
 
 	dw->wr_ch_cnt = min_t(u16, chip->ll_wr_cnt,
-			      dw_edma_v0_core_ch_count(dw, EDMA_DIR_WRITE));
+			      dw_edma_core_ch_count(dw, EDMA_DIR_WRITE));
 	dw->wr_ch_cnt = min_t(u16, dw->wr_ch_cnt, EDMA_MAX_WR_CH);
 
 	dw->rd_ch_cnt = min_t(u16, chip->ll_rd_cnt,
-			      dw_edma_v0_core_ch_count(dw, EDMA_DIR_READ));
+			      dw_edma_core_ch_count(dw, EDMA_DIR_READ));
 	dw->rd_ch_cnt = min_t(u16, dw->rd_ch_cnt, EDMA_MAX_RD_CH);
 
 	if (!dw->wr_ch_cnt && !dw->rd_ch_cnt)
@@ -982,7 +950,7 @@ int dw_edma_probe(struct dw_edma_chip *chip)
 		 dev_name(chip->dev));
 
 	/* Disable eDMA, only to establish the ideal initial conditions */
-	dw_edma_v0_core_off(dw);
+	dw_edma_core_off(dw);
 
 	/* Request IRQs */
 	err = dw_edma_irq_request(dw, &wr_alloc, &rd_alloc);
@@ -995,7 +963,7 @@ int dw_edma_probe(struct dw_edma_chip *chip)
 		goto err_irq_free;
 
 	/* Turn debugfs on */
-	dw_edma_v0_core_debugfs_on(dw);
+	dw_edma_core_debugfs_on(dw);
 
 	chip->dw = dw;
 
@@ -1021,7 +989,7 @@ int dw_edma_remove(struct dw_edma_chip *chip)
 		return -ENODEV;
 
 	/* Disable eDMA */
-	dw_edma_v0_core_off(dw);
+	dw_edma_core_off(dw);
 
 	/* Free irqs */
 	for (i = (dw->nr_irqs - 1); i >= 0; i--)
drivers/dma/dw-edma/dw-edma-core.h +58 -0

68 lines not on this page · raw message

drivers/dma/dw-edma/dw-edma-v0-core.c +72 -13

158 lines not on this page · raw message

drivers/dma/dw-edma/dw-edma-v0-core.h +2 -12
index ab96a1f48080..04a882222f99 100644
@@ -11,17 +11,7 @@
 
 #include <linux/dma/edma.h>
 
-/* eDMA management callbacks */
-void dw_edma_v0_core_off(struct dw_edma *chan);
-u16 dw_edma_v0_core_ch_count(struct dw_edma *chan, enum dw_edma_dir dir);
-enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan);
-void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan);
-void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan);
-u32 dw_edma_v0_core_status_done_int(struct dw_edma *chan, enum dw_edma_dir dir);
-u32 dw_edma_v0_core_status_abort_int(struct dw_edma *chan, enum dw_edma_dir dir);
-void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first);
-int dw_edma_v0_core_device_config(struct dw_edma_chan *chan);
-/* eDMA debug fs callbacks */
-void dw_edma_v0_core_debugfs_on(struct dw_edma *dw);
+/* eDMA core register */
+void dw_edma_v0_core_register(struct dw_edma *dw);
 
 #endif /* _DW_EDMA_V0_CORE_H */
Signature · 1 line
2.34.1
#3 lore raw

PATCH v11 3/4 dmaengine: dw-edma: Add support for native HDMA

In reply to Cai Huoqing

Add support for HDMA NATIVE, as long the IP design has set the compatible register map parameter-HDMA_NATIVE, which allows compatibility for native HDMA register configuration.

The HDMA Hyper-DMA IP is an enhancement of the eDMA embedded-DMA IP. And the native HDMA registers are different from eDMA, so this patch add support for HDMA NATIVE mode.

HDMA write and read channels operate independently to maximize the performance of the HDMA read and write data transfer over the link When you configure the HDMA with multiple read channels, then it uses a round robin (RR) arbitration scheme to select the next read channel to be serviced.The same applies when you have multiple write channels.

The native HDMA driver also supports a maximum of 16 independent channels (8 write + 8 read), which can run simultaneously. Both SAR (Source Address Register) and DAR (Destination Address Register) are aligned to byte.

---
v10->v11: Using single name in commit log.

v10 link:
https://lore.kernel.org/lkml/[email protected]/

 drivers/dma/dw-edma/Makefile          |   5 +-
 drivers/dma/dw-edma/dw-edma-core.c    |   6 +-
 drivers/dma/dw-edma/dw-hdma-v0-core.c | 294 ++++++++++++++++++++++++++
 drivers/dma/dw-edma/dw-hdma-v0-core.h |  17 ++
 drivers/dma/dw-edma/dw-hdma-v0-regs.h | 129 +++++++++++
 include/linux/dma/edma.h              |   3 +-
 6 files changed, 450 insertions(+), 4 deletions(-)
 create mode 100644 drivers/dma/dw-edma/dw-hdma-v0-core.c
 create mode 100644 drivers/dma/dw-edma/dw-hdma-v0-core.h
 create mode 100644 drivers/dma/dw-edma/dw-hdma-v0-regs.h
drivers/dma/dw-edma/Makefile +3 -2
index 8d45c0d5689d..b1c91ef2c63d 100644
@@ -2,6 +2,7 @@
 
 obj-$(CONFIG_DW_EDMA)		+= dw-edma.o
 dw-edma-$(CONFIG_DEBUG_FS)	:= dw-edma-v0-debugfs.o
-dw-edma-objs			:= dw-edma-core.o \
-					dw-edma-v0-core.o $(dw-edma-y)
+dw-edma-objs			:= dw-edma-core.o	\
+				   dw-edma-v0-core.o	\
+				   dw-hdma-v0-core.o $(dw-edma-y)
 obj-$(CONFIG_DW_EDMA_PCIE)	+= dw-edma-pcie.o
drivers/dma/dw-edma/dw-edma-core.c +5 -1
index f17207c66c19..68236247059d 100644
@@ -18,6 +18,7 @@
 
 #include "dw-edma-core.h"
 #include "dw-edma-v0-core.h"
+#include "dw-hdma-v0-core.h"
 #include "../dmaengine.h"
 #include "../virt-dma.h"
 
@@ -922,7 +923,10 @@ int dw_edma_probe(struct dw_edma_chip *chip)
 
 	dw->chip = chip;
 
-	dw_edma_v0_core_register(dw);
+	if (dw->chip->mf == EDMA_MF_HDMA_NATIVE)
+		dw_hdma_v0_core_register(dw);
+	else
+		dw_edma_v0_core_register(dw);
 
 	raw_spin_lock_init(&dw->lock);
 
drivers/dma/dw-edma/dw-hdma-v0-core.c new file +294 -0

294 lines not on this page · raw message

drivers/dma/dw-edma/dw-hdma-v0-core.h new file +17 -0
new file mode 100644
index 000000000000..c373b4f0bd8a
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2023 Cai Huoqing
+ * Synopsys DesignWare HDMA v0 core
+ *
+ * Author: Cai Huoqing <[email protected]>
+ */
+
+#ifndef _DW_HDMA_V0_CORE_H
+#define _DW_HDMA_V0_CORE_H
+
+#include <linux/dma/edma.h>
+
+/* HDMA core register */
+void dw_hdma_v0_core_register(struct dw_edma *dw);
+
+#endif /* _DW_HDMA_V0_CORE_H */
drivers/dma/dw-edma/dw-hdma-v0-regs.h new file +129 -0

129 lines not on this page · raw message

include/linux/dma/edma.h +2 -1
index ed401c965a87..3080747689f6 100644
@@ -48,7 +48,8 @@ struct dw_edma_plat_ops {
 enum dw_edma_map_format {
 	EDMA_MF_EDMA_LEGACY = 0x0,
 	EDMA_MF_EDMA_UNROLL = 0x1,
-	EDMA_MF_HDMA_COMPAT = 0x5
+	EDMA_MF_HDMA_COMPAT = 0x5,
+	EDMA_MF_HDMA_NATIVE = 0x7,
 };
 
 /**
Signature · 1 line
2.34.1
#4 lore raw
Quoted 3 lines · On Sat, 20 May 2023 13:08:51 +0800 Cai Huoqing <[email protected]> wrote:

Add support for HDMA NATIVE, as long the IP design has set the compatible register map parameter-HDMA_NATIVE, which allows compatibility for native HDMA register configuration.

I know the patch has been merged in dmaengine tree but something seems weird on my side.

The akida_dw_edma_probe function is selecting the minimum read and write channels by doing the minimum between ll_wr_cnt and the ch_count callback. The hdma ch_count callback is counting the number of channels enabled by reading the number of ch_en registers set. At probe time there is no channels registers that has been set as it is done later in the dw_hdma_v0_core_start function. Then the dw_hdma_v0_core_ch_count will always return 0 at probe time and the number of channels will be set to 0 which is not what we want. Could I miss something?

See the functions bellow:

Quoted 2 lines

int akida_dw_edma_probe(struct dw_edma_chip *chip) {

...

Quoted 10 lines
        dw->wr_ch_cnt = min_t(u16, chip->ll_wr_cnt,                        
                              dw_edma_core_ch_count(dw, EDMA_DIR_WRITE));  
        dw->wr_ch_cnt = min_t(u16, dw->wr_ch_cnt, EDMA_MAX_WR_CH);         
                                                                           
        dw->rd_ch_cnt = min_t(u16, chip->ll_rd_cnt,                        
                              dw_edma_core_ch_count(dw, EDMA_DIR_READ));   
        dw->rd_ch_cnt = min_t(u16, dw->rd_ch_cnt, EDMA_MAX_RD_CH);         
                                                                           
        if (!dw->wr_ch_cnt && !dw->rd_ch_cnt)                              
                return -EINVAL;            

...
}

Quoted 29 lines
drivers/dma/dw-edma/dw-hdma-v0-core.c new file +26 -0
+static u16 dw_hdma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir)
+{
+	u32 num_ch = 0;
+	int id;
+
+	for (id = 0; id < HDMA_V0_MAX_NR_CH; id++) {
+		if (GET_CH_32(dw, id, dir, ch_en) & BIT(0))
+			num_ch++;
+	}
+
+	if (num_ch > HDMA_V0_MAX_NR_CH)
+		num_ch = HDMA_V0_MAX_NR_CH;
+
+	return (u16)num_ch;
+}
 
 
 
+static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
+{
+	struct dw_edma_chan *chan = chunk->chan;
+	struct dw_edma *dw = chan->dw;
+	u32 tmp;
+
+	dw_hdma_v0_core_write_chunk(chunk);
+
+	if (first) {
+		/* Enable engine */
+		SET_CH_32(dw, chan->dir, chan->id, ch_en, BIT(0));

...

Quoted 1 line

+}

#5 lore raw

Hi Köry

Quoted 7 lines · Köry Maincent
Quoted 3 lines · On Sat, 20 May 2023 13:08:51 +0800 Cai Huoqing <[email protected]> wrote:

Add support for HDMA NATIVE, as long the IP design has set the compatible register map parameter-HDMA_NATIVE, which allows compatibility for native HDMA register configuration.

Quoted 5 lines

I know the patch has been merged in dmaengine tree but something seems weird on my side.

The akida_dw_edma_probe function is selecting the minimum read and write

First of all. What is akida platform you are referring to? I failed to find any mention in the mainline kernel repo.

Quoted 7 lines

channels by doing the minimum between ll_wr_cnt and the ch_count callback. The hdma ch_count callback is counting the number of channels enabled by reading the number of ch_en registers set. At probe time there is no channels registers that has been set as it is done later in the dw_hdma_v0_core_start function. Then the dw_hdma_v0_core_ch_count will always return 0 at probe time and the number of channels will be set to 0 which is not what we want. Could I miss something?

Based on the HDMA patches content you are right. The channels must be pre-enabled in order to have the dw_hdma_v0_core_ch_count() procedure to work correctly otherwise you'll indeed end up with an empty list of channels. I don't have any device with the HDMA engine embedded so I couldn't have possibly tracked that peculiarity on review. Anyway AFAICS Cai just implemented a method which seemed to work for his hardware setup.

If you think it doesn't work correctly or it isn't portable enough then you are free to provide your own implementation of the method and submit a patch. I hope Cai will be willing to test it out to make sure that it works correctly for you and his platforms.

As for me if I were on your place I would have implemented a loop which would walk over all possible HDMA channels (HDMA_V0_MAX_NR_CH) and counted all channels which could be enabled. If the ch_en CSR is writable (that is the channel could be enabled) then it shall be considered as existent. Of course before that I would have made sure that the non-existent channels had non-writable ch_en CSR.

-Serge(y)

Quoted 52 lines

See the functions bellow:

Quoted 2 lines

int akida_dw_edma_probe(struct dw_edma_chip *chip) {

...

Quoted 10 lines
        dw->wr_ch_cnt = min_t(u16, chip->ll_wr_cnt,                        
                              dw_edma_core_ch_count(dw, EDMA_DIR_WRITE));  
        dw->wr_ch_cnt = min_t(u16, dw->wr_ch_cnt, EDMA_MAX_WR_CH);         
                                                                           
        dw->rd_ch_cnt = min_t(u16, chip->ll_rd_cnt,                        
                              dw_edma_core_ch_count(dw, EDMA_DIR_READ));   
        dw->rd_ch_cnt = min_t(u16, dw->rd_ch_cnt, EDMA_MAX_RD_CH);         
                                                                           
        if (!dw->wr_ch_cnt && !dw->rd_ch_cnt)                              
                return -EINVAL;            

...
}

Quoted 29 lines
drivers/dma/dw-edma/dw-hdma-v0-core.c new file +26 -0
+static u16 dw_hdma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir)
+{
+	u32 num_ch = 0;
+	int id;
+
+	for (id = 0; id < HDMA_V0_MAX_NR_CH; id++) {
+		if (GET_CH_32(dw, id, dir, ch_en) & BIT(0))
+			num_ch++;
+	}
+
+	if (num_ch > HDMA_V0_MAX_NR_CH)
+		num_ch = HDMA_V0_MAX_NR_CH;
+
+	return (u16)num_ch;
+}
 
 
 
+static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
+{
+	struct dw_edma_chan *chan = chunk->chan;
+	struct dw_edma *dw = chan->dw;
+	u32 tmp;
+
+	dw_hdma_v0_core_write_chunk(chunk);
+
+	if (first) {
+		/* Enable engine */
+		SET_CH_32(dw, chan->dir, chan->id, ch_en, BIT(0));

...

Quoted 1 line

+}

#6 lore raw
Quoted 3 lines · On Wed, 7 Jun 2023 14:49:50 +0300 Serge Semin <[email protected]> wrote:

Hi Köry
First of all. What is akida platform you are referring to? I failed to find any mention in the mainline kernel repo.

Yes, sorry akida is the project prefix I am currently working on. It is simply a prefix for the symbol export to be different than mainline, don't take it into account.

Quoted 8 lines
Quoted 7 lines

channels by doing the minimum between ll_wr_cnt and the ch_count callback. The hdma ch_count callback is counting the number of channels enabled by reading the number of ch_en registers set. At probe time there is no channels registers that has been set as it is done later in the dw_hdma_v0_core_start function. Then the dw_hdma_v0_core_ch_count will always return 0 at probe time and the number of channels will be set to 0 which is not what we want. Could I miss something?

Quoted 7 lines

Based on the HDMA patches content you are right. The channels must be pre-enabled in order to have the dw_hdma_v0_core_ch_count() procedure to work correctly otherwise you'll indeed end up with an empty list of channels. I don't have any device with the HDMA engine embedded so I couldn't have possibly tracked that peculiarity on review. Anyway AFAICS Cai just implemented a method which seemed to work for his hardware setup.

Alright, on my side I have a board using this FPGA implementation and it indeed doesn't work as is.

Quoted 11 lines

If you think it doesn't work correctly or it isn't portable enough then you are free to provide your own implementation of the method and submit a patch. I hope Cai will be willing to test it out to make sure that it works correctly for you and his platforms.

As for me if I were on your place I would have implemented a loop which would walk over all possible HDMA channels (HDMA_V0_MAX_NR_CH) and counted all channels which could be enabled. If the ch_en CSR is writable (that is the channel could be enabled) then it shall be considered as existent. Of course before that I would have made sure that the non-existent channels had non-writable ch_en CSR.

This could be a nice idea but it doesn't work, non-existent channels seems to be writable. The datasheet of the HDMA IP doesn't have any register to find out the maximum existent channel.
As there is no way to know this, the dw_hdma_v0_core_ch_count will simply return HDMA_V0_MAX_NR_CH.
Cai how does it works on your side? does the ch_en register already enabled by the implementation?

#7 lore raw
Quoted 43 lines · Köry Maincent
Quoted 3 lines · On Wed, 7 Jun 2023 14:49:50 +0300 Serge Semin <[email protected]> wrote:

Hi Köry
First of all. What is akida platform you are referring to? I failed to find any mention in the mainline kernel repo.

Yes, sorry akida is the project prefix I am currently working on. It is simply a prefix for the symbol export to be different than mainline, don't take it into account.

Quoted 8 lines
Quoted 7 lines

channels by doing the minimum between ll_wr_cnt and the ch_count callback. The hdma ch_count callback is counting the number of channels enabled by reading the number of ch_en registers set. At probe time there is no channels registers that has been set as it is done later in the dw_hdma_v0_core_start function. Then the dw_hdma_v0_core_ch_count will always return 0 at probe time and the number of channels will be set to 0 which is not what we want. Could I miss something?

Quoted 7 lines

Based on the HDMA patches content you are right. The channels must be pre-enabled in order to have the dw_hdma_v0_core_ch_count() procedure to work correctly otherwise you'll indeed end up with an empty list of channels. I don't have any device with the HDMA engine embedded so I couldn't have possibly tracked that peculiarity on review. Anyway AFAICS Cai just implemented a method which seemed to work for his hardware setup.

Alright, on my side I have a board using this FPGA implementation and it indeed doesn't work as is.

Quoted 11 lines

If you think it doesn't work correctly or it isn't portable enough then you are free to provide your own implementation of the method and submit a patch. I hope Cai will be willing to test it out to make sure that it works correctly for you and his platforms.

As for me if I were on your place I would have implemented a loop which would walk over all possible HDMA channels (HDMA_V0_MAX_NR_CH) and counted all channels which could be enabled. If the ch_en CSR is writable (that is the channel could be enabled) then it shall be considered as existent. Of course before that I would have made sure that the non-existent channels had non-writable ch_en CSR.

Quoted 5 lines

This could be a nice idea but it doesn't work, non-existent channels seems to be writable. The datasheet of the HDMA IP doesn't have any register to find out the maximum existent channel.
As there is no way to know this, the dw_hdma_v0_core_ch_count will simply return HDMA_V0_MAX_NR_CH.

What about some other CSRs (llp, etc)? Are they all writable even if the respective channel doesn't exist?

If it isn't possible to auto-detect a number of channels then indeed we need to come up with a strategy to rely on the platform settings instead. Your suggestion seems to be the best choice seeing there is a commit 16b90dd94d3f ("dmaengine: dw-edma: Improve number of channels check") which converts the rely-on-platform-settings pattern to the current rely-on-min-between-plat-and-hw-capability.

Cai?

-Serge(y)

Quoted 2 lines

Cai how does it works on your side? does the ch_en register already enabled by the implementation?

#8 lore raw
In reply to Köry Maincent
Quoted 10 lines · Köry Maincent
Quoted 3 lines · On Sat, 20 May 2023 13:08:51 +0800 Cai Huoqing <[email protected]> wrote:

Add support for HDMA NATIVE, as long the IP design has set the compatible register map parameter-HDMA_NATIVE, which allows compatibility for native HDMA register configuration.

I know the patch has been merged in dmaengine tree but something seems weird on my side.

Quoted 7 lines

The akida_dw_edma_probe function is selecting the minimum read and write channels by doing the minimum between ll_wr_cnt and the ch_count callback. The hdma ch_count callback is counting the number of channels enabled by reading the number of ch_en registers set. At probe time there is no channels registers that has been set as it is done later in the dw_hdma_v0_core_start function. Then the dw_hdma_v0_core_ch_count will always return 0 at probe time and the number of channels will be set to 0 which is not what we want.

will check it

Thanks,
Cai-

Quoted 53 lines

Could I miss something?

See the functions bellow:

Quoted 2 lines

int akida_dw_edma_probe(struct dw_edma_chip *chip) {

...

Quoted 10 lines
        dw->wr_ch_cnt = min_t(u16, chip->ll_wr_cnt,                        
                              dw_edma_core_ch_count(dw, EDMA_DIR_WRITE));  
        dw->wr_ch_cnt = min_t(u16, dw->wr_ch_cnt, EDMA_MAX_WR_CH);         
                                                                           
        dw->rd_ch_cnt = min_t(u16, chip->ll_rd_cnt,                        
                              dw_edma_core_ch_count(dw, EDMA_DIR_READ));   
        dw->rd_ch_cnt = min_t(u16, dw->rd_ch_cnt, EDMA_MAX_RD_CH);         
                                                                           
        if (!dw->wr_ch_cnt && !dw->rd_ch_cnt)                              
                return -EINVAL;            

...
}

Quoted 29 lines
drivers/dma/dw-edma/dw-hdma-v0-core.c new file +26 -0
+static u16 dw_hdma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir)
+{
+	u32 num_ch = 0;
+	int id;
+
+	for (id = 0; id < HDMA_V0_MAX_NR_CH; id++) {
+		if (GET_CH_32(dw, id, dir, ch_en) & BIT(0))
+			num_ch++;
+	}
+
+	if (num_ch > HDMA_V0_MAX_NR_CH)
+		num_ch = HDMA_V0_MAX_NR_CH;
+
+	return (u16)num_ch;
+}
 
 
 
+static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
+{
+	struct dw_edma_chan *chan = chunk->chan;
+	struct dw_edma *dw = chan->dw;
+	u32 tmp;
+
+	dw_hdma_v0_core_write_chunk(chunk);
+
+	if (first) {
+		/* Enable engine */
+		SET_CH_32(dw, chan->dir, chan->id, ch_en, BIT(0));

...

Quoted 1 line

+}

#9 lore raw

PATCH v11 4/4 dmaengine: dw-edma: Add HDMA DebugFS support

In reply to Cai Huoqing

Add HDMA DebugFS support to show registers content

---
v10->v11: Using single name in commit log.

v10 link:
https://lore.kernel.org/lkml/[email protected]/

 drivers/dma/dw-edma/Makefile             |   3 +-
 drivers/dma/dw-edma/dw-hdma-v0-core.c    |   2 +
 drivers/dma/dw-edma/dw-hdma-v0-debugfs.c | 170 +++++++++++++++++++++++
 drivers/dma/dw-edma/dw-hdma-v0-debugfs.h |  22 +++
 4 files changed, 196 insertions(+), 1 deletion(-)
 create mode 100644 drivers/dma/dw-edma/dw-hdma-v0-debugfs.c
 create mode 100644 drivers/dma/dw-edma/dw-hdma-v0-debugfs.h
drivers/dma/dw-edma/Makefile +2 -1
index b1c91ef2c63d..83ab58f87760 100644
@@ -1,7 +1,8 @@
 # SPDX-License-Identifier: GPL-2.0
 
 obj-$(CONFIG_DW_EDMA)		+= dw-edma.o
-dw-edma-$(CONFIG_DEBUG_FS)	:= dw-edma-v0-debugfs.o
+dw-edma-$(CONFIG_DEBUG_FS)	:= dw-edma-v0-debugfs.o	\
+				   dw-hdma-v0-debugfs.o
 dw-edma-objs			:= dw-edma-core.o	\
 				   dw-edma-v0-core.o	\
 				   dw-hdma-v0-core.o $(dw-edma-y)
drivers/dma/dw-edma/dw-hdma-v0-core.c +2 -0
index 22b7b0410deb..00b735a0202a 100644
@@ -11,6 +11,7 @@
 #include "dw-edma-core.h"
 #include "dw-hdma-v0-core.h"
 #include "dw-hdma-v0-regs.h"
+#include "dw-hdma-v0-debugfs.h"
 
 enum dw_hdma_control {
 	DW_HDMA_V0_CB					= BIT(0),
@@ -276,6 +277,7 @@ static void dw_hdma_v0_core_ch_config(struct dw_edma_chan *chan)
 /* HDMA debugfs callbacks */
 static void dw_hdma_v0_core_debugfs_on(struct dw_edma *dw)
 {
+	dw_hdma_v0_debugfs_on(dw);
 }
 
 static const struct dw_edma_core_ops dw_hdma_v0_core = {
drivers/dma/dw-edma/dw-hdma-v0-debugfs.c new file +170 -0

170 lines not on this page · raw message

drivers/dma/dw-edma/dw-hdma-v0-debugfs.h new file +22 -0
new file mode 100644
index 000000000000..e6842c83777d
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2023 Cai Huoqing
+ * Synopsys DesignWare HDMA v0 debugfs
+ *
+ * Author: Cai Huoqing <[email protected]>
+ */
+
+#ifndef _DW_HDMA_V0_DEBUG_FS_H
+#define _DW_HDMA_V0_DEBUG_FS_H
+
+#include <linux/dma/edma.h>
+
+#ifdef CONFIG_DEBUG_FS
+void dw_hdma_v0_debugfs_on(struct dw_edma *dw);
+#else
+static inline void dw_hdma_v0_debugfs_on(struct dw_edma *dw)
+{
+}
+#endif /* CONFIG_DEBUG_FS */
+
+#endif /* _DW_HDMA_V0_DEBUG_FS_H */
Signature · 1 line
2.34.1
maintainer Applied
#10 lore raw
In reply to Cai Huoqing
Quoted 15 lines · Cai Huoqing

Add support for HDMA NATIVE, as long the IP design has set the compatible register map parameter-HDMA_NATIVE, which allows compatibility for native HDMA register configuration.

The HDMA Hyper-DMA IP is an enhancement of the eDMA embedded-DMA IP. And the native HDMA registers are different from eDMA,
so this patch add support for HDMA NATIVE mode.

HDMA write and read channels operate independently to maximize the performance of the HDMA read and write data transfer over the link When you configure the HDMA with multiple read channels, then it uses a round robin (RR) arbitration scheme to select the next read channel to be serviced.The same applies when youhave multiple write channels.

Quoted 4 lines

The native HDMA driver also supports a maximum of 16 independent channels (8 write + 8 read), which can run simultaneously. Both SAR (Source Address Register) and DAR (Destination Address Register) are aligned to byte.

Applied, thanks

Signature · 1 line
~Vinod