@@ -92,6 +92,14 @@ struct dma_mcux_channel_transfer_edma_settings {
92
92
enum dma_channel_direction direction ;
93
93
edma_transfer_type_t transfer_type ;
94
94
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 ;
95
103
};
96
104
97
105
@@ -130,6 +138,35 @@ struct dma_mcux_edma_data {
130
138
(ch % DEV_CFG(dev)->channels_per_mux) ^ (DEV_CFG(dev)->dmamux_reg_offset)
131
139
#endif
132
140
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
+
133
170
/*
134
171
* The hardware channel (takes the gap into account) is used when access DMA registers.
135
172
* 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
178
215
{
179
216
int ret = - EIO ;
180
217
struct call_back * data = (struct call_back * )param ;
181
-
182
218
uint32_t channel = dma_mcux_edma_remove_channel_gap (data -> dev , handle -> channel );
183
219
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 ) {
185
226
/* DMA is no longer busy when there are no remaining TCDs to transfer */
186
227
data -> busy = (handle -> tcdPool != NULL ) && (handle -> tcdUsed > 0 );
187
228
ret = DMA_STATUS_COMPLETE ;
@@ -254,6 +295,7 @@ static int dma_mcux_edma_configure(const struct device *dev, uint32_t channel,
254
295
edma_transfer_type_t transfer_type ;
255
296
unsigned int key ;
256
297
int ret = 0 ;
298
+ edma_tcd_t * tcd = NULL ;
257
299
258
300
if (slot >= DEV_CFG (dev )-> dma_requests ) {
259
301
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,
316
358
data -> transfer_settings .direction = config -> channel_direction ;
317
359
data -> transfer_settings .transfer_type = transfer_type ;
318
360
data -> transfer_settings .valid = true;
319
-
361
+ data -> transfer_settings . cyclic = config -> cyclic ;
320
362
321
363
/* Lock and page in the channel configuration */
322
364
key = irq_lock ();
@@ -357,26 +399,93 @@ static int dma_mcux_edma_configure(const struct device *dev, uint32_t channel,
357
399
LOG_DBG ("channel is %d" , channel );
358
400
EDMA_EnableChannelInterrupts (DEV_BASE (dev ), hw_channel , kEDMA_ErrorInterruptEnable );
359
401
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
+
360
408
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
+
364
414
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 ,
371
418
block_config -> block_size , transfer_type );
372
419
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 ;
378
488
}
379
- block_config = block_config -> next_block ;
380
489
}
381
490
} else {
382
491
/* block_count shall be 1 */
@@ -395,11 +504,8 @@ static int dma_mcux_edma_configure(const struct device *dev, uint32_t channel,
395
504
LOG_ERR ("Error submitting EDMA Transfer: 0x%x" , submit_status );
396
505
ret = - EFAULT ;
397
506
}
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 ));
403
509
}
404
510
405
511
if (config -> dest_chaining_en ) {
@@ -491,11 +597,23 @@ static int dma_mcux_edma_resume(const struct device *dev, uint32_t channel)
491
597
return 0 ;
492
598
}
493
599
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
+ }
494
609
495
610
static int dma_mcux_edma_reload (const struct device * dev , uint32_t channel ,
496
611
uint32_t src , uint32_t dst , size_t size )
497
612
{
498
613
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 ;
499
617
500
618
/* Lock the channel configuration */
501
619
const unsigned int key = irq_lock ();
@@ -507,29 +625,115 @@ static int dma_mcux_edma_reload(const struct device *dev, uint32_t channel,
507
625
goto cleanup ;
508
626
}
509
627
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
+ }
516
634
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
+ }
533
737
}
534
738
535
739
cleanup :
@@ -544,7 +748,9 @@ static int dma_mcux_edma_get_status(const struct device *dev, uint32_t channel,
544
748
545
749
if (DEV_CHANNEL_DATA (dev , channel )-> busy ) {
546
750
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
+ */
548
754
status -> pending_length =
549
755
EDMA_GetRemainingMajorLoopCount (DEV_BASE (dev ), hw_channel ) *
550
756
DEV_CHANNEL_DATA (dev , channel )-> transfer_settings .source_data_size ;
0 commit comments