Add AMD MDB Endpoint and non-LL mode Support
Subsystems (3)
v3 cover Add AMD MDB Endpoint and non-LL mode Support
This series of patch support the following:
- AMD MDB Endpoint Support, as part of this patch following are added:
o AMD supported device ID and vendor ID (Xilinx)
o AMD MDB specific driver data
o AMD specific VSEC capabilities to retrieve the base of phys address of MDB side DDR
o Logic to assign the offsets to LL and data blocks if more number of channels are enabled than configured in the given pci_data struct.
- Addition of non-LL mode
o The IP supported non-LL mode functions
o Flexibility to choose non-LL mode via dma_slave_config param peripheral_config, by the client
o Allow IP utilization if LL mode is not available
Devendra K Verma (2):
dmaengine: dw-edma: Add AMD MDB Endpoint Support
dmaengine: dw-edma: Add non-LL mode
drivers/dma/dw-edma/dw-edma-core.c | 38 +++++++- drivers/dma/dw-edma/dw-edma-core.h | 1 + drivers/dma/dw-edma/dw-edma-pcie.c | 178 ++++++++++++++++++++++++++++++++-- drivers/dma/dw-edma/dw-hdma-v0-core.c | 62 +++++++++++- include/linux/dma/edma.h | 1 + 5 files changed, 266 insertions(+), 14 deletions(-)
Signature · 1 line
1.8.3.1
PATCH v3 1/2 dmaengine: dw-edma: Add AMD MDB Endpoint Support
AMD MDB PCIe endpoint support. For AMD specific support added the following
- AMD supported PCIe Device IDs and Vendor ID (Xilinx).
- AMD MDB specific driver data
- AMD MDB specific VSEC capability to retrieve the device DDR base address.
---
Changes in v3:
Corrected a typo when assigning AMD (Xilinx) vsec id macro and condition check.
Changes in v2:
Reverted the devmem_phys_off type to u64. Renamed the function appropriately to suit the functionality for setting the LL & data region offsets.
Changes in v1:
Removed the pci device id from pci_ids.h file.
Added the vendor id macro as per the suggested method. Changed the type of the newly added devmem_phys_off variable. Added to logic to assign offsets for LL and data region blocks in case more number of channels are enabled than given in amd_mdb_data struct.
--- drivers/dma/dw-edma/dw-edma-pcie.c | 147 ++++++++++++++++++++++++++++++++++++- 1 file changed, 145 insertions(+), 2 deletions(-)
drivers/dma/dw-edma/dw-edma-pcie.c +145 -2
@@ -17,12 +17,19 @@#include "dw-edma-core.h" +/* Synopsys */ #define DW_PCIE_VSEC_DMA_ID 0x6#define DW_PCIE_VSEC_DMA_BAR GENMASK(10, 8)#define DW_PCIE_VSEC_DMA_MAP GENMASK(2, 0)#define DW_PCIE_VSEC_DMA_WR_CH GENMASK(9, 0)#define DW_PCIE_VSEC_DMA_RD_CH GENMASK(25, 16) +/* AMD MDB specific defines */ +#define DW_PCIE_XILINX_MDB_VSEC_DMA_ID 0x6 +#define DW_PCIE_XILINX_MDB_VSEC_ID 0x20 +#define PCI_DEVICE_ID_AMD_MDB_B054 0xb054 +#define DW_PCIE_AMD_MDB_INVALID_ADDR (~0ULL) +#define DW_BLOCK (a, b, c) \ { \ .bar = a, \
@@ -50,6 +57,7 @@ struct dw_edma_pcie_data {u8 irqs;u16 wr_ch_cnt;u16 rd_ch_cnt; +u64 devmem_phys_off; };static const struct dw_edma_pcie_data snps_edda_data = {
@@ -90,6 +98,91 @@ struct dw_edma_pcie_data { .rd_ch_cnt = 2, }; +static const struct dw_edma_pcie_data amd_mdb_data = { +/* MDB registers location */ + .rg.bar =BAR_0 , + .rg.off =0x00001000 ,/* 4 Kbytes */ + .rg.sz =0x00002000 ,/* 8 Kbytes */ +/* MDB memory linked list location */ + .ll_wr = { +/* Channel 0 - BAR 2, offset 0 Mbytes, size 2 Kbytes */ + DW_BLOCK(BAR_2, 0x00000000, 0x00000800) +/* Channel 1 - BAR 2, offset 2 Mbytes, size 2 Kbytes */ +DW_BLOCK (BAR_2 ,0x00200000 ,0x00000800 ) + }, + .ll_rd = { +/* Channel 0 - BAR 2, offset 4 Mbytes, size 2 Kbytes */ + DW_BLOCK(BAR_2, 0x00400000, 0x00000800) +/* Channel 1 - BAR 2, offset 6 Mbytes, size 2 Kbytes */ +DW_BLOCK (BAR_2 ,0x00600000 ,0x00000800 ) + }, +/* MDB memory data location */ + .dt_wr = { +/* Channel 0 - BAR 2, offset 8 Mbytes, size 2 Kbytes */ + DW_BLOCK(BAR_2, 0x00800000, 0x00000800) +/* Channel 1 - BAR 2, offset 9 Mbytes, size 2 Kbytes */ +DW_BLOCK (BAR_2 ,0x00900000 ,0x00000800 ) + }, + .dt_rd = { +/* Channel 0 - BAR 2, offset 10 Mbytes, size 2 Kbytes */ + DW_BLOCK(BAR_2, 0x00a00000, 0x00000800) +/* Channel 1 - BAR 2, offset 11 Mbytes, size 2 Kbytes */ +DW_BLOCK (BAR_2 ,0x00b00000 ,0x00000800 ) + }, +/* Other */ + .mf =EDMA_MF_HDMA_NATIVE , + .irqs =1 , + .wr_ch_cnt =2 , + .rd_ch_cnt =2 , +}; + +static void dw_edma_set_chan_region_offset (struct dw_edma_pcie_data *pdata, +enum pci_barno bar,off_t start_off, +off_t off_gap,size_t size) +{ +u16 i; +off_t off; +u16 wr_ch = pdata->wr_ch_cnt; +u16 rd_ch = pdata->rd_ch_cnt; + +if (wr_ch <=2 || rd_ch <=2 ) +return ; + + off = start_off; + +/* Write channel LL region */ +for (i =0 ; i < wr_ch; i++) { + pdata->ll_wr[i].bar = bar; + pdata->ll_wr[i].off = off; + pdata->ll_wr[i].sz = size; + off += off_gap + size; + } + +/* Read channel LL region */ +for (i =0 ; i < rd_ch; i++) { + pdata->ll_rd[i].bar = bar; + pdata->ll_rd[i].off = off; + pdata->ll_rd[i].sz = size; + off += off_gap + size; + } + +/* Write channel data region */ +for (i =0 ; i < wr_ch; i++) { + pdata->dt_wr[i].bar = bar; + pdata->dt_wr[i].off = off; + pdata->dt_wr[i].sz = size; + off += off_gap + size; + } + +/* Read channel data region */ +for (i =0 ; i < rd_ch; i++) { + pdata->dt_rd[i].bar = bar; + pdata->dt_rd[i].off = off; + pdata->dt_rd[i].sz = size; + off += off_gap + size; + } +} +static int dw_edma_pcie_irq_vector (struct device *dev,unsigned int nr) {return pci_irq_vector (to_pci_dev (dev), nr);
@@ -120,9 +213,22 @@ static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,u32 val, map;u16 vsec;u64 off; +u16 vendor = pdev->vendor; +int cap; - vsec =pci_find_vsec_capability (pdev,PCI_VENDOR_ID_SYNOPSYS , -DW_PCIE_VSEC_DMA_ID ); +/* +* Synopsys and AMD (Xilinx) use the same VSEC ID for the purpose +* of map, channel counts, etc. +*/ +if (vendor !=PCI_VENDOR_ID_SYNOPSYS && + vendor !=PCI_VENDOR_ID_XILINX ) +return ; + + cap =DW_PCIE_VSEC_DMA_ID ; +if (vendor ==PCI_VENDOR_ID_XILINX ) + cap =DW_PCIE_XILINX_MDB_VSEC_DMA_ID ; + + vsec =pci_find_vsec_capability (pdev, vendor, cap);if (!vsec)return ;
@@ -155,6 +261,24 @@ static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev, off <<=32 ; off |= val; pdata->rg.off = off; + +/* AMD specific VSEC capability */ + vsec =pci_find_vsec_capability (pdev, vendor, +DW_PCIE_XILINX_MDB_VSEC_ID ); +if (!vsec) +return ; + +pci_read_config_dword (pdev, vsec +PCI_VNDR_HEADER , &val); +if (PCI_VNDR_HEADER_ID (val) !=0x20 || +PCI_VNDR_HEADER_REV (val) !=0x1 ) +return ; + +pci_read_config_dword (pdev, vsec +0xc , &val); + off = val; +pci_read_config_dword (pdev, vsec +0x8 , &val); + off <<=32 ; + off |= val; + pdata->devmem_phys_off = off; } static int dw_edma_pcie_probe(struct pci_dev *pdev,
@@ -179,6 +303,7 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev, }memcpy (vsec_data, pdata,sizeof (struct dw_edma_pcie_data )); + vsec_data->devmem_phys_off =DW_PCIE_AMD_MDB_INVALID_ADDR ;/* * Tries to find if exists a PCIe Vendor-Specific Extended Capability
@@ -186,6 +311,22 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,*/ dw_edma_pcie_get_vsec_dma_data (pdev, vsec_data); +if (pdev->vendor ==PCI_VENDOR_ID_XILINX ) { +/* +* There is no valid address found for the LL memory +* space on the device side. +*/ +if (vsec_data->devmem_phys_off ==DW_PCIE_AMD_MDB_INVALID_ADDR ) +return -EINVAL ; + +/* +* Configure the channel LL and data blocks if number of +* channels enabled in VSEC capability are more than the +* channels configured in amd_mdb_data. +*/ +dw_edma_set_chan_region_offset (vsec_data,BAR_2 ,0 ,0x200000 ,0x800 ); + } +/* Mapping PCI BAR regions */ mask =BIT (vsec_data->rg.bar);for (i =0 ; i < vsec_data->wr_ch_cnt; i++) {
@@ -367,6 +508,8 @@ static void dw_edma_pcie_remove(struct pci_dev *pdev)static const struct pci_device_id dw_edma_pcie_id_table[] = { {PCI_DEVICE_DATA (SYNOPSYS ,EDDA , &snps_edda_data) }, + {PCI_VDEVICE (XILINX ,PCI_DEVICE_ID_AMD_MDB_B054 ), + (kernel_ulong_t)&amd_mdb_data }, { } };MODULE_DEVICE_TABLE (pci, dw_edma_pcie_id_table);
Signature · 1 line
1.8.3.1
PATCH v3 2/2 dmaengine: dw-edma: Add non-LL mode
In reply to Devendra K VermaAMD MDB IP supports Linked List (LL) mode as well as non-LL mode. The current code does not have the mechanisms to enable the DMA transactions using the non-LL mode. The following two cases are added with this patch:
- When a valid physical base address is not configured via the Xilinx VSEC capability then the IP can still be used in non-LL mode. The default mode for all the DMA transactions and for all the DMA channels then is non-LL mode.
- When a valid physical base address is configured but the client wants to use the non-LL mode for DMA transactions then also the flexibility is provided via the peripheral_config struct member of dma_slave_config. In this case the channels can be individually configured in non-LL mode. This use case is desirable for single DMA transfer of a chunk, this saves the effort of preparing the Link List.
---
Changes in v3
No change
Changes in v2
Reverted the function return type to u64 for
dw_edma_get_phys_addr().
Changes in v1
Changed the function return type for dw_edma_get_phys_addr(). Corrected the typo raised in review.
--- drivers/dma/dw-edma/dw-edma-core.c | 38 ++++++++++++++++++--- drivers/dma/dw-edma/dw-edma-core.h | 1 + drivers/dma/dw-edma/dw-edma-pcie.c | 37 ++++++++++++++++----- drivers/dma/dw-edma/dw-hdma-v0-core.c | 62 ++++++++++++++++++++++++++++++++++- include/linux/dma/edma.h | 1 + 5 files changed, 124 insertions(+), 15 deletions(-)
drivers/dma/dw-edma/dw-edma-core.c +33 -5
@@ -223,8 +223,28 @@ static int dw_edma_device_config(struct dma_chan *dchan, struct dma_slave_config *config) {struct dw_edma_chan *chan =dchan2dw_edma_chan (dchan); +int nollp =0 ; + +if (WARN_ON (config->peripheral_config && + config->peripheral_size !=sizeof (int ))) +return -EINVAL ;memcpy (&chan->config, config,sizeof (*config)); + +/* +* When there is no valid LLP base address available +* then the default DMA ops will use the non-LL mode. +* Cases where LL mode is enabled and client wants +* to use the non-LL mode then also client can do +* so via providing the peripheral_config param. +*/ +if (config->peripheral_config) + nollp = *(int *)config->peripheral_config; + + chan->nollp = false; +if (chan->dw->chip->nollp || (!chan->dw->chip->nollp && nollp)) + chan->nollp = true; + chan->configured = true;return 0 ;
@@ -353,7 +373,7 @@ static void dw_edma_device_issue_pending(struct dma_chan *dchan)struct dw_edma_chan *chan =dchan2dw_edma_chan (xfer->dchan);enum dma_transfer_direction dir = xfer->direction;struct scatterlist *sg =NULL ; -struct dw_edma_chunk *chunk; +struct dw_edma_chunk *chunk =NULL ;struct dw_edma_burst *burst;struct dw_edma_desc *desc;u64 src_addr, dst_addr;
@@ -419,9 +439,11 @@ static void dw_edma_device_issue_pending(struct dma_chan *dchan)if (unlikely (!desc)) goto err_alloc; - chunk =dw_edma_alloc_chunk (desc); -if (unlikely (!chunk)) - goto err_alloc; +if (!chan->nollp) { + chunk =dw_edma_alloc_chunk (desc); +if (unlikely (!chunk)) + goto err_alloc; + }if (xfer->type ==EDMA_XFER_INTERLEAVED ) { src_addr = xfer->xfer.il->src_start;
@@ -450,7 +472,13 @@ static void dw_edma_device_issue_pending(struct dma_chan *dchan)if (xfer->type ==EDMA_XFER_SCATTER_GATHER && !sg)break ; -if (chunk->bursts_alloc == chan->ll_max) { +/* +* For non-LL mode, only a single burst can be handled +* in a single chunk unlike LL mode where multiple bursts +* can be configured in a single chunk. +*/ +if ((chunk && chunk->bursts_alloc == chan->ll_max) || + chan->nollp) { chunk =dw_edma_alloc_chunk (desc);if (unlikely (!chunk)) goto err_alloc;
drivers/dma/dw-edma/dw-edma-core.h +1 -0
@@ -86,6 +86,7 @@ struct dw_edma_chan {u8 configured;struct dma_slave_config config; +bool nollp; };struct dw_edma_irq {
drivers/dma/dw-edma/dw-edma-pcie.c +28 -9
@@ -281,6 +281,15 @@ static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev, pdata->devmem_phys_off = off; } +static u64 dw_edma_get_phys_addr (struct pci_dev *pdev, +struct dw_edma_pcie_data *pdata, +enum pci_barno bar) +{ +if (pdev->vendor ==PCI_VENDOR_ID_XILINX ) +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) {
@@ -290,6 +299,7 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,struct dw_edma_chip *chip;int err, nr_irqs;int i, mask; +bool nollp = false; vsec_data =kmalloc (sizeof (*vsec_data),GFP_KERNEL );if (!vsec_data)
@@ -314,17 +324,21 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,if (pdev->vendor ==PCI_VENDOR_ID_XILINX ) {/* * There is no valid address found for the LL memory -* space on the device side. +* 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_AMD_MDB_INVALID_ADDR ) -return -EINVAL ; + nollp = true;/* * Configure the channel LL and data blocks if number of * channels enabled in VSEC capability are more than the * channels configured in amd_mdb_data. */ -dw_edma_set_chan_region_offset (vsec_data,BAR_2 ,0 ,0x200000 ,0x800 ); +if (!nollp) +dw_edma_set_chan_region_offset (vsec_data,BAR_2 ,0 , +0x200000 ,0x800 ); }/* Mapping PCI BAR regions */
@@ -372,6 +386,7 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev, chip->mf = vsec_data->mf; chip->nr_irqs = nr_irqs; chip->ops = &dw_edma_pcie_plat_ops; + chip->nollp = nollp; chip->ll_wr_cnt = vsec_data->wr_ch_cnt; chip->ll_rd_cnt = vsec_data->rd_ch_cnt;
@@ -380,7 +395,7 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,if (!chip->reg_base)return -ENOMEM ; -for (i =0 ; i < chip->ll_wr_cnt; i++) { +for (i =0 ; i < chip->ll_wr_cnt && !nollp; i++) {struct dw_edma_region *ll_region = &chip->ll_region_wr[i];struct dw_edma_region *dt_region = &chip->dt_region_wr[i];struct dw_edma_block *ll_block = &vsec_data->ll_wr[i];
@@ -391,7 +406,8 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,return -ENOMEM ; ll_region->vaddr.io += ll_block->off; - ll_region->paddr =pci_bus_address (pdev, ll_block->bar); + ll_region->paddr =dw_edma_get_phys_addr (pdev, vsec_data, + ll_block->bar); ll_region->paddr += ll_block->off; ll_region->sz = ll_block->sz;
@@ -400,12 +416,13 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,return -ENOMEM ; dt_region->vaddr.io += dt_block->off; - dt_region->paddr =pci_bus_address (pdev, dt_block->bar); + dt_region->paddr =dw_edma_get_phys_addr (pdev, vsec_data, + dt_block->bar); dt_region->paddr += dt_block->off; dt_region->sz = dt_block->sz; } -for (i =0 ; i < chip->ll_rd_cnt; i++) { +for (i =0 ; i < chip->ll_rd_cnt && !nollp; i++) {struct dw_edma_region *ll_region = &chip->ll_region_rd[i];struct dw_edma_region *dt_region = &chip->dt_region_rd[i];struct dw_edma_block *ll_block = &vsec_data->ll_rd[i];
@@ -416,7 +433,8 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,return -ENOMEM ; ll_region->vaddr.io += ll_block->off; - ll_region->paddr =pci_bus_address (pdev, ll_block->bar); + ll_region->paddr =dw_edma_get_phys_addr (pdev, vsec_data, + ll_block->bar); ll_region->paddr += ll_block->off; ll_region->sz = ll_block->sz;
@@ -425,7 +443,8 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,return -ENOMEM ; dt_region->vaddr.io += dt_block->off; - dt_region->paddr =pci_bus_address (pdev, dt_block->bar); + dt_region->paddr =dw_edma_get_phys_addr (pdev, vsec_data, + dt_block->bar); dt_region->paddr += dt_block->off; dt_region->sz = dt_block->sz; }
drivers/dma/dw-edma/dw-hdma-v0-core.c +61 -1
74 lines not on this page · raw message
include/linux/dma/edma.h +1 -0
@@ -99,6 +99,7 @@ struct dw_edma_chip {enum dw_edma_map_format mf;struct dw_edma *dw; +bool nollp; };/* Export to the platform drivers */
Signature · 1 line
1.8.3.1