From f3468b998a00c1491b774af188d5f741c64a1c71 Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Tue, 21 Jul 2026 15:28:08 +0900 Subject: [PATCH] dmaengine: dw-edma-pcie: Add capability match data 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 Reviewed-by: Frank Li Signed-off-by: Koichiro Den Link: https://patch.msgid.link/20260721062815.4117887-8-den@valinux.co.jp Signed-off-by: Vinod Koul --- drivers/dma/dw-edma/dw-edma-pcie.c | 137 ++++++++++++++++++++--------- 1 file changed, 95 insertions(+), 42 deletions(-) diff --git a/drivers/dma/dw-edma/dw-edma-pcie.c b/drivers/dma/dw-edma/dw-edma-pcie.c index 9b3d3df2ffcd..50d2a53f798d 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); + /* Let device-specific discovery override the static template data. */ + if (!match->parse_caps) + return -EINVAL; - 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); - } + err = match->parse_caps(pdev, vsec_data); + if (err) + return err; /* Mapping PCI BAR regions */ mask = BIT(vsec_data->rg.bar); @@ -441,8 +477,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; @@ -451,8 +487,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; } @@ -468,8 +504,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; @@ -478,8 +514,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; } @@ -557,12 +593,29 @@ static void dw_edma_pcie_remove(struct pci_dev *pdev) pci_warn(pdev, "can't remove device properly: %d\n", err); } +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);