From: Cai huoqing <[email protected]>
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 alignmented to byte.dmaengine: dw-edma: Add support for native HDMA
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
v2->v3:
[1/4]
1.Add more commit log to explain why use dw_edma_plat_ops.
2.Update the structure name in the DW PCIe driver.
[2/4]
3.Use the reverse xmas tree vars definition order.
4.Add edma core ops wrapper.
5.Add dw_edma_done_interrupt() and dw_edma_abort_interrupt()
global methods.
6.Fix some indentation.
7.Fix some typo
8.Make use off dw_edma_core prefix instead of dw_xdma_core_.
[3/4]
9.Remove unnecessary include: dw-edma-v0-regs.h and dw-edma-v0-regs.h
10.HDMA supports the LL descriptors placed on the CPU memory.
[4/4]
11.Split DebugFS to be a separate patch.
12.Refactor HDMA DebugFS like the series in @Bjorn tree.
v2 link:
https://lore.kernel.org/lkml/20220925173412.u2ez6rbmfc5fupdn@mobilestation/
drivers/dma/dw-edma/Makefile | 8 +-
drivers/dma/dw-edma/dw-edma-core.c | 63 ++--
drivers/dma/dw-edma/dw-edma-core.h | 92 ++++++
drivers/dma/dw-edma/dw-edma-pcie.c | 4 +-
drivers/dma/dw-edma/dw-edma-v0-core.c | 88 ++++-
drivers/dma/dw-edma/dw-edma-v0-core.h | 14 +-
drivers/dma/dw-edma/dw-hdma-v0-core.c | 317 +++++++++++++++++++
drivers/dma/dw-edma/dw-hdma-v0-core.h | 17 +
drivers/dma/dw-edma/dw-hdma-v0-debugfs.c | 175 ++++++++++
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, 860 insertions(+), 78 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
--
2.34.1
From: Cai huoqing <[email protected]>
Add HDMA DebugFS support to show register information
Signed-off-by: Cai huoqing <[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 | 175 +++++++++++++++++++++++
drivers/dma/dw-edma/dw-hdma-v0-debugfs.h | 22 +++
4 files changed, 201 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
diff --git a/drivers/dma/dw-edma/Makefile b/drivers/dma/dw-edma/Makefile
index b1c91ef2c63d..83ab58f87760 100644
--- a/drivers/dma/dw-edma/Makefile
+++ b/drivers/dma/dw-edma/Makefile
@@ -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)
diff --git a/drivers/dma/dw-edma/dw-hdma-v0-core.c b/drivers/dma/dw-edma/dw-hdma-v0-core.c
index 7e4f98987e29..3723d5d8127c 100644
--- a/drivers/dma/dw-edma/dw-hdma-v0-core.c
+++ b/drivers/dma/dw-edma/dw-hdma-v0-core.c
@@ -9,6 +9,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),
@@ -294,6 +295,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 hdma_core = {
diff --git a/drivers/dma/dw-edma/dw-hdma-v0-debugfs.c b/drivers/dma/dw-edma/dw-hdma-v0-debugfs.c
new file mode 100644
index 000000000000..a0fafd788c14
--- /dev/null
+++ b/drivers/dma/dw-edma/dw-hdma-v0-debugfs.c
@@ -0,0 +1,175 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2023 Cai Huoqing
+ * Synopsys DesignWare HDMA v0 debugfs
+ *
+ * Author: Cai Huoqing <[email protected]>
+ */
+
+#include <linux/debugfs.h>
+#include <linux/bitfield.h>
+
+#include "dw-hdma-v0-debugfs.h"
+#include "dw-hdma-v0-regs.h"
+#include "dw-edma-core.h"
+
+#define REGS_ADDR(dw, name) \
+ ({ \
+ struct dw_hdma_v0_regs __iomem *__regs = (dw)->chip->reg_base; \
+ \
+ (void __iomem *)&__regs->name; \
+ })
+
+#define REGS_CH_ADDR(dw, name, _dir, _ch) \
+ ({ \
+ struct dw_hdma_v0_ch_regs __iomem *__ch_regs; \
+ \
+ if (_dir == EDMA_DIR_READ) \
+ __ch_regs = REGS_ADDR(dw, ch[_ch].rd); \
+ else \
+ __ch_regs = REGS_ADDR(dw, ch[_ch].wr); \
+ \
+ (void __iomem *)&__ch_regs->name; \
+ })
+
+#define CTX_REGISTER(dw, name, dir, ch) \
+ { dw, #name, REGS_CH_ADDR(dw, name, dir, ch), dir, ch }
+
+#define REGISTER(dw, name) \
+ { dw, #name, REGS_ADDR(dw, name) }
+
+#define WRITE_STR "write"
+#define READ_STR "read"
+#define CHANNEL_STR "channel"
+#define REGISTERS_STR "registers"
+
+struct dw_hdma_debugfs_entry {
+ struct dw_edma *dw;
+ const char *name;
+ void __iomem *reg;
+ enum dw_edma_dir dir;
+ u16 ch;
+};
+
+static int dw_hdma_debugfs_u32_get(void *data, u64 *val)
+{
+ void __iomem *reg = (void __force __iomem *)data;
+ *val = readl(reg);
+
+ return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(fops_x32, dw_hdma_debugfs_u32_get, NULL, "0x%08llx\n");
+
+static void dw_hdma_debugfs_create_x32(struct dw_edma *dw,
+ const struct dw_hdma_debugfs_entry ini[],
+ int nr_entries, struct dentry *dent)
+{
+ struct dw_hdma_debugfs_entry *entries;
+ int i;
+
+ entries = devm_kcalloc(dw->chip->dev, nr_entries, sizeof(*entries),
+ GFP_KERNEL);
+ for (i = 0; i < nr_entries; i++) {
+ entries[i] = ini[i];
+
+ debugfs_create_file_unsafe(entries[i].name, 0444, dent,
+ &entries[i], &fops_x32);
+ }
+}
+
+static void dw_hdma_debugfs_regs_ch(struct dw_edma *dw, enum dw_edma_dir dir,
+ u16 ch, struct dentry *dent)
+{
+ int nr_entries;
+ struct dw_hdma_debugfs_entry debugfs_regs[] = {
+ CTX_REGISTER(dw, ch_en, dir, ch),
+ CTX_REGISTER(dw, doorbell, dir, ch),
+ CTX_REGISTER(dw, llp.lsb, dir, ch),
+ CTX_REGISTER(dw, llp.msb, dir, ch),
+ CTX_REGISTER(dw, cycle_sync, dir, ch),
+ CTX_REGISTER(dw, transfer_size, dir, ch),
+ CTX_REGISTER(dw, sar.lsb, dir, ch),
+ CTX_REGISTER(dw, sar.msb, dir, ch),
+ CTX_REGISTER(dw, dar.lsb, dir, ch),
+ CTX_REGISTER(dw, dar.msb, dir, ch),
+ CTX_REGISTER(dw, control1, dir, ch),
+ CTX_REGISTER(dw, ch_stat, dir, ch),
+ CTX_REGISTER(dw, int_stat, dir, ch),
+ CTX_REGISTER(dw, int_setup, dir, ch),
+ CTX_REGISTER(dw, int_clear, dir, ch),
+ CTX_REGISTER(dw, msi_stop.lsb, dir, ch),
+ CTX_REGISTER(dw, msi_stop.msb, dir, ch),
+ CTX_REGISTER(dw, msi_abort.lsb, dir, ch),
+ CTX_REGISTER(dw, msi_abort.msb, dir, ch),
+ CTX_REGISTER(dw, msi_msgdata, dir, ch),
+ };
+
+ nr_entries = ARRAY_SIZE(debugfs_regs);
+ dw_hdma_debugfs_create_x32(dw, debugfs_regs, nr_entries, dent);
+}
+
+static void dw_hdma_debugfs_regs_wr(struct dw_edma *dw, struct dentry *dent)
+{
+ struct dentry *regs_dent, *ch_dent;
+ int i;
+ char name[16];
+
+ regs_dent = debugfs_create_dir(WRITE_STR, dent);
+ if (!regs_dent)
+ return;
+
+ for (i = 0; i < dw->wr_ch_cnt; i++) {
+ snprintf(name, sizeof(name), "%s:%d", CHANNEL_STR, i);
+
+ ch_dent = debugfs_create_dir(name, regs_dent);
+ if (!ch_dent)
+ return;
+
+ dw_hdma_debugfs_regs_ch(dw, EDMA_DIR_WRITE, i, ch_dent);
+ }
+}
+
+static void dw_hdma_debugfs_regs_rd(struct dw_edma *dw, struct dentry *dent)
+{
+ struct dentry *regs_dent, *ch_dent;
+ int i;
+ char name[16];
+
+ regs_dent = debugfs_create_dir(READ_STR, dent);
+ if (!regs_dent)
+ return;
+
+ for (i = 0; i < dw->rd_ch_cnt; i++) {
+ snprintf(name, sizeof(name), "%s:%d", CHANNEL_STR, i);
+
+ ch_dent = debugfs_create_dir(name, regs_dent);
+ if (!ch_dent)
+ return;
+
+ dw_hdma_debugfs_regs_ch(dw, EDMA_DIR_READ, i, ch_dent);
+ }
+}
+
+static void dw_hdma_debugfs_regs(struct dw_edma *dw)
+{
+ struct dentry *regs_dent;
+
+ regs_dent = debugfs_create_dir(REGISTERS_STR, dw->dma.dbg_dev_root);
+ if (!regs_dent)
+ return;
+
+ dw_hdma_debugfs_regs_wr(dw, regs_dent);
+ dw_hdma_debugfs_regs_rd(dw, regs_dent);
+}
+
+void dw_hdma_v0_debugfs_on(struct dw_edma *dw)
+{
+ if (!debugfs_initialized())
+ return;
+
+ debugfs_create_u32("mf", 0444, dw->dma.dbg_dev_root, &dw->chip->mf);
+ debugfs_create_u16("wr_ch_cnt", 0444, dw->dma.dbg_dev_root, &dw->wr_ch_cnt);
+ debugfs_create_u16("rd_ch_cnt", 0444, dw->dma.dbg_dev_root, &dw->rd_ch_cnt);
+
+ dw_hdma_debugfs_regs(dw);
+}
diff --git a/drivers/dma/dw-edma/dw-hdma-v0-debugfs.h b/drivers/dma/dw-edma/dw-hdma-v0-debugfs.h
new file mode 100644
index 000000000000..e6842c83777d
--- /dev/null
+++ b/drivers/dma/dw-edma/dw-hdma-v0-debugfs.h
@@ -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 */
--
2.34.1
From: Cai huoqing <[email protected]>
Rename dw_edma_core_ops structure to dw_edma_plat_ops, the ops is platform
specific operations: the DMA device environment configs like IRQs,
address translation, etc.
The dw_edma_pcie_plat_ops name was supposed to refer to the platform which
the DW eDMA engine is embedded to, like PCIe end-point (accessible via
the PCIe bus) or a PCIe root port (directly accessible by CPU).
Needless to say that for them the IRQ-vector and PCI-addresses are
differently determined. The suggested name has a connection with the
kernel platform device only as a private case of the eDMA/hDMA embedded
into the DW PCI Root ports, though basically it was supposed to refer to
any platform in which the DMA hardware lives.
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.
dw_edma_pcie_core_ops is supposed to be used for the case 1, and
dw_edma_pcie_plat_ops - for the case 2.
Signed-off-by: Cai huoqing <[email protected]>
---
drivers/dma/dw-edma/dw-edma-pcie.c | 4 ++--
drivers/pci/controller/dwc/pcie-designware.c | 2 +-
include/linux/dma/edma.h | 7 ++++---
3 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/drivers/dma/dw-edma/dw-edma-pcie.c b/drivers/dma/dw-edma/dw-edma-pcie.c
index 2b40f2b44f5e..1c6043751dc9 100644
--- a/drivers/dma/dw-edma/dw-edma-pcie.c
+++ b/drivers/dma/dw-edma/dw-edma-pcie.c
@@ -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;
diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
index 53a16b8b6ac2..44e90b71d429 100644
--- a/drivers/pci/controller/dwc/pcie-designware.c
+++ b/drivers/pci/controller/dwc/pcie-designware.c
@@ -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,
};
diff --git a/include/linux/dma/edma.h b/include/linux/dma/edma.h
index d2638d9259dc..b2f3dd5e7e1a 100644
--- a/include/linux/dma/edma.h
+++ b/include/linux/dma/edma.h
@@ -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);
};
@@ -48,7 +48,8 @@ struct dw_edma_core_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
};
/**
@@ -80,7 +81,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;
--
2.34.1
From: Cai huoqing <[email protected]>
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.
Signed-off-by: Cai huoqing <[email protected]>
---
drivers/dma/dw-edma/dw-edma-core.c | 63 ++++++------------
drivers/dma/dw-edma/dw-edma-core.h | 92 +++++++++++++++++++++++++++
drivers/dma/dw-edma/dw-edma-v0-core.c | 88 +++++++++++++++++++++----
drivers/dma/dw-edma/dw-edma-v0-core.h | 14 +---
4 files changed, 188 insertions(+), 69 deletions(-)
diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
index 1906a836f0aa..c171809beba1 100644
--- a/drivers/dma/dw-edma/dw-edma-core.c
+++ b/drivers/dma/dw-edma/dw-edma-core.c
@@ -183,6 +183,7 @@ static void vchan_free_desc(struct virt_dma_desc *vdesc)
static void 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 void dw_edma_start_transfer(struct dw_edma_chan *chan)
if (!child)
return;
- 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);
@@ -285,7 +286,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
@@ -588,13 +589,13 @@ dw_edma_device_prep_interleaved_dma(struct dma_chan *dchan,
return dw_edma_device_transfer(&xfer);
}
-static void dw_edma_done_interrupt(struct dw_edma_chan *chan)
+void dw_edma_done_interrupt(struct dw_edma_chan *chan)
{
struct dw_edma_desc *desc;
struct virt_dma_desc *vd;
unsigned long flags;
- dw_edma_v0_core_clear_done_int(chan);
+ dw_edma_core_clear_done_int(chan);
spin_lock_irqsave(&chan->vc.lock, flags);
vd = vchan_next_desc(&chan->vc);
@@ -631,12 +632,12 @@ static void dw_edma_done_interrupt(struct dw_edma_chan *chan)
spin_unlock_irqrestore(&chan->vc.lock, flags);
}
-static void dw_edma_abort_interrupt(struct dw_edma_chan *chan)
+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);
+ dw_edma_core_clear_abort_int(chan);
spin_lock_irqsave(&chan->vc.lock, flags);
vd = vchan_next_desc(&chan->vc);
@@ -651,41 +652,11 @@ static void dw_edma_abort_interrupt(struct dw_edma_chan *chan)
static irqreturn_t dw_edma_interrupt(int irq, void *data, bool write)
{
+ enum dw_edma_dir dir = write ? EDMA_DIR_WRITE : EDMA_DIR_READ;
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);
- }
+ dw_edma_core_done_handle(dw_irq, dir);
+ dw_edma_core_abort_handle(dw_irq, dir);
return IRQ_HANDLED;
}
@@ -806,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 */
@@ -951,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)
@@ -977,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);
@@ -990,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;
@@ -1016,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--)
diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h
index 0ab2b6dba880..2555ae0e1b8b 100644
--- a/drivers/dma/dw-edma/dw-edma-core.h
+++ b/drivers/dma/dw-edma/dw-edma-core.h
@@ -111,6 +111,23 @@ struct dw_edma {
raw_spinlock_t lock; /* Only for legacy */
struct dw_edma_chip *chip;
+
+ const struct dw_edma_core_ops *core;
+};
+
+struct dw_edma_core_ops {
+ void (*off)(struct dw_edma *dw);
+ u16 (*ch_count)(struct dw_edma *dw, enum dw_edma_dir dir);
+ enum dma_status (*ch_status)(struct dw_edma_chan *chan);
+ void (*clear_done_int)(struct dw_edma_chan *chan);
+ void (*clear_abort_int)(struct dw_edma_chan *chan);
+ void (*done_handle)(struct dw_edma_irq *dw_irq,
+ enum dw_edma_dir dir);
+ void (*abort_handle)(struct dw_edma_irq *dw_irq,
+ enum dw_edma_dir dir);
+ void (*start)(struct dw_edma_chunk *chunk, bool first);
+ void (*ch_config)(struct dw_edma_chan *chan);
+ void (*debugfs_on)(struct dw_edma *dw);
};
struct dw_edma_sg {
@@ -136,6 +153,9 @@ struct dw_edma_transfer {
enum dw_edma_xfer_type type;
};
+void dw_edma_done_interrupt(struct dw_edma_chan *chan);
+void dw_edma_abort_interrupt(struct dw_edma_chan *chan);
+
static inline
struct dw_edma_chan *vc2dw_edma_chan(struct virt_dma_chan *vc)
{
@@ -148,4 +168,76 @@ struct dw_edma_chan *dchan2dw_edma_chan(struct dma_chan *dchan)
return vc2dw_edma_chan(to_virt_chan(dchan));
}
+static inline
+void dw_edma_core_off(struct dw_edma *dw)
+{
+ dw->core->off(dw);
+}
+
+static inline
+u16 dw_edma_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir)
+{
+ return dw->core->ch_count(dw, dir);
+}
+
+static inline
+enum dma_status dw_edma_core_ch_status(struct dw_edma_chan *chan)
+{
+ struct dw_edma *dw = chan->dw;
+
+ return dw->core->ch_status(chan);
+}
+
+static inline
+void dw_edma_core_clear_done_int(struct dw_edma_chan *chan)
+{
+ struct dw_edma *dw = chan->dw;
+
+ dw->core->clear_done_int(chan);
+}
+
+static inline
+void dw_edma_core_clear_abort_int(struct dw_edma_chan *chan)
+{
+ struct dw_edma *dw = chan->dw;
+
+ dw->core->clear_abort_int(chan);
+}
+
+static inline
+void dw_edma_core_done_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir)
+{
+ struct dw_edma *dw = dw_irq->dw;
+
+ dw->core->done_handle(dw_irq, dir);
+}
+
+static inline
+void dw_edma_core_abort_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir)
+{
+ struct dw_edma *dw = dw_irq->dw;
+
+ dw->core->abort_handle(dw_irq, dir);
+}
+
+static inline
+void dw_edma_core_start(struct dw_edma *dw, struct dw_edma_chunk *chunk, bool first)
+{
+ dw->core->start(chunk, first);
+}
+
+static inline
+void dw_edma_core_ch_config(struct dw_edma_chan *chan)
+{
+ struct dw_edma *dw = chan->dw;
+
+ dw->core->ch_config(chan);
+}
+
+static inline
+void dw_edma_core_debugfs_on(struct dw_edma *dw)
+{
+ dw->core->debugfs_on(dw);
+}
+
#endif /* _DW_EDMA_CORE_H */
diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c
index 72e79a0c0a4e..73dcf72ba3a9 100644
--- a/drivers/dma/dw-edma/dw-edma-v0-core.c
+++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
@@ -216,7 +216,7 @@ static inline u64 readq_ch(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch,
readq_ch(dw, dir, ch, &(__dw_ch_regs(dw, dir, ch)->name))
/* eDMA management callbacks */
-void dw_edma_v0_core_off(struct dw_edma *dw)
+static void dw_edma_v0_core_off(struct dw_edma *dw)
{
SET_BOTH_32(dw, int_mask,
EDMA_V0_DONE_INT_MASK | EDMA_V0_ABORT_INT_MASK);
@@ -225,7 +225,7 @@ void dw_edma_v0_core_off(struct dw_edma *dw)
SET_BOTH_32(dw, engine_en, 0);
}
-u16 dw_edma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir)
+static u16 dw_edma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir)
{
u32 num_ch;
@@ -242,7 +242,7 @@ u16 dw_edma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir)
return (u16)num_ch;
}
-enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan)
+static enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan)
{
struct dw_edma *dw = chan->dw;
u32 tmp;
@@ -258,7 +258,7 @@ enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan)
return DMA_ERROR;
}
-void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan)
+static void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan)
{
struct dw_edma *dw = chan->dw;
@@ -266,7 +266,7 @@ void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan)
FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id)));
}
-void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan)
+static void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan)
{
struct dw_edma *dw = chan->dw;
@@ -274,18 +274,66 @@ void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan)
FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id)));
}
-u32 dw_edma_v0_core_status_done_int(struct dw_edma *dw, enum dw_edma_dir dir)
+static u32 dw_edma_v0_core_status_done_int(struct dw_edma *dw, enum dw_edma_dir dir)
{
return FIELD_GET(EDMA_V0_DONE_INT_MASK,
GET_RW_32(dw, dir, int_status));
}
-u32 dw_edma_v0_core_status_abort_int(struct dw_edma *dw, enum dw_edma_dir dir)
+static u32 dw_edma_v0_core_status_abort_int(struct dw_edma *dw, enum dw_edma_dir dir)
{
return FIELD_GET(EDMA_V0_ABORT_INT_MASK,
GET_RW_32(dw, dir, int_status));
}
+static
+void dw_edma_v0_core_done_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir)
+{
+ struct dw_edma *dw = dw_irq->dw;
+ unsigned long total, pos, val;
+ unsigned long off;
+ u32 mask;
+
+ if (dir == EDMA_DIR_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, dir);
+ val &= mask;
+ for_each_set_bit(pos, &val, total)
+ dw_edma_done_interrupt(&dw->chan[pos + off]);
+}
+
+static
+void dw_edma_v0_core_abort_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir)
+{
+ struct dw_edma *dw = dw_irq->dw;
+ unsigned long total, pos, val;
+ unsigned long off;
+ u32 mask;
+
+ if (dir == EDMA_DIR_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_abort_int(dw, dir);
+ val &= mask;
+ for_each_set_bit(pos, &val, total)
+ dw_edma_abort_interrupt(&dw->chan[pos + off]);
+}
+
static void dw_edma_v0_write_ll_data(struct dw_edma_chunk *chunk, int i,
u32 control, u32 size, u64 sar, u64 dar)
{
@@ -356,7 +404,7 @@ static void dw_edma_v0_core_write_chunk(struct dw_edma_chunk *chunk)
dw_edma_v0_write_ll_link(chunk, i, control, chunk->ll_region.paddr);
}
-void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
+static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
{
struct dw_edma_chan *chan = chunk->chan;
struct dw_edma *dw = chan->dw;
@@ -427,7 +475,7 @@ void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
FIELD_PREP(EDMA_V0_DOORBELL_CH_MASK, chan->id));
}
-int dw_edma_v0_core_device_config(struct dw_edma_chan *chan)
+static void dw_edma_v0_core_ch_config(struct dw_edma_chan *chan)
{
struct dw_edma *dw = chan->dw;
u32 tmp = 0;
@@ -494,12 +542,28 @@ int dw_edma_v0_core_device_config(struct dw_edma_chan *chan)
SET_RW_32(dw, chan->dir, ch67_imwr_data, tmp);
break;
}
-
- return 0;
}
/* eDMA debugfs callbacks */
-void dw_edma_v0_core_debugfs_on(struct dw_edma *dw)
+static void dw_edma_v0_core_debugfs_on(struct dw_edma *dw)
{
dw_edma_v0_debugfs_on(dw);
}
+
+static const struct dw_edma_core_ops edma_core = {
+ .off = dw_edma_v0_core_off,
+ .ch_count = dw_edma_v0_core_ch_count,
+ .ch_status = dw_edma_v0_core_ch_status,
+ .clear_done_int = dw_edma_v0_core_clear_done_int,
+ .clear_abort_int = dw_edma_v0_core_clear_abort_int,
+ .done_handle = dw_edma_v0_core_done_handle,
+ .abort_handle = dw_edma_v0_core_abort_handle,
+ .start = dw_edma_v0_core_start,
+ .ch_config = dw_edma_v0_core_ch_config,
+ .debugfs_on = dw_edma_v0_core_debugfs_on,
+};
+
+void dw_edma_v0_core_register(struct dw_edma *dw)
+{
+ dw->core = &edma_core;
+}
diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.h b/drivers/dma/dw-edma/dw-edma-v0-core.h
index ab96a1f48080..04a882222f99 100644
--- a/drivers/dma/dw-edma/dw-edma-v0-core.h
+++ b/drivers/dma/dw-edma/dw-edma-v0-core.h
@@ -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 */
--
2.34.1
From: Cai huoqing <[email protected]>
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 alignmented to byte.
Signed-off-by: Cai huoqing <[email protected]>
---
drivers/dma/dw-edma/Makefile | 5 +-
drivers/dma/dw-edma/dw-hdma-v0-core.c | 315 ++++++++++++++++++++++++++
drivers/dma/dw-edma/dw-hdma-v0-core.h | 17 ++
drivers/dma/dw-edma/dw-hdma-v0-regs.h | 129 +++++++++++
4 files changed, 464 insertions(+), 2 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
diff --git a/drivers/dma/dw-edma/Makefile b/drivers/dma/dw-edma/Makefile
index 8d45c0d5689d..b1c91ef2c63d 100644
--- a/drivers/dma/dw-edma/Makefile
+++ b/drivers/dma/dw-edma/Makefile
@@ -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
diff --git a/drivers/dma/dw-edma/dw-hdma-v0-core.c b/drivers/dma/dw-edma/dw-hdma-v0-core.c
new file mode 100644
index 000000000000..7e4f98987e29
--- /dev/null
+++ b/drivers/dma/dw-edma/dw-hdma-v0-core.c
@@ -0,0 +1,315 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2023 Cai Huoqing
+ * Synopsys DesignWare HDMA v0 core
+ */
+
+#include <linux/bitfield.h>
+
+#include "dw-edma-core.h"
+#include "dw-hdma-v0-core.h"
+#include "dw-hdma-v0-regs.h"
+
+enum dw_hdma_control {
+ DW_HDMA_V0_CB = BIT(0),
+ DW_HDMA_V0_TCB = BIT(1),
+ DW_HDMA_V0_LLP = BIT(2),
+ DW_HDMA_V0_LIE = BIT(3),
+ DW_HDMA_V0_RIE = BIT(4),
+ DW_HDMA_V0_CCS = BIT(8),
+ DW_HDMA_V0_LLE = BIT(9),
+};
+
+static inline struct dw_hdma_v0_regs __iomem *__dw_regs(struct dw_edma *dw)
+{
+ return dw->chip->reg_base;
+}
+
+static inline struct dw_hdma_v0_ch_regs __iomem *
+__dw_ch_regs(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch)
+{
+ if (dir == EDMA_DIR_WRITE)
+ return &(__dw_regs(dw)->ch[ch].wr);
+ else
+ return &(__dw_regs(dw)->ch[ch].rd);
+}
+
+#define SET_CH_32(dw, dir, ch, name, value) \
+ writel(value, &(__dw_ch_regs(dw, dir, ch)->name))
+
+#define GET_CH_32(dw, dir, ch, name) \
+ readl(&(__dw_ch_regs(dw, dir, ch)->name))
+
+#define SET_BOTH_CH_32(dw, ch, name, value) \
+ do { \
+ writel(value, &(__dw_ch_regs(dw, EDMA_DIR_WRITE, ch)->name)); \
+ writel(value, &(__dw_ch_regs(dw, EDMA_DIR_READ, ch)->name)); \
+ } while (0)
+
+/* HDMA management callbacks */
+static void dw_hdma_v0_core_off(struct dw_edma *dw)
+{
+ int id;
+
+ for (id = 0; id < HDMA_V0_MAX_NR_CH; id++) {
+ SET_BOTH_CH_32(dw, id, int_setup,
+ HDMA_V0_STOP_INT_MASK | HDMA_V0_ABORT_INT_MASK);
+ SET_BOTH_CH_32(dw, id, int_clear,
+ HDMA_V0_STOP_INT_MASK | HDMA_V0_ABORT_INT_MASK);
+ SET_BOTH_CH_32(dw, id, ch_en, 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 enum dma_status dw_hdma_v0_core_ch_status(struct dw_edma_chan *chan)
+{
+ struct dw_edma *dw = chan->dw;
+ u32 tmp;
+
+ tmp = FIELD_GET(HDMA_V0_CH_STATUS_MASK,
+ GET_CH_32(dw, chan->id, chan->dir, ch_stat));
+
+ if (tmp == 1)
+ return DMA_IN_PROGRESS;
+ else if (tmp == 3)
+ return DMA_COMPLETE;
+ else
+ return DMA_ERROR;
+}
+
+static void dw_hdma_v0_core_clear_done_int(struct dw_edma_chan *chan)
+{
+ struct dw_edma *dw = chan->dw;
+
+ SET_CH_32(dw, chan->dir, chan->id, int_clear, HDMA_V0_STOP_INT_MASK);
+}
+
+static void dw_hdma_v0_core_clear_abort_int(struct dw_edma_chan *chan)
+{
+ struct dw_edma *dw = chan->dw;
+
+ SET_CH_32(dw, chan->dir, chan->id, int_clear, HDMA_V0_ABORT_INT_MASK);
+}
+
+static u32 dw_hdma_v0_core_status_int(struct dw_edma_chan *chan)
+{
+ struct dw_edma *dw = chan->dw;
+
+ return GET_CH_32(dw, chan->dir, chan->id, int_stat);
+}
+
+static u32 dw_hdma_v0_core_check_done_int(u32 val)
+{
+ return FIELD_GET(HDMA_V0_STOP_INT_MASK, val);
+}
+
+static u32 dw_hdma_v0_core_check_abort_int(u32 val)
+{
+ return FIELD_GET(HDMA_V0_ABORT_INT_MASK, val);
+}
+
+static
+void dw_hdma_v0_core_done_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir)
+{
+ struct dw_edma *dw = dw_irq->dw;
+ unsigned long total, pos, val;
+ unsigned long off, mask;
+
+ if (dir == EDMA_DIR_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;
+ }
+
+ for_each_set_bit(pos, &mask, total) {
+ struct dw_edma_chan *chan = &dw->chan[pos + off];
+
+ val = dw_hdma_v0_core_status_int(chan);
+ if (dw_hdma_v0_core_check_done_int(val))
+ dw_edma_done_interrupt(chan);
+ }
+}
+
+static
+void dw_hdma_v0_core_abort_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir)
+{
+ struct dw_edma *dw = dw_irq->dw;
+ unsigned long total, pos, val;
+ unsigned long off, mask;
+
+ if (dir == EDMA_DIR_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;
+ }
+
+ for_each_set_bit(pos, &mask, total) {
+ struct dw_edma_chan *chan = &dw->chan[pos + off];
+
+ val = dw_hdma_v0_core_status_int(chan);
+ if (dw_hdma_v0_core_check_abort_int(val))
+ dw_edma_abort_interrupt(&dw->chan[pos + off]);
+ }
+}
+
+static void dw_hdma_v0_write_ll_data(struct dw_edma_chunk *chunk, int i,
+ u32 control, u32 size, u64 sar, u64 dar)
+{
+ ptrdiff_t ofs = i * sizeof(struct dw_hdma_v0_lli);
+
+ if (chunk->chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) {
+ struct dw_hdma_v0_lli *lli = chunk->ll_region.vaddr.mem + ofs;
+
+ lli->control = control;
+ lli->transfer_size = size;
+ lli->sar.reg = sar;
+ lli->dar.reg = dar;
+ } else {
+ struct dw_hdma_v0_lli __iomem *lli = chunk->ll_region.vaddr.io + ofs;
+
+ writel(control, &lli->control);
+ writel(size, &lli->transfer_size);
+ writeq(sar, &lli->sar.reg);
+ writeq(dar, &lli->dar.reg);
+ }
+}
+
+static void dw_hdma_v0_write_ll_link(struct dw_edma_chunk *chunk,
+ int i, u32 control, u64 pointer)
+{
+ ptrdiff_t ofs = i * sizeof(struct dw_hdma_v0_lli);
+
+ if (chunk->chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) {
+ struct dw_hdma_v0_llp *llp = chunk->ll_region.vaddr.mem + ofs;
+
+ llp->control = control;
+ llp->llp.reg = pointer;
+ } else {
+ struct dw_hdma_v0_llp __iomem *llp = chunk->ll_region.vaddr.io + ofs;
+
+ writel(control, &llp->control);
+ writeq(pointer, &llp->llp.reg);
+ }
+}
+
+static void dw_hdma_v0_core_write_chunk(struct dw_edma_chunk *chunk)
+{
+ struct dw_edma_burst *child;
+ struct dw_edma_chan *chan = chunk->chan;
+ u32 control = 0, i = 0;
+ int j;
+
+ if (chunk->cb)
+ control = DW_HDMA_V0_CB;
+
+ j = chunk->bursts_alloc;
+ list_for_each_entry(child, &chunk->burst->list, list) {
+ j--;
+ if (!j) {
+ control |= DW_HDMA_V0_LIE;
+ if (!(chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL))
+ control |= DW_HDMA_V0_RIE;
+ }
+
+ dw_hdma_v0_write_ll_data(chunk, i++, control, child->sz,
+ child->sar, child->dar);
+ }
+
+ control = DW_HDMA_V0_LLP | DW_HDMA_V0_TCB;
+ if (!chunk->cb)
+ control |= DW_HDMA_V0_CB;
+
+ dw_hdma_v0_write_ll_link(chunk, i, control, chunk->ll_region.paddr);
+}
+
+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));
+ /* Interrupt enable&unmask - done, abort */
+ tmp = GET_CH_32(dw, chan->dir, chan->id, int_setup) | HDMA_V0_STOP_INT_MASK |
+ HDMA_V0_ABORT_INT_MASK | HDMA_V0_LOCAL_STOP_INT_EN |
+ HDMA_V0_LOCAL_STOP_INT_EN;
+ SET_CH_32(dw, chan->dir, chan->id, int_setup, tmp);
+ /* Channel control */
+ SET_CH_32(dw, chan->dir, chan->id, control1, HDMA_V0_LINKLIST_EN);
+ /* Linked list */
+ /* llp is not aligned on 64bit -> keep 32bit accesses */
+ SET_CH_32(dw, chan->dir, chan->id, llp.lsb,
+ lower_32_bits(chunk->ll_region.paddr));
+ SET_CH_32(dw, chan->dir, chan->id, llp.msb,
+ upper_32_bits(chunk->ll_region.paddr));
+ }
+ /* Set consumer cycle */
+ SET_CH_32(dw, chan->dir, chan->id, cycle_sync,
+ HDMA_V0_CONSUMER_CYCLE_STAT | HDMA_V0_CONSUMER_CYCLE_BIT);
+ /* Doorbell */
+ SET_CH_32(dw, chan->dir, chan->id, doorbell, HDMA_V0_DOORBELL_START);
+}
+
+static void dw_hdma_v0_core_ch_config(struct dw_edma_chan *chan)
+{
+ struct dw_edma *dw = chan->dw;
+
+ /* MSI done addr - low, high */
+ SET_CH_32(dw, chan->dir, chan->id, msi_stop.lsb, chan->msi.address_lo);
+ SET_CH_32(dw, chan->dir, chan->id, msi_stop.msb, chan->msi.address_hi);
+ /* MSI abort addr - low, high */
+ SET_CH_32(dw, chan->dir, chan->id, msi_abort.lsb, chan->msi.address_lo);
+ SET_CH_32(dw, chan->dir, chan->id, msi_abort.msb, chan->msi.address_hi);
+ /* config MSI data */
+ SET_CH_32(dw, chan->dir, chan->id, msi_msgdata, chan->msi.data);
+}
+
+/* HDMA debugfs callbacks */
+static void dw_hdma_v0_core_debugfs_on(struct dw_edma *dw)
+{
+}
+
+static const struct dw_edma_core_ops hdma_core = {
+ .off = dw_hdma_v0_core_off,
+ .ch_count = dw_hdma_v0_core_ch_count,
+ .ch_status = dw_hdma_v0_core_ch_status,
+ .clear_done_int = dw_hdma_v0_core_clear_done_int,
+ .clear_abort_int = dw_hdma_v0_core_clear_abort_int,
+ .done_handle = dw_hdma_v0_core_done_handle,
+ .abort_handle = dw_hdma_v0_core_abort_handle,
+ .start = dw_hdma_v0_core_start,
+ .ch_config = dw_hdma_v0_core_ch_config,
+ .debugfs_on = dw_hdma_v0_core_debugfs_on,
+};
+
+void dw_hdma_v0_core_register(struct dw_edma *dw)
+{
+ dw->core = &hdma_core;
+}
diff --git a/drivers/dma/dw-edma/dw-hdma-v0-core.h b/drivers/dma/dw-edma/dw-hdma-v0-core.h
new file mode 100644
index 000000000000..c373b4f0bd8a
--- /dev/null
+++ b/drivers/dma/dw-edma/dw-hdma-v0-core.h
@@ -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 */
diff --git a/drivers/dma/dw-edma/dw-hdma-v0-regs.h b/drivers/dma/dw-edma/dw-hdma-v0-regs.h
new file mode 100644
index 000000000000..f6b2bd03060c
--- /dev/null
+++ b/drivers/dma/dw-edma/dw-hdma-v0-regs.h
@@ -0,0 +1,129 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2023 Cai Huoqing
+ * Synopsys DesignWare HDMA v0 reg
+ *
+ * Author: Cai Huoqing <[email protected]>
+ */
+
+#ifndef _DW_HDMA_V0_REGS_H
+#define _DW_HDMA_V0_REGS_H
+
+#include <linux/dmaengine.h>
+
+#define HDMA_V0_MAX_NR_CH 8
+#define HDMA_V0_LOCAL_ABORT_INT_EN BIT(6)
+#define HDMA_V0_REMOTE_ABORT_INT_EN BIT(5)
+#define HDMA_V0_LOCAL_STOP_INT_EN BIT(4)
+#define HDMA_V0_REMOTEL_STOP_INT_EN BIT(3)
+#define HDMA_V0_ABORT_INT_MASK BIT(2)
+#define HDMA_V0_STOP_INT_MASK BIT(0)
+#define HDMA_V0_LINKLIST_EN BIT(0)
+#define HDMA_V0_CONSUMER_CYCLE_STAT BIT(1)
+#define HDMA_V0_CONSUMER_CYCLE_BIT BIT(0)
+#define HDMA_V0_DOORBELL_START BIT(0)
+#define HDMA_V0_CH_STATUS_MASK GENMASK(1, 0)
+
+struct dw_hdma_v0_ch_regs {
+ u32 ch_en; /* 0x0000 */
+ u32 doorbell; /* 0x0004 */
+ u32 prefetch; /* 0x0008 */
+ u32 handshake; /* 0x000c */
+ union {
+ u64 reg; /* 0x0010..0x0014 */
+ struct {
+ u32 lsb; /* 0x0010 */
+ u32 msb; /* 0x0014 */
+ };
+ } llp;
+ u32 cycle_sync; /* 0x0018 */
+ u32 transfer_size; /* 0x001c */
+ union {
+ u64 reg; /* 0x0020..0x0024 */
+ struct {
+ u32 lsb; /* 0x0020 */
+ u32 msb; /* 0x0024 */
+ };
+ } sar;
+ union {
+ u64 reg; /* 0x0028..0x002c */
+ struct {
+ u32 lsb; /* 0x0028 */
+ u32 msb; /* 0x002c */
+ };
+ } dar;
+
+ u32 watermark_en; /* 0x0030 */
+ u32 control1; /* 0x0034 */
+ u32 func_num; /* 0x0038 */
+ u32 qos; /* 0x003c */
+ u32 reserved; /* 0x0040..0x007c */
+ u32 ch_stat; /* 0x0080 */
+ u32 int_stat; /* 0x0084 */
+ u32 int_setup; /* 0x0088 */
+ u32 int_clear; /* 0x008c */
+ union {
+ u64 reg; /* 0x0090..0x0094 */
+ struct {
+ u32 lsb; /* 0x0090 */
+ u32 msb; /* 0x0094 */
+ };
+ } msi_stop;
+ union {
+ u64 reg; /* 0x0098..0x009c */
+ struct {
+ u32 lsb; /* 0x0098 */
+ u32 msb; /* 0x009c */
+ };
+ } msi_watermark;
+ union {
+ u64 reg; /* 0x00a0..0x00a4 */
+ struct {
+ u32 lsb; /* 0x00a0 */
+ u32 msb; /* 0x00a4 */
+ };
+ } msi_abort;
+ u32 msi_msgdata; /* 0x00a8 */
+} __packed;
+
+struct dw_hdma_v0_ch {
+ struct dw_hdma_v0_ch_regs wr; /* 0x0000 */
+ struct dw_hdma_v0_ch_regs rd; /* 0x0100 */
+} __packed;
+
+struct dw_hdma_v0_regs {
+ struct dw_hdma_v0_ch ch[HDMA_V0_MAX_NR_CH]; /* 0x0000..0x0fa8 */
+} __packed;
+
+struct dw_hdma_v0_lli {
+ u32 control;
+ u32 transfer_size;
+ union {
+ u64 reg;
+ struct {
+ u32 lsb;
+ u32 msb;
+ };
+ } sar;
+ union {
+ u64 reg;
+ struct {
+ u32 lsb;
+ u32 msb;
+ };
+ } dar;
+} __packed;
+
+struct dw_hdma_v0_llp {
+ u32 control;
+ u32 reserved;
+ union {
+ u64 reg;
+ struct {
+ u32 lsb;
+ u32 msb;
+ };
+ } llp;
+} __packed;
+
+#endif /* _DW_HDMA_V0_REGS_H */
--
2.34.1
Hi Cai,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on next-20230213]
[cannot apply to vkoul-dmaengine/next linus/master v6.2-rc8 v6.2-rc7 v6.2-rc6 v6.2-rc8]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Cai-Huoqing/dmaengine-dw-edma-Rename-dw_edma_core_ops-structure-to-dw_edma_plat_ops/20230213-213412
patch link: https://lore.kernel.org/r/20230213132411.65524-4-cai.huoqing%40linux.dev
patch subject: [PATCH v3 3/4] dmaengine: dw-edma: Add support for native HDMA
config: i386-randconfig-a016-20230213 (https://download.01.org/0day-ci/archive/20230213/[email protected]/config)
compiler: gcc-11 (Debian 11.3.0-8) 11.3.0
reproduce (this is a W=1 build):
# https://github.com/intel-lab-lkp/linux/commit/04d89cfa187deda4fa5a7cc947dbb797ce05e72f
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Cai-Huoqing/dmaengine-dw-edma-Rename-dw_edma_core_ops-structure-to-dw_edma_plat_ops/20230213-213412
git checkout 04d89cfa187deda4fa5a7cc947dbb797ce05e72f
# save the config file
mkdir build_dir && cp config build_dir/.config
make W=1 O=build_dir ARCH=i386 olddefconfig
make W=1 O=build_dir ARCH=i386 SHELL=/bin/bash
If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <[email protected]>
| Link: https://lore.kernel.org/oe-kbuild-all/[email protected]/
All errors (new ones prefixed by >>):
drivers/dma/dw-edma/dw-hdma-v0-core.c: In function 'dw_hdma_v0_write_ll_data':
>> drivers/dma/dw-edma/dw-hdma-v0-core.c:195:17: error: implicit declaration of function 'writeq'; did you mean 'writel'? [-Werror=implicit-function-declaration]
195 | writeq(sar, &lli->sar.reg);
| ^~~~~~
| writel
cc1: some warnings being treated as errors
vim +195 drivers/dma/dw-edma/dw-hdma-v0-core.c
177
178 static void dw_hdma_v0_write_ll_data(struct dw_edma_chunk *chunk, int i,
179 u32 control, u32 size, u64 sar, u64 dar)
180 {
181 ptrdiff_t ofs = i * sizeof(struct dw_hdma_v0_lli);
182
183 if (chunk->chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) {
184 struct dw_hdma_v0_lli *lli = chunk->ll_region.vaddr.mem + ofs;
185
186 lli->control = control;
187 lli->transfer_size = size;
188 lli->sar.reg = sar;
189 lli->dar.reg = dar;
190 } else {
191 struct dw_hdma_v0_lli __iomem *lli = chunk->ll_region.vaddr.io + ofs;
192
193 writel(control, &lli->control);
194 writel(size, &lli->transfer_size);
> 195 writeq(sar, &lli->sar.reg);
196 writeq(dar, &lli->dar.reg);
197 }
198 }
199
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests
On Mon, Feb 13, 2023 at 09:24:08PM +0800, Cai Huoqing wrote:
> From: Cai huoqing <[email protected]>
>
> 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.
Rewrap to fill 75 columns. Also applies below.
> 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.
Wrap into a single paragraph or add a blank line if you want the last
sentence to be a new paragraph.
> 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 alignmented to byte.
s/alignmented/aligned/
> + u32 watermark_en; /* 0x0030 */
> + u32 control1; /* 0x0034 */
> + u32 func_num; /* 0x0038 */
> + u32 qos; /* 0x003c */
> + u32 reserved; /* 0x0040..0x007c */
> + u32 ch_stat; /* 0x0080 */
Weird indentation of control1, func_num, etc. Is that meaningful or a
mistake?
> + union {
> + u64 reg; /* 0x00a0..0x00a4 */
> + struct {
> + u32 lsb; /* 0x00a0 */
> + u32 msb; /* 0x00a4 */
> + };
> + } msi_abort;
> + u32 msi_msgdata; /* 0x00a8 */
Again here.
Bjorn
On 13 2月 23 23:20:19, kernel test robot wrote:
> Hi Cai,
>
> Thank you for the patch! Yet something to improve:
Will include <linux/io-64-nonatomic-lo-hi.h>
>
> [auto build test ERROR on next-20230213]
> [cannot apply to vkoul-dmaengine/next linus/master v6.2-rc8 v6.2-rc7 v6.2-rc6 v6.2-rc8]
> [If your patch is applied to the wrong git tree, kindly drop us a note.
> And when submitting patch, we suggest to use '--base' as documented in
> https://git-scm.com/docs/git-format-patch#_base_tree_information]
>
> url: https://github.com/intel-lab-lkp/linux/commits/Cai-Huoqing/dmaengine-dw-edma-Rename-dw_edma_core_ops-structure-to-dw_edma_plat_ops/20230213-213412
> patch link: https://lore.kernel.org/r/20230213132411.65524-4-cai.huoqing%40linux.dev
> patch subject: [PATCH v3 3/4] dmaengine: dw-edma: Add support for native HDMA
> config: i386-randconfig-a016-20230213 (https://download.01.org/0day-ci/archive/20230213/[email protected]/config)
> compiler: gcc-11 (Debian 11.3.0-8) 11.3.0
> reproduce (this is a W=1 build):
> # https://github.com/intel-lab-lkp/linux/commit/04d89cfa187deda4fa5a7cc947dbb797ce05e72f
> git remote add linux-review https://github.com/intel-lab-lkp/linux
> git fetch --no-tags linux-review Cai-Huoqing/dmaengine-dw-edma-Rename-dw_edma_core_ops-structure-to-dw_edma_plat_ops/20230213-213412
> git checkout 04d89cfa187deda4fa5a7cc947dbb797ce05e72f
> # save the config file
> mkdir build_dir && cp config build_dir/.config
> make W=1 O=build_dir ARCH=i386 olddefconfig
> make W=1 O=build_dir ARCH=i386 SHELL=/bin/bash
>
> If you fix the issue, kindly add following tag where applicable
> | Reported-by: kernel test robot <[email protected]>
> | Link: https://lore.kernel.org/oe-kbuild-all/[email protected]/
>
> All errors (new ones prefixed by >>):
>
> drivers/dma/dw-edma/dw-hdma-v0-core.c: In function 'dw_hdma_v0_write_ll_data':
> >> drivers/dma/dw-edma/dw-hdma-v0-core.c:195:17: error: implicit declaration of function 'writeq'; did you mean 'writel'? [-Werror=implicit-function-declaration]
> 195 | writeq(sar, &lli->sar.reg);
> | ^~~~~~
> | writel
> cc1: some warnings being treated as errors
>
>
> vim +195 drivers/dma/dw-edma/dw-hdma-v0-core.c
>
> 177
> 178 static void dw_hdma_v0_write_ll_data(struct dw_edma_chunk *chunk, int i,
> 179 u32 control, u32 size, u64 sar, u64 dar)
> 180 {
> 181 ptrdiff_t ofs = i * sizeof(struct dw_hdma_v0_lli);
> 182
> 183 if (chunk->chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) {
> 184 struct dw_hdma_v0_lli *lli = chunk->ll_region.vaddr.mem + ofs;
> 185
> 186 lli->control = control;
> 187 lli->transfer_size = size;
> 188 lli->sar.reg = sar;
> 189 lli->dar.reg = dar;
> 190 } else {
> 191 struct dw_hdma_v0_lli __iomem *lli = chunk->ll_region.vaddr.io + ofs;
> 192
> 193 writel(control, &lli->control);
> 194 writel(size, &lli->transfer_size);
> > 195 writeq(sar, &lli->sar.reg);
> 196 writeq(dar, &lli->dar.reg);
> 197 }
> 198 }
> 199
>
> --
> 0-DAY CI Kernel Test Service
> https://github.com/intel/lkp-tests
On 13 2月 23 14:59:27, Bjorn Helgaas wrote:
> On Mon, Feb 13, 2023 at 09:24:08PM +0800, Cai Huoqing wrote:
> > From: Cai huoqing <[email protected]>
> >
> > 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.
>
> Rewrap to fill 75 columns. Also applies below.
>
> > 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.
>
> Wrap into a single paragraph or add a blank line if you want the last
> sentence to be a new paragraph.
>
> > 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 alignmented to byte.
>
> s/alignmented/aligned/
>
> > + u32 watermark_en; /* 0x0030 */
> > + u32 control1; /* 0x0034 */
> > + u32 func_num; /* 0x0038 */
> > + u32 qos; /* 0x003c */
> > + u32 reserved; /* 0x0040..0x007c */
> > + u32 ch_stat; /* 0x0080 */
>
> Weird indentation of control1, func_num, etc. Is that meaningful or a
> mistake?
>
> > + union {
> > + u64 reg; /* 0x00a0..0x00a4 */
> > + struct {
> > + u32 lsb; /* 0x00a0 */
> > + u32 msb; /* 0x00a4 */
> > + };
> > + } msi_abort;
> > + u32 msi_msgdata; /* 0x00a8 */
>
> Again here.
Will fix these in next version
Cai
>
> Bjorn
On 13 2月 23 21:24:05, Cai Huoqing wrote:
> From: Cai huoqing <[email protected]>
>
> 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 alignmented to byte.dmaengine: dw-edma: Add support for native HDMA
>
> 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
>
> v2->v3:
> [1/4]
> 1.Add more commit log to explain why use dw_edma_plat_ops.
> 2.Update the structure name in the DW PCIe driver.
> [2/4]
> 3.Use the reverse xmas tree vars definition order.
> 4.Add edma core ops wrapper.
> 5.Add dw_edma_done_interrupt() and dw_edma_abort_interrupt()
> global methods.
> 6.Fix some indentation.
> 7.Fix some typo
> 8.Make use off dw_edma_core prefix instead of dw_xdma_core_.
> [3/4]
> 9.Remove unnecessary include: dw-edma-v0-regs.h and dw-edma-v0-regs.h
> 10.HDMA supports the LL descriptors placed on the CPU memory.
> [4/4]
> 11.Split DebugFS to be a separate patch.
> 12.Refactor HDMA DebugFS like the series in @Bjorn tree.
>
> v2 link:
> https://lore.kernel.org/lkml/20220925173412.u2ez6rbmfc5fupdn@mobilestation/
Hi Sergey,
Could you please give some comments for this patch v3
Thanks,
Cai
>
> drivers/dma/dw-edma/Makefile | 8 +-
> drivers/dma/dw-edma/dw-edma-core.c | 63 ++--
> drivers/dma/dw-edma/dw-edma-core.h | 92 ++++++
> drivers/dma/dw-edma/dw-edma-pcie.c | 4 +-
> drivers/dma/dw-edma/dw-edma-v0-core.c | 88 ++++-
> drivers/dma/dw-edma/dw-edma-v0-core.h | 14 +-
> drivers/dma/dw-edma/dw-hdma-v0-core.c | 317 +++++++++++++++++++
> drivers/dma/dw-edma/dw-hdma-v0-core.h | 17 +
> drivers/dma/dw-edma/dw-hdma-v0-debugfs.c | 175 ++++++++++
> 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, 860 insertions(+), 78 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
>
> --
> 2.34.1
>
Hi Cai
On Thu, Feb 16, 2023 at 10:08:56AM +0800, Cai Huoqing wrote:
> On 13 2月 23 21:24:05, Cai Huoqing wrote:
> > From: Cai huoqing <[email protected]>
> >
> > 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 alignmented to byte.dmaengine: dw-edma: Add support for native HDMA
> >
> > 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
> >
> > v2->v3:
> > [1/4]
> > 1.Add more commit log to explain why use dw_edma_plat_ops.
> > 2.Update the structure name in the DW PCIe driver.
> > [2/4]
> > 3.Use the reverse xmas tree vars definition order.
> > 4.Add edma core ops wrapper.
> > 5.Add dw_edma_done_interrupt() and dw_edma_abort_interrupt()
> > global methods.
> > 6.Fix some indentation.
> > 7.Fix some typo
> > 8.Make use off dw_edma_core prefix instead of dw_xdma_core_.
> > [3/4]
> > 9.Remove unnecessary include: dw-edma-v0-regs.h and dw-edma-v0-regs.h
> > 10.HDMA supports the LL descriptors placed on the CPU memory.
> > [4/4]
> > 11.Split DebugFS to be a separate patch.
> > 12.Refactor HDMA DebugFS like the series in @Bjorn tree.
> >
> > v2 link:
> > https://lore.kernel.org/lkml/20220925173412.u2ez6rbmfc5fupdn@mobilestation/
> Hi Sergey,
>
> Could you please give some comments for this patch v3
The series has landed in my inbox. I'll give you some comments shortly
today or tomorrow.
-Serge(y)
>
> Thanks,
> Cai
> >
> > drivers/dma/dw-edma/Makefile | 8 +-
> > drivers/dma/dw-edma/dw-edma-core.c | 63 ++--
> > drivers/dma/dw-edma/dw-edma-core.h | 92 ++++++
> > drivers/dma/dw-edma/dw-edma-pcie.c | 4 +-
> > drivers/dma/dw-edma/dw-edma-v0-core.c | 88 ++++-
> > drivers/dma/dw-edma/dw-edma-v0-core.h | 14 +-
> > drivers/dma/dw-edma/dw-hdma-v0-core.c | 317 +++++++++++++++++++
> > drivers/dma/dw-edma/dw-hdma-v0-core.h | 17 +
> > drivers/dma/dw-edma/dw-hdma-v0-debugfs.c | 175 ++++++++++
> > 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, 860 insertions(+), 78 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
> >
> > --
> > 2.34.1
> >
On Mon, Feb 13, 2023 at 09:24:06PM +0800, Cai Huoqing wrote:
> From: Cai huoqing <[email protected]>
>
> Rename dw_edma_core_ops structure to dw_edma_plat_ops, the ops is platform
> specific operations: the DMA device environment configs like IRQs,
> address translation, etc.
>
> The dw_edma_pcie_plat_ops name was supposed to refer to the platform which
s/dw_edma_pcie_plat_ops/dw_edma_plat_ops
* The main goal is to update the structure name.
> the DW eDMA engine is embedded to, like PCIe end-point (accessible via
> the PCIe bus) or a PCIe root port (directly accessible by CPU).
> Needless to say that for them the IRQ-vector and PCI-addresses are
> differently determined. The suggested name has a connection with the
> kernel platform device only as a private case of the eDMA/hDMA embedded
> into the DW PCI Root ports, though basically it was supposed to refer to
> any platform in which the DMA hardware lives.
>
> 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.
>
> dw_edma_pcie_core_ops is supposed to be used for the case 1, and
> dw_edma_pcie_plat_ops - for the case 2.
ditto
>
> Signed-off-by: Cai huoqing <[email protected]>
> ---
> drivers/dma/dw-edma/dw-edma-pcie.c | 4 ++--
> drivers/pci/controller/dwc/pcie-designware.c | 2 +-
> include/linux/dma/edma.h | 7 ++++---
> 3 files changed, 7 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/dma/dw-edma/dw-edma-pcie.c b/drivers/dma/dw-edma/dw-edma-pcie.c
> index 2b40f2b44f5e..1c6043751dc9 100644
> --- a/drivers/dma/dw-edma/dw-edma-pcie.c
> +++ b/drivers/dma/dw-edma/dw-edma-pcie.c
> @@ -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;
> diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
> index 53a16b8b6ac2..44e90b71d429 100644
> --- a/drivers/pci/controller/dwc/pcie-designware.c
> +++ b/drivers/pci/controller/dwc/pcie-designware.c
> @@ -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,
> };
>
> diff --git a/include/linux/dma/edma.h b/include/linux/dma/edma.h
> index d2638d9259dc..b2f3dd5e7e1a 100644
> --- a/include/linux/dma/edma.h
> +++ b/include/linux/dma/edma.h
> @@ -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);
> };
> @@ -48,7 +48,8 @@ struct dw_edma_core_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
^
Please add a comma here ---------+
Thus if there is a new entry is added to the enum list in future the
update will consist of a single-line change. It's a common practice in
kernel to terminate the last entry in enums or struct initializers if
there is a possibility to add new entries to the list afterwards.
> };
>
> /**
> @@ -80,7 +81,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;
\ /
^
These are just three white-spaces ----+
Please replace them with either a tab or with a single space.
-Serge(y)
> u32 flags;
>
> void __iomem *reg_base;
> --
> 2.34.1
>
On Mon, Feb 13, 2023 at 09:24:07PM +0800, Cai Huoqing wrote:
> From: Cai huoqing <[email protected]>
>
> 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.
>
> Signed-off-by: Cai huoqing <[email protected]>
> ---
> drivers/dma/dw-edma/dw-edma-core.c | 63 ++++++------------
> drivers/dma/dw-edma/dw-edma-core.h | 92 +++++++++++++++++++++++++++
> drivers/dma/dw-edma/dw-edma-v0-core.c | 88 +++++++++++++++++++++----
> drivers/dma/dw-edma/dw-edma-v0-core.h | 14 +---
> 4 files changed, 188 insertions(+), 69 deletions(-)
>
> diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
> index 1906a836f0aa..c171809beba1 100644
> --- a/drivers/dma/dw-edma/dw-edma-core.c
> +++ b/drivers/dma/dw-edma/dw-edma-core.c
> @@ -183,6 +183,7 @@ static void vchan_free_desc(struct virt_dma_desc *vdesc)
>
> static void 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 void dw_edma_start_transfer(struct dw_edma_chan *chan)
> if (!child)
> return;
>
> - 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);
> @@ -285,7 +286,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
> @@ -588,13 +589,13 @@ dw_edma_device_prep_interleaved_dma(struct dma_chan *dchan,
> return dw_edma_device_transfer(&xfer);
> }
>
> -static void dw_edma_done_interrupt(struct dw_edma_chan *chan)
> +void dw_edma_done_interrupt(struct dw_edma_chan *chan)
> {
> struct dw_edma_desc *desc;
> struct virt_dma_desc *vd;
> unsigned long flags;
>
> - dw_edma_v0_core_clear_done_int(chan);
> + dw_edma_core_clear_done_int(chan); <+
|
As I already said in v2 just call this method + in the IRQ-handle loop
within the IP-core-specific module (see further for details). Thus you'll
be able to drop the dw_edma_core_ops.clear_done_int pointer. The same
concerns the dw_edma_core_ops.clear_abort_int callback.
>
> spin_lock_irqsave(&chan->vc.lock, flags);
> vd = vchan_next_desc(&chan->vc);
> @@ -631,12 +632,12 @@ static void dw_edma_done_interrupt(struct dw_edma_chan *chan)
> spin_unlock_irqrestore(&chan->vc.lock, flags);
> }
>
> -static void dw_edma_abort_interrupt(struct dw_edma_chan *chan)
> +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);
> + dw_edma_core_clear_abort_int(chan);
ditto
>
> spin_lock_irqsave(&chan->vc.lock, flags);
> vd = vchan_next_desc(&chan->vc);
> @@ -651,41 +652,11 @@ static void dw_edma_abort_interrupt(struct dw_edma_chan *chan)
>
> static irqreturn_t dw_edma_interrupt(int irq, void *data, bool write)
> {
> + enum dw_edma_dir dir = write ? EDMA_DIR_WRITE : EDMA_DIR_READ;
> 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);
> - }
> + dw_edma_core_done_handle(dw_irq, dir);
> + dw_edma_core_abort_handle(dw_irq, dir);
>
> return IRQ_HANDLED;
> }
> @@ -806,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 */
> @@ -951,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)
> @@ -977,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);
> @@ -990,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;
>
> @@ -1016,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--)
> diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h
> index 0ab2b6dba880..2555ae0e1b8b 100644
> --- a/drivers/dma/dw-edma/dw-edma-core.h
> +++ b/drivers/dma/dw-edma/dw-edma-core.h
> @@ -111,6 +111,23 @@ struct dw_edma {
> raw_spinlock_t lock; /* Only for legacy */
>
> struct dw_edma_chip *chip;
> +
> + const struct dw_edma_core_ops *core;
> +};
> +
> +struct dw_edma_core_ops {
> + void (*off)(struct dw_edma *dw);
> + u16 (*ch_count)(struct dw_edma *dw, enum dw_edma_dir dir);
> + enum dma_status (*ch_status)(struct dw_edma_chan *chan);
> + void (*clear_done_int)(struct dw_edma_chan *chan);
> + void (*clear_abort_int)(struct dw_edma_chan *chan);
As I noted above these methods could be called locally from the
corresponding IP-core-specific module. Thus you won't need to define
the callbacks. It shall simplify the interface a bit.
> + void (*done_handle)(struct dw_edma_irq *dw_irq,
> + enum dw_edma_dir dir);
> + void (*abort_handle)(struct dw_edma_irq *dw_irq,
> + enum dw_edma_dir dir);
Seeing both of these callbacks (done,abort handlers) are called from
the same IRQ-handler what about converting them to a single callback:
irqreturn_t (*handle_int)(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir);
?
The eDMA channels IRQ-handlers would look like this:
--- a/drivers/dma/dw-edma/dw-edma-core.c
+++ b/drivers/dma/dw-edma/dw-edma-core.c
@@ -692,12 +692,16 @@ static irqreturn_t dw_edma_interrupt(int irq, void *data, bool write)
static inline irqreturn_t dw_edma_interrupt_write(int irq, void *data)
{
- return dw_edma_interrupt(irq, data, true);
+ struct dw_edma_irq *dw_irq = data;
+
+ return dw_edma_core_handle_int(dw_irq, EDMA_DIR_WRITE);
}
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);
}
static irqreturn_t dw_edma_interrupt_common(int irq, void *data)
Therefore you'll be able to completely drop the
static irqreturn_t dw_edma_interrupt(int irq, void *data, bool write)
function from drivers/dma/dw-edma/dw-edma-core.c. The function body would
be almost fully moved to the handle_int() function of the eDMA/HDMA
core-specific ops.
For instance in case of the DW eDMA it would look like this:
static irqreturn_t dw_edma_v0_core_handle_int(struct dw_edma_irq, enum dw_edma_dir dir)
{
struct dw_edma *dw = dw_irq->dw;
unsigned long total, pos, val;
unsigned long off;
u32 mask;
if (dir == EDMA_DIR_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, dir) & mask;
for_each_set_bit(pos, &val, total) {
struct dw_edma_chan *chan = &dw->chan[pos + off];
dw_edma_v0_core_clear_done_int(chan);
dw_edma_done_interrupt(chan);
}
val = dw_edma_v0_core_status_abort_int(dw, dir) & mask;
for_each_set_bit(pos, &val, total) {
struct dw_edma_chan *chan = &dw->chan[pos + off];
dw_edma_v0_core_clear_abort_int(chan);
dw_edma_abort_interrupt(chan);
}
return IRQ_HANDLED;
}
What do you think?
> + void (*start)(struct dw_edma_chunk *chunk, bool first);
> + void (*ch_config)(struct dw_edma_chan *chan);
> + void (*debugfs_on)(struct dw_edma *dw);
> };
>
> struct dw_edma_sg {
> @@ -136,6 +153,9 @@ struct dw_edma_transfer {
> enum dw_edma_xfer_type type;
> };
>
> +void dw_edma_done_interrupt(struct dw_edma_chan *chan);
> +void dw_edma_abort_interrupt(struct dw_edma_chan *chan);
> +
> static inline
> struct dw_edma_chan *vc2dw_edma_chan(struct virt_dma_chan *vc)
> {
> @@ -148,4 +168,76 @@ struct dw_edma_chan *dchan2dw_edma_chan(struct dma_chan *dchan)
> return vc2dw_edma_chan(to_virt_chan(dchan));
> }
>
> +static inline
> +void dw_edma_core_off(struct dw_edma *dw)
> +{
> + dw->core->off(dw);
> +}
> +
> +static inline
> +u16 dw_edma_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir)
> +{
> + return dw->core->ch_count(dw, dir);
> +}
> +
> +static inline
> +enum dma_status dw_edma_core_ch_status(struct dw_edma_chan *chan)
> +{
> + struct dw_edma *dw = chan->dw;
> +
> + return dw->core->ch_status(chan);
> +}
> +
> +static inline
> +void dw_edma_core_clear_done_int(struct dw_edma_chan *chan)
> +{
> + struct dw_edma *dw = chan->dw;
> +
> + dw->core->clear_done_int(chan);
> +}
> +
> +static inline
> +void dw_edma_core_clear_abort_int(struct dw_edma_chan *chan)
> +{
> + struct dw_edma *dw = chan->dw;
> +
> + dw->core->clear_abort_int(chan);
> +}
> +
> +static inline
> +void dw_edma_core_done_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir)
> +{
> + struct dw_edma *dw = dw_irq->dw;
> +
> + dw->core->done_handle(dw_irq, dir);
> +}
> +
> +static inline
> +void dw_edma_core_abort_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir)
> +{
> + struct dw_edma *dw = dw_irq->dw;
> +
> + dw->core->abort_handle(dw_irq, dir);
> +}
As I suggested above these two methods can be replaced with a single
one: dw_edma_core_handle_int(...).
> +
> +static inline
> +void dw_edma_core_start(struct dw_edma *dw, struct dw_edma_chunk *chunk, bool first)
> +{
> + dw->core->start(chunk, first);
> +}
> +
> +static inline
> +void dw_edma_core_ch_config(struct dw_edma_chan *chan)
> +{
> + struct dw_edma *dw = chan->dw;
> +
> + dw->core->ch_config(chan);
> +}
> +
> +static inline
> +void dw_edma_core_debugfs_on(struct dw_edma *dw)
> +{
> + dw->core->debugfs_on(dw);
> +}
> +
> #endif /* _DW_EDMA_CORE_H */
> diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c
> index 72e79a0c0a4e..73dcf72ba3a9 100644
> --- a/drivers/dma/dw-edma/dw-edma-v0-core.c
> +++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
> @@ -216,7 +216,7 @@ static inline u64 readq_ch(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch,
> readq_ch(dw, dir, ch, &(__dw_ch_regs(dw, dir, ch)->name))
>
> /* eDMA management callbacks */
> -void dw_edma_v0_core_off(struct dw_edma *dw)
> +static void dw_edma_v0_core_off(struct dw_edma *dw)
> {
> SET_BOTH_32(dw, int_mask,
> EDMA_V0_DONE_INT_MASK | EDMA_V0_ABORT_INT_MASK);
> @@ -225,7 +225,7 @@ void dw_edma_v0_core_off(struct dw_edma *dw)
> SET_BOTH_32(dw, engine_en, 0);
> }
>
> -u16 dw_edma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir)
> +static u16 dw_edma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir)
> {
> u32 num_ch;
>
> @@ -242,7 +242,7 @@ u16 dw_edma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir)
> return (u16)num_ch;
> }
>
> -enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan)
> +static enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan)
> {
> struct dw_edma *dw = chan->dw;
> u32 tmp;
> @@ -258,7 +258,7 @@ enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan)
> return DMA_ERROR;
> }
>
> -void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan)
> +static void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan)
> {
> struct dw_edma *dw = chan->dw;
>
> @@ -266,7 +266,7 @@ void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan)
> FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id)));
> }
>
> -void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan)
> +static void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan)
> {
> struct dw_edma *dw = chan->dw;
>
> @@ -274,18 +274,66 @@ void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan)
> FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id)));
> }
>
> -u32 dw_edma_v0_core_status_done_int(struct dw_edma *dw, enum dw_edma_dir dir)
> +static u32 dw_edma_v0_core_status_done_int(struct dw_edma *dw, enum dw_edma_dir dir)
> {
> return FIELD_GET(EDMA_V0_DONE_INT_MASK,
> GET_RW_32(dw, dir, int_status));
> }
>
> -u32 dw_edma_v0_core_status_abort_int(struct dw_edma *dw, enum dw_edma_dir dir)
> +static u32 dw_edma_v0_core_status_abort_int(struct dw_edma *dw, enum dw_edma_dir dir)
> {
> return FIELD_GET(EDMA_V0_ABORT_INT_MASK,
> GET_RW_32(dw, dir, int_status));
> }
>
> +static
> +void dw_edma_v0_core_done_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir)
> +{
> + struct dw_edma *dw = dw_irq->dw;
> + unsigned long total, pos, val;
> + unsigned long off;
> + u32 mask;
> +
> + if (dir == EDMA_DIR_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, dir);
> + val &= mask;
> + for_each_set_bit(pos, &val, total)
> + dw_edma_done_interrupt(&dw->chan[pos + off]);
As I suggested above replace this with:
+ for_each_set_bit(pos, &val, total) {
+ struct dw_edma_chan *chan = &dw->chan[pos + off];
+
+ dw_edma_v0_core_clear_done_int(chan);
+
+ dw_edma_done_interrupt(chan);
+ }
Therefore you'll be able to drop the clear_done_int callback and its
invocation from the dw_edma_done_interrupt() function.
BTW Why did you change the dw_edma_v0_core_done_handle() semantics
from taking the function pointers as an argument to calling the global
version of dw_edma_done_interrupt() ?
> +}
> +
> +static
> +void dw_edma_v0_core_abort_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir)
> +{
> + struct dw_edma *dw = dw_irq->dw;
> + unsigned long total, pos, val;
> + unsigned long off;
> + u32 mask;
> +
> + if (dir == EDMA_DIR_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_abort_int(dw, dir);
> + val &= mask;
> + for_each_set_bit(pos, &val, total)
> + dw_edma_abort_interrupt(&dw->chan[pos + off]);
ditto
> +}
> +
> static void dw_edma_v0_write_ll_data(struct dw_edma_chunk *chunk, int i,
> u32 control, u32 size, u64 sar, u64 dar)
> {
> @@ -356,7 +404,7 @@ static void dw_edma_v0_core_write_chunk(struct dw_edma_chunk *chunk)
> dw_edma_v0_write_ll_link(chunk, i, control, chunk->ll_region.paddr);
> }
>
> -void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
> +static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
> {
> struct dw_edma_chan *chan = chunk->chan;
> struct dw_edma *dw = chan->dw;
> @@ -427,7 +475,7 @@ void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
> FIELD_PREP(EDMA_V0_DOORBELL_CH_MASK, chan->id));
> }
>
> -int dw_edma_v0_core_device_config(struct dw_edma_chan *chan)
> +static void dw_edma_v0_core_ch_config(struct dw_edma_chan *chan)
> {
> struct dw_edma *dw = chan->dw;
> u32 tmp = 0;
> @@ -494,12 +542,28 @@ int dw_edma_v0_core_device_config(struct dw_edma_chan *chan)
> SET_RW_32(dw, chan->dir, ch67_imwr_data, tmp);
> break;
> }
> -
> - return 0;
> }
>
> /* eDMA debugfs callbacks */
> -void dw_edma_v0_core_debugfs_on(struct dw_edma *dw)
> +static void dw_edma_v0_core_debugfs_on(struct dw_edma *dw)
> {
> dw_edma_v0_debugfs_on(dw);
> }
> +
> +static const struct dw_edma_core_ops edma_core = {
> + .off = dw_edma_v0_core_off,
> + .ch_count = dw_edma_v0_core_ch_count,
> + .ch_status = dw_edma_v0_core_ch_status,
> + .clear_done_int = dw_edma_v0_core_clear_done_int,
> + .clear_abort_int = dw_edma_v0_core_clear_abort_int,
> + .done_handle = dw_edma_v0_core_done_handle,
> + .abort_handle = dw_edma_v0_core_abort_handle,
If you take into account what I suggested above these four callbacks will
be replaced with a single one handle_int. Thus the interface will look
much simpler so will the whole IRQs-handling logic:
irq: dw_edma_interrupt_{write,read}()
+-> dw_edma_core_handle_int() (dw-edma-v0-core.c)
+-> dw_edma_v0_core_status_done_int() (dw-edma-v0-core.c)
+-> dw_edma_v0_core_clear_done_int() (dw-edma-v0-core.c)
+-> dw_edma_done_interrupt() (dw-edma-core.c)
+-> dw_edma_v0_core_status_abort_int() (dw-edma-v0-core.c)
+-> dw_edma_v0_core_clear_abort_int() (dw-edma-v0-core.c)
+-> dw_edma_abort_interrupt() (dw-edma-core.c)
In your case the calls hierarchy looks like this:
irq: dw_edma_interrupt_{write,read}()
+-> dw_edma_interrupt()
+-> dw_edma_core_done_handle() (dw-edma-v0-core.c)
+-> dw_edma_v0_core_status_done_int() (dw-edma-v0-core.c)
+-> dw_edma_done_interrupt() (dw-edma-core.c)
+-> dw_edma_core_clear_done_int() (dw-edma-v0-core.c)
+-> dw_edma_core_abort_handle() (dw-edma-v0-core.c)
+-> dw_edma_v0_core_status_abort_int() (dw-edma-v0-core.c)
+-> dw_edma_abort_interrupt() (dw-edma-core.c)
+-> dw_edma_core_clear_abort_int() (dw-edma-v0-core.c)
See the calls thread is more linear in my case and the reader won't
need to get several times back and forth between the generic part of
the driver and the IP-core-specific one.
-Serge(y)
> + .start = dw_edma_v0_core_start,
> + .ch_config = dw_edma_v0_core_ch_config,
> + .debugfs_on = dw_edma_v0_core_debugfs_on,
> +};
> +
> +void dw_edma_v0_core_register(struct dw_edma *dw)
> +{
> + dw->core = &edma_core;
> +}
> diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.h b/drivers/dma/dw-edma/dw-edma-v0-core.h
> index ab96a1f48080..04a882222f99 100644
> --- a/drivers/dma/dw-edma/dw-edma-v0-core.h
> +++ b/drivers/dma/dw-edma/dw-edma-v0-core.h
> @@ -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 */
> --
> 2.34.1
>
On Mon, Feb 13, 2023 at 09:24:07PM +0800, Cai Huoqing wrote:
> From: Cai huoqing <[email protected]>
>
> 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.
>
> Signed-off-by: Cai huoqing <[email protected]>
> ---
> drivers/dma/dw-edma/dw-edma-core.c | 63 ++++++------------
> drivers/dma/dw-edma/dw-edma-core.h | 92 +++++++++++++++++++++++++++
> drivers/dma/dw-edma/dw-edma-v0-core.c | 88 +++++++++++++++++++++----
> drivers/dma/dw-edma/dw-edma-v0-core.h | 14 +---
> 4 files changed, 188 insertions(+), 69 deletions(-)
>
> diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
> index 1906a836f0aa..c171809beba1 100644
> --- a/drivers/dma/dw-edma/dw-edma-core.c
> +++ b/drivers/dma/dw-edma/dw-edma-core.c
> @@ -183,6 +183,7 @@ static void vchan_free_desc(struct virt_dma_desc *vdesc)
>
> static void 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 void dw_edma_start_transfer(struct dw_edma_chan *chan)
> if (!child)
> return;
>
> - 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);
> @@ -285,7 +286,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
> @@ -588,13 +589,13 @@ dw_edma_device_prep_interleaved_dma(struct dma_chan *dchan,
> return dw_edma_device_transfer(&xfer);
> }
>
> -static void dw_edma_done_interrupt(struct dw_edma_chan *chan)
> +void dw_edma_done_interrupt(struct dw_edma_chan *chan)
> {
> struct dw_edma_desc *desc;
> struct virt_dma_desc *vd;
> unsigned long flags;
>
> - dw_edma_v0_core_clear_done_int(chan);
> + dw_edma_core_clear_done_int(chan);
>
> spin_lock_irqsave(&chan->vc.lock, flags);
> vd = vchan_next_desc(&chan->vc);
> @@ -631,12 +632,12 @@ static void dw_edma_done_interrupt(struct dw_edma_chan *chan)
> spin_unlock_irqrestore(&chan->vc.lock, flags);
> }
>
> -static void dw_edma_abort_interrupt(struct dw_edma_chan *chan)
> +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);
> + dw_edma_core_clear_abort_int(chan);
>
> spin_lock_irqsave(&chan->vc.lock, flags);
> vd = vchan_next_desc(&chan->vc);
> @@ -651,41 +652,11 @@ static void dw_edma_abort_interrupt(struct dw_edma_chan *chan)
>
> static irqreturn_t dw_edma_interrupt(int irq, void *data, bool write)
> {
> + enum dw_edma_dir dir = write ? EDMA_DIR_WRITE : EDMA_DIR_READ;
> 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);
> - }
> + dw_edma_core_done_handle(dw_irq, dir);
> + dw_edma_core_abort_handle(dw_irq, dir);
>
> return IRQ_HANDLED;
> }
> @@ -806,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 */
> @@ -951,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)
> @@ -977,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);
> @@ -990,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;
>
> @@ -1016,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--)
> diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h
> index 0ab2b6dba880..2555ae0e1b8b 100644
> --- a/drivers/dma/dw-edma/dw-edma-core.h
> +++ b/drivers/dma/dw-edma/dw-edma-core.h
> @@ -111,6 +111,23 @@ struct dw_edma {
> raw_spinlock_t lock; /* Only for legacy */
>
> struct dw_edma_chip *chip;
> +
> + const struct dw_edma_core_ops *core;
> +};
> +
> +struct dw_edma_core_ops {
> + void (*off)(struct dw_edma *dw);
> + u16 (*ch_count)(struct dw_edma *dw, enum dw_edma_dir dir);
> + enum dma_status (*ch_status)(struct dw_edma_chan *chan);
> + void (*clear_done_int)(struct dw_edma_chan *chan);
> + void (*clear_abort_int)(struct dw_edma_chan *chan);
> + void (*done_handle)(struct dw_edma_irq *dw_irq,
> + enum dw_edma_dir dir);
> + void (*abort_handle)(struct dw_edma_irq *dw_irq,
> + enum dw_edma_dir dir);
> + void (*start)(struct dw_edma_chunk *chunk, bool first);
> + void (*ch_config)(struct dw_edma_chan *chan);
> + void (*debugfs_on)(struct dw_edma *dw);
> };
>
> struct dw_edma_sg {
> @@ -136,6 +153,9 @@ struct dw_edma_transfer {
> enum dw_edma_xfer_type type;
> };
>
> +void dw_edma_done_interrupt(struct dw_edma_chan *chan);
> +void dw_edma_abort_interrupt(struct dw_edma_chan *chan);
> +
> static inline
> struct dw_edma_chan *vc2dw_edma_chan(struct virt_dma_chan *vc)
> {
> @@ -148,4 +168,76 @@ struct dw_edma_chan *dchan2dw_edma_chan(struct dma_chan *dchan)
> return vc2dw_edma_chan(to_virt_chan(dchan));
> }
>
> +static inline
> +void dw_edma_core_off(struct dw_edma *dw)
> +{
> + dw->core->off(dw);
> +}
> +
> +static inline
> +u16 dw_edma_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir)
> +{
> + return dw->core->ch_count(dw, dir);
> +}
> +
> +static inline
> +enum dma_status dw_edma_core_ch_status(struct dw_edma_chan *chan)
> +{
> + struct dw_edma *dw = chan->dw;
> +
> + return dw->core->ch_status(chan);
> +}
> +
> +static inline
> +void dw_edma_core_clear_done_int(struct dw_edma_chan *chan)
> +{
> + struct dw_edma *dw = chan->dw;
> +
> + dw->core->clear_done_int(chan);
> +}
> +
> +static inline
> +void dw_edma_core_clear_abort_int(struct dw_edma_chan *chan)
> +{
> + struct dw_edma *dw = chan->dw;
> +
> + dw->core->clear_abort_int(chan);
> +}
> +
> +static inline
> +void dw_edma_core_done_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir)
> +{
> + struct dw_edma *dw = dw_irq->dw;
> +
> + dw->core->done_handle(dw_irq, dir);
> +}
> +
> +static inline
> +void dw_edma_core_abort_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir)
> +{
> + struct dw_edma *dw = dw_irq->dw;
> +
> + dw->core->abort_handle(dw_irq, dir);
> +}
> +
> +static inline
> +void dw_edma_core_start(struct dw_edma *dw, struct dw_edma_chunk *chunk, bool first)
> +{
> + dw->core->start(chunk, first);
> +}
> +
> +static inline
> +void dw_edma_core_ch_config(struct dw_edma_chan *chan)
> +{
> + struct dw_edma *dw = chan->dw;
> +
> + dw->core->ch_config(chan);
> +}
> +
> +static inline
> +void dw_edma_core_debugfs_on(struct dw_edma *dw)
> +{
> + dw->core->debugfs_on(dw);
> +}
> +
> #endif /* _DW_EDMA_CORE_H */
> diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c
> index 72e79a0c0a4e..73dcf72ba3a9 100644
> --- a/drivers/dma/dw-edma/dw-edma-v0-core.c
> +++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
> @@ -216,7 +216,7 @@ static inline u64 readq_ch(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch,
> readq_ch(dw, dir, ch, &(__dw_ch_regs(dw, dir, ch)->name))
>
> /* eDMA management callbacks */
> -void dw_edma_v0_core_off(struct dw_edma *dw)
> +static void dw_edma_v0_core_off(struct dw_edma *dw)
> {
> SET_BOTH_32(dw, int_mask,
> EDMA_V0_DONE_INT_MASK | EDMA_V0_ABORT_INT_MASK);
> @@ -225,7 +225,7 @@ void dw_edma_v0_core_off(struct dw_edma *dw)
> SET_BOTH_32(dw, engine_en, 0);
> }
>
> -u16 dw_edma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir)
> +static u16 dw_edma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir)
> {
> u32 num_ch;
>
> @@ -242,7 +242,7 @@ u16 dw_edma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir)
> return (u16)num_ch;
> }
>
> -enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan)
> +static enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan)
> {
> struct dw_edma *dw = chan->dw;
> u32 tmp;
> @@ -258,7 +258,7 @@ enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan)
> return DMA_ERROR;
> }
>
> -void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan)
> +static void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan)
> {
> struct dw_edma *dw = chan->dw;
>
> @@ -266,7 +266,7 @@ void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan)
> FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id)));
> }
>
> -void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan)
> +static void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan)
> {
> struct dw_edma *dw = chan->dw;
>
> @@ -274,18 +274,66 @@ void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan)
> FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id)));
> }
>
> -u32 dw_edma_v0_core_status_done_int(struct dw_edma *dw, enum dw_edma_dir dir)
> +static u32 dw_edma_v0_core_status_done_int(struct dw_edma *dw, enum dw_edma_dir dir)
> {
> return FIELD_GET(EDMA_V0_DONE_INT_MASK,
> GET_RW_32(dw, dir, int_status));
> }
>
> -u32 dw_edma_v0_core_status_abort_int(struct dw_edma *dw, enum dw_edma_dir dir)
> +static u32 dw_edma_v0_core_status_abort_int(struct dw_edma *dw, enum dw_edma_dir dir)
> {
> return FIELD_GET(EDMA_V0_ABORT_INT_MASK,
> GET_RW_32(dw, dir, int_status));
> }
>
> +static
> +void dw_edma_v0_core_done_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir)
> +{
> + struct dw_edma *dw = dw_irq->dw;
> + unsigned long total, pos, val;
> + unsigned long off;
> + u32 mask;
> +
> + if (dir == EDMA_DIR_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, dir);
> + val &= mask;
> + for_each_set_bit(pos, &val, total)
> + dw_edma_done_interrupt(&dw->chan[pos + off]);
> +}
> +
> +static
> +void dw_edma_v0_core_abort_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir)
> +{
> + struct dw_edma *dw = dw_irq->dw;
> + unsigned long total, pos, val;
> + unsigned long off;
> + u32 mask;
> +
> + if (dir == EDMA_DIR_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_abort_int(dw, dir);
> + val &= mask;
> + for_each_set_bit(pos, &val, total)
> + dw_edma_abort_interrupt(&dw->chan[pos + off]);
> +}
> +
> static void dw_edma_v0_write_ll_data(struct dw_edma_chunk *chunk, int i,
> u32 control, u32 size, u64 sar, u64 dar)
> {
> @@ -356,7 +404,7 @@ static void dw_edma_v0_core_write_chunk(struct dw_edma_chunk *chunk)
> dw_edma_v0_write_ll_link(chunk, i, control, chunk->ll_region.paddr);
> }
>
> -void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
> +static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
> {
> struct dw_edma_chan *chan = chunk->chan;
> struct dw_edma *dw = chan->dw;
> @@ -427,7 +475,7 @@ void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
> FIELD_PREP(EDMA_V0_DOORBELL_CH_MASK, chan->id));
> }
>
> -int dw_edma_v0_core_device_config(struct dw_edma_chan *chan)
> +static void dw_edma_v0_core_ch_config(struct dw_edma_chan *chan)
> {
> struct dw_edma *dw = chan->dw;
> u32 tmp = 0;
> @@ -494,12 +542,28 @@ int dw_edma_v0_core_device_config(struct dw_edma_chan *chan)
> SET_RW_32(dw, chan->dir, ch67_imwr_data, tmp);
> break;
> }
> -
> - return 0;
> }
>
> /* eDMA debugfs callbacks */
> -void dw_edma_v0_core_debugfs_on(struct dw_edma *dw)
> +static void dw_edma_v0_core_debugfs_on(struct dw_edma *dw)
> {
> dw_edma_v0_debugfs_on(dw);
> }
> +
> +static const struct dw_edma_core_ops edma_core = {
Please use the dw_edma_v0_core name for the structure instance in
order to maintain the local naming convention.
-Serge(y)
> + .off = dw_edma_v0_core_off,
> + .ch_count = dw_edma_v0_core_ch_count,
> + .ch_status = dw_edma_v0_core_ch_status,
> + .clear_done_int = dw_edma_v0_core_clear_done_int,
> + .clear_abort_int = dw_edma_v0_core_clear_abort_int,
> + .done_handle = dw_edma_v0_core_done_handle,
> + .abort_handle = dw_edma_v0_core_abort_handle,
> + .start = dw_edma_v0_core_start,
> + .ch_config = dw_edma_v0_core_ch_config,
> + .debugfs_on = dw_edma_v0_core_debugfs_on,
> +};
> +
> +void dw_edma_v0_core_register(struct dw_edma *dw)
> +{
> + dw->core = &edma_core;
> +}
> diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.h b/drivers/dma/dw-edma/dw-edma-v0-core.h
> index ab96a1f48080..04a882222f99 100644
> --- a/drivers/dma/dw-edma/dw-edma-v0-core.h
> +++ b/drivers/dma/dw-edma/dw-edma-v0-core.h
> @@ -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 */
> --
> 2.34.1
>
On Mon, Feb 13, 2023 at 09:24:08PM +0800, Cai Huoqing wrote:
> From: Cai huoqing <[email protected]>
>
> 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 alignmented to byte.
>
> Signed-off-by: Cai huoqing <[email protected]>
> ---
> drivers/dma/dw-edma/Makefile | 5 +-
> drivers/dma/dw-edma/dw-hdma-v0-core.c | 315 ++++++++++++++++++++++++++
> drivers/dma/dw-edma/dw-hdma-v0-core.h | 17 ++
> drivers/dma/dw-edma/dw-hdma-v0-regs.h | 129 +++++++++++
> 4 files changed, 464 insertions(+), 2 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
>
> diff --git a/drivers/dma/dw-edma/Makefile b/drivers/dma/dw-edma/Makefile
> index 8d45c0d5689d..b1c91ef2c63d 100644
> --- a/drivers/dma/dw-edma/Makefile
> +++ b/drivers/dma/dw-edma/Makefile
> @@ -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
> diff --git a/drivers/dma/dw-edma/dw-hdma-v0-core.c b/drivers/dma/dw-edma/dw-hdma-v0-core.c
> new file mode 100644
> index 000000000000..7e4f98987e29
> --- /dev/null
> +++ b/drivers/dma/dw-edma/dw-hdma-v0-core.c
> @@ -0,0 +1,315 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2023 Cai Huoqing
> + * Synopsys DesignWare HDMA v0 core
> + */
> +
> +#include <linux/bitfield.h>
> +
> +#include "dw-edma-core.h"
> +#include "dw-hdma-v0-core.h"
> +#include "dw-hdma-v0-regs.h"
> +
> +enum dw_hdma_control {
> + DW_HDMA_V0_CB = BIT(0),
> + DW_HDMA_V0_TCB = BIT(1),
> + DW_HDMA_V0_LLP = BIT(2),
> + DW_HDMA_V0_LIE = BIT(3),
> + DW_HDMA_V0_RIE = BIT(4),
> + DW_HDMA_V0_CCS = BIT(8),
> + DW_HDMA_V0_LLE = BIT(9),
> +};
> +
> +static inline struct dw_hdma_v0_regs __iomem *__dw_regs(struct dw_edma *dw)
> +{
> + return dw->chip->reg_base;
> +}
> +
> +static inline struct dw_hdma_v0_ch_regs __iomem *
> +__dw_ch_regs(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch)
> +{
> + if (dir == EDMA_DIR_WRITE)
> + return &(__dw_regs(dw)->ch[ch].wr);
> + else
> + return &(__dw_regs(dw)->ch[ch].rd);
> +}
> +
> +#define SET_CH_32(dw, dir, ch, name, value) \
> + writel(value, &(__dw_ch_regs(dw, dir, ch)->name))
> +
> +#define GET_CH_32(dw, dir, ch, name) \
> + readl(&(__dw_ch_regs(dw, dir, ch)->name))
> +
> +#define SET_BOTH_CH_32(dw, ch, name, value) \
> + do { \
> + writel(value, &(__dw_ch_regs(dw, EDMA_DIR_WRITE, ch)->name)); \
> + writel(value, &(__dw_ch_regs(dw, EDMA_DIR_READ, ch)->name)); \
> + } while (0)
> +
> +/* HDMA management callbacks */
> +static void dw_hdma_v0_core_off(struct dw_edma *dw)
> +{
> + int id;
> +
> + for (id = 0; id < HDMA_V0_MAX_NR_CH; id++) {
> + SET_BOTH_CH_32(dw, id, int_setup,
> + HDMA_V0_STOP_INT_MASK | HDMA_V0_ABORT_INT_MASK);
> + SET_BOTH_CH_32(dw, id, int_clear,
> + HDMA_V0_STOP_INT_MASK | HDMA_V0_ABORT_INT_MASK);
> + SET_BOTH_CH_32(dw, id, ch_en, 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 enum dma_status dw_hdma_v0_core_ch_status(struct dw_edma_chan *chan)
> +{
> + struct dw_edma *dw = chan->dw;
> + u32 tmp;
> +
> + tmp = FIELD_GET(HDMA_V0_CH_STATUS_MASK,
> + GET_CH_32(dw, chan->id, chan->dir, ch_stat));
> +
> + if (tmp == 1)
> + return DMA_IN_PROGRESS;
> + else if (tmp == 3)
> + return DMA_COMPLETE;
> + else
> + return DMA_ERROR;
> +}
> +
> +static void dw_hdma_v0_core_clear_done_int(struct dw_edma_chan *chan)
> +{
> + struct dw_edma *dw = chan->dw;
> +
> + SET_CH_32(dw, chan->dir, chan->id, int_clear, HDMA_V0_STOP_INT_MASK);
> +}
> +
> +static void dw_hdma_v0_core_clear_abort_int(struct dw_edma_chan *chan)
> +{
> + struct dw_edma *dw = chan->dw;
> +
> + SET_CH_32(dw, chan->dir, chan->id, int_clear, HDMA_V0_ABORT_INT_MASK);
> +}
> +
> +static u32 dw_hdma_v0_core_status_int(struct dw_edma_chan *chan)
> +{
> + struct dw_edma *dw = chan->dw;
> +
> + return GET_CH_32(dw, chan->dir, chan->id, int_stat);
> +}
> +
> +static u32 dw_hdma_v0_core_check_done_int(u32 val)
> +{
> + return FIELD_GET(HDMA_V0_STOP_INT_MASK, val);
> +}
> +
> +static u32 dw_hdma_v0_core_check_abort_int(u32 val)
> +{
> + return FIELD_GET(HDMA_V0_ABORT_INT_MASK, val);
> +}
> +
> +static
> +void dw_hdma_v0_core_done_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir)
> +{
> + struct dw_edma *dw = dw_irq->dw;
> + unsigned long total, pos, val;
> + unsigned long off, mask;
> +
> + if (dir == EDMA_DIR_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;
> + }
> +
> + for_each_set_bit(pos, &mask, total) {
> + struct dw_edma_chan *chan = &dw->chan[pos + off];
> +
> + val = dw_hdma_v0_core_status_int(chan);
> + if (dw_hdma_v0_core_check_done_int(val))
> + dw_edma_done_interrupt(chan);
> + }
> +}
> +
> +static
> +void dw_hdma_v0_core_abort_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir)
> +{
> + struct dw_edma *dw = dw_irq->dw;
> + unsigned long total, pos, val;
> + unsigned long off, mask;
> +
> + if (dir == EDMA_DIR_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;
> + }
> +
> + for_each_set_bit(pos, &mask, total) {
> + struct dw_edma_chan *chan = &dw->chan[pos + off];
> +
> + val = dw_hdma_v0_core_status_int(chan);
> + if (dw_hdma_v0_core_check_abort_int(val))
> + dw_edma_abort_interrupt(&dw->chan[pos + off]);
> + }
> +}
Please see my comments to the previous patch in the series regarding
the done/abort IRQ-handlers and the core_clear_{done,abort}_int
callbacks usage.
> +
> +static void dw_hdma_v0_write_ll_data(struct dw_edma_chunk *chunk, int i,
> + u32 control, u32 size, u64 sar, u64 dar)
> +{
> + ptrdiff_t ofs = i * sizeof(struct dw_hdma_v0_lli);
> +
> + if (chunk->chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) {
> + struct dw_hdma_v0_lli *lli = chunk->ll_region.vaddr.mem + ofs;
> +
> + lli->control = control;
> + lli->transfer_size = size;
> + lli->sar.reg = sar;
> + lli->dar.reg = dar;
> + } else {
> + struct dw_hdma_v0_lli __iomem *lli = chunk->ll_region.vaddr.io + ofs;
> +
> + writel(control, &lli->control);
> + writel(size, &lli->transfer_size);
> + writeq(sar, &lli->sar.reg);
> + writeq(dar, &lli->dar.reg);
> + }
> +}
> +
> +static void dw_hdma_v0_write_ll_link(struct dw_edma_chunk *chunk,
> + int i, u32 control, u64 pointer)
> +{
> + ptrdiff_t ofs = i * sizeof(struct dw_hdma_v0_lli);
> +
> + if (chunk->chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) {
> + struct dw_hdma_v0_llp *llp = chunk->ll_region.vaddr.mem + ofs;
> +
> + llp->control = control;
> + llp->llp.reg = pointer;
> + } else {
> + struct dw_hdma_v0_llp __iomem *llp = chunk->ll_region.vaddr.io + ofs;
> +
> + writel(control, &llp->control);
> + writeq(pointer, &llp->llp.reg);
> + }
> +}
> +
> +static void dw_hdma_v0_core_write_chunk(struct dw_edma_chunk *chunk)
> +{
> + struct dw_edma_burst *child;
> + struct dw_edma_chan *chan = chunk->chan;
> + u32 control = 0, i = 0;
> + int j;
> +
> + if (chunk->cb)
> + control = DW_HDMA_V0_CB;
> +
> + j = chunk->bursts_alloc;
> + list_for_each_entry(child, &chunk->burst->list, list) {
> + j--;
> + if (!j) {
> + control |= DW_HDMA_V0_LIE;
> + if (!(chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL))
> + control |= DW_HDMA_V0_RIE;
> + }
> +
> + dw_hdma_v0_write_ll_data(chunk, i++, control, child->sz,
> + child->sar, child->dar);
> + }
> +
> + control = DW_HDMA_V0_LLP | DW_HDMA_V0_TCB;
> + if (!chunk->cb)
> + control |= DW_HDMA_V0_CB;
> +
> + dw_hdma_v0_write_ll_link(chunk, i, control, chunk->ll_region.paddr);
> +}
> +
> +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));
> + /* Interrupt enable&unmask - done, abort */
> + tmp = GET_CH_32(dw, chan->dir, chan->id, int_setup) | HDMA_V0_STOP_INT_MASK |
> + HDMA_V0_ABORT_INT_MASK | HDMA_V0_LOCAL_STOP_INT_EN |
> + HDMA_V0_LOCAL_STOP_INT_EN;
> + SET_CH_32(dw, chan->dir, chan->id, int_setup, tmp);
> + /* Channel control */
> + SET_CH_32(dw, chan->dir, chan->id, control1, HDMA_V0_LINKLIST_EN);
> + /* Linked list */
> + /* llp is not aligned on 64bit -> keep 32bit accesses */
> + SET_CH_32(dw, chan->dir, chan->id, llp.lsb,
> + lower_32_bits(chunk->ll_region.paddr));
> + SET_CH_32(dw, chan->dir, chan->id, llp.msb,
> + upper_32_bits(chunk->ll_region.paddr));
> + }
> + /* Set consumer cycle */
> + SET_CH_32(dw, chan->dir, chan->id, cycle_sync,
> + HDMA_V0_CONSUMER_CYCLE_STAT | HDMA_V0_CONSUMER_CYCLE_BIT);
> + /* Doorbell */
> + SET_CH_32(dw, chan->dir, chan->id, doorbell, HDMA_V0_DOORBELL_START);
> +}
> +
> +static void dw_hdma_v0_core_ch_config(struct dw_edma_chan *chan)
> +{
> + struct dw_edma *dw = chan->dw;
> +
> + /* MSI done addr - low, high */
> + SET_CH_32(dw, chan->dir, chan->id, msi_stop.lsb, chan->msi.address_lo);
> + SET_CH_32(dw, chan->dir, chan->id, msi_stop.msb, chan->msi.address_hi);
> + /* MSI abort addr - low, high */
> + SET_CH_32(dw, chan->dir, chan->id, msi_abort.lsb, chan->msi.address_lo);
> + SET_CH_32(dw, chan->dir, chan->id, msi_abort.msb, chan->msi.address_hi);
> + /* config MSI data */
> + SET_CH_32(dw, chan->dir, chan->id, msi_msgdata, chan->msi.data);
> +}
> +
> +/* HDMA debugfs callbacks */
> +static void dw_hdma_v0_core_debugfs_on(struct dw_edma *dw)
> +{
> +}
> +
> +static const struct dw_edma_core_ops hdma_core = {
> + .off = dw_hdma_v0_core_off,
> + .ch_count = dw_hdma_v0_core_ch_count,
> + .ch_status = dw_hdma_v0_core_ch_status,
> + .clear_done_int = dw_hdma_v0_core_clear_done_int,
> + .clear_abort_int = dw_hdma_v0_core_clear_abort_int,
> + .done_handle = dw_hdma_v0_core_done_handle,
> + .abort_handle = dw_hdma_v0_core_abort_handle,
> + .start = dw_hdma_v0_core_start,
> + .ch_config = dw_hdma_v0_core_ch_config,
> + .debugfs_on = dw_hdma_v0_core_debugfs_on,
> +};
> +
> +void dw_hdma_v0_core_register(struct dw_edma *dw)
> +{
> + dw->core = &hdma_core;
> +}
> diff --git a/drivers/dma/dw-edma/dw-hdma-v0-core.h b/drivers/dma/dw-edma/dw-hdma-v0-core.h
> new file mode 100644
> index 000000000000..c373b4f0bd8a
> --- /dev/null
> +++ b/drivers/dma/dw-edma/dw-hdma-v0-core.h
> @@ -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 */
> diff --git a/drivers/dma/dw-edma/dw-hdma-v0-regs.h b/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> new file mode 100644
> index 000000000000..f6b2bd03060c
> --- /dev/null
> +++ b/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> @@ -0,0 +1,129 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2023 Cai Huoqing
> + * Synopsys DesignWare HDMA v0 reg
> + *
> + * Author: Cai Huoqing <[email protected]>
> + */
> +
> +#ifndef _DW_HDMA_V0_REGS_H
> +#define _DW_HDMA_V0_REGS_H
> +
> +#include <linux/dmaengine.h>
> +
> +#define HDMA_V0_MAX_NR_CH 8
> +#define HDMA_V0_LOCAL_ABORT_INT_EN BIT(6)
> +#define HDMA_V0_REMOTE_ABORT_INT_EN BIT(5)
> +#define HDMA_V0_LOCAL_STOP_INT_EN BIT(4)
> +#define HDMA_V0_REMOTEL_STOP_INT_EN BIT(3)
> +#define HDMA_V0_ABORT_INT_MASK BIT(2)
> +#define HDMA_V0_STOP_INT_MASK BIT(0)
> +#define HDMA_V0_LINKLIST_EN BIT(0)
> +#define HDMA_V0_CONSUMER_CYCLE_STAT BIT(1)
> +#define HDMA_V0_CONSUMER_CYCLE_BIT BIT(0)
> +#define HDMA_V0_DOORBELL_START BIT(0)
> +#define HDMA_V0_CH_STATUS_MASK GENMASK(1, 0)
> +
> +struct dw_hdma_v0_ch_regs {
> + u32 ch_en; /* 0x0000 */
> + u32 doorbell; /* 0x0004 */
> + u32 prefetch; /* 0x0008 */
> + u32 handshake; /* 0x000c */
> + union {
> + u64 reg; /* 0x0010..0x0014 */
> + struct {
> + u32 lsb; /* 0x0010 */
> + u32 msb; /* 0x0014 */
> + };
> + } llp;
> + u32 cycle_sync; /* 0x0018 */
> + u32 transfer_size; /* 0x001c */
> + union {
> + u64 reg; /* 0x0020..0x0024 */
> + struct {
> + u32 lsb; /* 0x0020 */
> + u32 msb; /* 0x0024 */
> + };
> + } sar;
> + union {
> + u64 reg; /* 0x0028..0x002c */
> + struct {
> + u32 lsb; /* 0x0028 */
> + u32 msb; /* 0x002c */
> + };
> + } dar;
> +
> + u32 watermark_en; /* 0x0030 */
> + u32 control1; /* 0x0034 */
> + u32 func_num; /* 0x0038 */
> + u32 qos; /* 0x003c */
> + u32 reserved; /* 0x0040..0x007c */
As Bjorn correctly noted, please fix the indentation.
> + u32 ch_stat; /* 0x0080 */
> + u32 int_stat; /* 0x0084 */
> + u32 int_setup; /* 0x0088 */
> + u32 int_clear; /* 0x008c */
> + union {
> + u64 reg; /* 0x0090..0x0094 */
> + struct {
> + u32 lsb; /* 0x0090 */
> + u32 msb; /* 0x0094 */
> + };
> + } msi_stop;
> + union {
> + u64 reg; /* 0x0098..0x009c */
> + struct {
> + u32 lsb; /* 0x0098 */
> + u32 msb; /* 0x009c */
> + };
> + } msi_watermark;
> + union {
> + u64 reg; /* 0x00a0..0x00a4 */
> + struct {
> + u32 lsb; /* 0x00a0 */
> + u32 msb; /* 0x00a4 */
> + };
> + } msi_abort;
> + u32 msi_msgdata; /* 0x00a8 */
ditto
-Serge(y)
> +} __packed;
> +
> +struct dw_hdma_v0_ch {
> + struct dw_hdma_v0_ch_regs wr; /* 0x0000 */
> + struct dw_hdma_v0_ch_regs rd; /* 0x0100 */
> +} __packed;
> +
> +struct dw_hdma_v0_regs {
> + struct dw_hdma_v0_ch ch[HDMA_V0_MAX_NR_CH]; /* 0x0000..0x0fa8 */
> +} __packed;
> +
> +struct dw_hdma_v0_lli {
> + u32 control;
> + u32 transfer_size;
> + union {
> + u64 reg;
> + struct {
> + u32 lsb;
> + u32 msb;
> + };
> + } sar;
> + union {
> + u64 reg;
> + struct {
> + u32 lsb;
> + u32 msb;
> + };
> + } dar;
> +} __packed;
> +
> +struct dw_hdma_v0_llp {
> + u32 control;
> + u32 reserved;
> + union {
> + u64 reg;
> + struct {
> + u32 lsb;
> + u32 msb;
> + };
> + } llp;
> +} __packed;
> +
> +#endif /* _DW_HDMA_V0_REGS_H */
> --
> 2.34.1
>
On Mon, Feb 13, 2023 at 09:24:08PM +0800, Cai Huoqing wrote:
> From: Cai huoqing <[email protected]>
>
> 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 alignmented to byte.
>
> Signed-off-by: Cai huoqing <[email protected]>
> ---
> drivers/dma/dw-edma/Makefile | 5 +-
> drivers/dma/dw-edma/dw-hdma-v0-core.c | 315 ++++++++++++++++++++++++++
> drivers/dma/dw-edma/dw-hdma-v0-core.h | 17 ++
> drivers/dma/dw-edma/dw-hdma-v0-regs.h | 129 +++++++++++
> 4 files changed, 464 insertions(+), 2 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
>
> diff --git a/drivers/dma/dw-edma/Makefile b/drivers/dma/dw-edma/Makefile
> index 8d45c0d5689d..b1c91ef2c63d 100644
> --- a/drivers/dma/dw-edma/Makefile
> +++ b/drivers/dma/dw-edma/Makefile
> @@ -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
> diff --git a/drivers/dma/dw-edma/dw-hdma-v0-core.c b/drivers/dma/dw-edma/dw-hdma-v0-core.c
> new file mode 100644
> index 000000000000..7e4f98987e29
> --- /dev/null
> +++ b/drivers/dma/dw-edma/dw-hdma-v0-core.c
> @@ -0,0 +1,315 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2023 Cai Huoqing
> + * Synopsys DesignWare HDMA v0 core
> + */
> +
> +#include <linux/bitfield.h>
> +
> +#include "dw-edma-core.h"
> +#include "dw-hdma-v0-core.h"
> +#include "dw-hdma-v0-regs.h"
> +
> +enum dw_hdma_control {
> + DW_HDMA_V0_CB = BIT(0),
> + DW_HDMA_V0_TCB = BIT(1),
> + DW_HDMA_V0_LLP = BIT(2),
> + DW_HDMA_V0_LIE = BIT(3),
> + DW_HDMA_V0_RIE = BIT(4),
> + DW_HDMA_V0_CCS = BIT(8),
> + DW_HDMA_V0_LLE = BIT(9),
> +};
> +
> +static inline struct dw_hdma_v0_regs __iomem *__dw_regs(struct dw_edma *dw)
> +{
> + return dw->chip->reg_base;
> +}
> +
> +static inline struct dw_hdma_v0_ch_regs __iomem *
> +__dw_ch_regs(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch)
> +{
> + if (dir == EDMA_DIR_WRITE)
> + return &(__dw_regs(dw)->ch[ch].wr);
> + else
> + return &(__dw_regs(dw)->ch[ch].rd);
> +}
> +
> +#define SET_CH_32(dw, dir, ch, name, value) \
> + writel(value, &(__dw_ch_regs(dw, dir, ch)->name))
> +
> +#define GET_CH_32(dw, dir, ch, name) \
> + readl(&(__dw_ch_regs(dw, dir, ch)->name))
> +
> +#define SET_BOTH_CH_32(dw, ch, name, value) \
> + do { \
> + writel(value, &(__dw_ch_regs(dw, EDMA_DIR_WRITE, ch)->name)); \
> + writel(value, &(__dw_ch_regs(dw, EDMA_DIR_READ, ch)->name)); \
> + } while (0)
> +
> +/* HDMA management callbacks */
> +static void dw_hdma_v0_core_off(struct dw_edma *dw)
> +{
> + int id;
> +
> + for (id = 0; id < HDMA_V0_MAX_NR_CH; id++) {
> + SET_BOTH_CH_32(dw, id, int_setup,
> + HDMA_V0_STOP_INT_MASK | HDMA_V0_ABORT_INT_MASK);
> + SET_BOTH_CH_32(dw, id, int_clear,
> + HDMA_V0_STOP_INT_MASK | HDMA_V0_ABORT_INT_MASK);
> + SET_BOTH_CH_32(dw, id, ch_en, 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 enum dma_status dw_hdma_v0_core_ch_status(struct dw_edma_chan *chan)
> +{
> + struct dw_edma *dw = chan->dw;
> + u32 tmp;
> +
> + tmp = FIELD_GET(HDMA_V0_CH_STATUS_MASK,
> + GET_CH_32(dw, chan->id, chan->dir, ch_stat));
> +
> + if (tmp == 1)
> + return DMA_IN_PROGRESS;
> + else if (tmp == 3)
> + return DMA_COMPLETE;
> + else
> + return DMA_ERROR;
> +}
> +
> +static void dw_hdma_v0_core_clear_done_int(struct dw_edma_chan *chan)
> +{
> + struct dw_edma *dw = chan->dw;
> +
> + SET_CH_32(dw, chan->dir, chan->id, int_clear, HDMA_V0_STOP_INT_MASK);
> +}
> +
> +static void dw_hdma_v0_core_clear_abort_int(struct dw_edma_chan *chan)
> +{
> + struct dw_edma *dw = chan->dw;
> +
> + SET_CH_32(dw, chan->dir, chan->id, int_clear, HDMA_V0_ABORT_INT_MASK);
> +}
> +
> +static u32 dw_hdma_v0_core_status_int(struct dw_edma_chan *chan)
> +{
> + struct dw_edma *dw = chan->dw;
> +
> + return GET_CH_32(dw, chan->dir, chan->id, int_stat);
> +}
> +
> +static u32 dw_hdma_v0_core_check_done_int(u32 val)
> +{
> + return FIELD_GET(HDMA_V0_STOP_INT_MASK, val);
> +}
> +
> +static u32 dw_hdma_v0_core_check_abort_int(u32 val)
> +{
> + return FIELD_GET(HDMA_V0_ABORT_INT_MASK, val);
> +}
> +
> +static
> +void dw_hdma_v0_core_done_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir)
> +{
> + struct dw_edma *dw = dw_irq->dw;
> + unsigned long total, pos, val;
> + unsigned long off, mask;
> +
> + if (dir == EDMA_DIR_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;
> + }
> +
> + for_each_set_bit(pos, &mask, total) {
> + struct dw_edma_chan *chan = &dw->chan[pos + off];
> +
> + val = dw_hdma_v0_core_status_int(chan);
> + if (dw_hdma_v0_core_check_done_int(val))
> + dw_edma_done_interrupt(chan);
> + }
> +}
> +
> +static
> +void dw_hdma_v0_core_abort_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir)
> +{
> + struct dw_edma *dw = dw_irq->dw;
> + unsigned long total, pos, val;
> + unsigned long off, mask;
> +
> + if (dir == EDMA_DIR_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;
> + }
> +
> + for_each_set_bit(pos, &mask, total) {
> + struct dw_edma_chan *chan = &dw->chan[pos + off];
> +
> + val = dw_hdma_v0_core_status_int(chan);
> + if (dw_hdma_v0_core_check_abort_int(val))
> + dw_edma_abort_interrupt(&dw->chan[pos + off]);
> + }
> +}
> +
> +static void dw_hdma_v0_write_ll_data(struct dw_edma_chunk *chunk, int i,
> + u32 control, u32 size, u64 sar, u64 dar)
> +{
> + ptrdiff_t ofs = i * sizeof(struct dw_hdma_v0_lli);
> +
> + if (chunk->chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) {
> + struct dw_hdma_v0_lli *lli = chunk->ll_region.vaddr.mem + ofs;
> +
> + lli->control = control;
> + lli->transfer_size = size;
> + lli->sar.reg = sar;
> + lli->dar.reg = dar;
> + } else {
> + struct dw_hdma_v0_lli __iomem *lli = chunk->ll_region.vaddr.io + ofs;
> +
> + writel(control, &lli->control);
> + writel(size, &lli->transfer_size);
> + writeq(sar, &lli->sar.reg);
> + writeq(dar, &lli->dar.reg);
> + }
> +}
> +
> +static void dw_hdma_v0_write_ll_link(struct dw_edma_chunk *chunk,
> + int i, u32 control, u64 pointer)
> +{
> + ptrdiff_t ofs = i * sizeof(struct dw_hdma_v0_lli);
> +
> + if (chunk->chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) {
> + struct dw_hdma_v0_llp *llp = chunk->ll_region.vaddr.mem + ofs;
> +
> + llp->control = control;
> + llp->llp.reg = pointer;
> + } else {
> + struct dw_hdma_v0_llp __iomem *llp = chunk->ll_region.vaddr.io + ofs;
> +
> + writel(control, &llp->control);
> + writeq(pointer, &llp->llp.reg);
> + }
> +}
> +
> +static void dw_hdma_v0_core_write_chunk(struct dw_edma_chunk *chunk)
> +{
> + struct dw_edma_burst *child;
> + struct dw_edma_chan *chan = chunk->chan;
> + u32 control = 0, i = 0;
> + int j;
> +
> + if (chunk->cb)
> + control = DW_HDMA_V0_CB;
> +
> + j = chunk->bursts_alloc;
> + list_for_each_entry(child, &chunk->burst->list, list) {
> + j--;
> + if (!j) {
> + control |= DW_HDMA_V0_LIE;
> + if (!(chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL))
> + control |= DW_HDMA_V0_RIE;
> + }
> +
> + dw_hdma_v0_write_ll_data(chunk, i++, control, child->sz,
> + child->sar, child->dar);
> + }
> +
> + control = DW_HDMA_V0_LLP | DW_HDMA_V0_TCB;
> + if (!chunk->cb)
> + control |= DW_HDMA_V0_CB;
> +
> + dw_hdma_v0_write_ll_link(chunk, i, control, chunk->ll_region.paddr);
> +}
> +
> +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));
> + /* Interrupt enable&unmask - done, abort */
> + tmp = GET_CH_32(dw, chan->dir, chan->id, int_setup) | HDMA_V0_STOP_INT_MASK |
> + HDMA_V0_ABORT_INT_MASK | HDMA_V0_LOCAL_STOP_INT_EN |
> + HDMA_V0_LOCAL_STOP_INT_EN;
> + SET_CH_32(dw, chan->dir, chan->id, int_setup, tmp);
> + /* Channel control */
> + SET_CH_32(dw, chan->dir, chan->id, control1, HDMA_V0_LINKLIST_EN);
> + /* Linked list */
> + /* llp is not aligned on 64bit -> keep 32bit accesses */
> + SET_CH_32(dw, chan->dir, chan->id, llp.lsb,
> + lower_32_bits(chunk->ll_region.paddr));
> + SET_CH_32(dw, chan->dir, chan->id, llp.msb,
> + upper_32_bits(chunk->ll_region.paddr));
> + }
> + /* Set consumer cycle */
> + SET_CH_32(dw, chan->dir, chan->id, cycle_sync,
> + HDMA_V0_CONSUMER_CYCLE_STAT | HDMA_V0_CONSUMER_CYCLE_BIT);
> + /* Doorbell */
> + SET_CH_32(dw, chan->dir, chan->id, doorbell, HDMA_V0_DOORBELL_START);
> +}
> +
> +static void dw_hdma_v0_core_ch_config(struct dw_edma_chan *chan)
> +{
> + struct dw_edma *dw = chan->dw;
> +
> + /* MSI done addr - low, high */
> + SET_CH_32(dw, chan->dir, chan->id, msi_stop.lsb, chan->msi.address_lo);
> + SET_CH_32(dw, chan->dir, chan->id, msi_stop.msb, chan->msi.address_hi);
> + /* MSI abort addr - low, high */
> + SET_CH_32(dw, chan->dir, chan->id, msi_abort.lsb, chan->msi.address_lo);
> + SET_CH_32(dw, chan->dir, chan->id, msi_abort.msb, chan->msi.address_hi);
> + /* config MSI data */
> + SET_CH_32(dw, chan->dir, chan->id, msi_msgdata, chan->msi.data);
> +}
> +
> +/* HDMA debugfs callbacks */
> +static void dw_hdma_v0_core_debugfs_on(struct dw_edma *dw)
> +{
> +}
> +
> +static const struct dw_edma_core_ops hdma_core = {
Please use the dw_hdma_v0_core structure instance name to maintain the
local naming convention.
-Serge(y)
> + .off = dw_hdma_v0_core_off,
> + .ch_count = dw_hdma_v0_core_ch_count,
> + .ch_status = dw_hdma_v0_core_ch_status,
> + .clear_done_int = dw_hdma_v0_core_clear_done_int,
> + .clear_abort_int = dw_hdma_v0_core_clear_abort_int,
> + .done_handle = dw_hdma_v0_core_done_handle,
> + .abort_handle = dw_hdma_v0_core_abort_handle,
> + .start = dw_hdma_v0_core_start,
> + .ch_config = dw_hdma_v0_core_ch_config,
> + .debugfs_on = dw_hdma_v0_core_debugfs_on,
> +};
> +
> +void dw_hdma_v0_core_register(struct dw_edma *dw)
> +{
> + dw->core = &hdma_core;
> +}
> diff --git a/drivers/dma/dw-edma/dw-hdma-v0-core.h b/drivers/dma/dw-edma/dw-hdma-v0-core.h
> new file mode 100644
> index 000000000000..c373b4f0bd8a
> --- /dev/null
> +++ b/drivers/dma/dw-edma/dw-hdma-v0-core.h
> @@ -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 */
> diff --git a/drivers/dma/dw-edma/dw-hdma-v0-regs.h b/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> new file mode 100644
> index 000000000000..f6b2bd03060c
> --- /dev/null
> +++ b/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> @@ -0,0 +1,129 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2023 Cai Huoqing
> + * Synopsys DesignWare HDMA v0 reg
> + *
> + * Author: Cai Huoqing <[email protected]>
> + */
> +
> +#ifndef _DW_HDMA_V0_REGS_H
> +#define _DW_HDMA_V0_REGS_H
> +
> +#include <linux/dmaengine.h>
> +
> +#define HDMA_V0_MAX_NR_CH 8
> +#define HDMA_V0_LOCAL_ABORT_INT_EN BIT(6)
> +#define HDMA_V0_REMOTE_ABORT_INT_EN BIT(5)
> +#define HDMA_V0_LOCAL_STOP_INT_EN BIT(4)
> +#define HDMA_V0_REMOTEL_STOP_INT_EN BIT(3)
> +#define HDMA_V0_ABORT_INT_MASK BIT(2)
> +#define HDMA_V0_STOP_INT_MASK BIT(0)
> +#define HDMA_V0_LINKLIST_EN BIT(0)
> +#define HDMA_V0_CONSUMER_CYCLE_STAT BIT(1)
> +#define HDMA_V0_CONSUMER_CYCLE_BIT BIT(0)
> +#define HDMA_V0_DOORBELL_START BIT(0)
> +#define HDMA_V0_CH_STATUS_MASK GENMASK(1, 0)
> +
> +struct dw_hdma_v0_ch_regs {
> + u32 ch_en; /* 0x0000 */
> + u32 doorbell; /* 0x0004 */
> + u32 prefetch; /* 0x0008 */
> + u32 handshake; /* 0x000c */
> + union {
> + u64 reg; /* 0x0010..0x0014 */
> + struct {
> + u32 lsb; /* 0x0010 */
> + u32 msb; /* 0x0014 */
> + };
> + } llp;
> + u32 cycle_sync; /* 0x0018 */
> + u32 transfer_size; /* 0x001c */
> + union {
> + u64 reg; /* 0x0020..0x0024 */
> + struct {
> + u32 lsb; /* 0x0020 */
> + u32 msb; /* 0x0024 */
> + };
> + } sar;
> + union {
> + u64 reg; /* 0x0028..0x002c */
> + struct {
> + u32 lsb; /* 0x0028 */
> + u32 msb; /* 0x002c */
> + };
> + } dar;
> +
> + u32 watermark_en; /* 0x0030 */
> + u32 control1; /* 0x0034 */
> + u32 func_num; /* 0x0038 */
> + u32 qos; /* 0x003c */
> + u32 reserved; /* 0x0040..0x007c */
> + u32 ch_stat; /* 0x0080 */
> + u32 int_stat; /* 0x0084 */
> + u32 int_setup; /* 0x0088 */
> + u32 int_clear; /* 0x008c */
> + union {
> + u64 reg; /* 0x0090..0x0094 */
> + struct {
> + u32 lsb; /* 0x0090 */
> + u32 msb; /* 0x0094 */
> + };
> + } msi_stop;
> + union {
> + u64 reg; /* 0x0098..0x009c */
> + struct {
> + u32 lsb; /* 0x0098 */
> + u32 msb; /* 0x009c */
> + };
> + } msi_watermark;
> + union {
> + u64 reg; /* 0x00a0..0x00a4 */
> + struct {
> + u32 lsb; /* 0x00a0 */
> + u32 msb; /* 0x00a4 */
> + };
> + } msi_abort;
> + u32 msi_msgdata; /* 0x00a8 */
> +} __packed;
> +
> +struct dw_hdma_v0_ch {
> + struct dw_hdma_v0_ch_regs wr; /* 0x0000 */
> + struct dw_hdma_v0_ch_regs rd; /* 0x0100 */
> +} __packed;
> +
> +struct dw_hdma_v0_regs {
> + struct dw_hdma_v0_ch ch[HDMA_V0_MAX_NR_CH]; /* 0x0000..0x0fa8 */
> +} __packed;
> +
> +struct dw_hdma_v0_lli {
> + u32 control;
> + u32 transfer_size;
> + union {
> + u64 reg;
> + struct {
> + u32 lsb;
> + u32 msb;
> + };
> + } sar;
> + union {
> + u64 reg;
> + struct {
> + u32 lsb;
> + u32 msb;
> + };
> + } dar;
> +} __packed;
> +
> +struct dw_hdma_v0_llp {
> + u32 control;
> + u32 reserved;
> + union {
> + u64 reg;
> + struct {
> + u32 lsb;
> + u32 msb;
> + };
> + } llp;
> +} __packed;
> +
> +#endif /* _DW_HDMA_V0_REGS_H */
> --
> 2.34.1
>
On Mon, Feb 13, 2023 at 09:24:09PM +0800, Cai Huoqing wrote:
> From: Cai huoqing <[email protected]>
>
> Add HDMA DebugFS support to show register information
>
> Signed-off-by: Cai huoqing <[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 | 175 +++++++++++++++++++++++
> drivers/dma/dw-edma/dw-hdma-v0-debugfs.h | 22 +++
> 4 files changed, 201 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
>
> diff --git a/drivers/dma/dw-edma/Makefile b/drivers/dma/dw-edma/Makefile
> index b1c91ef2c63d..83ab58f87760 100644
> --- a/drivers/dma/dw-edma/Makefile
> +++ b/drivers/dma/dw-edma/Makefile
> @@ -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)
> diff --git a/drivers/dma/dw-edma/dw-hdma-v0-core.c b/drivers/dma/dw-edma/dw-hdma-v0-core.c
> index 7e4f98987e29..3723d5d8127c 100644
> --- a/drivers/dma/dw-edma/dw-hdma-v0-core.c
> +++ b/drivers/dma/dw-edma/dw-hdma-v0-core.c
> @@ -9,6 +9,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),
> @@ -294,6 +295,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 hdma_core = {
> diff --git a/drivers/dma/dw-edma/dw-hdma-v0-debugfs.c b/drivers/dma/dw-edma/dw-hdma-v0-debugfs.c
> new file mode 100644
> index 000000000000..a0fafd788c14
> --- /dev/null
> +++ b/drivers/dma/dw-edma/dw-hdma-v0-debugfs.c
> @@ -0,0 +1,175 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2023 Cai Huoqing
> + * Synopsys DesignWare HDMA v0 debugfs
> + *
> + * Author: Cai Huoqing <[email protected]>
> + */
> +
> +#include <linux/debugfs.h>
> +#include <linux/bitfield.h>
> +
> +#include "dw-hdma-v0-debugfs.h"
> +#include "dw-hdma-v0-regs.h"
> +#include "dw-edma-core.h"
> +
> +#define REGS_ADDR(dw, name) \
> + ({ \
> + struct dw_hdma_v0_regs __iomem *__regs = (dw)->chip->reg_base; \
> + \
> + (void __iomem *)&__regs->name; \
> + })
> +
> +#define REGS_CH_ADDR(dw, name, _dir, _ch) \
> + ({ \
> + struct dw_hdma_v0_ch_regs __iomem *__ch_regs; \
> + \
> + if (_dir == EDMA_DIR_READ) \
> + __ch_regs = REGS_ADDR(dw, ch[_ch].rd); \
> + else \
> + __ch_regs = REGS_ADDR(dw, ch[_ch].wr); \
> + \
> + (void __iomem *)&__ch_regs->name; \
> + })
> +
> +#define CTX_REGISTER(dw, name, dir, ch) \
> + { dw, #name, REGS_CH_ADDR(dw, name, dir, ch), dir, ch }
> +
> +#define REGISTER(dw, name) \
> + { dw, #name, REGS_ADDR(dw, name) }
> +
> +#define WRITE_STR "write"
> +#define READ_STR "read"
> +#define CHANNEL_STR "channel"
> +#define REGISTERS_STR "registers"
> +
> +struct dw_hdma_debugfs_entry {
> + struct dw_edma *dw;
> + const char *name;
> + void __iomem *reg;
> + enum dw_edma_dir dir;
> + u16 ch;
> +};
> +
> +static int dw_hdma_debugfs_u32_get(void *data, u64 *val)
> +{
> + void __iomem *reg = (void __force __iomem *)data;
> + *val = readl(reg);
> +
> + return 0;
> +}
> +DEFINE_DEBUGFS_ATTRIBUTE(fops_x32, dw_hdma_debugfs_u32_get, NULL, "0x%08llx\n");
> +
> +static void dw_hdma_debugfs_create_x32(struct dw_edma *dw,
> + const struct dw_hdma_debugfs_entry ini[],
> + int nr_entries, struct dentry *dent)
> +{
> + struct dw_hdma_debugfs_entry *entries;
> + int i;
> +
> + entries = devm_kcalloc(dw->chip->dev, nr_entries, sizeof(*entries),
> + GFP_KERNEL);
> + for (i = 0; i < nr_entries; i++) {
> + entries[i] = ini[i];
> +
> + debugfs_create_file_unsafe(entries[i].name, 0444, dent,
> + &entries[i], &fops_x32);
> + }
> +}
> +
> +static void dw_hdma_debugfs_regs_ch(struct dw_edma *dw, enum dw_edma_dir dir,
> + u16 ch, struct dentry *dent)
> +{
> + int nr_entries;
> + struct dw_hdma_debugfs_entry debugfs_regs[] = {
should be const.
> + CTX_REGISTER(dw, ch_en, dir, ch),
> + CTX_REGISTER(dw, doorbell, dir, ch),
Did you intentionally miss the prefetch and handshake fields here?
> + CTX_REGISTER(dw, llp.lsb, dir, ch),
> + CTX_REGISTER(dw, llp.msb, dir, ch),
> + CTX_REGISTER(dw, cycle_sync, dir, ch),
> + CTX_REGISTER(dw, transfer_size, dir, ch),
> + CTX_REGISTER(dw, sar.lsb, dir, ch),
> + CTX_REGISTER(dw, sar.msb, dir, ch),
> + CTX_REGISTER(dw, dar.lsb, dir, ch),
> + CTX_REGISTER(dw, dar.msb, dir, ch),
watermark_en field?
> + CTX_REGISTER(dw, control1, dir, ch),
func_num, qos?
> + CTX_REGISTER(dw, ch_stat, dir, ch),
> + CTX_REGISTER(dw, int_stat, dir, ch),
> + CTX_REGISTER(dw, int_setup, dir, ch),
> + CTX_REGISTER(dw, int_clear, dir, ch),
> + CTX_REGISTER(dw, msi_stop.lsb, dir, ch),
> + CTX_REGISTER(dw, msi_stop.msb, dir, ch),
msi_watermark?
Why did you miss all of these fields? It was no problem to add all
them seeing they are declared in the dw_hdma_v0_ch_regs structure.
> + CTX_REGISTER(dw, msi_abort.lsb, dir, ch),
> + CTX_REGISTER(dw, msi_abort.msb, dir, ch),
> + CTX_REGISTER(dw, msi_msgdata, dir, ch),
> + };
Use reverse xmas tree order as per the driver convention.
> +
> + nr_entries = ARRAY_SIZE(debugfs_regs);
> + dw_hdma_debugfs_create_x32(dw, debugfs_regs, nr_entries, dent);
> +}
> +
> +static void dw_hdma_debugfs_regs_wr(struct dw_edma *dw, struct dentry *dent)
> +{
> + struct dentry *regs_dent, *ch_dent;
> + int i;
> + char name[16];
reverse xmas tree order
> +
> + regs_dent = debugfs_create_dir(WRITE_STR, dent);
> + if (!regs_dent)
> + return;
> +
> + for (i = 0; i < dw->wr_ch_cnt; i++) {
> + snprintf(name, sizeof(name), "%s:%d", CHANNEL_STR, i);
> +
> + ch_dent = debugfs_create_dir(name, regs_dent);
> + if (!ch_dent)
> + return;
> +
> + dw_hdma_debugfs_regs_ch(dw, EDMA_DIR_WRITE, i, ch_dent);
> + }
> +}
> +
> +static void dw_hdma_debugfs_regs_rd(struct dw_edma *dw, struct dentry *dent)
> +{
> + struct dentry *regs_dent, *ch_dent;
> + int i;
> + char name[16];
reverse xmas tree order.
-Serge(y)
> +
> + regs_dent = debugfs_create_dir(READ_STR, dent);
> + if (!regs_dent)
> + return;
> +
> + for (i = 0; i < dw->rd_ch_cnt; i++) {
> + snprintf(name, sizeof(name), "%s:%d", CHANNEL_STR, i);
> +
> + ch_dent = debugfs_create_dir(name, regs_dent);
> + if (!ch_dent)
> + return;
> +
> + dw_hdma_debugfs_regs_ch(dw, EDMA_DIR_READ, i, ch_dent);
> + }
> +}
> +
> +static void dw_hdma_debugfs_regs(struct dw_edma *dw)
> +{
> + struct dentry *regs_dent;
> +
> + regs_dent = debugfs_create_dir(REGISTERS_STR, dw->dma.dbg_dev_root);
> + if (!regs_dent)
> + return;
> +
> + dw_hdma_debugfs_regs_wr(dw, regs_dent);
> + dw_hdma_debugfs_regs_rd(dw, regs_dent);
> +}
> +
> +void dw_hdma_v0_debugfs_on(struct dw_edma *dw)
> +{
> + if (!debugfs_initialized())
> + return;
> +
> + debugfs_create_u32("mf", 0444, dw->dma.dbg_dev_root, &dw->chip->mf);
> + debugfs_create_u16("wr_ch_cnt", 0444, dw->dma.dbg_dev_root, &dw->wr_ch_cnt);
> + debugfs_create_u16("rd_ch_cnt", 0444, dw->dma.dbg_dev_root, &dw->rd_ch_cnt);
> +
> + dw_hdma_debugfs_regs(dw);
> +}
> diff --git a/drivers/dma/dw-edma/dw-hdma-v0-debugfs.h b/drivers/dma/dw-edma/dw-hdma-v0-debugfs.h
> new file mode 100644
> index 000000000000..e6842c83777d
> --- /dev/null
> +++ b/drivers/dma/dw-edma/dw-hdma-v0-debugfs.h
> @@ -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 */
> --
> 2.34.1
>
Hi Serge, thanks for your reply.
On 16 2月 23 19:17:17, Serge Semin wrote:
> On Mon, Feb 13, 2023 at 09:24:07PM +0800, Cai Huoqing wrote:
> > From: Cai huoqing <[email protected]>
> >
> > 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.
> >
> > Signed-off-by: Cai huoqing <[email protected]>
> > ---
> > drivers/dma/dw-edma/dw-edma-core.c | 63 ++++++------------
> > drivers/dma/dw-edma/dw-edma-core.h | 92 +++++++++++++++++++++++++++
> > drivers/dma/dw-edma/dw-edma-v0-core.c | 88 +++++++++++++++++++++----
> > drivers/dma/dw-edma/dw-edma-v0-core.h | 14 +---
> > 4 files changed, 188 insertions(+), 69 deletions(-)
> >
> > diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
> > index 1906a836f0aa..c171809beba1 100644
> > --- a/drivers/dma/dw-edma/dw-edma-core.c
> > +++ b/drivers/dma/dw-edma/dw-edma-core.c
> > @@ -183,6 +183,7 @@ static void vchan_free_desc(struct virt_dma_desc *vdesc)
> >
> > static void 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 void dw_edma_start_transfer(struct dw_edma_chan *chan)
> > if (!child)
> > return;
> >
> > - 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);
> > @@ -285,7 +286,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
> > @@ -588,13 +589,13 @@ dw_edma_device_prep_interleaved_dma(struct dma_chan *dchan,
> > return dw_edma_device_transfer(&xfer);
> > }
> >
> > -static void dw_edma_done_interrupt(struct dw_edma_chan *chan)
> > +void dw_edma_done_interrupt(struct dw_edma_chan *chan)
> > {
> > struct dw_edma_desc *desc;
> > struct virt_dma_desc *vd;
> > unsigned long flags;
> >
>
> > - dw_edma_v0_core_clear_done_int(chan);
> > + dw_edma_core_clear_done_int(chan); <+
> |
> As I already said in v2 just call this method + in the IRQ-handle loop
> within the IP-core-specific module (see further for details). Thus you'll
> be able to drop the dw_edma_core_ops.clear_done_int pointer. The same
> concerns the dw_edma_core_ops.clear_abort_int callback.
>
> >
> > spin_lock_irqsave(&chan->vc.lock, flags);
> > vd = vchan_next_desc(&chan->vc);
> > @@ -631,12 +632,12 @@ static void dw_edma_done_interrupt(struct dw_edma_chan *chan)
> > spin_unlock_irqrestore(&chan->vc.lock, flags);
> > }
> >
> > -static void dw_edma_abort_interrupt(struct dw_edma_chan *chan)
> > +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);
> > + dw_edma_core_clear_abort_int(chan);
>
> ditto
>
> >
> > spin_lock_irqsave(&chan->vc.lock, flags);
> > vd = vchan_next_desc(&chan->vc);
> > @@ -651,41 +652,11 @@ static void dw_edma_abort_interrupt(struct dw_edma_chan *chan)
> >
> > static irqreturn_t dw_edma_interrupt(int irq, void *data, bool write)
> > {
> > + enum dw_edma_dir dir = write ? EDMA_DIR_WRITE : EDMA_DIR_READ;
> > 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);
> > - }
> > + dw_edma_core_done_handle(dw_irq, dir);
> > + dw_edma_core_abort_handle(dw_irq, dir);
> >
> > return IRQ_HANDLED;
> > }
> > @@ -806,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 */
> > @@ -951,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)
> > @@ -977,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);
> > @@ -990,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;
> >
> > @@ -1016,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--)
> > diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h
> > index 0ab2b6dba880..2555ae0e1b8b 100644
> > --- a/drivers/dma/dw-edma/dw-edma-core.h
> > +++ b/drivers/dma/dw-edma/dw-edma-core.h
> > @@ -111,6 +111,23 @@ struct dw_edma {
> > raw_spinlock_t lock; /* Only for legacy */
> >
> > struct dw_edma_chip *chip;
> > +
> > + const struct dw_edma_core_ops *core;
> > +};
> > +
> > +struct dw_edma_core_ops {
> > + void (*off)(struct dw_edma *dw);
> > + u16 (*ch_count)(struct dw_edma *dw, enum dw_edma_dir dir);
> > + enum dma_status (*ch_status)(struct dw_edma_chan *chan);
>
> > + void (*clear_done_int)(struct dw_edma_chan *chan);
> > + void (*clear_abort_int)(struct dw_edma_chan *chan);
>
> As I noted above these methods could be called locally from the
> corresponding IP-core-specific module. Thus you won't need to define
> the callbacks. It shall simplify the interface a bit.
>
> > + void (*done_handle)(struct dw_edma_irq *dw_irq,
> > + enum dw_edma_dir dir);
> > + void (*abort_handle)(struct dw_edma_irq *dw_irq,
> > + enum dw_edma_dir dir);
>
> Seeing both of these callbacks (done,abort handlers) are called from
> the same IRQ-handler what about converting them to a single callback:
> irqreturn_t (*handle_int)(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir);
> ?
> The eDMA channels IRQ-handlers would look like this:
> --- a/drivers/dma/dw-edma/dw-edma-core.c
> +++ b/drivers/dma/dw-edma/dw-edma-core.c
> @@ -692,12 +692,16 @@ static irqreturn_t dw_edma_interrupt(int irq, void *data, bool write)
> static inline irqreturn_t dw_edma_interrupt_write(int irq, void *data)
> {
> - return dw_edma_interrupt(irq, data, true);
> + struct dw_edma_irq *dw_irq = data;
> +
> + return dw_edma_core_handle_int(dw_irq, EDMA_DIR_WRITE);
> }
>
> 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);
> }
>
> static irqreturn_t dw_edma_interrupt_common(int irq, void *data)
>
> Therefore you'll be able to completely drop the
> static irqreturn_t dw_edma_interrupt(int irq, void *data, bool write)
> function from drivers/dma/dw-edma/dw-edma-core.c. The function body would
> be almost fully moved to the handle_int() function of the eDMA/HDMA
> core-specific ops.
>
> For instance in case of the DW eDMA it would look like this:
> static irqreturn_t dw_edma_v0_core_handle_int(struct dw_edma_irq, enum dw_edma_dir dir)
> {
> struct dw_edma *dw = dw_irq->dw;
> unsigned long total, pos, val;
> unsigned long off;
> u32 mask;
>
> if (dir == EDMA_DIR_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, dir) & mask;
> for_each_set_bit(pos, &val, total) {
> struct dw_edma_chan *chan = &dw->chan[pos + off];
>
> dw_edma_v0_core_clear_done_int(chan);
>
> dw_edma_done_interrupt(chan);
> }
>
> val = dw_edma_v0_core_status_abort_int(dw, dir) & mask;
> for_each_set_bit(pos, &val, total) {
> struct dw_edma_chan *chan = &dw->chan[pos + off];
>
> dw_edma_v0_core_clear_abort_int(chan);
>
> dw_edma_abort_interrupt(chan);
> }
>
> return IRQ_HANDLED;
> }
>
> What do you think?
>
> > + void (*start)(struct dw_edma_chunk *chunk, bool first);
> > + void (*ch_config)(struct dw_edma_chan *chan);
> > + void (*debugfs_on)(struct dw_edma *dw);
> > };
> >
> > struct dw_edma_sg {
> > @@ -136,6 +153,9 @@ struct dw_edma_transfer {
> > enum dw_edma_xfer_type type;
> > };
> >
> > +void dw_edma_done_interrupt(struct dw_edma_chan *chan);
> > +void dw_edma_abort_interrupt(struct dw_edma_chan *chan);
> > +
> > static inline
> > struct dw_edma_chan *vc2dw_edma_chan(struct virt_dma_chan *vc)
> > {
> > @@ -148,4 +168,76 @@ struct dw_edma_chan *dchan2dw_edma_chan(struct dma_chan *dchan)
> > return vc2dw_edma_chan(to_virt_chan(dchan));
> > }
> >
> > +static inline
> > +void dw_edma_core_off(struct dw_edma *dw)
> > +{
> > + dw->core->off(dw);
> > +}
> > +
> > +static inline
> > +u16 dw_edma_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir)
> > +{
> > + return dw->core->ch_count(dw, dir);
> > +}
> > +
> > +static inline
> > +enum dma_status dw_edma_core_ch_status(struct dw_edma_chan *chan)
> > +{
> > + struct dw_edma *dw = chan->dw;
> > +
> > + return dw->core->ch_status(chan);
> > +}
> > +
> > +static inline
> > +void dw_edma_core_clear_done_int(struct dw_edma_chan *chan)
> > +{
> > + struct dw_edma *dw = chan->dw;
> > +
> > + dw->core->clear_done_int(chan);
> > +}
> > +
> > +static inline
> > +void dw_edma_core_clear_abort_int(struct dw_edma_chan *chan)
> > +{
> > + struct dw_edma *dw = chan->dw;
> > +
> > + dw->core->clear_abort_int(chan);
> > +}
> > +
>
> > +static inline
> > +void dw_edma_core_done_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir)
> > +{
> > + struct dw_edma *dw = dw_irq->dw;
> > +
> > + dw->core->done_handle(dw_irq, dir);
> > +}
> > +
> > +static inline
> > +void dw_edma_core_abort_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir)
> > +{
> > + struct dw_edma *dw = dw_irq->dw;
> > +
> > + dw->core->abort_handle(dw_irq, dir);
> > +}
>
> As I suggested above these two methods can be replaced with a single
> one: dw_edma_core_handle_int(...).
>
> > +
> > +static inline
> > +void dw_edma_core_start(struct dw_edma *dw, struct dw_edma_chunk *chunk, bool first)
> > +{
> > + dw->core->start(chunk, first);
> > +}
> > +
> > +static inline
> > +void dw_edma_core_ch_config(struct dw_edma_chan *chan)
> > +{
> > + struct dw_edma *dw = chan->dw;
> > +
> > + dw->core->ch_config(chan);
> > +}
> > +
> > +static inline
> > +void dw_edma_core_debugfs_on(struct dw_edma *dw)
> > +{
> > + dw->core->debugfs_on(dw);
> > +}
> > +
> > #endif /* _DW_EDMA_CORE_H */
> > diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c
> > index 72e79a0c0a4e..73dcf72ba3a9 100644
> > --- a/drivers/dma/dw-edma/dw-edma-v0-core.c
> > +++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
> > @@ -216,7 +216,7 @@ static inline u64 readq_ch(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch,
> > readq_ch(dw, dir, ch, &(__dw_ch_regs(dw, dir, ch)->name))
> >
> > /* eDMA management callbacks */
> > -void dw_edma_v0_core_off(struct dw_edma *dw)
> > +static void dw_edma_v0_core_off(struct dw_edma *dw)
> > {
> > SET_BOTH_32(dw, int_mask,
> > EDMA_V0_DONE_INT_MASK | EDMA_V0_ABORT_INT_MASK);
> > @@ -225,7 +225,7 @@ void dw_edma_v0_core_off(struct dw_edma *dw)
> > SET_BOTH_32(dw, engine_en, 0);
> > }
> >
> > -u16 dw_edma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir)
> > +static u16 dw_edma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir)
> > {
> > u32 num_ch;
> >
> > @@ -242,7 +242,7 @@ u16 dw_edma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir)
> > return (u16)num_ch;
> > }
> >
> > -enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan)
> > +static enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan)
> > {
> > struct dw_edma *dw = chan->dw;
> > u32 tmp;
> > @@ -258,7 +258,7 @@ enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan)
> > return DMA_ERROR;
> > }
> >
> > -void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan)
> > +static void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan)
> > {
> > struct dw_edma *dw = chan->dw;
> >
> > @@ -266,7 +266,7 @@ void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan)
> > FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id)));
> > }
> >
> > -void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan)
> > +static void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan)
> > {
> > struct dw_edma *dw = chan->dw;
> >
> > @@ -274,18 +274,66 @@ void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan)
> > FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id)));
> > }
> >
> > -u32 dw_edma_v0_core_status_done_int(struct dw_edma *dw, enum dw_edma_dir dir)
> > +static u32 dw_edma_v0_core_status_done_int(struct dw_edma *dw, enum dw_edma_dir dir)
> > {
> > return FIELD_GET(EDMA_V0_DONE_INT_MASK,
> > GET_RW_32(dw, dir, int_status));
> > }
> >
> > -u32 dw_edma_v0_core_status_abort_int(struct dw_edma *dw, enum dw_edma_dir dir)
> > +static u32 dw_edma_v0_core_status_abort_int(struct dw_edma *dw, enum dw_edma_dir dir)
> > {
> > return FIELD_GET(EDMA_V0_ABORT_INT_MASK,
> > GET_RW_32(dw, dir, int_status));
> > }
> >
> > +static
> > +void dw_edma_v0_core_done_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir)
> > +{
> > + struct dw_edma *dw = dw_irq->dw;
> > + unsigned long total, pos, val;
> > + unsigned long off;
> > + u32 mask;
> > +
> > + if (dir == EDMA_DIR_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, dir);
> > + val &= mask;
>
> > + for_each_set_bit(pos, &val, total)
> > + dw_edma_done_interrupt(&dw->chan[pos + off]);
>
> As I suggested above replace this with:
> + for_each_set_bit(pos, &val, total) {
> + struct dw_edma_chan *chan = &dw->chan[pos + off];
> +
> + dw_edma_v0_core_clear_done_int(chan);
> +
> + dw_edma_done_interrupt(chan);
> + }
>
> Therefore you'll be able to drop the clear_done_int callback and its
> invocation from the dw_edma_done_interrupt() function.
>
> BTW Why did you change the dw_edma_v0_core_done_handle() semantics
> from taking the function pointers as an argument to calling the global
> version of dw_edma_done_interrupt() ?
In my patch, 'dw_edma_core_clear_done_int' is called by 'dw_edma_done_interrupt',
'dw_edma_core_clear_done_int' point to 'dw_edma_v0_core_clear_done_int'.
-static void dw_edma_done_interrupt(struct dw_edma_chan *chan)
+void dw_edma_done_interrupt(struct dw_edma_chan *chan)
{
struct dw_edma_desc *desc;
struct virt_dma_desc *vd;
unsigned long flags;
- dw_edma_v0_core_clear_done_int(chan);
+ dw_edma_core_clear_done_int(chan);
So, my patch only drop the callback params, like this in v2 patch
"dw_irq, dw_edma_done_interrupt, dir)"
- for_each_set_bit(pos, &val, total) {
- struct dw_edma_chan *chan = &dw->chan[pos + off];
+ enum dw_edma_dir dir = write ? EDMA_DIR_WRITE : EDMA_DIR_READ;
- dw_edma_abort_interrupt(chan);
- }
+ dw->core->dw_xdma_core_done_handle(dw_irq, dw_edma_done_interrupt, dir);
+ dw->core->dw_xdma_core_abort_handle(dw_irq, dw_edma_abort_interrupt, dir);
And I will fix it in next version.
Thanks,
Cai-
>
> > +}
> > +
> > +static
> > +void dw_edma_v0_core_abort_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir)
> > +{
> > + struct dw_edma *dw = dw_irq->dw;
> > + unsigned long total, pos, val;
> > + unsigned long off;
> > + u32 mask;
> > +
> > + if (dir == EDMA_DIR_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_abort_int(dw, dir);
> > + val &= mask;
>
> > + for_each_set_bit(pos, &val, total)
> > + dw_edma_abort_interrupt(&dw->chan[pos + off]);
>
> ditto
>
> > +}
> > +
> > static void dw_edma_v0_write_ll_data(struct dw_edma_chunk *chunk, int i,
> > u32 control, u32 size, u64 sar, u64 dar)
> > {
> > @@ -356,7 +404,7 @@ static void dw_edma_v0_core_write_chunk(struct dw_edma_chunk *chunk)
> > dw_edma_v0_write_ll_link(chunk, i, control, chunk->ll_region.paddr);
> > }
> >
> > -void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
> > +static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
> > {
> > struct dw_edma_chan *chan = chunk->chan;
> > struct dw_edma *dw = chan->dw;
> > @@ -427,7 +475,7 @@ void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
> > FIELD_PREP(EDMA_V0_DOORBELL_CH_MASK, chan->id));
> > }
> >
> > -int dw_edma_v0_core_device_config(struct dw_edma_chan *chan)
> > +static void dw_edma_v0_core_ch_config(struct dw_edma_chan *chan)
> > {
> > struct dw_edma *dw = chan->dw;
> > u32 tmp = 0;
> > @@ -494,12 +542,28 @@ int dw_edma_v0_core_device_config(struct dw_edma_chan *chan)
> > SET_RW_32(dw, chan->dir, ch67_imwr_data, tmp);
> > break;
> > }
> > -
> > - return 0;
> > }
> >
> > /* eDMA debugfs callbacks */
> > -void dw_edma_v0_core_debugfs_on(struct dw_edma *dw)
> > +static void dw_edma_v0_core_debugfs_on(struct dw_edma *dw)
> > {
> > dw_edma_v0_debugfs_on(dw);
> > }
> > +
> > +static const struct dw_edma_core_ops edma_core = {
> > + .off = dw_edma_v0_core_off,
> > + .ch_count = dw_edma_v0_core_ch_count,
> > + .ch_status = dw_edma_v0_core_ch_status,
>
> > + .clear_done_int = dw_edma_v0_core_clear_done_int,
> > + .clear_abort_int = dw_edma_v0_core_clear_abort_int,
> > + .done_handle = dw_edma_v0_core_done_handle,
> > + .abort_handle = dw_edma_v0_core_abort_handle,
>
> If you take into account what I suggested above these four callbacks will
> be replaced with a single one handle_int. Thus the interface will look
> much simpler so will the whole IRQs-handling logic:
>
> irq: dw_edma_interrupt_{write,read}()
> +-> dw_edma_core_handle_int() (dw-edma-v0-core.c)
> +-> dw_edma_v0_core_status_done_int() (dw-edma-v0-core.c)
> +-> dw_edma_v0_core_clear_done_int() (dw-edma-v0-core.c)
> +-> dw_edma_done_interrupt() (dw-edma-core.c)
> +-> dw_edma_v0_core_status_abort_int() (dw-edma-v0-core.c)
> +-> dw_edma_v0_core_clear_abort_int() (dw-edma-v0-core.c)
> +-> dw_edma_abort_interrupt() (dw-edma-core.c)
>
> In your case the calls hierarchy looks like this:
> irq: dw_edma_interrupt_{write,read}()
> +-> dw_edma_interrupt()
> +-> dw_edma_core_done_handle() (dw-edma-v0-core.c)
> +-> dw_edma_v0_core_status_done_int() (dw-edma-v0-core.c)
> +-> dw_edma_done_interrupt() (dw-edma-core.c)
> +-> dw_edma_core_clear_done_int() (dw-edma-v0-core.c)
> +-> dw_edma_core_abort_handle() (dw-edma-v0-core.c)
> +-> dw_edma_v0_core_status_abort_int() (dw-edma-v0-core.c)
> +-> dw_edma_abort_interrupt() (dw-edma-core.c)
> +-> dw_edma_core_clear_abort_int() (dw-edma-v0-core.c)
>
> See the calls thread is more linear in my case and the reader won't
> need to get several times back and forth between the generic part of
> the driver and the IP-core-specific one.
>
> -Serge(y)
>
> > + .start = dw_edma_v0_core_start,
> > + .ch_config = dw_edma_v0_core_ch_config,
> > + .debugfs_on = dw_edma_v0_core_debugfs_on,
> > +};
> > +
> > +void dw_edma_v0_core_register(struct dw_edma *dw)
> > +{
> > + dw->core = &edma_core;
> > +}
> > diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.h b/drivers/dma/dw-edma/dw-edma-v0-core.h
> > index ab96a1f48080..04a882222f99 100644
> > --- a/drivers/dma/dw-edma/dw-edma-v0-core.h
> > +++ b/drivers/dma/dw-edma/dw-edma-v0-core.h
> > @@ -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 */
> > --
> > 2.34.1
> >
On Mon, Feb 20, 2023 at 02:01:50PM +0800, Cai Huoqing wrote:
> Hi Serge, thanks for your reply.
>
> On 16 2月 23 19:17:17, Serge Semin wrote:
> > On Mon, Feb 13, 2023 at 09:24:07PM +0800, Cai Huoqing wrote:
> > > From: Cai huoqing <[email protected]>
> > >
> > > 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.
> > >
> > > Signed-off-by: Cai huoqing <[email protected]>
> > > ---
> > > drivers/dma/dw-edma/dw-edma-core.c | 63 ++++++------------
> > > drivers/dma/dw-edma/dw-edma-core.h | 92 +++++++++++++++++++++++++++
> > > drivers/dma/dw-edma/dw-edma-v0-core.c | 88 +++++++++++++++++++++----
> > > drivers/dma/dw-edma/dw-edma-v0-core.h | 14 +---
> > > 4 files changed, 188 insertions(+), 69 deletions(-)
> > >
> > > diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
> > > index 1906a836f0aa..c171809beba1 100644
> > > --- a/drivers/dma/dw-edma/dw-edma-core.c
> > > +++ b/drivers/dma/dw-edma/dw-edma-core.c
> > > @@ -183,6 +183,7 @@ static void vchan_free_desc(struct virt_dma_desc *vdesc)
> > >
> > > static void 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 void dw_edma_start_transfer(struct dw_edma_chan *chan)
> > > if (!child)
> > > return;
> > >
> > > - 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);
> > > @@ -285,7 +286,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
> > > @@ -588,13 +589,13 @@ dw_edma_device_prep_interleaved_dma(struct dma_chan *dchan,
> > > return dw_edma_device_transfer(&xfer);
> > > }
> > >
> > > -static void dw_edma_done_interrupt(struct dw_edma_chan *chan)
> > > +void dw_edma_done_interrupt(struct dw_edma_chan *chan)
> > > {
> > > struct dw_edma_desc *desc;
> > > struct virt_dma_desc *vd;
> > > unsigned long flags;
> > >
> >
> > > - dw_edma_v0_core_clear_done_int(chan);
> > > + dw_edma_core_clear_done_int(chan); <+
> > |
> > As I already said in v2 just call this method + in the IRQ-handle loop
> > within the IP-core-specific module (see further for details). Thus you'll
> > be able to drop the dw_edma_core_ops.clear_done_int pointer. The same
> > concerns the dw_edma_core_ops.clear_abort_int callback.
> >
> > >
> > > spin_lock_irqsave(&chan->vc.lock, flags);
> > > vd = vchan_next_desc(&chan->vc);
> > > @@ -631,12 +632,12 @@ static void dw_edma_done_interrupt(struct dw_edma_chan *chan)
> > > spin_unlock_irqrestore(&chan->vc.lock, flags);
> > > }
> > >
> > > -static void dw_edma_abort_interrupt(struct dw_edma_chan *chan)
> > > +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);
> > > + dw_edma_core_clear_abort_int(chan);
> >
> > ditto
> >
> > >
> > > spin_lock_irqsave(&chan->vc.lock, flags);
> > > vd = vchan_next_desc(&chan->vc);
> > > @@ -651,41 +652,11 @@ static void dw_edma_abort_interrupt(struct dw_edma_chan *chan)
> > >
> > > static irqreturn_t dw_edma_interrupt(int irq, void *data, bool write)
> > > {
> > > + enum dw_edma_dir dir = write ? EDMA_DIR_WRITE : EDMA_DIR_READ;
> > > 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);
> > > - }
> > > + dw_edma_core_done_handle(dw_irq, dir);
> > > + dw_edma_core_abort_handle(dw_irq, dir);
> > >
> > > return IRQ_HANDLED;
> > > }
> > > @@ -806,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 */
> > > @@ -951,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)
> > > @@ -977,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);
> > > @@ -990,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;
> > >
> > > @@ -1016,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--)
> > > diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h
> > > index 0ab2b6dba880..2555ae0e1b8b 100644
> > > --- a/drivers/dma/dw-edma/dw-edma-core.h
> > > +++ b/drivers/dma/dw-edma/dw-edma-core.h
> > > @@ -111,6 +111,23 @@ struct dw_edma {
> > > raw_spinlock_t lock; /* Only for legacy */
> > >
> > > struct dw_edma_chip *chip;
> > > +
> > > + const struct dw_edma_core_ops *core;
> > > +};
> > > +
> > > +struct dw_edma_core_ops {
> > > + void (*off)(struct dw_edma *dw);
> > > + u16 (*ch_count)(struct dw_edma *dw, enum dw_edma_dir dir);
> > > + enum dma_status (*ch_status)(struct dw_edma_chan *chan);
> >
> > > + void (*clear_done_int)(struct dw_edma_chan *chan);
> > > + void (*clear_abort_int)(struct dw_edma_chan *chan);
> >
> > As I noted above these methods could be called locally from the
> > corresponding IP-core-specific module. Thus you won't need to define
> > the callbacks. It shall simplify the interface a bit.
> >
> > > + void (*done_handle)(struct dw_edma_irq *dw_irq,
> > > + enum dw_edma_dir dir);
> > > + void (*abort_handle)(struct dw_edma_irq *dw_irq,
> > > + enum dw_edma_dir dir);
> >
> > Seeing both of these callbacks (done,abort handlers) are called from
> > the same IRQ-handler what about converting them to a single callback:
> > irqreturn_t (*handle_int)(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir);
> > ?
> > The eDMA channels IRQ-handlers would look like this:
> > --- a/drivers/dma/dw-edma/dw-edma-core.c
> > +++ b/drivers/dma/dw-edma/dw-edma-core.c
> > @@ -692,12 +692,16 @@ static irqreturn_t dw_edma_interrupt(int irq, void *data, bool write)
> > static inline irqreturn_t dw_edma_interrupt_write(int irq, void *data)
> > {
> > - return dw_edma_interrupt(irq, data, true);
> > + struct dw_edma_irq *dw_irq = data;
> > +
> > + return dw_edma_core_handle_int(dw_irq, EDMA_DIR_WRITE);
> > }
> >
> > 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);
> > }
> >
> > static irqreturn_t dw_edma_interrupt_common(int irq, void *data)
> >
> > Therefore you'll be able to completely drop the
> > static irqreturn_t dw_edma_interrupt(int irq, void *data, bool write)
> > function from drivers/dma/dw-edma/dw-edma-core.c. The function body would
> > be almost fully moved to the handle_int() function of the eDMA/HDMA
> > core-specific ops.
> >
> > For instance in case of the DW eDMA it would look like this:
> > static irqreturn_t dw_edma_v0_core_handle_int(struct dw_edma_irq, enum dw_edma_dir dir)
> > {
> > struct dw_edma *dw = dw_irq->dw;
> > unsigned long total, pos, val;
> > unsigned long off;
> > u32 mask;
> >
> > if (dir == EDMA_DIR_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, dir) & mask;
> > for_each_set_bit(pos, &val, total) {
> > struct dw_edma_chan *chan = &dw->chan[pos + off];
> >
> > dw_edma_v0_core_clear_done_int(chan);
> >
> > dw_edma_done_interrupt(chan);
> > }
> >
> > val = dw_edma_v0_core_status_abort_int(dw, dir) & mask;
> > for_each_set_bit(pos, &val, total) {
> > struct dw_edma_chan *chan = &dw->chan[pos + off];
> >
> > dw_edma_v0_core_clear_abort_int(chan);
> >
> > dw_edma_abort_interrupt(chan);
> > }
> >
> > return IRQ_HANDLED;
> > }
> >
> > What do you think?
> >
> > > + void (*start)(struct dw_edma_chunk *chunk, bool first);
> > > + void (*ch_config)(struct dw_edma_chan *chan);
> > > + void (*debugfs_on)(struct dw_edma *dw);
> > > };
> > >
> > > struct dw_edma_sg {
> > > @@ -136,6 +153,9 @@ struct dw_edma_transfer {
> > > enum dw_edma_xfer_type type;
> > > };
> > >
> > > +void dw_edma_done_interrupt(struct dw_edma_chan *chan);
> > > +void dw_edma_abort_interrupt(struct dw_edma_chan *chan);
> > > +
> > > static inline
> > > struct dw_edma_chan *vc2dw_edma_chan(struct virt_dma_chan *vc)
> > > {
> > > @@ -148,4 +168,76 @@ struct dw_edma_chan *dchan2dw_edma_chan(struct dma_chan *dchan)
> > > return vc2dw_edma_chan(to_virt_chan(dchan));
> > > }
> > >
> > > +static inline
> > > +void dw_edma_core_off(struct dw_edma *dw)
> > > +{
> > > + dw->core->off(dw);
> > > +}
> > > +
> > > +static inline
> > > +u16 dw_edma_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir)
> > > +{
> > > + return dw->core->ch_count(dw, dir);
> > > +}
> > > +
> > > +static inline
> > > +enum dma_status dw_edma_core_ch_status(struct dw_edma_chan *chan)
> > > +{
> > > + struct dw_edma *dw = chan->dw;
> > > +
> > > + return dw->core->ch_status(chan);
> > > +}
> > > +
> > > +static inline
> > > +void dw_edma_core_clear_done_int(struct dw_edma_chan *chan)
> > > +{
> > > + struct dw_edma *dw = chan->dw;
> > > +
> > > + dw->core->clear_done_int(chan);
> > > +}
> > > +
> > > +static inline
> > > +void dw_edma_core_clear_abort_int(struct dw_edma_chan *chan)
> > > +{
> > > + struct dw_edma *dw = chan->dw;
> > > +
> > > + dw->core->clear_abort_int(chan);
> > > +}
> > > +
> >
> > > +static inline
> > > +void dw_edma_core_done_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir)
> > > +{
> > > + struct dw_edma *dw = dw_irq->dw;
> > > +
> > > + dw->core->done_handle(dw_irq, dir);
> > > +}
> > > +
> > > +static inline
> > > +void dw_edma_core_abort_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir)
> > > +{
> > > + struct dw_edma *dw = dw_irq->dw;
> > > +
> > > + dw->core->abort_handle(dw_irq, dir);
> > > +}
> >
> > As I suggested above these two methods can be replaced with a single
> > one: dw_edma_core_handle_int(...).
> >
> > > +
> > > +static inline
> > > +void dw_edma_core_start(struct dw_edma *dw, struct dw_edma_chunk *chunk, bool first)
> > > +{
> > > + dw->core->start(chunk, first);
> > > +}
> > > +
> > > +static inline
> > > +void dw_edma_core_ch_config(struct dw_edma_chan *chan)
> > > +{
> > > + struct dw_edma *dw = chan->dw;
> > > +
> > > + dw->core->ch_config(chan);
> > > +}
> > > +
> > > +static inline
> > > +void dw_edma_core_debugfs_on(struct dw_edma *dw)
> > > +{
> > > + dw->core->debugfs_on(dw);
> > > +}
> > > +
> > > #endif /* _DW_EDMA_CORE_H */
> > > diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c
> > > index 72e79a0c0a4e..73dcf72ba3a9 100644
> > > --- a/drivers/dma/dw-edma/dw-edma-v0-core.c
> > > +++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
> > > @@ -216,7 +216,7 @@ static inline u64 readq_ch(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch,
> > > readq_ch(dw, dir, ch, &(__dw_ch_regs(dw, dir, ch)->name))
> > >
> > > /* eDMA management callbacks */
> > > -void dw_edma_v0_core_off(struct dw_edma *dw)
> > > +static void dw_edma_v0_core_off(struct dw_edma *dw)
> > > {
> > > SET_BOTH_32(dw, int_mask,
> > > EDMA_V0_DONE_INT_MASK | EDMA_V0_ABORT_INT_MASK);
> > > @@ -225,7 +225,7 @@ void dw_edma_v0_core_off(struct dw_edma *dw)
> > > SET_BOTH_32(dw, engine_en, 0);
> > > }
> > >
> > > -u16 dw_edma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir)
> > > +static u16 dw_edma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir)
> > > {
> > > u32 num_ch;
> > >
> > > @@ -242,7 +242,7 @@ u16 dw_edma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir)
> > > return (u16)num_ch;
> > > }
> > >
> > > -enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan)
> > > +static enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan)
> > > {
> > > struct dw_edma *dw = chan->dw;
> > > u32 tmp;
> > > @@ -258,7 +258,7 @@ enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan)
> > > return DMA_ERROR;
> > > }
> > >
> > > -void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan)
> > > +static void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan)
> > > {
> > > struct dw_edma *dw = chan->dw;
> > >
> > > @@ -266,7 +266,7 @@ void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan)
> > > FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id)));
> > > }
> > >
> > > -void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan)
> > > +static void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan)
> > > {
> > > struct dw_edma *dw = chan->dw;
> > >
> > > @@ -274,18 +274,66 @@ void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan)
> > > FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id)));
> > > }
> > >
> > > -u32 dw_edma_v0_core_status_done_int(struct dw_edma *dw, enum dw_edma_dir dir)
> > > +static u32 dw_edma_v0_core_status_done_int(struct dw_edma *dw, enum dw_edma_dir dir)
> > > {
> > > return FIELD_GET(EDMA_V0_DONE_INT_MASK,
> > > GET_RW_32(dw, dir, int_status));
> > > }
> > >
> > > -u32 dw_edma_v0_core_status_abort_int(struct dw_edma *dw, enum dw_edma_dir dir)
> > > +static u32 dw_edma_v0_core_status_abort_int(struct dw_edma *dw, enum dw_edma_dir dir)
> > > {
> > > return FIELD_GET(EDMA_V0_ABORT_INT_MASK,
> > > GET_RW_32(dw, dir, int_status));
> > > }
> > >
> > > +static
> > > +void dw_edma_v0_core_done_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir)
> > > +{
> > > + struct dw_edma *dw = dw_irq->dw;
> > > + unsigned long total, pos, val;
> > > + unsigned long off;
> > > + u32 mask;
> > > +
> > > + if (dir == EDMA_DIR_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, dir);
> > > + val &= mask;
> >
> > > + for_each_set_bit(pos, &val, total)
> > > + dw_edma_done_interrupt(&dw->chan[pos + off]);
> >
> > As I suggested above replace this with:
> > + for_each_set_bit(pos, &val, total) {
> > + struct dw_edma_chan *chan = &dw->chan[pos + off];
> > +
> > + dw_edma_v0_core_clear_done_int(chan);
> > +
> > + dw_edma_done_interrupt(chan);
> > + }
> >
> > Therefore you'll be able to drop the clear_done_int callback and its
> > invocation from the dw_edma_done_interrupt() function.
> >
> > BTW Why did you change the dw_edma_v0_core_done_handle() semantics
> > from taking the function pointers as an argument to calling the global
> > version of dw_edma_done_interrupt() ?
> In my patch, 'dw_edma_core_clear_done_int' is called by 'dw_edma_done_interrupt',
>
> 'dw_edma_core_clear_done_int' point to 'dw_edma_v0_core_clear_done_int'.
>
> -static void dw_edma_done_interrupt(struct dw_edma_chan *chan)
> +void dw_edma_done_interrupt(struct dw_edma_chan *chan)
> {
> struct dw_edma_desc *desc;
> struct virt_dma_desc *vd;
> unsigned long flags;
>
> - dw_edma_v0_core_clear_done_int(chan);
> + dw_edma_core_clear_done_int(chan);
>
>
> So, my patch only drop the callback params, like this in v2 patch
> "dw_irq, dw_edma_done_interrupt, dir)"
>
>
> - for_each_set_bit(pos, &val, total) {
> - struct dw_edma_chan *chan = &dw->chan[pos + off];
> + enum dw_edma_dir dir = write ? EDMA_DIR_WRITE : EDMA_DIR_READ;
>
> - dw_edma_abort_interrupt(chan);
> - }
> + dw->core->dw_xdma_core_done_handle(dw_irq, dw_edma_done_interrupt, dir);
> + dw->core->dw_xdma_core_abort_handle(dw_irq, dw_edma_abort_interrupt, dir);
>
>
> And I will fix it in next version.
I was talking about this change:
v1: dw_edma_v0_core_abort_handle(struct dw_edma_irq *dw_irq,
void (*done_cb)(struct dw_edma_chan *chan),
enum dw_edma_dir dir)
v2: dw_edma_v0_core_done_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir)
I don't have strong opinion about that but having a function
pointer passed as parameter seemed a tiny bit more context-wise
readable since you won't need to memories that
dw_edma_done_interrupt() and dw_edma_abort_interrupt() are global,
they are defined in the core part of the driver and they are supposed
to be called from the IP-core abstraction layers. On the other hand
the handle_int() callback prototype will look a bit more bulky.
Note if you decide to get the previous semantics back please pass the
callbacks as the last arguments:
dw_edma_v0_core_handle_int(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir,
void (*done)(struct dw_edma_chan *chan),
void (*abort)(struct dw_edma_chan *chan))
-Serge(y)
>
> Thanks,
> Cai-
> >
> > > +}
> > > +
> > > +static
> > > +void dw_edma_v0_core_abort_handle(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir)
> > > +{
> > > + struct dw_edma *dw = dw_irq->dw;
> > > + unsigned long total, pos, val;
> > > + unsigned long off;
> > > + u32 mask;
> > > +
> > > + if (dir == EDMA_DIR_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_abort_int(dw, dir);
> > > + val &= mask;
> >
> > > + for_each_set_bit(pos, &val, total)
> > > + dw_edma_abort_interrupt(&dw->chan[pos + off]);
> >
> > ditto
> >
> > > +}
> > > +
> > > static void dw_edma_v0_write_ll_data(struct dw_edma_chunk *chunk, int i,
> > > u32 control, u32 size, u64 sar, u64 dar)
> > > {
> > > @@ -356,7 +404,7 @@ static void dw_edma_v0_core_write_chunk(struct dw_edma_chunk *chunk)
> > > dw_edma_v0_write_ll_link(chunk, i, control, chunk->ll_region.paddr);
> > > }
> > >
> > > -void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
> > > +static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
> > > {
> > > struct dw_edma_chan *chan = chunk->chan;
> > > struct dw_edma *dw = chan->dw;
> > > @@ -427,7 +475,7 @@ void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
> > > FIELD_PREP(EDMA_V0_DOORBELL_CH_MASK, chan->id));
> > > }
> > >
> > > -int dw_edma_v0_core_device_config(struct dw_edma_chan *chan)
> > > +static void dw_edma_v0_core_ch_config(struct dw_edma_chan *chan)
> > > {
> > > struct dw_edma *dw = chan->dw;
> > > u32 tmp = 0;
> > > @@ -494,12 +542,28 @@ int dw_edma_v0_core_device_config(struct dw_edma_chan *chan)
> > > SET_RW_32(dw, chan->dir, ch67_imwr_data, tmp);
> > > break;
> > > }
> > > -
> > > - return 0;
> > > }
> > >
> > > /* eDMA debugfs callbacks */
> > > -void dw_edma_v0_core_debugfs_on(struct dw_edma *dw)
> > > +static void dw_edma_v0_core_debugfs_on(struct dw_edma *dw)
> > > {
> > > dw_edma_v0_debugfs_on(dw);
> > > }
> > > +
> > > +static const struct dw_edma_core_ops edma_core = {
> > > + .off = dw_edma_v0_core_off,
> > > + .ch_count = dw_edma_v0_core_ch_count,
> > > + .ch_status = dw_edma_v0_core_ch_status,
> >
> > > + .clear_done_int = dw_edma_v0_core_clear_done_int,
> > > + .clear_abort_int = dw_edma_v0_core_clear_abort_int,
> > > + .done_handle = dw_edma_v0_core_done_handle,
> > > + .abort_handle = dw_edma_v0_core_abort_handle,
> >
> > If you take into account what I suggested above these four callbacks will
> > be replaced with a single one handle_int. Thus the interface will look
> > much simpler so will the whole IRQs-handling logic:
> >
> > irq: dw_edma_interrupt_{write,read}()
> > +-> dw_edma_core_handle_int() (dw-edma-v0-core.c)
> > +-> dw_edma_v0_core_status_done_int() (dw-edma-v0-core.c)
> > +-> dw_edma_v0_core_clear_done_int() (dw-edma-v0-core.c)
> > +-> dw_edma_done_interrupt() (dw-edma-core.c)
> > +-> dw_edma_v0_core_status_abort_int() (dw-edma-v0-core.c)
> > +-> dw_edma_v0_core_clear_abort_int() (dw-edma-v0-core.c)
> > +-> dw_edma_abort_interrupt() (dw-edma-core.c)
> >
> > In your case the calls hierarchy looks like this:
> > irq: dw_edma_interrupt_{write,read}()
> > +-> dw_edma_interrupt()
> > +-> dw_edma_core_done_handle() (dw-edma-v0-core.c)
> > +-> dw_edma_v0_core_status_done_int() (dw-edma-v0-core.c)
> > +-> dw_edma_done_interrupt() (dw-edma-core.c)
> > +-> dw_edma_core_clear_done_int() (dw-edma-v0-core.c)
> > +-> dw_edma_core_abort_handle() (dw-edma-v0-core.c)
> > +-> dw_edma_v0_core_status_abort_int() (dw-edma-v0-core.c)
> > +-> dw_edma_abort_interrupt() (dw-edma-core.c)
> > +-> dw_edma_core_clear_abort_int() (dw-edma-v0-core.c)
> >
> > See the calls thread is more linear in my case and the reader won't
> > need to get several times back and forth between the generic part of
> > the driver and the IP-core-specific one.
> >
> > -Serge(y)
> >
> > > + .start = dw_edma_v0_core_start,
> > > + .ch_config = dw_edma_v0_core_ch_config,
> > > + .debugfs_on = dw_edma_v0_core_debugfs_on,
> > > +};
> > > +
> > > +void dw_edma_v0_core_register(struct dw_edma *dw)
> > > +{
> > > + dw->core = &edma_core;
> > > +}
> > > diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.h b/drivers/dma/dw-edma/dw-edma-v0-core.h
> > > index ab96a1f48080..04a882222f99 100644
> > > --- a/drivers/dma/dw-edma/dw-edma-v0-core.h
> > > +++ b/drivers/dma/dw-edma/dw-edma-v0-core.h
> > > @@ -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 */
> > > --
> > > 2.34.1
> > >