Skip to content

Commit dfb1652

Browse files
Raymond0225mmahadevan108
authored andcommitted
drivers: dma: edma: eDMA loop SG mode support
Existing dynamic SG mode have some issues which cause uart sync api and I2S speed test failed. These issues are: a wrong "Done" bit issue found on UART async api test. An invalid destination address issue found on I2S speed test. By introducing loop SG mode, these issues are all fixed. Some data structures are different between eDMA and eDMA V4, use different macros defined in SDK driver for each of them. Signed-off-by: Raymond Lei <[email protected]>
1 parent fddc009 commit dfb1652

File tree

1 file changed

+252
-46
lines changed

1 file changed

+252
-46
lines changed

drivers/dma/dma_mcux_edma.c

Lines changed: 252 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,14 @@ struct dma_mcux_channel_transfer_edma_settings {
9292
enum dma_channel_direction direction;
9393
edma_transfer_type_t transfer_type;
9494
bool valid;
95+
/* This var indicate it is dynamic SG mode or loop SG mode. */
96+
bool cyclic;
97+
/* These parameters are for cyclic mode only.
98+
* Next empty TCD idx which can be used for transfer
99+
*/
100+
volatile int8_t write_idx;
101+
/* How many TCDs in TCD pool is emtpy(can be used to write transfer parameters) */
102+
volatile uint8_t empty_tcds;
95103
};
96104

97105

