Skip to content

Commit b756079

Browse files
Devendra K Vermavinodkoul
authored andcommitted
dmaengine: dw-edma: Add non-LL mode
AMD 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: - For the AMD (Xilinx) only, when a valid physical base address of the device side DDR is not configured, then the IP can still be used in non-LL mode. For all the channels DMA transactions will be using the non-LL mode only. This, the default non-LL mode, is not applicable for Synopsys IP with the current code addition. - If the default mode is LL-mode, for both AMD (Xilinx) and Synosys, and if user wants to use non-LL mode then user can do so via configuring the peripheral_config param of dma_slave_config. Signed-off-by: Devendra K Verma <devendra.verma@amd.com> Reviewed-by: Frank Li <Frank.Li@nxp.com> Link: https://patch.msgid.link/20260318070403.1634706-3-devendra.verma@amd.com Signed-off-by: Vinod Koul <vkoul@kernel.org>
1 parent 14eb9a1 commit b756079

6 files changed

Lines changed: 143 additions & 15 deletions

File tree

drivers/dma/dw-edma/dw-edma-core.c

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,43 @@ static int dw_edma_device_config(struct dma_chan *dchan,
223223
struct dma_slave_config *config)
224224
{
225225
struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
226+
bool cfg_non_ll;
227+
int non_ll = 0;
228+
229+
chan->non_ll = false;
230+
if (chan->dw->chip->mf == EDMA_MF_HDMA_NATIVE) {
231+
if (config->peripheral_config &&
232+
config->peripheral_size != sizeof(int)) {
233+
dev_err(dchan->device->dev,
234+
"config param peripheral size mismatch\n");
235+
return -EINVAL;
236+
}
237+
238+
/*
239+
* When there is no valid LLP base address available then the
240+
* default DMA ops will use the non-LL mode.
241+
*
242+
* Cases where LL mode is enabled and client wants to use the
243+
* non-LL mode then also client can do so via providing the
244+
* peripheral_config param.
245+
*/
246+
cfg_non_ll = chan->dw->chip->cfg_non_ll;
247+
if (config->peripheral_config) {
248+
non_ll = *(int *)config->peripheral_config;
249+
250+
if (cfg_non_ll && !non_ll) {
251+
dev_err(dchan->device->dev, "invalid configuration\n");
252+
return -EINVAL;
253+
}
254+
}
255+
256+
if (cfg_non_ll || non_ll)
257+
chan->non_ll = true;
258+
} else if (config->peripheral_config) {
259+
dev_err(dchan->device->dev,
260+
"peripheral config param applicable only for HDMA\n");
261+
return -EINVAL;
262+
}
226263

227264
memcpy(&chan->config, config, sizeof(*config));
228265
chan->configured = true;
@@ -358,6 +395,7 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer)
358395
struct dw_edma_desc *desc;
359396
u64 src_addr, dst_addr;
360397
size_t fsz = 0;
398+
u32 bursts_max;
361399
u32 cnt = 0;
362400
int i;
363401

@@ -415,6 +453,13 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer)
415453
return NULL;
416454
}
417455

456+
/*
457+
* For non-LL mode, only a single burst can be handled
458+
* in a single chunk unlike LL mode where multiple bursts
459+
* can be configured in a single chunk.
460+
*/
461+
bursts_max = chan->non_ll ? 1 : chan->ll_max;
462+
418463
desc = dw_edma_alloc_desc(chan);
419464
if (unlikely(!desc))
420465
goto err_alloc;
@@ -450,7 +495,7 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer)
450495
if (xfer->type == EDMA_XFER_SCATTER_GATHER && !sg)
451496
break;
452497

