3737
3838// Transfer mode configuration (MICROPY_HW_SDCARD_NONBLOCKING default in sdcard.h)
3939// Mode 0: SDK blocking (USDHC_TransferBlocking) - simple, no scheduler yields
40- // Mode 1: Interrupt-based non-blocking with callbacks (original, has cache issues)
41- // Mode 2: Polling-based with scheduler yields (no interrupts )
40+ // Mode 1: Interrupt-based non-blocking with callbacks (has cache issues when SDRAM disabled )
41+ // Mode 2: Polling-based with scheduler yields (SDK blocking + wait-for-ready yields )
4242#if MICROPY_HW_SDCARD_NONBLOCKING
4343#ifndef MICROPY_HW_SDCARD_USE_DMA
4444#define MICROPY_HW_SDCARD_USE_DMA (1)
4545#endif
4646
47- // Transfer status flags (Zephyr-style) - only used in interrupt mode
47+ // Transfer status flags - only used in interrupt-based non-blocking mode
4848#define TRANSFER_CMD_COMPLETE (1U << 0)
4949#define TRANSFER_CMD_FAILED (1U << 1)
5050#define TRANSFER_DATA_COMPLETE (1U << 2)
5454#define TRANSFER_DATA_FLAGS (TRANSFER_DATA_COMPLETE | TRANSFER_DATA_FAILED)
5555#endif // MICROPY_HW_SDCARD_NONBLOCKING
5656
57- // Polling mode: use DMA but poll status flags instead of using interrupts
58- // This avoids interrupt-related cache coherency issues while still allowing scheduler yields
57+ // Polling mode: uses SDK blocking transfer but yields to scheduler while waiting for card ready
5958#ifndef MICROPY_HW_SDCARD_POLLING
6059#define MICROPY_HW_SDCARD_POLLING (1)
6160#endif
@@ -305,7 +304,7 @@ void sdcard_dummy_callback(USDHC_Type *base, void *userData) {
305304
306305#if MICROPY_HW_SDCARD_NONBLOCKING
307306
308- // Transfer complete callback (Zephyr-style)
307+ // Transfer complete callback - only used in interrupt-based non-blocking mode
309308static void sdcard_transfer_complete_callback (USDHC_Type * base ,
310309 usdhc_handle_t * handle , status_t status , void * userData ) {
311310 mimxrt_sdcard_obj_t * card = (mimxrt_sdcard_obj_t * )userData ;
@@ -450,47 +449,30 @@ static status_t sdcard_transfer_blocking(USDHC_Type *base, usdhc_transfer_t *tra
450449
451450#elif MICROPY_HW_SDCARD_POLLING
452451// ============================================================================
453- // Polling-based transfer mode: DMA with scheduler-friendly flag polling
454- // This mode manually configures DMA and polls status flags (no interrupts).
455- // Uses mp_event_handle_nowait() in polling loops to allow scheduler yields.
452+ // Polling-based transfer mode: Uses SDK's USDHC_TransferBlocking for correct
453+ // register setup and DMA handling, but yields to scheduler before data transfers
454+ // to allow background tasks to run. The actual DMA transfer uses tight polling
455+ // (fast), but we yield during the wait-for-ready phase.
456456// ============================================================================
457457
458- // Read command response from USDHC registers (mirrors SDK's internal function)
459- static status_t sdcard_polling_receive_response (USDHC_Type * base , usdhc_command_t * command ) {
460- uint32_t response0 = base -> CMD_RSP0 ;
461- uint32_t response1 = base -> CMD_RSP1 ;
462- uint32_t response2 = base -> CMD_RSP2 ;
463-
464- if (command -> responseType != kCARD_ResponseTypeNone ) {
465- command -> response [0U ] = response0 ;
466- if (command -> responseType == kCARD_ResponseTypeR2 ) {
467- // R3-R2-R1-R0 format (CID/CSD response)
468- command -> response [0U ] <<= 8U ;
469- command -> response [1U ] = (response1 << 8U ) | ((response0 & 0xFF000000U ) >> 24U );
470- command -> response [2U ] = (response2 << 8U ) | ((response1 & 0xFF000000U ) >> 24U );
471- command -> response [3U ] = (base -> CMD_RSP3 << 8U ) | ((response2 & 0xFF000000U ) >> 24U );
472- }
473- }
474-
475- // Check response error flags
476- if ((command -> responseErrorFlags != 0U ) &&
477- ((command -> responseType == kCARD_ResponseTypeR1 ) || (command -> responseType == kCARD_ResponseTypeR1b ) ||
478- (command -> responseType == kCARD_ResponseTypeR6 ) || (command -> responseType == kCARD_ResponseTypeR5 ))) {
479- if (((command -> responseErrorFlags ) & (command -> response [0U ])) != 0U ) {
480- return kStatus_USDHC_SendCommandFailed ;
458+ // Wait for card to be ready by polling Data0 line.
459+ // Data0 low indicates card is busy. Yields to scheduler while waiting.
460+ static bool sdcard_polling_wait_data0_ready (USDHC_Type * base , uint32_t timeout_ms ) {
461+ uint32_t start = mp_hal_ticks_ms ();
462+ while ((mp_hal_ticks_ms () - start ) < timeout_ms ) {
463+ if ((USDHC_GetPresentStatusFlags (base ) & (uint32_t )kUSDHC_Data0LineLevelFlag ) != 0 ) {
464+ return true;
481465 }
466+ mp_event_handle_nowait ();
482467 }
483-
484- return kStatus_Success ;
468+ return false;
485469}
486470
487- // Polling-based transfer with scheduler yields
471+ // Scheduler-friendly blocking transfer.
472+ // Uses SDK's USDHC_TransferBlocking which handles all the complex register setup,
473+ // but waits for card ready state with scheduler yields beforehand.
488474static status_t sdcard_transfer_blocking (USDHC_Type * base , usdhc_transfer_t * transfer , uint32_t timeout_ms ) {
489- usdhc_command_t * command = transfer -> command ;
490475 usdhc_data_t * data = transfer -> data ;
491- status_t error = kStatus_Success ;
492- uint32_t interruptStatus = 0U ;
493- uint32_t start_ms ;
494476
495477 usdhc_adma_config_t dma_config = {
496478 .dmaMode = kUSDHC_DmaModeAdma2 ,
@@ -501,144 +483,21 @@ static status_t sdcard_transfer_blocking(USDHC_Type *base, usdhc_transfer_t *tra
501483 .admaTableWords = DMA_DESCRIPTOR_BUFFER_SIZE ,
502484 };
503485
504- // Check for re-tuning request
505- #if defined(FSL_FEATURE_USDHC_HAS_SDR50_MODE ) && (FSL_FEATURE_USDHC_HAS_SDR50_MODE )
506- if ((USDHC_GetInterruptStatusFlags (base ) & (uint32_t )kUSDHC_ReTuningEventFlag ) != 0UL ) {
507- USDHC_ClearInterruptStatusFlags (base , kUSDHC_ReTuningEventFlag );
508- return kStatus_USDHC_ReTuningRequest ;
509- }
510- #endif
511-
512- // Configure DMA for data transfers
513- if (data != NULL ) {
514- // Check data inhibit flag before configuring data transfer
515- if ((USDHC_GetPresentStatusFlags (base ) & (uint32_t )kUSDHC_DataInhibitFlag ) != 0U ) {
516- return kStatus_USDHC_BusyTransferring ;
517- }
518-
519- error = USDHC_SetAdmaTableConfig (base , & dma_config , data , kUSDHC_AdmaDescriptorSingleFlag );
520- if (error != kStatus_Success ) {
521- return kStatus_USDHC_PrepareAdmaDescriptorFailed ;
522- }
523-
524- // Configure data transfer parameters
525- // Note: USDHC_SetDataConfig doesn't set all required flags, so we configure MIX_CTRL directly
526- usdhc_transfer_direction_t direction = (data -> txData != NULL ) ?
527- kUSDHC_TransferDirectionSend : kUSDHC_TransferDirectionReceive ;
528-
529- // Set block attributes
530- base -> BLK_ATT = ((base -> BLK_ATT & ~(USDHC_BLK_ATT_BLKSIZE_MASK | USDHC_BLK_ATT_BLKCNT_MASK )) |
531- (USDHC_BLK_ATT_BLKSIZE (data -> blockSize ) | USDHC_BLK_ATT_BLKCNT (data -> blockCount )));
532-
533- // Configure MIX_CTRL for data transfer
534- uint32_t mixCtrl = base -> MIX_CTRL ;
535- mixCtrl &= ~(USDHC_MIX_CTRL_MSBSEL_MASK | USDHC_MIX_CTRL_BCEN_MASK | USDHC_MIX_CTRL_DTDSEL_MASK |
536- USDHC_MIX_CTRL_AC12EN_MASK | USDHC_MIX_CTRL_AC23EN_MASK );
537-
538- // Set direction
539- mixCtrl |= USDHC_MIX_CTRL_DTDSEL (direction );
540-
541- // Multi-block transfers need MSBSEL and BCEN
542- if (data -> blockCount > 1U ) {
543- mixCtrl |= USDHC_MIX_CTRL_MSBSEL_MASK | USDHC_MIX_CTRL_BCEN_MASK ;
544- if (data -> enableAutoCommand12 ) {
545- mixCtrl |= USDHC_MIX_CTRL_AC12EN_MASK ;
546- }
547- }
548-
549- // Auto CMD23 setup
550- if (data -> enableAutoCommand23 ) {
551- mixCtrl |= USDHC_MIX_CTRL_AC23EN_MASK ;
552- base -> VEND_SPEC2 |= USDHC_VEND_SPEC2_ACMD23_ARGU2_EN_MASK ;
553- base -> DS_ADDR = data -> blockCount ;
554- } else {
555- base -> VEND_SPEC2 &= ~USDHC_VEND_SPEC2_ACMD23_ARGU2_EN_MASK ;
556- }
557-
558- base -> MIX_CTRL = mixCtrl ;
559-
560- // Enable DMA
561- USDHC_EnableInternalDMA (base , true);
562-
563- // Set data present flag for command
564- command -> flags |= (uint32_t )kUSDHC_DataPresentFlag ;
565- } else {
566- // Command-only: check command inhibit flag
567- if ((USDHC_GetPresentStatusFlags (base ) & (uint32_t )kUSDHC_CommandInhibitFlag ) != 0U ) {
568- return kStatus_USDHC_BusyTransferring ;
569- }
570-
571- // Clear data flags for command-only transfers
572- uint32_t mixCtrl = base -> MIX_CTRL ;
573- mixCtrl &= ~(USDHC_MIX_CTRL_MSBSEL_MASK | USDHC_MIX_CTRL_BCEN_MASK | USDHC_MIX_CTRL_DTDSEL_MASK |
574- USDHC_MIX_CTRL_AC12EN_MASK | USDHC_MIX_CTRL_AC23EN_MASK );
575- base -> MIX_CTRL = mixCtrl ;
576- }
577-
578- // Clear any pending status flags before starting
579- USDHC_ClearInterruptStatusFlags (base , kUSDHC_CommandFlag | kUSDHC_DataDMAFlag );
580-
581- // Send command
582- USDHC_SendCommand (base , command );
583-
584- // Poll for command completion with scheduler yields
585- start_ms = mp_hal_ticks_ms ();
586- while (!(interruptStatus & kUSDHC_CommandFlag )) {
587- interruptStatus = USDHC_GetInterruptStatusFlags (base );
588- if ((mp_hal_ticks_ms () - start_ms ) >= timeout_ms ) {
589- sdcard_error_recovery (base );
590- return kStatus_Timeout ;
591- }
592- mp_event_handle_nowait (); // Allow scheduler to run
593- }
486+ // For data transfers, wait for card to be ready (yields to scheduler)
487+ bool has_data = (data != NULL ) && ((data -> txData != NULL ) || (data -> rxData != NULL ));
594488
595- // Check for command errors
596- if ((interruptStatus & (uint32_t )kUSDHC_CommandErrorFlag ) != 0UL ) {
597- USDHC_ClearInterruptStatusFlags (base , kUSDHC_CommandFlag );
598- sdcard_error_recovery (base );
599- return kStatus_USDHC_SendCommandFailed ;
489+ if (has_data && !sdcard_polling_wait_data0_ready (base , timeout_ms )) {
490+ return kStatus_Timeout ;
600491 }
601492
602- // Read command response
603- error = sdcard_polling_receive_response ( base , command );
604- USDHC_ClearInterruptStatusFlags (base , kUSDHC_CommandFlag );
493+ // Use SDK blocking transfer - handles complex register setup correctly
494+ // and polls hardware status flags directly (no interrupts/callbacks)
495+ status_t error = USDHC_TransferBlocking (base , & dma_config , transfer );
605496
606497 if (error != kStatus_Success ) {
607498 sdcard_error_recovery (base );
608- return kStatus_USDHC_SendCommandFailed ;
609499 }
610-
611- // Poll for data DMA completion with scheduler yields
612- if (data != NULL ) {
613- interruptStatus = 0U ;
614- start_ms = mp_hal_ticks_ms ();
615- while (!(interruptStatus & (kUSDHC_DataDMAFlag | kUSDHC_TuningErrorFlag ))) {
616- interruptStatus = USDHC_GetInterruptStatusFlags (base );
617- if ((mp_hal_ticks_ms () - start_ms ) >= timeout_ms ) {
618- sdcard_error_recovery (base );
619- return kStatus_Timeout ;
620- }
621- mp_event_handle_nowait (); // Allow scheduler to run
622- }
623-
624- #if defined(FSL_FEATURE_USDHC_HAS_SDR50_MODE ) && (FSL_FEATURE_USDHC_HAS_SDR50_MODE )
625- if (interruptStatus & kUSDHC_TuningErrorFlag ) {
626- USDHC_ClearInterruptStatusFlags (base , kUSDHC_TuningErrorFlag );
627- sdcard_error_recovery (base );
628- return kStatus_USDHC_TuningError ;
629- }
630- #endif
631-
632- if (interruptStatus & ((uint32_t )kUSDHC_DataErrorFlag | (uint32_t )kUSDHC_DmaErrorFlag )) {
633- USDHC_ClearInterruptStatusFlags (base , kUSDHC_DataDMAFlag | kUSDHC_TuningErrorFlag );
634- sdcard_error_recovery (base );
635- return kStatus_USDHC_TransferDataFailed ;
636- }
637-
638- USDHC_ClearInterruptStatusFlags (base , kUSDHC_DataDMAFlag | kUSDHC_TuningErrorFlag );
639- }
640-
641- return kStatus_Success ;
500+ return error ;
642501}
643502
644503#else // !MICROPY_HW_SDCARD_NONBLOCKING && !MICROPY_HW_SDCARD_POLLING
@@ -1201,14 +1060,14 @@ void sdcard_init(mimxrt_sdcard_obj_t *card, uint32_t base_clk) {
12011060 #if MICROPY_HW_SDCARD_NONBLOCKING
12021061 .TransferComplete = sdcard_transfer_complete_callback ,
12031062 #else
1204- .TransferComplete = NULL , // Not used - we use blocking/polling transfers
1063+ .TransferComplete = NULL , // Not used - polling/blocking modes don't use callbacks
12051064 #endif
12061065 .ReTuning = sdcard_dummy_callback ,
12071066 };
12081067
12091068 #if MICROPY_HW_SDCARD_NONBLOCKING
12101069 // Enable USDHC interrupt for interrupt-based non-blocking transfers
1211- IRQn_Type irqn ;
1070+ IRQn_Type irqn = 0 ;
12121071 #if defined MICROPY_USDHC1 && USDHC1_AVAIL
12131072 if (card -> usdhc_inst == USDHC1 ) {
12141073 irqn = USDHC1_IRQn ;
0 commit comments