@@ -130,6 +138,35 @@ struct dma_mcux_edma_data {
130138
(ch % DEV_CFG(dev)->channels_per_mux) ^ (DEV_CFG(dev)->dmamux_reg_offset)
131139
#endif
132140

141+
/* Definations for SW TCD fields */
142+
#if defined(CONFIG_DMA_MCUX_EDMA) || defined(CONFIG_DMA_MCUX_EDMA_V3)
143+
#define EDMA_TCD_SADDR(tcd, flag) ((tcd)->SADDR)
144+
#define EDMA_TCD_DADDR(tcd, flag) ((tcd)->DADDR)
145+
#define EDMA_TCD_BITER(tcd, flag) ((tcd)->BITER)
146+
#define EDMA_TCD_CITER(tcd, flag) ((tcd)->CITER)
147+
#define EDMA_TCD_CSR(tcd, flag) ((tcd)->CSR)
148+
#define EDMA_TCD_DLAST_SGA(tcd, flag) ((tcd)->DLAST_SGA)
149+
#define EDMA_HW_TCD_CH_ACTIVE_MASK (DMA_CSR_ACTIVE_MASK)
150+
#elif defined(CONFIG_DMA_MCUX_EDMA_V4)
151+
/* Above macros have been defined in fsl_edma_core.h */
152+
#define EDMA_HW_TCD_CH_ACTIVE_MASK (DMA_CH_CSR_ACTIVE_MASK)
153+
#endif
154+
155+
/* Definations for HW TCD fields */
156+
#ifdef CONFIG_DMA_MCUX_EDMA
157+
#define EDMA_HW_TCD_SADDR(dev, ch) (DEV_BASE(dev)->TCD[ch].SADDR)
158+
#define EDMA_HW_TCD_DADDR(dev, ch) (DEV_BASE(dev)->TCD[ch].DADDR)
159+
#define EDMA_HW_TCD_BITER(dev, ch) (DEV_BASE(dev)->TCD[ch].BITER_ELINKNO)
160+
#define EDMA_HW_TCD_CITER(dev, ch) (DEV_BASE(dev)->TCD[ch].CITER_ELINKNO)
161+
#define EDMA_HW_TCD_CSR(dev, ch) (DEV_BASE(dev)->TCD[ch].CSR)
162+
#elif defined(CONFIG_DMA_MCUX_EDMA_V3) || defined(CONFIG_DMA_MCUX_EDMA_V4)
163+
#define EDMA_HW_TCD_SADDR(dev, ch) (DEV_BASE(dev)->CH[ch].TCD_SADDR)
164+
#define EDMA_HW_TCD_DADDR(dev, ch) (DEV_BASE(dev)->CH[ch].TCD_DADDR)
165+
#define EDMA_HW_TCD_BITER(dev, ch) (DEV_BASE(dev)->CH[ch].TCD_BITER_ELINKNO)
166+
#define EDMA_HW_TCD_CITER(dev, ch) (DEV_BASE(dev)->CH[ch].TCD_CITER_ELINKNO)
167+
#define EDMA_HW_TCD_CSR(dev, ch) (DEV_BASE(dev)->CH[ch].TCD_CSR)
168+
#endif
169+
133170
/*
134171
* The hardware channel (takes the gap into account) is used when access DMA registers.
135172
* For data structures in the shim driver still use the primitive channel.
@@ -178,10 +215,14 @@ static void nxp_edma_callback(edma_handle_t *handle, void *param, bool transferD
178215
{
179216
int ret = -EIO;
180217
struct call_back *data = (struct call_back *)param;
181-
182218
uint32_t channel = dma_mcux_edma_remove_channel_gap(data->dev, handle->channel);
183219

184-
if (transferDone) {
220+
if (data->transfer_settings.cyclic) {
221+
data->transfer_settings.empty_tcds++;
222+
/*In loop mode, DMA is always busy*/
223+
data->busy = 1;
224+
ret = DMA_STATUS_COMPLETE;
225+
} else if (transferDone) {
185226
/* DMA is no longer busy when there are no remaining TCDs to transfer */
186227
data->busy = (handle->tcdPool != NULL) && (handle->tcdUsed > 0);
187228
ret = DMA_STATUS_COMPLETE;
@@ -254,6 +295,7 @@ static int dma_mcux_edma_configure(const struct device *dev, uint32_t channel,
254295
edma_transfer_type_t transfer_type;
255296
unsigned int key;
256297
int ret = 0;
298+
edma_tcd_t *tcd = NULL;
257299

258300
if (slot >= DEV_CFG(dev)->dma_requests) {
259301
LOG_ERR("source number is out of scope %d", slot);
@@ -316,7 +358,7 @@ static int dma_mcux_edma_configure(const struct device *dev, uint32_t channel,
316358
data->transfer_settings.direction = config->channel_direction;
317359
data->transfer_settings.transfer_type = transfer_type;
318360
data->transfer_settings.valid = true;
319-
361+
data->transfer_settings.cyclic = config->cyclic;
320362

321363
/* Lock and page in the channel configuration */
322364
key = irq_lock();
@@ -357,26 +399,93 @@ static int dma_mcux_edma_configure(const struct device *dev, uint32_t channel,
357399
LOG_DBG("channel is %d", channel);
358400
EDMA_EnableChannelInterrupts(DEV_BASE(dev), hw_channel, kEDMA_ErrorInterruptEnable);
359401

402+
/* Initialize all TCD pool as 0*/
403+
for (int i = 0; i < CONFIG_DMA_TCD_QUEUE_SIZE; i++) {
404+
memset(&DEV_CFG(dev)->tcdpool[channel][i], 0,
405+
sizeof(DEV_CFG(dev)->tcdpool[channel][i]));
406+
}
407+
360408
if (block_config->source_gather_en || block_config->dest_scatter_en) {
361-
EDMA_InstallTCDMemory(p_handle, DEV_CFG(dev)->tcdpool[channel],
362-
CONFIG_DMA_TCD_QUEUE_SIZE);
363-
while (block_config != NULL) {
409+
if (config->cyclic) {
410+
/* Loop SG mode */
411+
data->transfer_settings.write_idx = 0;
412+
data->transfer_settings.empty_tcds = CONFIG_DMA_TCD_QUEUE_SIZE;
413+
364414
EDMA_PrepareTransfer(
365-
&(data->transferConfig),
366-
(void *)block_config->source_address,
367-
config->source_data_size,
368-
(void *)block_config->dest_address,
369-
config->dest_data_size,
370-
config->source_burst_length,
415+
&data->transferConfig, (void *)block_config->source_address,
416+
config->source_data_size, (void *)block_config->dest_address,
417+
config->dest_data_size, config->source_burst_length,
371418
block_config->block_size, transfer_type);
372419

373-
const status_t submit_status =
374-
EDMA_SubmitTransfer(p_handle, &(data->transferConfig));
375-
if (submit_status != kStatus_Success) {
376-
LOG_ERR("Error submitting EDMA Transfer: 0x%x", submit_status);
377-
ret = -EFAULT;
420+
/* Init all TCDs with the para in transfer config and link them. */
421+
for (int i = 0; i < CONFIG_DMA_TCD_QUEUE_SIZE; i++) {
422+
EDMA_TcdSetTransferConfig(
423+
&DEV_CFG(dev)->tcdpool[channel][i], &data->transferConfig,
424+
&DEV_CFG(dev)->tcdpool[channel][(i + 1) %
425+
CONFIG_DMA_TCD_QUEUE_SIZE]);
426+
427+
/* Enable Major loop interrupt.*/
428+
EDMA_TcdEnableInterrupts(&DEV_CFG(dev)->tcdpool[channel][i],
429+
kEDMA_MajorInterruptEnable);
430+
}
431+
432+
/* Load valid transfer parameters */
433+
while (block_config != NULL && data->transfer_settings.empty_tcds > 0) {
434+
tcd = &(DEV_CFG(dev)->tcdpool[channel]
435+
[data->transfer_settings.write_idx]);
436+
437+
EDMA_TCD_SADDR(tcd, kEDMA_EDMA4Flag) = block_config->source_address;
438+
EDMA_TCD_DADDR(tcd, kEDMA_EDMA4Flag) = block_config->dest_address;
439+
EDMA_TCD_BITER(tcd, kEDMA_EDMA4Flag) =
440+
block_config->block_size / config->source_data_size;
441+
EDMA_TCD_CITER(tcd, kEDMA_EDMA4Flag) =
442+
block_config->block_size / config->source_data_size;
443+
/*Enable auto stop for last transfer.*/
444+
if (block_config->next_block == NULL) {
445+
EDMA_TCD_CSR(tcd, kEDMA_EDMA4Flag) |= DMA_CSR_DREQ(1U);
446+
} else {
447+
EDMA_TCD_CSR(tcd, kEDMA_EDMA4Flag) &= ~DMA_CSR_DREQ(1U);
448+
}
449+
450+
data->transfer_settings.write_idx =
451+
(data->transfer_settings.write_idx + 1) %
452+
CONFIG_DMA_TCD_QUEUE_SIZE;
453+
data->transfer_settings.empty_tcds--;
454+
block_config = block_config->next_block;
455+
}
456+
457+
if (block_config != NULL && data->transfer_settings.empty_tcds == 0) {
458+
/* User input more blocks than TCD number, return error */
459+
LOG_ERR("Too much request blocks,increase TCD buffer size!");
460+
ret = -ENOBUFS;
461+
}
462+
/* Push the 1st TCD into HW */
463+
EDMA_InstallTCD(p_handle->base, hw_channel,
464+
&DEV_CFG(dev)->tcdpool[channel][0]);
465+
466+
} else {
467+
/* Dynamic Scatter Gather mode */
468+
EDMA_InstallTCDMemory(p_handle, DEV_CFG(dev)->tcdpool[channel],
469+
CONFIG_DMA_TCD_QUEUE_SIZE);
470+
471+
while (block_config != NULL) {
472+
EDMA_PrepareTransfer(&(data->transferConfig),
473+
(void *)block_config->source_address,
474+
config->source_data_size,
475+
(void *)block_config->dest_address,
476+
config->dest_data_size,
477+
config->source_burst_length,
478+
block_config->block_size, transfer_type);
479+
480+
const status_t submit_status =
481+
EDMA_SubmitTransfer(p_handle, &(data->transferConfig));
482+
if (submit_status != kStatus_Success) {
483+
LOG_ERR("Error submitting EDMA Transfer: 0x%x",
484+
submit_status);
485+
ret = -EFAULT;
486+
}
487+
block_config = block_config->next_block;
378488
}
379-
block_config = block_config->next_block;
380489
}
381490
} else {
382491
/* block_count shall be 1 */
@@ -395,11 +504,8 @@ static int dma_mcux_edma_configure(const struct device *dev, uint32_t channel,
395504
LOG_ERR("Error submitting EDMA Transfer: 0x%x", submit_status);
396505
ret = -EFAULT;
397506
}
398-
#if defined(CONFIG_DMA_MCUX_EDMA_V3) || defined(CONFIG_DMA_MCUX_EDMA_V4)
399-
LOG_DBG("DMA TCD_CSR 0x%x", DEV_BASE(dev)->CH[hw_channel].TCD_CSR);
400-
#else
401-
LOG_DBG("data csr is 0x%x", DEV_BASE(dev)->TCD[hw_channel].CSR);
402-
#endif
507+
508+
LOG_DBG("DMA TCD CSR 0x%x", EDMA_HW_TCD_CSR(dev, hw_channel));
403509
}
404510

405511
if (config->dest_chaining_en) {
@@ -491,11 +597,23 @@ static int dma_mcux_edma_resume(const struct device *dev, uint32_t channel)
491597
return 0;
492598
}
493599

600+
static void dma_mcux_edma_update_hw_tcd(const struct device *dev, uint32_t channel, uint32_t src,
601+
uint32_t dst, size_t size)
602+
{
603+
EDMA_HW_TCD_SADDR(dev, channel) = src;
604+
EDMA_HW_TCD_DADDR(dev, channel) = dst;
605+
EDMA_HW_TCD_BITER(dev, channel) = size;
606+
EDMA_HW_TCD_CITER(dev, channel) = size;
607+
EDMA_HW_TCD_CSR(dev, channel) |= DMA_CSR_DREQ(1U);
608+
}
494609

495610
static int dma_mcux_edma_reload(const struct device *dev, uint32_t channel,
496611
uint32_t src, uint32_t dst, size_t size)
497612
{
498613
struct call_back *data = DEV_CHANNEL_DATA(dev, channel);
614+
edma_tcd_t *tcd = NULL;
615+
edma_tcd_t *pre_tcd = NULL;
616+
uint32_t hw_id, sw_id;
499617

500618
/* Lock the channel configuration */
501619
const unsigned int key = irq_lock();
@@ -507,29 +625,115 @@ static int dma_mcux_edma_reload(const struct device *dev, uint32_t channel,
507625
goto cleanup;
508626
}
509627

510-
/* If the tcdPool is not in use (no s/g) then only a single TCD can be active at once. */
511-
if (data->busy && data->edma_handle.tcdPool == NULL) {
512-
LOG_ERR("EDMA busy. Wait until the transfer completes before reloading.");
513-
ret = -EBUSY;
514-
goto cleanup;
515-
}
628+
if (data->transfer_settings.cyclic) {
629+
if (data->transfer_settings.empty_tcds == 0) {
630+
LOG_ERR("TCD list is full in loop mode.");
631+
ret = -ENOBUFS;
632+
goto cleanup;
633+
}
516634

517-
EDMA_PrepareTransfer(
518-
&(data->transferConfig),
519-
(void *)src,
520-
data->transfer_settings.source_data_size,
521-
(void *)dst,
522-
data->transfer_settings.dest_data_size,
523-
data->transfer_settings.source_burst_length,
524-
size,
525-
data->transfer_settings.transfer_type);
526-
527-
const status_t submit_status =
528-
EDMA_SubmitTransfer(DEV_EDMA_HANDLE(dev, channel), &(data->transferConfig));
529-
530-
if (submit_status != kStatus_Success) {
531-
LOG_ERR("Error submitting EDMA Transfer: 0x%x", submit_status);
532-
ret = -EFAULT;
635+
/* Convert size into major loop count */
636+
size = size / data->transfer_settings.dest_data_size;
637+
638+
/* Configure a TCD for the transfer */
639+
tcd = &(DEV_CFG(dev)->tcdpool[channel][data->transfer_settings.write_idx]);
640+
pre_tcd = &(DEV_CFG(dev)->tcdpool[channel][(data->transfer_settings.write_idx - 1) %
641+
CONFIG_DMA_TCD_QUEUE_SIZE]);
642+
643+
EDMA_TCD_SADDR(tcd, kEDMA_EDMA4Flag) = src;
644+
EDMA_TCD_DADDR(tcd, kEDMA_EDMA4Flag) = dst;
645+
EDMA_TCD_BITER(tcd, kEDMA_EDMA4Flag) = size;
646+
EDMA_TCD_CITER(tcd, kEDMA_EDMA4Flag) = size;
647+
/* Enable automatically stop */
648+
EDMA_TCD_CSR(tcd, kEDMA_EDMA4Flag) |= DMA_CSR_DREQ(1U);
649+
sw_id = EDMA_TCD_DLAST_SGA(tcd, kEDMA_EDMA4Flag);
650+
651+
/* Block the peripheral's hardware request trigger to prevent
652+
* starting the DMA before updating the TCDs. Make sure the
653+
* code between EDMA_DisableChannelRequest() and
654+
* EDMA_EnableChannelRequest() is minimum.
655+
*/
656+
EDMA_DisableChannelRequest(DEV_BASE(dev), channel);
657+
658+
/* Wait for the DMA to be inactive before updating the TCDs.
659+
* The CSR[ACTIVE] bit will deassert quickly after the EDMA's
660+
* minor loop burst completes.
661+
*/
662+
while (EDMA_HW_TCD_CSR(dev, channel) & EDMA_HW_TCD_CH_ACTIVE_MASK) {
663+
;
664+
}
665+
666+
/* Identify the current active TCD. Use DLAST_SGA as the HW ID */
667+
hw_id = EDMA_GetNextTCDAddress(DEV_EDMA_HANDLE(dev, channel));
668+
if (data->transfer_settings.empty_tcds >= CONFIG_DMA_TCD_QUEUE_SIZE ||
669+
hw_id == sw_id) {
670+
/* All transfers have been done.DMA is stopped automatically,
671+
* invalid TCD has been loaded into the HW, update HW.
672+
*/
673+
dma_mcux_edma_update_hw_tcd(dev, channel, src, dst, size);
674+
LOG_DBG("Transfer done,auto stop");
675+
676+
} else {
677+
/* Previous TCD can automatically start this TCD.
678+
* Enable the peripheral DMA request in the previous TCD
679+
*/
680+
EDMA_TCD_CSR(pre_tcd, kEDMA_EDMA4Flag) &= ~DMA_CSR_DREQ(1U);
681+
682+
if (data->transfer_settings.empty_tcds == CONFIG_DMA_TCD_QUEUE_SIZE - 1 ||
683+
hw_id == (uint32_t)tcd) {
684+
/* DMA is running on last transfer. HW has loaded the last one,
685+
* we need ensure it's DREQ is cleared.
686+
*/
687+
EDMA_EnableAutoStopRequest(DEV_BASE(dev), channel, false);
688+
LOG_DBG("Last transfer.");
689+
}
690+
LOG_DBG("Manu stop");
691+
}
692+
693+
#ifdef CONFIG_DMA_MCUX_EDMA
694+
/* It seems that there is HW issue which may cause ESG bit is cleared.
695+
* This is a workaround. Clear the DONE bit before setting ESG bit.
696+
*/
697+
EDMA_ClearChannelStatusFlags(DEV_BASE(dev), channel, kEDMA_DoneFlag);
698+
EDMA_HW_TCD_CSR(dev, channel) |= DMA_CSR_ESG_MASK;
699+
#elif (CONFIG_DMA_MCUX_EDMA_V3 || CONFIG_DMA_MCUX_EDMA_V4)
700+
/*We have not verified if this issue exist on V3/V4 HW, jut place a holder here. */
701+
#endif
702+
/* TCDs are configured. Resume DMA */
703+
EDMA_EnableChannelRequest(DEV_BASE(dev), channel);
704+
705+
/* Update the write index and available TCD numbers. */
706+
data->transfer_settings.write_idx =
707+
(data->transfer_settings.write_idx + 1) % CONFIG_DMA_TCD_QUEUE_SIZE;
708+
data->transfer_settings.empty_tcds--;
709+
710+
LOG_DBG("w_idx:%d no:%d(ch:%d)", data->transfer_settings.write_idx,
711+
data->transfer_settings.empty_tcds, channel);
712+
713+
} else {
714+
/* Dynamice Scatter/Gather mode:
715+
* If the tcdPool is not in use (no s/g) then only a single TCD
716+
* can be active at once.
717+
*/
718+
if (data->busy && data->edma_handle.tcdPool == NULL) {
719+
LOG_ERR("EDMA busy. Wait until the transfer completes before reloading.");
720+
ret = -EBUSY;
721+
goto cleanup;
722+
}
723+
724+
EDMA_PrepareTransfer(&(data->transferConfig), (void *)src,
725+
data->transfer_settings.source_data_size, (void *)dst,
726+
data->transfer_settings.dest_data_size,
727+
data->transfer_settings.source_burst_length, size,
728+
data->transfer_settings.transfer_type);
729+
730+
const status_t submit_status =
731+
EDMA_SubmitTransfer(DEV_EDMA_HANDLE(dev, channel), &(data->transferConfig));
732+
733+
if (submit_status != kStatus_Success) {
734+
LOG_ERR("Error submitting EDMA Transfer: 0x%x", submit_status);
735+
ret = -EFAULT;
736+
}
533737
}
534738

535739
cleanup:
@@ -544,7 +748,9 @@ static int dma_mcux_edma_get_status(const struct device *dev, uint32_t channel,
544748

545749
if (DEV_CHANNEL_DATA(dev, channel)->busy) {
546750
status->busy = true;
547-
/* Be aware that here we need multiply NBYTES for each minor loops */
751+
/* pending_length is in bytes. Multiply remaining major loop
752+
* count by NBYTES for each minor loop
753+
*/
548754
status->pending_length =
549755
EDMA_GetRemainingMajorLoopCount(DEV_BASE(dev), hw_channel) *
550756
DEV_CHANNEL_DATA(dev, channel)->transfer_settings.source_data_size;

0 commit comments

Comments
 (0)