453-
if (chunk->bursts_alloc == chan->ll_max) {
498+
if (chunk->bursts_alloc == bursts_max) {
454499
chunk = dw_edma_alloc_chunk(desc);
455500
if (unlikely(!chunk))
456501
goto err_alloc;

drivers/dma/dw-edma/dw-edma-core.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ struct dw_edma_chan {
8686
u8 configured;
8787

8888
struct dma_slave_config config;
89+
bool non_ll;
8990
};
9091

9192
struct dw_edma_irq {

drivers/dma/dw-edma/dw-edma-pcie.c

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,15 @@ static void dw_edma_pcie_get_xilinx_dma_data(struct pci_dev *pdev,
295295
pdata->devmem_phys_off = off;
296296
}
297297

298+
static u64 dw_edma_get_phys_addr(struct pci_dev *pdev,
299+
struct dw_edma_pcie_data *pdata,
300+
enum pci_barno bar)
301+
{
302+
if (pdev->vendor == PCI_VENDOR_ID_XILINX)
303+
return pdata->devmem_phys_off;
304+
return pci_bus_address(pdev, bar);
305+
}
306+
298307
static int dw_edma_pcie_probe(struct pci_dev *pdev,
299308
const struct pci_device_id *pid)
300309
{
@@ -303,6 +312,7 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
303312
struct dw_edma_chip *chip;
304313
int err, nr_irqs;
305314
int i, mask;
315+
bool non_ll = false;
306316

307317
struct dw_edma_pcie_data *vsec_data __free(kfree) =
308318
kmalloc_obj(*vsec_data);
@@ -329,21 +339,24 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
329339

330340
/*
331341
* There is no valid address found for the LL memory
332-
* space on the device side.
342+
* space on the device side. In the absence of LL base
343+
* address use the non-LL mode or simple mode supported by
344+
* the HDMA IP.
333345
*/
334346
if (vsec_data->devmem_phys_off == DW_PCIE_XILINX_MDB_INVALID_ADDR)
335-
return -ENOMEM;
347+
non_ll = true;
336348

337349
/*
338350
* Configure the channel LL and data blocks if number of
339351
* channels enabled in VSEC capability are more than the
340352
* channels configured in xilinx_mdb_data.
341353
*/
342-
dw_edma_set_chan_region_offset(vsec_data, BAR_2, 0,
343-
DW_PCIE_XILINX_MDB_LL_OFF_GAP,
344-
DW_PCIE_XILINX_MDB_LL_SIZE,
345-
DW_PCIE_XILINX_MDB_DT_OFF_GAP,
346-
DW_PCIE_XILINX_MDB_DT_SIZE);
354+
if (!non_ll)
355+
dw_edma_set_chan_region_offset(vsec_data, BAR_2, 0,
356+
DW_PCIE_XILINX_MDB_LL_OFF_GAP,
357+
DW_PCIE_XILINX_MDB_LL_SIZE,
358+
DW_PCIE_XILINX_MDB_DT_OFF_GAP,
359+
DW_PCIE_XILINX_MDB_DT_SIZE);
347360
}
348361

349362
/* Mapping PCI BAR regions */
@@ -391,6 +404,7 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
391404
chip->mf = vsec_data->mf;
392405
chip->nr_irqs = nr_irqs;
393406
chip->ops = &dw_edma_pcie_plat_ops;
407+
chip->cfg_non_ll = non_ll;
394408

395409
chip->ll_wr_cnt = vsec_data->wr_ch_cnt;
396410
chip->ll_rd_cnt = vsec_data->rd_ch_cnt;
@@ -399,7 +413,7 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
399413
if (!chip->reg_base)
400414
return -ENOMEM;
401415

402-
for (i = 0; i < chip->ll_wr_cnt; i++) {
416+
for (i = 0; i < chip->ll_wr_cnt && !non_ll; i++) {
403417
struct dw_edma_region *ll_region = &chip->ll_region_wr[i];
404418
struct dw_edma_region *dt_region = &chip->dt_region_wr[i];
405419
struct dw_edma_block *ll_block = &vsec_data->ll_wr[i];
@@ -410,7 +424,8 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
410424
return -ENOMEM;
411425

412426
ll_region->vaddr.io += ll_block->off;
413-
ll_region->paddr = pci_bus_address(pdev, ll_block->bar);
427+
ll_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
428+
ll_block->bar);
414429
ll_region->paddr += ll_block->off;
415430
ll_region->sz = ll_block->sz;
416431

@@ -419,12 +434,13 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
419434
return -ENOMEM;
420435

421436
dt_region->vaddr.io += dt_block->off;
422-
dt_region->paddr = pci_bus_address(pdev, dt_block->bar);
437+
dt_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
438+
dt_block->bar);
423439
dt_region->paddr += dt_block->off;
424440
dt_region->sz = dt_block->sz;
425441
}
426442

427-
for (i = 0; i < chip->ll_rd_cnt; i++) {
443+
for (i = 0; i < chip->ll_rd_cnt && !non_ll; i++) {
428444
struct dw_edma_region *ll_region = &chip->ll_region_rd[i];
429445
struct dw_edma_region *dt_region = &chip->dt_region_rd[i];
430446
struct dw_edma_block *ll_block = &vsec_data->ll_rd[i];
@@ -435,7 +451,8 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
435451
return -ENOMEM;
436452

437453
ll_region->vaddr.io += ll_block->off;
438-
ll_region->paddr = pci_bus_address(pdev, ll_block->bar);
454+
ll_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
455+
ll_block->bar);
439456
ll_region->paddr += ll_block->off;
440457
ll_region->sz = ll_block->sz;
441458

@@ -444,7 +461,8 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
444461
return -ENOMEM;
445462

446463
dt_region->vaddr.io += dt_block->off;
447-
dt_region->paddr = pci_bus_address(pdev, dt_block->bar);
464+
dt_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
465+
dt_block->bar);
448466
dt_region->paddr += dt_block->off;
449467
dt_region->sz = dt_block->sz;
450468
}

drivers/dma/dw-edma/dw-hdma-v0-core.c

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ static void dw_hdma_v0_sync_ll_data(struct dw_edma_chunk *chunk)
225225
readl(chunk->ll_region.vaddr.io);
226226
}
227227

