@@ -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
495610static 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
535739cleanup :
@@ -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