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 <Frank.Li@nxp.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Koichiro Den <den@valinux.co.jp>
Link: https://patch.msgid.link/20260721062815.4117887-8-den@valinux.co.jp
Signed-off-by: Vinod Koul <vkoul@kernel.org>
This commit is contained in:
Koichiro Den 2026-07-21 15:28:08 +09:00 committed by Vinod Koul
parent 1415d6d1f1
commit f3468b998a

View File

@ -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);