2022-03-28 22:12:04

by Serge Semin

[permalink] [raw]
Subject: [PATCH 00/25] dmaengine: dw-edma: Add RP/EP local DMA controllers support

This is a final patchset in the series created in the framework of
my Baikal-T1 PCIe/eDMA-related work:

[1: In-progress] clk: Baikal-T1 DDR/PCIe resets and some xGMAC fixes
Link: --submitted--
[2: In-progress] PCI: dwc: Various fixes and cleanups
Link: --submitted--
[3: In-progress] PCI: dwc: Add dma-ranges/YAML-schema/Baikal-T1 support
Link: --submitted--
[4: In-progress] dmaengine: dw-edma: Add RP/EP local DMA controllers support
Link: --you are looking at it--

Note it is recommended to merge the last patchset after the former ones in
order to prevent merge conflicts. @Bjorn could you merge in this patchset
through your PCIe repo? After getting all the ack'es of course.

Please note originally this series was self content, but due to Frank
being a bit faster in his work submission I had to rebase my patchset onto
his one. So now this patchset turns to be dependent on the Frank' work:
Link: https://lore.kernel.org/dmaengine/[email protected]/
So please review and merge his series first before applying this one.

@Frank, @Manivannan as we agreed here:
Link: https://lore.kernel.org/dmaengine/[email protected]/
this series contains two patches with our joint work. Here they are:
[PATCH 1/25] dmaengine: dw-edma: Drop dma_slave_config.direction field usage
[PATCH 2/25] dmaengine: dw-edma: Fix eDMA Rd/Wr-channels and DMA-direction semantics
@Frank, could you please pick them up and add them to your series in place
of the patches:
[PATCH v5 6/9] dmaengine: dw-edma: Don't rely on the deprecated "direction" member
Link: https://lore.kernel.org/dmaengine/[email protected]/
[PATCH v5 5/9] dmaengine: dw-edma: Fix programming the source & dest addresses for ep
Link: https://lore.kernel.org/dmaengine/[email protected]/
respectively?

@Frank please don't forget to fix your series so the chip->dw field is
initialized after all the probe() initializations are done. For that sake
you also need to make sure that the dw_edma_irq_request(),
dw_edma_channel_setup() and dw_edma_v0_core_debugfs_on() methods take
dw_edma structure pointer as a parameter.

Here is a short summary regarding this patchset. The series starts with
fixes patches. The very first two patches have been modified based on
discussion with @Frank and @Manivannan as I noted in the previous
paragraph. They concern fixing the Read/Write channels xfer semantics.
See the patches description for more details. After that goes the fix of
the dma_direct_map_resource() method, which turned out to be not working
correctly for the case of having devive.dma_range_map being non-empty
(non-empty dma-ranges DT property). Then we discovered that the
dw-edma-pcie.c driver incorrectly initializes the LL/DT base addresses for
the platforms with not matching CPU and PCIe memory spaces. It is fixed by
using the pci_bus_address() method to get a correct base address. After
that you can find a series of interleaved xfers fixes. It turned out the
interleaved transfers implementation didn't work quite correctly from the
very beginning for instance missing src/dst addresses initialization, etc.
In the framework of the next two patches we suggest to add a new
platform-specific callback - pci_addrees() and use to convert the CPU
address to the PCIe space address. It is at least required for the DW eDAM
remote End-point setup on the platforms with not-matching address spaces.
In case of the DW eDMA local RP/EP setup the conversion will be done
automatically by the outbound iATU (if no DMA-bypass flag is specified for
the corresponding iATU window). Then we introduce a set of patches to make
the DebugFS part of the code supporting the multi-eDMA controllers
platforms. It starts with several cleanup patches and is closed joining
the Read/Write channels into a single DMA-device as they originally should
have been. After that you can find the patches with adding the non-atomic
io-64 methods usage, dropping DT-region descriptors allocation, replacing
chip IDs with device name. In addition to that in order to have the eDMA
embedded into the DW PCIe RP/EP supported we need to bypass the
dma-ranges-based memory ranges mapping since in case of the root port DT
node it's applicable for the peripheral PCIe devices only. Finally at the
series closure we introduce a generic DW eDMA controller support being
available in the DW PCIe Host/End-point driver.

Signed-off-by: Serge Semin <[email protected]>
Cc: Alexey Malahov <[email protected]>
Cc: Pavel Parkhomenko <[email protected]>
Cc: Lorenzo Pieralisi <[email protected]>
Cc: Rob Herring <[email protected]>
Cc: "Krzysztof WilczyƄski" <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]

Serge Semin (25):
dmaengine: dw-edma: Drop dma_slave_config.direction field usage
dmaengine: dw-edma: Fix eDMA Rd/Wr-channels and DMA-direction
semantics
dma-direct: take dma-ranges/offsets into account in resource mapping
dmaengine: Fix dma_slave_config.dst_addr description
dmaengine: dw-edma: Convert ll/dt phys-address to PCIe bus/DMA address
dmaengine: dw-edma: Fix missing src/dst address of the interleaved
xfers
dmaengine: dw-edma: Don't permit non-inc interleaved xfers
dmaengine: dw-edma: Fix invalid interleaved xfers semantics
dmaengine: dw-edma: Add CPU to PCIe bus address translation
dmaengine: dw-edma: Add PCIe bus address getter to the remote EP
glue-driver
dmaengine: dw-edma: Drop chancnt initialization
dmaengine: dw-edma: Fix DebugFS reg entry type
dmaengine: dw-edma: Stop checking debugfs_create_*() return value
dmaengine: dw-edma: Add dw_edma prefix to the DebugFS nodes descriptor
dmaengine: dw-edma: Convert DebugFS descs to being kz-allocated
dmaengine: dw-edma: Simplify the DebugFS context CSRs init procedure
dmaengine: dw-edma: Move eDMA data pointer to DebugFS node descriptor
dmaengine: dw-edma: Join Write/Read channels into a single device
dmaengine: dw-edma: Use DMA-engine device DebugFS subdirectory
dmaengine: dw-edma: Use non-atomic io-64 methods
dmaengine: dw-edma: Drop DT-region allocation
dmaengine: dw-edma: Replace chip ID number with device name
dmaengine: dw-edma: Bypass dma-ranges mapping for the local setup
dmaengine: dw-edma: Skip cleanup procedure if no private data found
PCI: dwc: Add DW eDMA engine support

drivers/dma/dw-edma/dw-edma-core.c | 249 +++++++------
drivers/dma/dw-edma/dw-edma-core.h | 10 +-
drivers/dma/dw-edma/dw-edma-pcie.c | 24 +-
drivers/dma/dw-edma/dw-edma-v0-core.c | 76 ++--
drivers/dma/dw-edma/dw-edma-v0-core.h | 1 -
drivers/dma/dw-edma/dw-edma-v0-debugfs.c | 350 ++++++++----------
drivers/dma/dw-edma/dw-edma-v0-debugfs.h | 5 -
.../pci/controller/dwc/pcie-designware-ep.c | 4 +
.../pci/controller/dwc/pcie-designware-host.c | 13 +-
drivers/pci/controller/dwc/pcie-designware.c | 188 ++++++++++
drivers/pci/controller/dwc/pcie-designware.h | 23 +-
include/linux/dma/edma.h | 18 +-
include/linux/dmaengine.h | 2 +-
kernel/dma/direct.c | 2 +-
14 files changed, 598 insertions(+), 367 deletions(-)

--
2.35.1


2022-03-28 22:14:38

by Serge Semin

[permalink] [raw]
Subject: [PATCH 14/25] dmaengine: dw-edma: Add dw_edma prefix to the DebugFS nodes descriptor

The rest of the locally defined and used methods and structures have
dw_edma prefix in their names. It's right in accordance with the kernel
coding style to follow the locally defined rule of naming. Let's add that
prefix to the debugfs_entries structure too especially seeing it's name
may be confusing as if that structure belongs to the global DebugFS space.

Signed-off-by: Serge Semin <[email protected]>
---
drivers/dma/dw-edma/dw-edma-v0-debugfs.c | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/drivers/dma/dw-edma/dw-edma-v0-debugfs.c b/drivers/dma/dw-edma/dw-edma-v0-debugfs.c
index 808eed212be8..afd519d9568b 100644
--- a/drivers/dma/dw-edma/dw-edma-v0-debugfs.c
+++ b/drivers/dma/dw-edma/dw-edma-v0-debugfs.c
@@ -46,7 +46,7 @@ static struct {
void __iomem *end;
} lim[2][EDMA_V0_MAX_NR_CH];

-struct debugfs_entries {
+struct dw_edma_debugfs_entry {
const char *name;
void __iomem *reg;
};
@@ -94,7 +94,7 @@ static int dw_edma_debugfs_u32_get(void *data, u64 *val)
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_x32, dw_edma_debugfs_u32_get, NULL, "0x%08llx\n");

