[PATCH v3 07/13] dmaengine: dw-edma-pcie: Add capability match data
From: Koichiro Den
Date: Sat Jun 20 2026 - 13:02:06 EST
Move device-specific capability parsing behind per-device match data.
The existing probe path mixes two decisions: which static template a PCI
ID uses, and which device-specific capability parser adjusts that
template. Split those decisions so device-specific discovery can be
added through match data instead of adding more vendor checks to
dw_edma_pcie_probe().
No functional change is intended for the existing Synopsys EDDA and
AMD (Xilinx) MDB/CPM6 matches. They still copy the same static template
data and run the same capability parsing logic before BAR mapping. The
AMD (Xilinx) MDB/CPM6 entries also keep using endpoint memory physical
addresses for descriptor windows through a new match-data flag.
Suggested-by: Frank Li <Frank.Li@xxxxxxx>
Reviewed-by: Frank Li <Frank.Li@xxxxxxx>
Signed-off-by: Koichiro Den <den@xxxxxxxxxxxxx>
---
Changes in v3:
- Adjust context for the AMD (Xilinx) CPM6 match added in the new
base; carry the same match-data conversion over that entry and
update the commit message accordingly.
- Reject dynamic PCI IDs without match data before dereferencing the
match data.
drivers/dma/dw-edma/dw-edma-pcie.c | 139 ++++++++++++++++++++---------
1 file changed, 96 insertions(+), 43 deletions(-)
diff --git a/drivers/dma/dw-edma/dw-edma-pcie.c b/drivers/dma/dw-edma/dw-edma-pcie.c
index 0ea8d59782b4..c08a77c0e508 100644
--- a/drivers/dma/dw-edma/dw-edma-pcie.c
+++ b/drivers/dma/dw-edma/dw-edma-pcie.c
@@ -76,6 +76,19 @@ struct dw_edma_pcie_data {
bool cfg_non_ll;
};
+struct dw_edma_pcie_match_data {
+ const struct dw_edma_pcie_data *data;
+ /*
+ * Mandatory callback. It may leave @pdata unchanged when the static
+ * template already describes the device.
+ */
+ int (*parse_caps)(struct pci_dev *pdev,
+ struct dw_edma_pcie_data *pdata);
+ unsigned long flags;
+};
+
+#define DW_EDMA_PCIE_F_DEVMEM_PHYS_OFF BIT(0)
+
static const struct dw_edma_pcie_data snps_edda_data = {
/* eDMA registers location */
.rg.bar = BAR_0,
@@ -310,24 +323,70 @@ static void dw_edma_pcie_get_xilinx_dma_data(struct pci_dev *pdev,
pdata->devmem_phys_off = off;
}
+static int
+dw_edma_pcie_parse_synopsys_caps(struct pci_dev *pdev,
+ struct dw_edma_pcie_data *pdata)
+{
+ dw_edma_pcie_get_synopsys_dma_data(pdev, pdata);
+
+ return 0;
+}
+
+static int
+dw_edma_pcie_parse_xilinx_caps(struct pci_dev *pdev,
+ struct dw_edma_pcie_data *pdata)
+{
+ dw_edma_pcie_get_xilinx_dma_data(pdev, pdata);
+
+ /*
+ * There is no valid address found for the LL memory space on the
+ * device side. In the absence of LL base address use the non-LL mode or
+ * simple mode supported by the HDMA IP.
+ */
+ if (pdata->devmem_phys_off == DW_PCIE_XILINX_MDB_INVALID_ADDR) {
+ pdata->cfg_non_ll = true;
+ return 0;
+ }
+
+ /*
+ * Configure the channel LL and data blocks if number of channels
+ * enabled in VSEC capability are more than the channels configured in
+ * xilinx_mdb_data.
+ */
+ dw_edma_set_chan_region_offset(pdata, BAR_2, 0,
+ DW_PCIE_XILINX_MDB_LL_OFF_GAP,
+ DW_PCIE_XILINX_MDB_LL_SIZE,
+ DW_PCIE_XILINX_MDB_DT_OFF_GAP,
+ DW_PCIE_XILINX_MDB_DT_SIZE);
+
+ return 0;
+}
+
static u64 dw_edma_get_phys_addr(struct pci_dev *pdev,
+ const struct dw_edma_pcie_match_data *match,
struct dw_edma_pcie_data *pdata,
enum pci_barno bar)
{
- if (pdev->vendor == PCI_VENDOR_ID_XILINX)
+ if (match->flags & DW_EDMA_PCIE_F_DEVMEM_PHYS_OFF)
return pdata->devmem_phys_off;
+
return pci_bus_address(pdev, bar);
}
static int dw_edma_pcie_probe(struct pci_dev *pdev,
const struct pci_device_id *pid)
{
- struct dw_edma_pcie_data *pdata = (void *)pid->driver_data;
+ const struct dw_edma_pcie_match_data *match = (void *)pid->driver_data;
+ const struct dw_edma_pcie_data *pdata;
struct device *dev = &pdev->dev;
struct dw_edma_chip *chip;
int err, nr_irqs;
int i, mask;
+ if (!match)
+ return -ENODEV;
+ pdata = match->data;
+
if (!pdata)
return -ENODEV;
@@ -345,36 +404,13 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
memcpy(vsec_data, pdata, sizeof(struct dw_edma_pcie_data));
- /*
- * Tries to find if exists a PCIe Vendor-Specific Extended Capability
- * for the DMA, if one exists, then reconfigures it.
- */
- dw_edma_pcie_get_synopsys_dma_data(pdev, vsec_data);
-
- if (pdev->vendor == PCI_VENDOR_ID_XILINX) {
- dw_edma_pcie_get_xilinx_dma_data(pdev, vsec_data);
-
- /*
- * There is no valid address found for the LL memory
- * space on the device side. In the absence of LL base
- * address use the non-LL mode or simple mode supported by
- * the HDMA IP.
- */
- if (vsec_data->devmem_phys_off == DW_PCIE_XILINX_MDB_INVALID_ADDR)
- vsec_data->cfg_non_ll = true;
-
- /*
- * Configure the channel LL and data blocks if number of
- * channels enabled in VSEC capability are more than the
- * channels configured in xilinx_mdb_data.
- */
- if (!vsec_data->cfg_non_ll)
- dw_edma_set_chan_region_offset(vsec_data, BAR_2, 0,
- DW_PCIE_XILINX_MDB_LL_OFF_GAP,
- DW_PCIE_XILINX_MDB_LL_SIZE,
- DW_PCIE_XILINX_MDB_DT_OFF_GAP,
- DW_PCIE_XILINX_MDB_DT_SIZE);
- }
+ /* Let device-specific discovery override the static template data. */
+ if (!match->parse_caps)
+ return -EINVAL;
+
+ err = match->parse_caps(pdev, vsec_data);
+ if (err)
+ return err;
/* Mapping PCI BAR regions */
mask = BIT(vsec_data->rg.bar);
@@ -442,8 +478,8 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
return -ENOMEM;
ll_region->vaddr.io += ll_block->off;
- ll_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
- ll_block->bar);
+ ll_region->paddr = dw_edma_get_phys_addr(pdev, match,
+ vsec_data, ll_block->bar);
ll_region->paddr += ll_block->off;
ll_region->sz = ll_block->sz;
@@ -452,8 +488,8 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
return -ENOMEM;
dt_region->vaddr.io += dt_block->off;
- dt_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
- dt_block->bar);
+ dt_region->paddr = dw_edma_get_phys_addr(pdev, match,
+ vsec_data, dt_block->bar);
dt_region->paddr += dt_block->off;
dt_region->sz = dt_block->sz;
}
@@ -469,8 +505,8 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
return -ENOMEM;
ll_region->vaddr.io += ll_block->off;
- ll_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
- ll_block->bar);
+ ll_region->paddr = dw_edma_get_phys_addr(pdev, match,
+ vsec_data, ll_block->bar);
ll_region->paddr += ll_block->off;
ll_region->sz = ll_block->sz;
@@ -479,8 +515,8 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
return -ENOMEM;
dt_region->vaddr.io += dt_block->off;
- dt_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
- dt_block->bar);
+ dt_region->paddr = dw_edma_get_phys_addr(pdev, match,
+ vsec_data, dt_block->bar);
dt_region->paddr += dt_block->off;
dt_region->sz = dt_block->sz;
}
@@ -561,12 +597,29 @@ static void dw_edma_pcie_remove(struct pci_dev *pdev)
pci_free_irq_vectors(pdev);
}
+static const struct dw_edma_pcie_match_data snps_edda_match_data = {
+ .data = &snps_edda_data,
+ .parse_caps = dw_edma_pcie_parse_synopsys_caps,
+};
+
+static const struct dw_edma_pcie_match_data xilinx_mdb_match_data = {
+ .data = &xilinx_mdb_data,
+ .parse_caps = dw_edma_pcie_parse_xilinx_caps,
+ .flags = DW_EDMA_PCIE_F_DEVMEM_PHYS_OFF,
+};
+
+static const struct dw_edma_pcie_match_data xilinx_cpm6_dma_match_data = {
+ .data = &xilinx_cpm6_dma_data,
+ .parse_caps = dw_edma_pcie_parse_xilinx_caps,
+ .flags = DW_EDMA_PCIE_F_DEVMEM_PHYS_OFF,
+};
+
static const struct pci_device_id dw_edma_pcie_id_table[] = {
- { PCI_DEVICE_DATA(SYNOPSYS, EDDA, &snps_edda_data) },
+ { PCI_DEVICE_DATA(SYNOPSYS, EDDA, &snps_edda_match_data) },
{ PCI_VDEVICE(XILINX, PCI_DEVICE_ID_XILINX_B054),
- (kernel_ulong_t)&xilinx_mdb_data },
+ .driver_data = (kernel_ulong_t)&xilinx_mdb_match_data },
{ PCI_VDEVICE(XILINX, PCI_DEVICE_ID_XILINX_B00F),
- .driver_data = (kernel_ulong_t)&xilinx_cpm6_dma_data },
+ .driver_data = (kernel_ulong_t)&xilinx_cpm6_dma_match_data },
{ }
};
MODULE_DEVICE_TABLE(pci, dw_edma_pcie_id_table);
--
2.51.0