[PATCH v2 05/12] dmaengine: dw-edma-pcie: Add capability match data
From: Koichiro Den
Date: Mon May 25 2026 - 02:25:17 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 matches. They still copy the same static template data and
run the same capability parsing logic before BAR mapping. The MDB entry
also keeps using endpoint memory physical addresses for descriptor
windows through a new match-data flag.
Suggested-by: Frank Li <Frank.Li@xxxxxxx>
Signed-off-by: Koichiro Den <den@xxxxxxxxxxxxx>
---
Changes in v2:
- Keep non-LL mode in dw_edma_pcie_data instead of a separate
parse_caps() output parameter.
- While at here, use a named .driver_data initializer for the Xilinx MDB ID
entry, per Frank's suggestion.
drivers/dma/dw-edma/dw-edma-pcie.c | 127 +++++++++++++++++++----------
1 file changed, 85 insertions(+), 42 deletions(-)
diff --git a/drivers/dma/dw-edma/dw-edma-pcie.c b/drivers/dma/dw-edma/dw-edma-pcie.c
index e92ff5dc6f67..5a6f5af358d0 100644
--- a/drivers/dma/dw-edma/dw-edma-pcie.c
+++ b/drivers/dma/dw-edma/dw-edma-pcie.c
@@ -75,6 +75,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,
@@ -296,19 +309,61 @@ 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 = match->data;
struct device *dev = &pdev->dev;
struct dw_edma_chip *chip;
int err, nr_irqs;
@@ -328,36 +383,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);
@@ -424,8 +456,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;
@@ -434,8 +466,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;
}
@@ -451,8 +483,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;
@@ -461,8 +493,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;
}
@@ -543,10 +575,21 @@ 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 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 },
{ }
};
MODULE_DEVICE_TABLE(pci, dw_edma_pcie_id_table);
--
2.51.0