-static void dw_edma_debugfs_create_x32(const struct debugfs_entries entries[],
+static void dw_edma_debugfs_create_x32(const struct dw_edma_debugfs_entry entries[],
int nr_entries, struct dentry *dir)
{
int i;
@@ -108,8 +108,7 @@ static void dw_edma_debugfs_create_x32(const struct debugfs_entries entries[],
static void dw_edma_debugfs_regs_ch(struct dw_edma_v0_ch_regs __iomem *regs,
struct dentry *dir)
{
- int nr_entries;
- const struct debugfs_entries debugfs_regs[] = {
+ const struct dw_edma_debugfs_entry debugfs_regs[] = {
REGISTER(ch_control1),
REGISTER(ch_control2),
REGISTER(transfer_size),
@@ -120,6 +119,7 @@ static void dw_edma_debugfs_regs_ch(struct dw_edma_v0_ch_regs __iomem *regs,
REGISTER(llp.lsb),
REGISTER(llp.msb),
};
+ int nr_entries;

nr_entries = ARRAY_SIZE(debugfs_regs);
dw_edma_debugfs_create_x32(debugfs_regs, nr_entries, dir);
@@ -127,7 +127,7 @@ static void dw_edma_debugfs_regs_ch(struct dw_edma_v0_ch_regs __iomem *regs,

static void dw_edma_debugfs_regs_wr(struct dentry *dir)
{
- const struct debugfs_entries debugfs_regs[] = {
+ const struct dw_edma_debugfs_entry debugfs_regs[] = {
/* eDMA global registers */
WR_REGISTER(engine_en),
WR_REGISTER(doorbell),
@@ -148,7 +148,7 @@ static void dw_edma_debugfs_regs_wr(struct dentry *dir)
WR_REGISTER(ch67_imwr_data),
WR_REGISTER(linked_list_err_en),
};
- const struct debugfs_entries debugfs_unroll_regs[] = {
+ const struct dw_edma_debugfs_entry debugfs_unroll_regs[] = {
/* eDMA channel context grouping */
WR_REGISTER_UNROLL(engine_chgroup),
WR_REGISTER_UNROLL(engine_hshake_cnt.lsb),
@@ -191,7 +191,7 @@ static void dw_edma_debugfs_regs_wr(struct dentry *dir)

static void dw_edma_debugfs_regs_rd(struct dentry *dir)
{
- const struct debugfs_entries debugfs_regs[] = {
+ const struct dw_edma_debugfs_entry debugfs_regs[] = {
/* eDMA global registers */
RD_REGISTER(engine_en),
RD_REGISTER(doorbell),
@@ -213,7 +213,7 @@ static void dw_edma_debugfs_regs_rd(struct dentry *dir)
RD_REGISTER(ch45_imwr_data),
RD_REGISTER(ch67_imwr_data),
};
- const struct debugfs_entries debugfs_unroll_regs[] = {
+ const struct dw_edma_debugfs_entry debugfs_unroll_regs[] = {
/* eDMA channel context grouping */
RD_REGISTER_UNROLL(engine_chgroup),
RD_REGISTER_UNROLL(engine_hshake_cnt.lsb),
@@ -256,7 +256,7 @@ static void dw_edma_debugfs_regs_rd(struct dentry *dir)

static void dw_edma_debugfs_regs(void)
{
- const struct debugfs_entries debugfs_regs[] = {
+ const struct dw_edma_debugfs_entry debugfs_regs[] = {
REGISTER(ctrl_data_arb_prior),
REGISTER(ctrl),
};
--
2.35.1

2022-03-28 22:25:06

by Serge Semin

[permalink] [raw]
Subject: [PATCH 17/25] dmaengine: dw-edma: Move eDMA data pointer to DebugFS node descriptor

The last thing that really stops the DebugFS part of the eDMA driver from
supporting the multi-eDMA platform in is keeping the eDMA private data
pointer in the static area of the DebugFS module. Since the DebugFS node
descriptors are now kz-allocated we can freely move that pointer to being
preserved in the descriptors. After the DebugFS initialization procedure
that pointer will be used in the DebugFS files getter to access the common
CSRs space and the context CSRs spin-lock. So the main part of this change
is connected with the DebugFS nodes descriptors initialization macros,
which aside with already defined prototypes now require to have the DW
eDMA private data pointer passed.

Signed-off-by: Serge Semin <[email protected]>
---
drivers/dma/dw-edma/dw-edma-v0-debugfs.c | 240 +++++++++++------------
1 file changed, 118 insertions(+), 122 deletions(-)

diff --git a/drivers/dma/dw-edma/dw-edma-v0-debugfs.c b/drivers/dma/dw-edma/dw-edma-v0-debugfs.c
index b34a68964232..353269a3680b 100644
--- a/drivers/dma/dw-edma/dw-edma-v0-debugfs.c
+++ b/drivers/dma/dw-edma/dw-edma-v0-debugfs.c
@@ -13,53 +13,55 @@
#include "dw-edma-v0-regs.h"
#include "dw-edma-core.h"

-#define REGS_ADDR(name) \
- ((void __iomem *)&regs->name)
+#define REGS_ADDR(dw, name) \
+ ({ \
+ struct dw_edma_v0_regs __iomem *__regs = (dw)->chip->reg_base; \
+ \
+ (void __iomem *)&__regs->name; \
+ })

-#define REGS_CH_ADDR(name, _dir, _ch) \
+#define REGS_CH_ADDR(dw, name, _dir, _ch) \
({ \
struct dw_edma_v0_ch_regs __iomem *__ch_regs; \
\
if ((dw)->chip->mf == EDMA_MF_EDMA_LEGACY) \
- __ch_regs = &regs->type.legacy.ch; \
+ __ch_regs = REGS_ADDR(dw, type.legacy.ch); \
else if (_dir == EDMA_DIR_READ) \
- __ch_regs = &regs->type.unroll.ch[_ch].rd; \
+ __ch_regs = REGS_ADDR(dw, type.unroll.ch[_ch].rd); \
else \
- __ch_regs = &regs->type.unroll.ch[_ch].wr; \
+ __ch_regs = REGS_ADDR(dw, type.unroll.ch[_ch].wr); \
\
(void __iomem *)&__ch_regs->name; \
})

-#define REGISTER(name) \
- { #name, REGS_ADDR(name) }
+#define REGISTER(dw, name) \
+ { dw, #name, REGS_ADDR(dw, name) }

-#define CTX_REGISTER(name, dir, ch) \
- { #name, REGS_CH_ADDR(name, dir, ch), dir, ch }
+#define CTX_REGISTER(dw, name, dir, ch) \
+ { dw, #name, REGS_CH_ADDR(dw, name, dir, ch), dir, ch }

-#define WR_REGISTER(name) \
- { #name, REGS_ADDR(wr_##name) }
-#define RD_REGISTER(name) \
- { #name, REGS_ADDR(rd_##name) }
+#define WR_REGISTER(dw, name) \
+ { dw, #name, REGS_ADDR(dw, wr_##name) }
+#define RD_REGISTER(dw, name) \
+ { dw, #name, REGS_ADDR(dw, rd_##name) }

-#define WR_REGISTER_LEGACY(name) \
- { #name, REGS_ADDR(type.legacy.wr_##name) }
+#define WR_REGISTER_LEGACY(dw, name) \
+ { dw, #name, REGS_ADDR(dw, type.legacy.wr_##name) }
#define RD_REGISTER_LEGACY(name) \
- { #name, REGS_ADDR(type.legacy.rd_##name) }
+ { dw, #name, REGS_ADDR(dw, type.legacy.rd_##name) }

-#define WR_REGISTER_UNROLL(name) \
- { #name, REGS_ADDR(type.unroll.wr_##name) }
-#define RD_REGISTER_UNROLL(name) \
- { #name, REGS_ADDR(type.unroll.rd_##name) }
+#define WR_REGISTER_UNROLL(dw, name) \
+ { dw, #name, REGS_ADDR(dw, type.unroll.wr_##name) }
+#define RD_REGISTER_UNROLL(dw, name) \
+ { dw, #name, REGS_ADDR(dw, type.unroll.rd_##name) }

#define WRITE_STR "write"
#define READ_STR "read"
#define CHANNEL_STR "channel"
#define REGISTERS_STR "registers"

-static struct dw_edma *dw;
-static struct dw_edma_v0_regs __iomem *regs;
-
struct dw_edma_debugfs_entry {
+ struct dw_edma *dw;
const char *name;
void __iomem *reg;
enum dw_edma_dir dir;
@@ -69,10 +71,11 @@ struct dw_edma_debugfs_entry {
static int dw_edma_debugfs_u32_get(void *data, u64 *val)
{
struct dw_edma_debugfs_entry __iomem *entry = data;
+ struct dw_edma *dw = entry->dw;
void __iomem *reg = entry->reg;

if (dw->chip->mf == EDMA_MF_EDMA_LEGACY &&
- reg >= (void __iomem *)&regs->type.legacy.ch) {
+ reg >= REGS_ADDR(dw, type.legacy.ch)) {
unsigned long flags;
u32 viewport_sel;

@@ -81,7 +84,7 @@ static int dw_edma_debugfs_u32_get(void *data, u64 *val)

raw_spin_lock_irqsave(&dw->lock, flags);

- writel(viewport_sel, &regs->type.legacy.viewport_sel);
+ writel(viewport_sel, REGS_ADDR(dw, type.legacy.viewport_sel));
*val = readl(reg);

raw_spin_unlock_irqrestore(&dw->lock, flags);
@@ -93,7 +96,8 @@ static int dw_edma_debugfs_u32_get(void *data, u64 *val)
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_x32, dw_edma_debugfs_u32_get, NULL, "0x%08llx\n");

-static void dw_edma_debugfs_create_x32(const struct dw_edma_debugfs_entry ini[],
+static void dw_edma_debugfs_create_x32(struct dw_edma *dw,
+ const struct dw_edma_debugfs_entry ini[],
int nr_entries, struct dentry *dir)
{
struct dw_edma_debugfs_entry *entries;
@@ -112,62 +116,62 @@ static void dw_edma_debugfs_create_x32(const struct dw_edma_debugfs_entry ini[],
}
}

-static void dw_edma_debugfs_regs_ch(enum dw_edma_dir edma_dir, u16 ch,
- struct dentry *dir)
+static void dw_edma_debugfs_regs_ch(struct dw_edma *dw, enum dw_edma_dir edma_dir,
+ u16 ch, struct dentry *dir)
{
struct dw_edma_debugfs_entry debugfs_regs[] = {
- CTX_REGISTER(ch_control1, edma_dir, ch),
- CTX_REGISTER(ch_control2, edma_dir, ch),
- CTX_REGISTER(transfer_size, edma_dir, ch),
- CTX_REGISTER(sar.lsb, edma_dir, ch),
- CTX_REGISTER(sar.msb, edma_dir, ch),
- CTX_REGISTER(dar.lsb, edma_dir, ch),
- CTX_REGISTER(dar.msb, edma_dir, ch),
- CTX_REGISTER(llp.lsb, edma_dir, ch),
- CTX_REGISTER(llp.msb, edma_dir, ch),
+ CTX_REGISTER(dw, ch_control1, edma_dir, ch),
+ CTX_REGISTER(dw, ch_control2, edma_dir, ch),
+ CTX_REGISTER(dw, transfer_size, edma_dir, ch),
+ CTX_REGISTER(dw, sar.lsb, edma_dir, ch),
+ CTX_REGISTER(dw, sar.msb, edma_dir, ch),
+ CTX_REGISTER(dw, dar.lsb, edma_dir, ch),
+ CTX_REGISTER(dw, dar.msb, edma_dir, ch),
+ CTX_REGISTER(dw, llp.lsb, edma_dir, ch),
+ CTX_REGISTER(dw, llp.msb, edma_dir, ch),
};
int nr_entries;

nr_entries = ARRAY_SIZE(debugfs_regs);
- dw_edma_debugfs_create_x32(debugfs_regs, nr_entries, dir);
+ dw_edma_debugfs_create_x32(dw, debugfs_regs, nr_entries, dir);
}

-static void dw_edma_debugfs_regs_wr(struct dentry *dir)
+static void dw_edma_debugfs_regs_wr(struct dw_edma *dw, struct dentry *dir)
{
const struct dw_edma_debugfs_entry debugfs_regs[] = {
/* eDMA global registers */
- WR_REGISTER(engine_en),
- WR_REGISTER(doorbell),
- WR_REGISTER(ch_arb_weight.lsb),
- WR_REGISTER(ch_arb_weight.msb),
+ WR_REGISTER(dw, engine_en),
+ WR_REGISTER(dw, doorbell),
+ WR_REGISTER(dw, ch_arb_weight.lsb),
+ WR_REGISTER(dw, ch_arb_weight.msb),
/* eDMA interrupts registers */
- WR_REGISTER(int_status),
- WR_REGISTER(int_mask),
- WR_REGISTER(int_clear),
- WR_REGISTER(err_status),
- WR_REGISTER(done_imwr.lsb),
- WR_REGISTER(done_imwr.msb),
- WR_REGISTER(abort_imwr.lsb),
- WR_REGISTER(abort_imwr.msb),
- WR_REGISTER(ch01_imwr_data),
- WR_REGISTER(ch23_imwr_data),
- WR_REGISTER(ch45_imwr_data),
- WR_REGISTER(ch67_imwr_data),
- WR_REGISTER(linked_list_err_en),
+ WR_REGISTER(dw, int_status),
+ WR_REGISTER(dw, int_mask),
+ WR_REGISTER(dw, int_clear),
+ WR_REGISTER(dw, err_status),
+ WR_REGISTER(dw, done_imwr.lsb),
+ WR_REGISTER(dw, done_imwr.msb),
+ WR_REGISTER(dw, abort_imwr.lsb),
+ WR_REGISTER(dw, abort_imwr.msb),
+ WR_REGISTER(dw, ch01_imwr_data),
+ WR_REGISTER(dw, ch23_imwr_data),
+ WR_REGISTER(dw, ch45_imwr_data),
+ WR_REGISTER(dw, ch67_imwr_data),
+ WR_REGISTER(dw, linked_list_err_en),
};
const struct dw_edma_debugfs_entry debugfs_unroll_regs[] = {
/* eDMA channel context grouping */
- WR_REGISTER_UNROLL(engine_chgroup),
- WR_REGISTER_UNROLL(engine_hshake_cnt.lsb),
- WR_REGISTER_UNROLL(engine_hshake_cnt.msb),
- WR_REGISTER_UNROLL(ch0_pwr_en),
- WR_REGISTER_UNROLL(ch1_pwr_en),
- WR_REGISTER_UNROLL(ch2_pwr_en),
- WR_REGISTER_UNROLL(ch3_pwr_en),
- WR_REGISTER_UNROLL(ch4_pwr_en),
- WR_REGISTER_UNROLL(ch5_pwr_en),
- WR_REGISTER_UNROLL(ch6_pwr_en),
- WR_REGISTER_UNROLL(ch7_pwr_en),
+ WR_REGISTER_UNROLL(dw, engine_chgroup),
+ WR_REGISTER_UNROLL(dw, engine_hshake_cnt.lsb),
+ WR_REGISTER_UNROLL(dw, engine_hshake_cnt.msb),
+ WR_REGISTER_UNROLL(dw, ch0_pwr_en),
+ WR_REGISTER_UNROLL(dw, ch1_pwr_en),
+ WR_REGISTER_UNROLL(dw, ch2_pwr_en),
+ WR_REGISTER_UNROLL(dw, ch3_pwr_en),
+ WR_REGISTER_UNROLL(dw, ch4_pwr_en),
+ WR_REGISTER_UNROLL(dw, ch5_pwr_en),
+ WR_REGISTER_UNROLL(dw, ch6_pwr_en),
+ WR_REGISTER_UNROLL(dw, ch7_pwr_en),
};
struct dentry *regs_dir, *ch_dir;
int nr_entries, i;
@@ -176,11 +180,11 @@ static void dw_edma_debugfs_regs_wr(struct dentry *dir)
regs_dir = debugfs_create_dir(WRITE_STR, dir);

nr_entries = ARRAY_SIZE(debugfs_regs);
- dw_edma_debugfs_create_x32(debugfs_regs, nr_entries, regs_dir);
+ dw_edma_debugfs_create_x32(dw, debugfs_regs, nr_entries, regs_dir);

if (dw->chip->mf == EDMA_MF_HDMA_COMPAT) {
nr_entries = ARRAY_SIZE(debugfs_unroll_regs);
- dw_edma_debugfs_create_x32(debugfs_unroll_regs, nr_entries,
+ dw_edma_debugfs_create_x32(dw, debugfs_unroll_regs, nr_entries,
regs_dir);
}

@@ -189,47 +193,47 @@ static void dw_edma_debugfs_regs_wr(struct dentry *dir)

ch_dir = debugfs_create_dir(name, regs_dir);

- dw_edma_debugfs_regs_ch(EDMA_DIR_WRITE, i, ch_dir);
+ dw_edma_debugfs_regs_ch(dw, EDMA_DIR_WRITE, i, ch_dir);
}
}

-static void dw_edma_debugfs_regs_rd(struct dentry *dir)
+static void dw_edma_debugfs_regs_rd(struct dw_edma *dw, struct dentry *dir)
{
const struct dw_edma_debugfs_entry debugfs_regs[] = {
/* eDMA global registers */
- RD_REGISTER(engine_en),
- RD_REGISTER(doorbell),
- RD_REGISTER(ch_arb_weight.lsb),
- RD_REGISTER(ch_arb_weight.msb),
+ RD_REGISTER(dw, engine_en),
+ RD_REGISTER(dw, doorbell),
+ RD_REGISTER(dw, ch_arb_weight.lsb),
+ RD_REGISTER(dw, ch_arb_weight.msb),
/* eDMA interrupts registers */
- RD_REGISTER(int_status),
- RD_REGISTER(int_mask),
- RD_REGISTER(int_clear),
- RD_REGISTER(err_status.lsb),
- RD_REGISTER(err_status.msb),
- RD_REGISTER(linked_list_err_en),
- RD_REGISTER(done_imwr.lsb),
- RD_REGISTER(done_imwr.msb),
- RD_REGISTER(abort_imwr.lsb),
- RD_REGISTER(abort_imwr.msb),
- RD_REGISTER(ch01_imwr_data),
- RD_REGISTER(ch23_imwr_data),
- RD_REGISTER(ch45_imwr_data),
- RD_REGISTER(ch67_imwr_data),
+ RD_REGISTER(dw, int_status),
+ RD_REGISTER(dw, int_mask),
+ RD_REGISTER(dw, int_clear),
+ RD_REGISTER(dw, err_status.lsb),
+ RD_REGISTER(dw, err_status.msb),
+ RD_REGISTER(dw, linked_list_err_en),
+ RD_REGISTER(dw, done_imwr.lsb),
+ RD_REGISTER(dw, done_imwr.msb),
+ RD_REGISTER(dw, abort_imwr.lsb),
+ RD_REGISTER(dw, abort_imwr.msb),
+ RD_REGISTER(dw, ch01_imwr_data),
+ RD_REGISTER(dw, ch23_imwr_data),
+ RD_REGISTER(dw, ch45_imwr_data),
+ RD_REGISTER(dw, ch67_imwr_data),
};
const struct dw_edma_debugfs_entry debugfs_unroll_regs[] = {
/* eDMA channel context grouping */
- RD_REGISTER_UNROLL(engine_chgroup),
- RD_REGISTER_UNROLL(engine_hshake_cnt.lsb),
- RD_REGISTER_UNROLL(engine_hshake_cnt.msb),
- RD_REGISTER_UNROLL(ch0_pwr_en),
- RD_REGISTER_UNROLL(ch1_pwr_en),
- RD_REGISTER_UNROLL(ch2_pwr_en),
- RD_REGISTER_UNROLL(ch3_pwr_en),
- RD_REGISTER_UNROLL(ch4_pwr_en),
- RD_REGISTER_UNROLL(ch5_pwr_en),
- RD_REGISTER_UNROLL(ch6_pwr_en),
- RD_REGISTER_UNROLL(ch7_pwr_en),
+ RD_REGISTER_UNROLL(dw, engine_chgroup),
+ RD_REGISTER_UNROLL(dw, engine_hshake_cnt.lsb),
+ RD_REGISTER_UNROLL(dw, engine_hshake_cnt.msb),
+ RD_REGISTER_UNROLL(dw, ch0_pwr_en),
+ RD_REGISTER_UNROLL(dw, ch1_pwr_en),
+ RD_REGISTER_UNROLL(dw, ch2_pwr_en),
+ RD_REGISTER_UNROLL(dw, ch3_pwr_en),
+ RD_REGISTER_UNROLL(dw, ch4_pwr_en),
+ RD_REGISTER_UNROLL(dw, ch5_pwr_en),
+ RD_REGISTER_UNROLL(dw, ch6_pwr_en),
+ RD_REGISTER_UNROLL(dw, ch7_pwr_en),
};
struct dentry *regs_dir, *ch_dir;
int nr_entries, i;
@@ -238,11 +242,11 @@ static void dw_edma_debugfs_regs_rd(struct dentry *dir)
regs_dir = debugfs_create_dir(READ_STR, dir);

nr_entries = ARRAY_SIZE(debugfs_regs);
- dw_edma_debugfs_create_x32(debugfs_regs, nr_entries, regs_dir);
+ dw_edma_debugfs_create_x32(dw, debugfs_regs, nr_entries, regs_dir);

if (dw->chip->mf == EDMA_MF_HDMA_COMPAT) {
nr_entries = ARRAY_SIZE(debugfs_unroll_regs);
- dw_edma_debugfs_create_x32(debugfs_unroll_regs, nr_entries,
+ dw_edma_debugfs_create_x32(dw, debugfs_unroll_regs, nr_entries,
regs_dir);
}

@@ -251,15 +255,15 @@ static void dw_edma_debugfs_regs_rd(struct dentry *dir)

ch_dir = debugfs_create_dir(name, regs_dir);

- dw_edma_debugfs_regs_ch(EDMA_DIR_READ, i, ch_dir);
+ dw_edma_debugfs_regs_ch(dw, EDMA_DIR_READ, i, ch_dir);
}
}

-static void dw_edma_debugfs_regs(void)
+static void dw_edma_debugfs_regs(struct dw_edma *dw)
{
const struct dw_edma_debugfs_entry debugfs_regs[] = {
- REGISTER(ctrl_data_arb_prior),
- REGISTER(ctrl),
+ REGISTER(dw, ctrl_data_arb_prior),
+ REGISTER(dw, ctrl),
};
struct dentry *regs_dir;
int nr_entries;
@@ -267,23 +271,17 @@ static void dw_edma_debugfs_regs(void)
regs_dir = debugfs_create_dir(REGISTERS_STR, dw->debugfs);

nr_entries = ARRAY_SIZE(debugfs_regs);
- dw_edma_debugfs_create_x32(debugfs_regs, nr_entries, regs_dir);
+ dw_edma_debugfs_create_x32(dw, debugfs_regs, nr_entries, regs_dir);

- dw_edma_debugfs_regs_wr(regs_dir);
- dw_edma_debugfs_regs_rd(regs_dir);
+ dw_edma_debugfs_regs_wr(dw, regs_dir);
+ dw_edma_debugfs_regs_rd(dw, regs_dir);
}

void dw_edma_v0_debugfs_on(struct dw_edma_chip *chip)
{
- if (!debugfs_initialized())
- return;
-
- dw = chip->dw;
- if (!dw)
- return;
+ struct dw_edma *dw = chip->dw;

- regs = dw->chip->reg_base;
- if (!regs)
+ if (!debugfs_initialized())
return;

dw->debugfs = debugfs_create_dir(dw->name, NULL);
@@ -292,14 +290,12 @@ void dw_edma_v0_debugfs_on(struct dw_edma_chip *chip)
debugfs_create_u16("wr_ch_cnt", 0444, dw->debugfs, &dw->wr_ch_cnt);
debugfs_create_u16("rd_ch_cnt", 0444, dw->debugfs, &dw->rd_ch_cnt);

- dw_edma_debugfs_regs();
+ dw_edma_debugfs_regs(dw);
}

void dw_edma_v0_debugfs_off(struct dw_edma_chip *chip)
{
- dw = chip->dw;
- if (!dw)
- return;
+ struct dw_edma *dw = chip->dw;

debugfs_remove_recursive(dw->debugfs);
dw->debugfs = NULL;
--
2.35.1

2022-03-28 22:27:32

by Serge Semin

[permalink] [raw]
Subject: [PATCH 04/25] dmaengine: Fix dma_slave_config.dst_addr description

Most likely due to a copy-paste mistake the dst_addr member of the
dma_slave_config structure has been marked as ignored if the !source!
address belong to the memory. That is relevant to the src_addr field of
the structure while the dst_addr field as containing a destination device
address is supposed to be ignored if the destination is the CPU memory.
Let's fix the field description accordingly.

Signed-off-by: Serge Semin <[email protected]>
---
include/linux/dmaengine.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
index 842d4f7ca752..f204ea16ac1c 100644
--- a/include/linux/dmaengine.h
+++ b/include/linux/dmaengine.h
@@ -395,7 +395,7 @@ enum dma_slave_buswidth {
* should be read (RX), if the source is memory this argument is
* ignored.
* @dst_addr: this is the physical address where DMA slave data
- * should be written (TX), if the source is memory this argument
+ * should be written (TX), if the destination is memory this argument
* is ignored.
* @src_addr_width: this is the width in bytes of the source (RX)
* register where DMA data shall be read. If the source
--
2.35.1

2022-03-28 22:27:53

by Serge Semin

[permalink] [raw]
Subject: [PATCH 13/25] dmaengine: dw-edma: Stop checking debugfs_create_*() return value

First of all they never return NULL. So checking their return value for
being not NULL just pointless. Secondly the DebugFS subsystem is designed
in a way to be used as simple as possible. So if one of the
debugfs_create_*() method in a hierarchy fails, the following methods will
just silently return the passed erroneous parental dentry. Finally the
code is supposed to be working no matter whether anything DebugFS-related
fails. So in order to make code simpler and DebugFS-independent let's drop
the debugfs_create_*() methods return value checking in the same way as
the most of the kernel drivers do.

Note in order to preserve some memory space we suggest to skip the DebugFS
nodes initialization if the file system in unavailable.

Signed-off-by: Serge Semin <[email protected]>
---
drivers/dma/dw-edma/dw-edma-v0-debugfs.c | 20 +++++---------------
1 file changed, 5 insertions(+), 15 deletions(-)

diff --git a/drivers/dma/dw-edma/dw-edma-v0-debugfs.c b/drivers/dma/dw-edma/dw-edma-v0-debugfs.c
index 12845a2dc016..808eed212be8 100644
--- a/drivers/dma/dw-edma/dw-edma-v0-debugfs.c
+++ b/drivers/dma/dw-edma/dw-edma-v0-debugfs.c
@@ -100,9 +100,8 @@ static void dw_edma_debugfs_create_x32(const struct debugfs_entries entries[],
int i;

for (i = 0; i < nr_entries; i++) {
- if (!debugfs_create_file_unsafe(entries[i].name, 0444, dir,
- entries[i].reg, &fops_x32))
- break;
+ debugfs_create_file_unsafe(entries[i].name, 0444, dir,
+ entries[i].reg, &fops_x32);
}
}

@@ -168,8 +167,6 @@ static void dw_edma_debugfs_regs_wr(struct dentry *dir)
char name[16];

regs_dir = debugfs_create_dir(WRITE_STR, dir);
- if (!regs_dir)
- return;

nr_entries = ARRAY_SIZE(debugfs_regs);
dw_edma_debugfs_create_x32(debugfs_regs, nr_entries, regs_dir);
@@ -184,8 +181,6 @@ static void dw_edma_debugfs_regs_wr(struct dentry *dir)
snprintf(name, sizeof(name), "%s:%d", CHANNEL_STR, i);

ch_dir = debugfs_create_dir(name, regs_dir);
- if (!ch_dir)
- return;

dw_edma_debugfs_regs_ch(&regs->type.unroll.ch[i].wr, ch_dir);

@@ -237,8 +232,6 @@ static void dw_edma_debugfs_regs_rd(struct dentry *dir)
char name[16];

regs_dir = debugfs_create_dir(READ_STR, dir);
- if (!regs_dir)
- return;

nr_entries = ARRAY_SIZE(debugfs_regs);
dw_edma_debugfs_create_x32(debugfs_regs, nr_entries, regs_dir);
@@ -253,8 +246,6 @@ static void dw_edma_debugfs_regs_rd(struct dentry *dir)
snprintf(name, sizeof(name), "%s:%d", CHANNEL_STR, i);

ch_dir = debugfs_create_dir(name, regs_dir);
- if (!ch_dir)
- return;

dw_edma_debugfs_regs_ch(&regs->type.unroll.ch[i].rd, ch_dir);

@@ -273,8 +264,6 @@ static void dw_edma_debugfs_regs(void)
int nr_entries;

regs_dir = debugfs_create_dir(REGISTERS_STR, dw->debugfs);
- if (!regs_dir)
- return;

nr_entries = ARRAY_SIZE(debugfs_regs);
dw_edma_debugfs_create_x32(debugfs_regs, nr_entries, regs_dir);
@@ -285,6 +274,9 @@ static void dw_edma_debugfs_regs(void)

void dw_edma_v0_debugfs_on(struct dw_edma_chip *chip)
{
+ if (!debugfs_initialized())
+ return;
+
dw = chip->dw;
if (!dw)
return;
@@ -294,8 +286,6 @@ void dw_edma_v0_debugfs_on(struct dw_edma_chip *chip)
return;

dw->debugfs = debugfs_create_dir(dw->name, NULL);
- if (!dw->debugfs)
- return;

debugfs_create_u32("mf", 0444, dw->debugfs, &dw->chip->mf);
debugfs_create_u16("wr_ch_cnt", 0444, dw->debugfs, &dw->wr_ch_cnt);
--
2.35.1

2022-03-28 22:29:59

by Serge Semin

[permalink] [raw]
Subject: [PATCH 23/25] dmaengine: dw-edma: Bypass dma-ranges mapping for the local setup

DW eDMA doesn't perform any translation of the traffic generated on the
CPU/Application side. It just generates read/write AXI-bus requests with
the specified addresses. But in case if the dma-ranges DT-property is
specified for a platform device node, Linux will use it to map the CPU
memory regions into the DMAable bus ranges. This isn't what we want for
the eDMA embedded into the locally accessed DW PCIe Root Port and
End-point. In order to work that around let's set the chan_dma_dev flag
for each DW eDMA channel thus forcing the client drivers to getting a
custom dma-ranges-less parental device for the mappings.

Note it will only work for the client drivers using the
dmaengine_get_dma_device() method to get the parental DMA device.

Signed-off-by: Serge Semin <[email protected]>
---
drivers/dma/dw-edma/dw-edma-core.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)

diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
index 72a51970bfba..ca5cd7c99571 100644
--- a/drivers/dma/dw-edma/dw-edma-core.c
+++ b/drivers/dma/dw-edma/dw-edma-core.c
@@ -716,6 +716,21 @@ static int dw_edma_alloc_chan_resources(struct dma_chan *dchan)
if (chan->status != EDMA_ST_IDLE)
return -EBUSY;

+ /* Bypass the dma-ranges based memory regions mapping since the
+ * inbound iATU only affects the traffic incoming from the
+ * PCIe bus.
+ */
+ if (chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) {
+ dchan->dev->chan_dma_dev = true;
+
+ dchan->dev->device.dma_coherent = chan->dw->chip->dev->dma_coherent;
+ dma_coerce_mask_and_coherent(&dchan->dev->device,
+ dma_get_mask(chan->dw->chip->dev));
+ dchan->dev->device.dma_parms = chan->dw->chip->dev->dma_parms;
+ } else {
+ dchan->dev->chan_dma_dev = false;
+ }
+
pm_runtime_get(chan->dw->chip->dev);

return 0;
--
2.35.1

2022-03-28 22:33:06

by Serge Semin

[permalink] [raw]
Subject: [PATCH 25/25] PCI: dwc: Add DW eDMA engine support

Since the DW eDMA driver now supports eDMA controllers embedded into the
locally accessible DW PCIe Root Ports and End-points, we can use the
updated interface to register DW eDMA as DMA engine device if it's
available. In order to successfully do that the DW PCIe core driver need
to perform some preparations first. First of all it needs to find out the
eDMA controller CSRs base address, whether they are accessible over the
Port Logic or iATU unrolled space. Afterwards it can try to auto-detect
the eDMA controller availability and number of it's read/write channels.
If none was found the procedure will just silently halt with no error
returned. Secondly the platform is supposed to provide either combined or
per-channel IRQ signals. If no valid IRQs set is found the procedure will
also halt with no error returned so to be backward compatible with
platforms where DW PCIe controllers have eDMA embedded but lack of the
IRQs defined for them. Finally before actually probing the eDMA device we
need to allocate LLP items buffers. After that the DW eDMA can be
registered. If registration is successful the info-message regarding the
number of detected Read/Write eDMA channels will be printed to the system
log in the same way as it's done for iATU settings.

Signed-off-by: Serge Semin <[email protected]>
---
.../pci/controller/dwc/pcie-designware-ep.c | 4 +
.../pci/controller/dwc/pcie-designware-host.c | 13 +-
drivers/pci/controller/dwc/pcie-designware.c | 188 ++++++++++++++++++
drivers/pci/controller/dwc/pcie-designware.h | 23 ++-
4 files changed, 225 insertions(+), 3 deletions(-)

diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
index 23401f17e8f0..b2840d1a5b9a 100644
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -712,6 +712,10 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)

dw_pcie_iatu_detect(pci);

+ ret = dw_pcie_edma_detect(pci);
+ if (ret)
+ return ret;
+
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "addr_space");
if (!res)
return -EINVAL;
diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
index 715a13b90e43..048b452ee4f3 100644
--- a/drivers/pci/controller/dwc/pcie-designware-host.c
+++ b/drivers/pci/controller/dwc/pcie-designware-host.c
@@ -405,14 +405,18 @@ int dw_pcie_host_init(struct pcie_port *pp)

dw_pcie_iatu_detect(pci);

- ret = dw_pcie_setup_rc(pp);
+ ret = dw_pcie_edma_detect(pci);
if (ret)
goto err_free_msi;

+ ret = dw_pcie_setup_rc(pp);
+ if (ret)
+ goto err_edma_remove;
+
if (!dw_pcie_link_up(pci) && pci->ops && pci->ops->start_link) {
ret = pci->ops->start_link(pci);
if (ret)
- goto err_free_msi;
+ goto err_edma_remove;
}

/* Ignore errors, the link may come up later */
@@ -430,6 +434,9 @@ int dw_pcie_host_init(struct pcie_port *pp)
if (pci->ops && pci->ops->stop_link)
pci->ops->stop_link(pci);

+err_edma_remove:
+ dw_pcie_edma_remove(pci);
+
err_free_msi:
if (pp->has_msi_ctrl)
dw_pcie_free_msi(pp);
@@ -452,6 +459,8 @@ void dw_pcie_host_deinit(struct pcie_port *pp)
if (pci->ops && pci->ops->stop_link)
pci->ops->stop_link(pci);

+ dw_pcie_edma_remove(pci);
+
if (pp->has_msi_ctrl)
dw_pcie_free_msi(pp);

diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
index 4a95a7b112e9..dbe39a7ecb71 100644
--- a/drivers/pci/controller/dwc/pcie-designware.c
+++ b/drivers/pci/controller/dwc/pcie-designware.c
@@ -11,6 +11,7 @@
#include <linux/align.h>
#include <linux/bitops.h>
#include <linux/delay.h>
+#include <linux/dma/edma.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/sizes.h>
@@ -680,6 +681,193 @@ void dw_pcie_iatu_detect(struct dw_pcie *pci)
pci->region_align / SZ_1K, (pci->region_limit + 1) / SZ_1G);
}

+static u32 dw_pcie_readl_dma(struct dw_pcie *pci, u32 reg)
+{
+ u32 val = 0;
+ int ret;
+
+ if (pci->ops && pci->ops->read_dbi)
+ return pci->ops->read_dbi(pci, pci->edma.reg_base, reg, 4);
+
+ ret = dw_pcie_read(pci->edma.reg_base + reg, 4, &val);
+ if (ret)
+ dev_err(pci->dev, "Read DMA address failed\n");
+
+ return val;
+}
+
+static bool dw_pcie_edma_unroll_enabled(struct dw_pcie *pci)
+{
+ u32 val;
+
+ val = dw_pcie_readl_dbi(pci, PCIE_DMA_VIEWPORT_BASE + PCIE_DMA_CTRL);
+ if (val == 0xffffffff)
+ return true;
+
+ return false;
+}
+
+static int dw_pcie_edma_irq_vector(struct device *dev, unsigned int nr)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ char name[6];
+ int ret;
+
+ if (nr >= EDMA_MAX_WR_CH + EDMA_MAX_RD_CH)
+ return -EINVAL;
+
+ ret = platform_get_irq_byname_optional(pdev, "dma");
+ if (ret > 0)
+ return ret;
+
+ snprintf(name, sizeof(name), "dma%u", nr);
+
+ return platform_get_irq_byname_optional(pdev, name);
+}
+
+static struct dw_edma_core_ops dw_pcie_edma_ops = {
+ .irq_vector = dw_pcie_edma_irq_vector,
+};
+
+static int dw_pcie_edma_detect_channels(struct dw_pcie *pci)
+{
+ u32 val;
+
+ val = dw_pcie_readl_dma(pci, PCIE_DMA_CTRL);
+ if (!val || val == 0xffffffff)
+ return 0;
+
+ pci->edma.ll_wr_cnt = FIELD_GET(PCIE_DMA_NUM_WR_CHAN, val);
+ pci->edma.ll_rd_cnt = FIELD_GET(PCIE_DMA_NUM_RD_CHAN, val);
+
+ if (pci->edma.ll_wr_cnt > EDMA_MAX_WR_CH ||
+ pci->edma.ll_rd_cnt > EDMA_MAX_RD_CH)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int dw_pcie_edma_irq_verify(struct dw_pcie *pci)
+{
+ struct platform_device *pdev = to_platform_device(pci->dev);
+ u16 ch_cnt = pci->edma.ll_wr_cnt + pci->edma.ll_rd_cnt;
+ char name[6];
+ int ret;
+
+ if (pci->edma.nr_irqs == 1)
+ return 0;
+ else if (pci->edma.nr_irqs > 1)
+ return pci->edma.nr_irqs != ch_cnt ? -EINVAL : 0;
+
+ ret = platform_get_irq_byname_optional(pdev, "dma");
+ if (ret > 0) {
+ pci->edma.nr_irqs = 1;
+ return 0;
+ }
+
+ for (; pci->edma.nr_irqs < ch_cnt; pci->edma.nr_irqs++) {
+ snprintf(name, sizeof(name), "dma%d", pci->edma.nr_irqs);
+
+ ret = platform_get_irq_byname_optional(pdev, name);
+ if (ret <= 0)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int dw_pcie_edma_ll_alloc(struct dw_pcie *pci)
+{
+ struct dw_edma_region *ll;
+ dma_addr_t paddr;
+ int i;
+
+ for (i = 0; i < pci->edma.ll_wr_cnt; i++) {
+ ll = &pci->edma.ll_region_wr[i];
+ ll->sz = DMA_LLP_MEM_SIZE;
+ ll->vaddr = dmam_alloc_coherent(pci->dev, ll->sz,
+ &paddr, GFP_KERNEL);
+ if (!ll->vaddr)
+ return -ENOMEM;
+
+ ll->paddr = paddr;
+ }
+
+ for (i = 0; i < pci->edma.ll_rd_cnt; i++) {
+ ll = &pci->edma.ll_region_rd[i];
+ ll->sz = DMA_LLP_MEM_SIZE;
+ ll->vaddr = dmam_alloc_coherent(pci->dev, ll->sz,
+ &paddr, GFP_KERNEL);
+ if (!ll->vaddr)
+ return -ENOMEM;
+
+ ll->paddr = paddr;
+ }
+
+ return 0;
+}
+
+int dw_pcie_edma_detect(struct dw_pcie *pci)
+{
+ int ret;
+
+ pci->edma.dev = pci->dev;
+ if (!pci->edma.ops)
+ pci->edma.ops = &dw_pcie_edma_ops;
+ pci->edma.flags |= DW_EDMA_CHIP_LOCAL;
+
+ pci->edma_unroll_enabled = dw_pcie_edma_unroll_enabled(pci);
+ if (pci->edma_unroll_enabled && pci->iatu_unroll_enabled) {
+ pci->edma.mf = EDMA_MF_EDMA_UNROLL;
+ if (pci->atu_base != pci->dbi_base + DEFAULT_DBI_ATU_OFFSET)
+ pci->edma.reg_base = pci->atu_base + PCIE_DMA_UNROLL_BASE;
+ else
+ pci->edma.reg_base = pci->dbi_base + DEFAULT_DBI_DMA_OFFSET;
+ } else {
+ pci->edma.mf = EDMA_MF_EDMA_LEGACY;
+ pci->edma.reg_base = pci->dbi_base + PCIE_DMA_VIEWPORT_BASE;
+ }
+
+ ret = dw_pcie_edma_detect_channels(pci);
+ if (ret) {
+ dev_err(pci->dev, "Unexpected NoF eDMA channels found\n");
+ return ret;
+ }
+
+ /* Skip any further initialization if no eDMA found */
+ if (!pci->edma.ll_wr_cnt && !pci->edma.ll_rd_cnt)
+ return 0;
+
+ /* Don't return failure here for the backward compatibility */
+ ret = dw_pcie_edma_irq_verify(pci);
+ if (ret) {
+ dev_err(pci->dev, "No valid eDMA IRQs set found\n");
+ return 0;
+ }
+
+ ret = dw_pcie_edma_ll_alloc(pci);
+ if (ret) {
+ dev_err(pci->dev, "Couldn't allocate LLP memory\n");
+ return ret;
+ }
+
+ ret = dw_edma_probe(&pci->edma);
+ if (ret) {
+ dev_err(pci->dev, "Couldn't register eDMA device\n");
+ return ret;
+ }
+
+ dev_info(pci->dev, "eDMA channels: %hu wr, %hu rd\n",
+ pci->edma.ll_wr_cnt, pci->edma.ll_rd_cnt);
+
+ return 0;
+}
+
+void dw_pcie_edma_remove(struct dw_pcie *pci)
+{
+ dw_edma_remove(&pci->edma);
+}
+
void dw_pcie_setup(struct dw_pcie *pci)
{
u32 val;
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index 11c52d2eaf79..9b92f79664f2 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -13,6 +13,7 @@

#include <linux/bitfield.h>
#include <linux/dma-mapping.h>
+#include <linux/dma/edma.h>
#include <linux/irq.h>
#include <linux/msi.h>
#include <linux/pci.h>
@@ -144,6 +145,18 @@
#define PCIE_MSIX_DOORBELL 0x948
#define PCIE_MSIX_DOORBELL_PF_SHIFT 24

+/*
+ * eDMA CSRs. DW PCIe IP-core v4.70a and older had the eDMA registers accessible
+ * over the Port Logic registers space. Afterwords the unrolled mapping was
+ * introduced so eDMA and iATU could be accessed via a dedicated registers
+ * space.
+ */
+#define PCIE_DMA_VIEWPORT_BASE 0x970
+#define PCIE_DMA_UNROLL_BASE 0x80000
+#define PCIE_DMA_CTRL 0x008
+#define PCIE_DMA_NUM_WR_CHAN GENMASK(3, 0)
+#define PCIE_DMA_NUM_RD_CHAN GENMASK(19, 16)
+
#define PCIE_PL_CHK_REG_CONTROL_STATUS 0xB20
#define PCIE_PL_CHK_REG_CHK_REG_START BIT(0)
#define PCIE_PL_CHK_REG_CHK_REG_CONTINUOUS BIT(1)
@@ -160,6 +173,7 @@
* this offset, if atu_base not set.
*/
#define DEFAULT_DBI_ATU_OFFSET (0x3 << 20)
+#define DEFAULT_DBI_DMA_OFFSET (0x7 << 19)

#define MAX_MSI_IRQS 256
#define MAX_MSI_IRQS_PER_CTRL 32
@@ -171,6 +185,9 @@
#define MAX_IATU_IN 256
#define MAX_IATU_OUT 256

+/* Default eDMA LLP memory size */
+#define DMA_LLP_MEM_SIZE PAGE_SIZE
+
struct pcie_port;
struct dw_pcie;
struct dw_pcie_ep;
@@ -295,7 +312,7 @@ struct dw_pcie {
struct device *dev;
void __iomem *dbi_base;
void __iomem *dbi_base2;
- /* Used when iatu_unroll_enabled is true */
+ /* Used when {iatu,edma}_unroll_enabled is true */
void __iomem *atu_base;
size_t atu_size;
u32 num_ib_windows;
@@ -310,7 +327,9 @@ struct dw_pcie {
int num_lanes;
int link_gen;
u8 n_fts[2];
+ struct dw_edma_chip edma;
bool iatu_unroll_enabled: 1;
+ bool edma_unroll_enabled: 1;
bool io_cfg_atu_shared: 1;
};

@@ -344,6 +363,8 @@ int dw_pcie_prog_ep_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
void dw_pcie_disable_atu(struct dw_pcie *pci, u32 dir, int index);
void dw_pcie_setup(struct dw_pcie *pci);
void dw_pcie_iatu_detect(struct dw_pcie *pci);
+int dw_pcie_edma_detect(struct dw_pcie *pci);
+void dw_pcie_edma_remove(struct dw_pcie *pci);

static inline void dw_pcie_writel_dbi(struct dw_pcie *pci, u32 reg, u32 val)
{
--
2.35.1

2022-03-28 22:42:12

by Serge Semin

[permalink] [raw]
Subject: [PATCH 02/25] dmaengine: dw-edma: Fix eDMA Rd/Wr-channels and DMA-direction semantics

In accordance with [1, 2] the DW eDMA controller has been created to be
part of the DW PCIe Root Port and DW PCIe End-point controllers and to
offload the transferring of large blocks of data between application and
remote PCIe domains leaving the system CPU free for other tasks. In the
first case (eDMA being part of DW PCIe Root Port) the eDMA controller is
always accessible via the CPU DBI interface and never over the PCIe wire.
The later case is more complex. Depending on the DW PCIe End-Point IP-core
synthesize parameters it's possible to have the eDMA registers accessible
not only from the application CPU side, but also via mapping the eDMA CSRs
over a dedicated end-point BAR. So based on the specifics denoted above
the eDMA driver is supposed to support two types of the DMA controller
setups:
1) eDMA embedded into the DW PCIe Root Port/End-point and accessible over
the local CPU from the application side.
2) eDMA embedded into the DW PCIe End-point and accessible via the PCIe
wire with MWr/MRd TLPs generated by the CPU PCIe host controller.
Since the CPU memory resides different sides in these cases the semantics
of the MEM_TO_DEV and DEV_TO_MEM operations is flipped with respect to the
Tx and Rx DMA channels. So MEM_TO_DEV/DEV_TO_MEM corresponds to the Tx/Rx
channels in setup 1) and to the Rx/Tx channels in case of setup 2).

The DW eDMA driver has supported the case 2) since the initial
commit e63d79d1ffcd ("dmaengine: Add Synopsys eDMA IP core driver") in the
framework of the drivers/dma/dw-edma/dw-edma-pcie.c driver. The case 1)
support was added a bit later in commit bd96f1b2f43a ("dmaengine: dw-edma:
support local dma device transfer semantics"). Afterwards the driver was
supposed to cover the both possible eDMA setups, but the later commit
turned to be not fully correct. The problem was that the commit together
with the new functionality support also changed the channel direction
semantics in a way so the eDMA Read-channel (corresponding to the
DMA_DEV_TO_MEM direction for the case 1.) now uses the sgl/cyclic base
addresses as the Source addresses of the DMA-transfers and
dma_slave_config.dst_addr as the Destination address of the DMA-transfers.
Similarly the eDMA Write-channel (corresponding to the DMA_MEM_TO_DEV
direction for case 1.) now utilizes dma_slave_config.src_addr as a source
address of the DMA-transfers and sgl/cyclic base address as the
Destination address of the DMA-transfers. This contradicts to the logic of
the DMA-interface, which implies that DEV side is supposed to belong to
the PCIe device memory and MEM - to the CPU/Application memory. Indeed it
seems irrational to have the SG-list defined in the PCIe bus space, while
expecting a contiguous buffer allocated in the CPU memory. Moreover the
passed SG-list and cyclic DMA buffers are supposed to be mapped in a way
so to be seen by the DW eDMA Application (CPU) interface. So in order to
have the correct DW eDMA interface we need to invert the eDMA
Rd/Wr-channels and DMA-slave directions semantics by selecting the src/dst
addresses based on the DMA transfer direction instead of using the channel
direction capability.

[1] DesignWare Cores PCI Express Controller Databook - DWC PCIe Root Port,
v.5.40a, March 2019, p.1092
[2] DesignWare Cores PCI Express Controller Databook - DWC PCIe Endpoint,
v.5.40a, March 2019, p.1189

Fixes: bd96f1b2f43a ("dmaengine: dw-edma: support local dma device transfer semantics")
Co-developed-by: Manivannan Sadhasivam <[email protected]>
Signed-off-by: Manivannan Sadhasivam <[email protected]>
Signed-off-by: Serge Semin <[email protected]>

---

In accordance with agreement with Frank and Manivannan this patch is
supposed to be moved to the series:
Link: https://lore.kernel.org/dmaengine/[email protected]/
in place of the patch:
[PATCH v5 5/9] dmaengine: dw-edma: Fix programming the source & dest addresses for ep
Link: https://lore.kernel.org/dmaengine/[email protected]/
---
drivers/dma/dw-edma/dw-edma-core.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
index e9e32ed74aa9..519d4b3c9fa0 100644
--- a/drivers/dma/dw-edma/dw-edma-core.c
+++ b/drivers/dma/dw-edma/dw-edma-core.c
@@ -442,7 +442,7 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer)
chunk->ll_region.sz += burst->sz;
desc->alloc_sz += burst->sz;

- if (chan->dir == EDMA_DIR_WRITE) {
+ if (dir == DMA_DEV_TO_MEM) {
burst->sar = src_addr;
if (xfer->type == EDMA_XFER_CYCLIC) {
burst->dar = xfer->xfer.cyclic.paddr;
--
2.35.1

2022-03-28 22:48:35

by Serge Semin

[permalink] [raw]
Subject: [PATCH 15/25] dmaengine: dw-edma: Convert DebugFS descs to being kz-allocated

Currently all the DW eDMA DebugFS nodes descriptors are allocated on
stack, while the DW eDMA driver private data and CSR limits are statically
preserved. Such design won't work for the multi-eDMA platforms. As a
preparation to adding the multi-eDMA system setups support we need to have
each DebugFS node separately allocated and described. Afterwards we'll put
an addition info there like Read/Write channel flag, channel ID, DW eDMA
private data reference.

Note this conversion is mainly required due to having the legacy DW eDMA
controllers with indirect Read/Write channels context CSRs access. If we
didn't need to have a synchronized access to these registers the DebugFS
code of the driver would have been much simpler.

Signed-off-by: Serge Semin <[email protected]>
---
drivers/dma/dw-edma/dw-edma-v0-debugfs.c | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/drivers/dma/dw-edma/dw-edma-v0-debugfs.c b/drivers/dma/dw-edma/dw-edma-v0-debugfs.c
index afd519d9568b..7eb0147912fa 100644
--- a/drivers/dma/dw-edma/dw-edma-v0-debugfs.c
+++ b/drivers/dma/dw-edma/dw-edma-v0-debugfs.c
@@ -53,7 +53,8 @@ struct dw_edma_debugfs_entry {

static int dw_edma_debugfs_u32_get(void *data, u64 *val)
{
- void __iomem *reg = data;
+ struct dw_edma_debugfs_entry __iomem *entry = data;
+ void __iomem *reg = entry->reg;

if (dw->chip->mf == EDMA_MF_EDMA_LEGACY &&
reg >= (void __iomem *)&regs->type.legacy.ch) {
@@ -94,14 +95,22 @@ static int dw_edma_debugfs_u32_get(void *data, u64 *val)
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_x32, dw_edma_debugfs_u32_get, NULL, "0x%08llx\n");

-static void dw_edma_debugfs_create_x32(const struct dw_edma_debugfs_entry entries[],
+static void dw_edma_debugfs_create_x32(const struct dw_edma_debugfs_entry ini[],
int nr_entries, struct dentry *dir)
{
+ struct dw_edma_debugfs_entry *entries;
int i;

+ entries = devm_kcalloc(dw->chip->dev, nr_entries, sizeof(*entries),
+ GFP_KERNEL);
+ if (!entries)
+ return;
+
for (i = 0; i < nr_entries; i++) {
+ entries[i] = ini[i];
+
debugfs_create_file_unsafe(entries[i].name, 0444, dir,
- entries[i].reg, &fops_x32);
+ &entries[i], &fops_x32);
}
}

--
2.35.1

2022-03-28 22:49:10

by Serge Semin

[permalink] [raw]
Subject: [PATCH 09/25] dmaengine: dw-edma: Add CPU to PCIe bus address translation

Starting from commit 9575632052ba ("dmaengine: make slave address
physical") the source and destination addresses of the DMA-slave device
have been converted to being defined in CPU address space. It's DMA-device
driver responsibility to properly convert them to the reachable DMA bus
spaces. In case of the DW eDMA device, the source or destination
peripheral (slave) devices reside PCIe bus space. Thus we need to perform
the PCIe Host/EP windows-based (i.e. ranges DT-property) addresses
translation otherwise the eDMA transactions won't work as expected (or can
be even harmful) in case if the CPU and PCIe address spaces don't match.

Note 1. Even though the DMA interleaved template has both source and
destination addresses declared of dma_addr_t type only CPU memory range is
supposed to be mapped in a way so to be seen by the DMA device since it's
a subject of the DMA getting towards the system side. The device part must
not be mapped since slave device resides in the PCIe bus space, which
isn't affected by IOMMUs or iATU translations. DW PCIe eDMA generates
corresponding MWr/MRd TLPs on its own.

Note 2. This functionality is mainly required for the remote eDMA setup
since the CPU address must be manually translated into the PCIe bus space
before being written to LLI.{SAR,DAR}. If eDMA is embedded into the
locally accessible DW PCIe RP/EP software-based translation isn't required
since it will be done by hardware by means of the Outbound iATU as long as
the DMA_BYPASS flag is cleared. If the later flag is set or there is no
Outbound iATU entry found to which the SAR or DAR falls in (for Read and
Write channel respectfully), there won't be any translation performed but
DMA will proceed with the corresponding source/destination address as is.

Signed-off-by: Serge Semin <[email protected]>
---
drivers/dma/dw-edma/dw-edma-core.c | 18 +++++++++++++++++-
include/linux/dma/edma.h | 15 +++++++++++++++
2 files changed, 32 insertions(+), 1 deletion(-)

diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
index 97743fe44ebf..418b201fef67 100644
--- a/drivers/dma/dw-edma/dw-edma-core.c
+++ b/drivers/dma/dw-edma/dw-edma-core.c
@@ -40,6 +40,17 @@ struct dw_edma_desc *vd2dw_edma_desc(struct virt_dma_desc *vd)
return container_of(vd, struct dw_edma_desc, vd);
}

+static inline
+u64 dw_edma_get_pci_address(struct dw_edma_chan *chan, phys_addr_t cpu_addr)
+{
+ struct dw_edma_chip *chip = chan->dw->chip;
+
+ if (chip->ops->pci_address)
+ return chip->ops->pci_address(chip->dev, cpu_addr);
+
+ return cpu_addr;
+}
+
static struct dw_edma_burst *dw_edma_alloc_burst(struct dw_edma_chunk *chunk)
{
struct dw_edma_burst *burst;
@@ -328,11 +339,11 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer)
{
struct dw_edma_chan *chan = dchan2dw_edma_chan(xfer->dchan);
enum dma_transfer_direction dir = xfer->direction;
- phys_addr_t src_addr, dst_addr;
struct scatterlist *sg = NULL;
struct dw_edma_chunk *chunk;
struct dw_edma_burst *burst;
struct dw_edma_desc *desc;
+ u64 src_addr, dst_addr;
size_t fsz = 0;
u32 cnt = 0;
int i;
@@ -407,6 +418,11 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer)
dst_addr = chan->config.dst_addr;
}

+ if (dir == DMA_DEV_TO_MEM)
+ src_addr = dw_edma_get_pci_address(chan, (phys_addr_t)src_addr);
+ else
+ dst_addr = dw_edma_get_pci_address(chan, (phys_addr_t)dst_addr);
+
if (xfer->type == EDMA_XFER_CYCLIC) {
cnt = xfer->xfer.cyclic.cnt;
} else if (xfer->type == EDMA_XFER_SCATTER_GATHER) {
diff --git a/include/linux/dma/edma.h b/include/linux/dma/edma.h
index 5abac9640a4e..5cc87cfdd685 100644
--- a/include/linux/dma/edma.h
+++ b/include/linux/dma/edma.h
@@ -23,8 +23,23 @@ struct dw_edma_region {
size_t sz;
};

+/**
+ * struct dw_edma_core_ops - platform-specific eDMA methods
+ * @irq_vector: Get IRQ number of the passed eDMA channel. Note the
+ * method accepts the channel id in the end-to-end
+ * numbering with the eDMA write channels being placed
+ * first in the row.
+ * @pci_address: Get PCIe bus address corresponding to the passed CPU
+ * address. Note there is no need in specifying this
+ * function if the address translation is performed by
+ * the DW PCIe RP/EP controller with the DW eDMA device in
+ * subject and DMA_BYPASS isn't set for all the outbound
+ * iATU windows. That will be done by the controller
+ * automatically.
+ */
struct dw_edma_core_ops {
int (*irq_vector)(struct device *dev, unsigned int nr);
+ u64 (*pci_address)(struct device *dev, phys_addr_t cpu_addr);
};

enum dw_edma_map_format {
--
2.35.1

2022-05-03 00:55:19

by Manivannan Sadhasivam

[permalink] [raw]
Subject: Re: [PATCH 25/25] PCI: dwc: Add DW eDMA engine support

On Fri, Apr 29, 2022 at 07:13:48PM +0300, Serge Semin wrote:
> On Thu, Apr 28, 2022 at 10:39:29PM +0530, Manivannan Sadhasivam wrote:
> > On Thu, Apr 28, 2022 at 05:05:23PM +0300, Serge Semin wrote:
> >
> > [...]
> >
> > > > If iATU has unroll enabled then I think we can assume that edma will also be
> > > > the same. So I was wondering if we could just depend on iatu_unroll_enabled
> > > > here.
> > >
> > > I thought about that, but then I decided it was easier to just define
> > > a new flag. Anyway according to the hw manuals indeed the unroll
> > > mapping is enabled either for both iATU and eDMA modules or for none
> > > of them just because they are mapped over a single space. It's
> > > determined by the internal VHL parameter CC_UNROLL_ENABLE.
> > > On the second thought I agree with you then. I'll convert the
> > > iatu_unroll_enabled flag into a more generic 'reg_unroll' and make
> > > sure it's used for both modules.
> > >
> >
> > Sounds good!
> >
> > > >
> > > > > >
> > > > > > > + if (pci->edma_unroll_enabled && pci->iatu_unroll_enabled) {
> > > > > > > + pci->edma.mf = EDMA_MF_EDMA_UNROLL;
> > > > > > > + if (pci->atu_base != pci->dbi_base + DEFAULT_DBI_ATU_OFFSET)
> > > > > > > + pci->edma.reg_base = pci->atu_base + PCIE_DMA_UNROLL_BASE;
> > > > > > > + else
> > > > > > > + pci->edma.reg_base = pci->dbi_base + DEFAULT_DBI_DMA_OFFSET;
> > > > > >
> > > > >
> > > > > > This assumption won't work on all platforms. Atleast on our platform, the
> > > > > > offsets vary. So I'd suggest to try getting the reg_base from DT first and use
> > > > > > these offsets as a fallback as we do for iATU.
> > > > >
> > > > > I don't know how the eDMA offset can vary at least concerning the
> > > > > normal DW PCIe setup. In any case the DW eDMA controller CSRs are
> > > > > mapped in the same way as the iATU space: CS2=1 CDM=1. They are either
> > > > > created as an unrolled region mapped into the particular MMIO space
> > > > > (as a separate MMIO space or as a part of the DBI space), or
> > > > > accessible over the PL viewports (as a part of the Port Logic CSRs).
> > > > > Nothing else is described in the hardware manuals. Based on that I
> > > > > don't see a reason to add one more reg space binding.
> > > > >
> > > >
> > >
> > > > This is not true. Vendors can customize the iATU location inside DBI region
> > > > for unroll too. That's one of the reason why dw_pcie_iatu_detect() works on
> > > > qcom platforms as it tries to get iatu address from DT first and then falls
> > > > back to the default offset if not found.
> > > >
> > > > So please define an additional DT region for edma.
> > >
> > > It's obvious that iATU location can vary. I never said it didn't. We
> > > are talking about eDMA here. In accordance with what the DW PCIe hw
> > > manuals say eDMA always resides the same space as the iATU. The space
> > > is enabled by setting the CS2=1 and CDM=1 wires in case of the Native
> > > Controller DBI access. In this case eDMA is defined with the 0x80000
> > > offset over the iATU base address while the iATU base can be placed at
> > > whatever region platform engineer needs.
> > >
> > > Alternatively the AXI Bridge-based DBI access can be enabled thus
> > > having the DBI+iATU+eDMA mapped over the same MMIO space with
> > > respective offsets 0x0;0x300000;0x380000. This case is handled in the
> > > branch of the conditional statement above if it's found that iATU base
> > > is having the default offset with respect to the DBI base address
> > > (pci->atu_base == pci->dbi_base + DEFAULT_DBI_ATU_OFFSET).
> > >
> > > To sum up seeing I couldn't find the eDMA region defined in the qcom
> > > bindings and judging by what you say doesn't really contradict to what
> > > is done in my code, I guess there must be some misunderstanding either in
> > > what you see in the code above or what I understand from what you say.
> > > So please be more specific what offsets and whether they are really
> > > different from what I use in the code above.
> > >
> >
> > You won't see any edma register offset because no one bothered to define it
> > since it was not used until now. But the memory region should've been
> > documented...
> >
>
> > Anyway, here is the offset for the Qcom SoC I'm currently working on:
> >
> > DBI - 0x0
> > iATU - 0x1000
> > eDMA - 0x2000
>
> Finally we've got to something. Earlier you said:
>
> > > > This is not true. Vendors can customize the iATU location inside DBI region
> > > > for unroll too.
>
> Actually it is if we are talking about the standard Syopsys DW PCIe
> IP-CoreConsultant methods, which don't imply any eDMA base address
> customization parameter. Thus there must be some address translation
> performed at some layer before the address reaches the DW PCIe DBI
> interface. So it's platform-specific. That happens in your case too.
>

Seems like it.

> > > > That's one of the reason why dw_pcie_iatu_detect() works on
> > > > qcom platforms as it tries to get iatu address from DT first and then falls
> > > > back to the default offset if not found.
> >
> > As you can see, these offsets doesn't really fit in both the cases you shared
> > above.
>
> Seeing the iATU address bits layout can be changed the next
> reg-space calculation code shall work for all the discussed cases:
>
> if (!unroll) {
> pci->edma.reg_base = pci->dbi_base + PCIE_DMA_VIEWPORT_BASE;
> } else if (!pci->edma.reg_base) {
> res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma");
> if (res) {
> pci->edma.reg_base = devm_ioremap_resource(dev, res);
> if (IS_ERR(pci->edma.reg_base))
> return PTR_ERR(pci->edma.reg_base);

This should work for me. Thanks for the work!

Regards,
Mani

> } else (pci->atu_size >= 2 * 0x80000) {
> pci->edma.reg_base = pci->atu_base + 0x80000;
> } else {
> /* No standard eDMA CSRs mapping found. Just skip */
> return 0;
> }
> } else {
> /* pci->edma.reg_base can be specified by the platform code. This shall
> * be useful for the tegra194 or intel gw SoCs. The former
> * platform has the "atu_dma" resource declared which implies
> * having the joint iATU+eDMA CSR space, while the later has
> * specific iATU offset with respect to the DBI base address
> * (address is two bits shorter).
> */
> }
>
> -Sergey
>
> >
> > I don't have the knowledge about the internal representation of the IP or what
> > customization Qcom did apart from some high level information.
> >
> > Hope this clarifies!
> >
> > Thanks,
> > Mani