228-
static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
228+
static void dw_hdma_v0_core_ll_start(struct dw_edma_chunk *chunk, bool first)
229229
{
230230
struct dw_edma_chan *chan = chunk->chan;
231231
struct dw_edma *dw = chan->dw;
@@ -263,6 +263,68 @@ static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
263263
SET_CH_32(dw, chan->dir, chan->id, doorbell, HDMA_V0_DOORBELL_START);
264264
}
265265

266+
static void dw_hdma_v0_core_non_ll_start(struct dw_edma_chunk *chunk)
267+
{
268+
struct dw_edma_chan *chan = chunk->chan;
269+
struct dw_edma *dw = chan->dw;
270+
struct dw_edma_burst *child;
271+
u32 val;
272+
273+
child = list_first_entry_or_null(&chunk->burst->list,
274+
struct dw_edma_burst, list);
275+
if (!child)
276+
return;
277+
278+
SET_CH_32(dw, chan->dir, chan->id, ch_en, HDMA_V0_CH_EN);
279+
280+
/* Source address */
281+
SET_CH_32(dw, chan->dir, chan->id, sar.lsb,
282+
lower_32_bits(child->sar));
283+
SET_CH_32(dw, chan->dir, chan->id, sar.msb,
284+
upper_32_bits(child->sar));
285+
286+
/* Destination address */
287+
SET_CH_32(dw, chan->dir, chan->id, dar.lsb,
288+
lower_32_bits(child->dar));
289+
SET_CH_32(dw, chan->dir, chan->id, dar.msb,
290+
upper_32_bits(child->dar));
291+
292+
/* Transfer size */
293+
SET_CH_32(dw, chan->dir, chan->id, transfer_size, child->sz);
294+
295+
/* Interrupt setup */
296+
val = GET_CH_32(dw, chan->dir, chan->id, int_setup) |
297+
HDMA_V0_STOP_INT_MASK |
298+
HDMA_V0_ABORT_INT_MASK |
299+
HDMA_V0_LOCAL_STOP_INT_EN |
300+
HDMA_V0_LOCAL_ABORT_INT_EN;
301+
302+
if (!(dw->chip->flags & DW_EDMA_CHIP_LOCAL)) {
303+
val |= HDMA_V0_REMOTE_STOP_INT_EN |
304+
HDMA_V0_REMOTE_ABORT_INT_EN;
305+
}
306+
307+
SET_CH_32(dw, chan->dir, chan->id, int_setup, val);
308+
309+
/* Channel control setup */
310+
val = GET_CH_32(dw, chan->dir, chan->id, control1);
311+
val &= ~HDMA_V0_LINKLIST_EN;
312+
SET_CH_32(dw, chan->dir, chan->id, control1, val);
313+
314+
SET_CH_32(dw, chan->dir, chan->id, doorbell,
315+
HDMA_V0_DOORBELL_START);
316+
}
317+
318+
static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
319+
{
320+
struct dw_edma_chan *chan = chunk->chan;
321+
322+
if (chan->non_ll)
323+
dw_hdma_v0_core_non_ll_start(chunk);
324+
else
325+
dw_hdma_v0_core_ll_start(chunk, first);
326+
}
327+
266328
static void dw_hdma_v0_core_ch_config(struct dw_edma_chan *chan)
267329
{
268330
struct dw_edma *dw = chan->dw;

drivers/dma/dw-edma/dw-hdma-v0-regs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <linux/dmaengine.h>
1313

1414
#define HDMA_V0_MAX_NR_CH 8
15+
#define HDMA_V0_CH_EN BIT(0)
1516
#define HDMA_V0_LOCAL_ABORT_INT_EN BIT(6)
1617
#define HDMA_V0_REMOTE_ABORT_INT_EN BIT(5)
1718
#define HDMA_V0_LOCAL_STOP_INT_EN BIT(4)

include/linux/dma/edma.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ struct dw_edma_chip {
103103
enum dw_edma_map_format mf;
104104

105105
struct dw_edma *dw;
106+
bool cfg_non_ll;
106107
};
107108

108109
/* Export to the platform drivers */

0 commit comments

Comments
 (0)