Skip to content

Commit 6234090

Browse files
committed
mimxrt/sdcard: Improve robustness of sdcard driver.
Add ERR050396 workaround (GPR28/GPR13 cache bypass) for additional safety. Signed-off-by: Andrew Leech <[email protected]>
1 parent 5dd59d4 commit 6234090

File tree

2 files changed

+132
-50
lines changed

2 files changed

+132
-50
lines changed

ports/mimxrt/machine_sdcard.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ static MP_DEFINE_CONST_FUN_OBJ_1(machine_sdcard_info_obj, machine_sdcard_info);
142142
static mp_obj_t machine_sdcard_writeblocks(mp_obj_t self_in, mp_obj_t block_num, mp_obj_t buf) {
143143
mp_buffer_info_t bufinfo;
144144
mimxrt_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in);
145-
mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_WRITE);
145+
mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_READ);
146146

147147
if (sdcard_write(self, bufinfo.buf, mp_obj_get_int(block_num), bufinfo.len / SDCARD_DEFAULT_BLOCK_SIZE)) {
148148
return MP_OBJ_NEW_SMALL_INT(0);

ports/mimxrt/sdcard.c

Lines changed: 131 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -211,18 +211,14 @@ mimxrt_sdcard_obj_t mimxrt_sdcard_objs[] =
211211
// ---
212212
// Local function declarations
213213
// ---
214-
static status_t sdcard_transfer_blocking(USDHC_Type *base,
215-
usdhc_handle_t *handle,
216-
usdhc_transfer_t *transfer,
217-
uint32_t timeout_ms);
214+
static status_t sdcard_transfer_blocking(USDHC_Type *base, usdhc_transfer_t *transfer, uint32_t timeout_ms);
218215
static void sdcard_decode_csd(mimxrt_sdcard_obj_t *sdcard, csd_t *csd);
219216
static bool sdcard_reset(mimxrt_sdcard_obj_t *card);
220217
static inline void sdcard_init_pin(const machine_pin_obj_t *pin, uint8_t af_idx, uint32_t config_value);
221218

222219
// SD Card interrupt callbacks
223220
void sdcard_card_inserted_callback(USDHC_Type *base, void *userData);
224221
void sdcard_card_removed_callback(USDHC_Type *base, void *userData);
225-
void sdcard_transfer_complete_callback(USDHC_Type *base, usdhc_handle_t *handle, status_t status, void *userData);
226222
void sdcard_dummy_callback(USDHC_Type *base, void *userData);
227223

228224
// SD Card commands
@@ -237,6 +233,8 @@ static bool sdcard_cmd_send_csd(mimxrt_sdcard_obj_t *card, csd_t *csd);
237233
static bool sdcard_cmd_select_card(mimxrt_sdcard_obj_t *sdcard);
238234
static bool sdcard_cmd_set_blocklen(mimxrt_sdcard_obj_t *sdcard);
239235
static bool sdcard_cmd_set_bus_width(mimxrt_sdcard_obj_t *sdcard, usdhc_data_bus_width_t bus_width);
236+
static bool sdcard_cmd_send_status(mimxrt_sdcard_obj_t *card);
237+
static bool sdcard_wait_ready(mimxrt_sdcard_obj_t *card, uint32_t timeout_ms);
240238

241239
void sdcard_card_inserted_callback(USDHC_Type *base, void *userData) {
242240
#if defined MICROPY_USDHC1 && USDHC1_AVAIL
@@ -291,38 +289,43 @@ static void sdcard_error_recovery(USDHC_Type *base) {
291289
}
292290
}
293291

294-
static status_t sdcard_transfer_blocking(USDHC_Type *base, usdhc_handle_t *handle, usdhc_transfer_t *transfer, uint32_t timeout_ms) {
295-
status_t status;
292+
// Wait for card to be ready by polling Data0 line.
293+
// Data0 low indicates card is busy.
294+
static bool sdcard_wait_data0_ready(USDHC_Type *base, uint32_t timeout_ms) {
295+
for (uint32_t i = 0; i < timeout_ms; i++) {
296+
if ((USDHC_GetPresentStatusFlags(base) & (uint32_t)kUSDHC_Data0LineLevelFlag) != 0) {
297+
return true;
298+
}
299+
mp_hal_delay_ms(1);
300+
}
301+
return false;
302+
}
296303

297-
usdhc_adma_config_t dma_config;
304+
// Wrapper around SDK's USDHC_TransferBlocking.
305+
// Waits for card to be ready before starting transfer.
306+
static status_t sdcard_transfer_blocking(USDHC_Type *base, usdhc_transfer_t *transfer, uint32_t timeout_ms) {
307+
usdhc_adma_config_t dma_config = {
308+
.dmaMode = kUSDHC_DmaModeAdma2,
309+
#if !(defined(FSL_FEATURE_USDHC_HAS_NO_RW_BURST_LEN) && FSL_FEATURE_USDHC_HAS_NO_RW_BURST_LEN)
310+
.burstLen = kUSDHC_EnBurstLenForINCR,
311+
#endif
312+
.admaTable = sdcard_adma_descriptor_table,
313+
.admaTableWords = DMA_DESCRIPTOR_BUFFER_SIZE,
314+
};
298315

299-
(void)memset(&dma_config, 0, sizeof(usdhc_adma_config_t));
300-
dma_config.dmaMode = kUSDHC_DmaModeAdma2;
316+
// For command-only transfers (no data), skip the busy wait
317+
bool has_data = (transfer->data != NULL) &&
318+
((transfer->data->txData != NULL) || (transfer->data->rxData != NULL));
301319

302-
#if !(defined(FSL_FEATURE_USDHC_HAS_NO_RW_BURST_LEN) && FSL_FEATURE_USDHC_HAS_NO_RW_BURST_LEN)
303-
dma_config.burstLen = kUSDHC_EnBurstLenForINCR;
304-
#endif
320+
if (has_data && !sdcard_wait_data0_ready(base, timeout_ms)) {
321+
return kStatus_Timeout;
322+
}
305323

306-
dma_config.admaTable = sdcard_adma_descriptor_table;
307-
dma_config.admaTableWords = DMA_DESCRIPTOR_BUFFER_SIZE;
308-
309-
// Wait while the card is busy before a transfer
310-
status = kStatus_Timeout;
311-
for (int i = 0; i < timeout_ms * 100; i++) {
312-
// Wait until Data0 is low any more. Low indicates "Busy".
313-
if (((transfer->data->txData == NULL) && (transfer->data->rxData == NULL)) ||
314-
(USDHC_GetPresentStatusFlags(base) & (uint32_t)kUSDHC_Data0LineLevelFlag) != 0) {
315-
// Not busy anymore or no TX-Data
316-
status = USDHC_TransferBlocking(base, &dma_config, transfer);
317-
if (status != kStatus_Success) {
318-
sdcard_error_recovery(base);
319-
}
320-
break;
321-
}
322-
ticks_delay_us64(10);
324+
status_t status = USDHC_TransferBlocking(base, &dma_config, transfer);
325+
if (status != kStatus_Success) {
326+
sdcard_error_recovery(base);
323327
}
324328
return status;
325-
326329
}
327330

328331
static void sdcard_decode_csd(mimxrt_sdcard_obj_t *card, csd_t *csd) {
@@ -381,7 +384,7 @@ static bool sdcard_cmd_go_idle_state(mimxrt_sdcard_obj_t *card) {
381384
.command = &command,
382385
};
383386

384-
status = sdcard_transfer_blocking(card->usdhc_inst, &card->handle, &transfer, 250);
387+
status = sdcard_transfer_blocking(card->usdhc_inst, &transfer, 250);
385388

386389
if (status == kStatus_Success) {
387390
return true;
@@ -403,7 +406,7 @@ static bool sdcard_cmd_oper_cond(mimxrt_sdcard_obj_t *card) {
403406
.command = &command,
404407
};
405408

406-
status = sdcard_transfer_blocking(card->usdhc_inst, &card->handle, &transfer, 250);
409+
status = sdcard_transfer_blocking(card->usdhc_inst, &transfer, 250);
407410

408411
if (status == kStatus_Success) {
409412
card->oper_cond = command.response[0];
@@ -426,7 +429,7 @@ static bool sdcard_cmd_app_cmd(mimxrt_sdcard_obj_t *card) {
426429
.command = &command,
427430
};
428431

429-
status = sdcard_transfer_blocking(card->usdhc_inst, &card->handle, &transfer, 250);
432+
status = sdcard_transfer_blocking(card->usdhc_inst, &transfer, 250);
430433

431434
if (status == kStatus_Success) {
432435
card->status = command.response[0];
@@ -454,7 +457,7 @@ static bool sdcard_cmd_sd_app_op_cond(mimxrt_sdcard_obj_t *card, uint32_t argume
454457
.command = &command,
455458
};
456459

457-
status = sdcard_transfer_blocking(card->usdhc_inst, &card->handle, &transfer, 250);
460+
status = sdcard_transfer_blocking(card->usdhc_inst, &transfer, 250);
458461

459462
if (status == kStatus_Success) {
460463
card->oper_cond = command.response[0];
@@ -477,7 +480,7 @@ static bool sdcard_cmd_all_send_cid(mimxrt_sdcard_obj_t *card, cid_t *cid) {
477480
.command = &command,
478481
};
479482

480-
status = sdcard_transfer_blocking(card->usdhc_inst, &card->handle, &transfer, 250);
483+
status = sdcard_transfer_blocking(card->usdhc_inst, &transfer, 250);
481484

482485
if (status == kStatus_Success) {
483486
cid->mdt = (uint16_t)((command.response[0] & 0xFFF00U) >> 8U);
@@ -510,7 +513,7 @@ static bool sdcard_cmd_send_cid(mimxrt_sdcard_obj_t *card, cid_t *cid) {
510513
.command = &command,
511514
};
512515

513-
status = sdcard_transfer_blocking(card->usdhc_inst, &card->handle, &transfer, 250);
516+
status = sdcard_transfer_blocking(card->usdhc_inst, &transfer, 250);
514517

515518
if (status == kStatus_Success) {
516519
cid->mdt = (uint16_t)((command.response[0] & 0xFFF00U) >> 8U);
@@ -543,7 +546,7 @@ static bool sdcard_cmd_set_rel_add(mimxrt_sdcard_obj_t *card) {
543546
.command = &command,
544547
};
545548

546-
status = sdcard_transfer_blocking(card->usdhc_inst, &card->handle, &transfer, 250);
549+
status = sdcard_transfer_blocking(card->usdhc_inst, &transfer, 250);
547550

548551
if (status == kStatus_Success) {
549552
card->rca = 0xFFFFFFFF & (command.response[0] >> 16);
@@ -566,7 +569,7 @@ static bool sdcard_cmd_send_csd(mimxrt_sdcard_obj_t *card, csd_t *csd) {
566569
.command = &command,
567570
};
568571

569-
status = sdcard_transfer_blocking(card->usdhc_inst, &card->handle, &transfer, 250);
572+
status = sdcard_transfer_blocking(card->usdhc_inst, &transfer, 250);
570573

571574
if (status == kStatus_Success) {
572575
csd->data[0] = command.response[0];
@@ -593,7 +596,7 @@ static bool sdcard_cmd_select_card(mimxrt_sdcard_obj_t *card) {
593596
.command = &command,
594597
};
595598

596-
status = sdcard_transfer_blocking(card->usdhc_inst, &card->handle, &transfer, 250);
599+
status = sdcard_transfer_blocking(card->usdhc_inst, &transfer, 250);
597600

598601
if (status == kStatus_Success) {
599602
card->status = command.response[0];
@@ -617,7 +620,7 @@ static bool sdcard_cmd_set_blocklen(mimxrt_sdcard_obj_t *card) {
617620
.command = &command,
618621
};
619622

620-
status = sdcard_transfer_blocking(card->usdhc_inst, &card->handle, &transfer, 250);
623+
status = sdcard_transfer_blocking(card->usdhc_inst, &transfer, 250);
621624

622625
if (status == kStatus_Success) {
623626
card->status = command.response[0];
@@ -652,7 +655,31 @@ static bool sdcard_cmd_set_bus_width(mimxrt_sdcard_obj_t *card, usdhc_data_bus_w
652655
.command = &command,
653656
};
654657

655-
status = sdcard_transfer_blocking(card->usdhc_inst, &card->handle, &transfer, 250);
658+
status = sdcard_transfer_blocking(card->usdhc_inst, &transfer, 250);
659+
660+
if (status == kStatus_Success) {
661+
card->status = command.response[0];
662+
return true;
663+
} else {
664+
return false;
665+
}
666+
}
667+
668+
static bool sdcard_cmd_send_status(mimxrt_sdcard_obj_t *card) {
669+
status_t status;
670+
usdhc_command_t command = {
671+
.index = SDCARD_CMD_SEND_STATUS,
672+
.argument = (card->rca << 16),
673+
.type = kCARD_CommandTypeNormal,
674+
.responseType = kCARD_ResponseTypeR1,
675+
.responseErrorFlags = SDMMC_R1_ALL_ERROR_FLAG,
676+
};
677+
usdhc_transfer_t transfer = {
678+
.data = NULL,
679+
.command = &command,
680+
};
681+
682+
status = sdcard_transfer_blocking(card->usdhc_inst, &transfer, 250);
656683

657684
if (status == kStatus_Success) {
658685
card->status = command.response[0];
@@ -662,6 +689,30 @@ static bool sdcard_cmd_set_bus_width(mimxrt_sdcard_obj_t *card, usdhc_data_bus_w
662689
}
663690
}
664691

692+
static bool sdcard_wait_ready(mimxrt_sdcard_obj_t *card, uint32_t timeout_ms) {
693+
uint32_t start = mp_hal_ticks_ms();
694+
695+
while ((mp_hal_ticks_ms() - start) < timeout_ms) {
696+
if (!sdcard_cmd_send_status(card)) {
697+
return false;
698+
}
699+
700+
// Check if card is ready for data (bit 8) and in transfer state (bits 9-12 == 4)
701+
uint32_t card_state = SDMMC_R1_CURRENT_STATE(card->status);
702+
bool ready_for_data = (card->status & SDMMC_MASK(SDCARD_STATUS_READY_FOR_DATA_SHIFT)) != 0;
703+
704+
if (ready_for_data && (card_state == SDCARD_STATE_TRANSFER)) {
705+
return true;
706+
}
707+
708+
// Allow other MicroPython tasks to run while polling
709+
MICROPY_EVENT_POLL_HOOK;
710+
mp_hal_delay_ms(1); // Wait 1ms between polls
711+
}
712+
713+
return false; // Timeout
714+
}
715+
665716
static bool sdcard_reset(mimxrt_sdcard_obj_t *card) {
666717
card->block_len = SDCARD_DEFAULT_BLOCK_SIZE;
667718
card->rca = 0UL;
@@ -708,6 +759,19 @@ void sdcard_init(mimxrt_sdcard_obj_t *card, uint32_t base_clk) {
708759

709760
#endif // MIMXRT117x_SERIES
710761

762+
// ERR050396 workaround: Disable cacheable AXI transactions for USDHC
763+
// This is critical when GC heap is in cacheable OCRAM (SDRAM disabled).
764+
// The GPR28/GPR13 ARCACHE/AWCACHE bits control USDHC's AXI cache attributes
765+
// at the hardware level, ensuring DMA accesses bypass the L1 D-cache.
766+
#ifdef MIMXRT117x_SERIES
767+
IOMUXC_GPR->GPR28 &= ~(IOMUXC_GPR_GPR28_ARCACHE_USDHC_MASK | IOMUXC_GPR_GPR28_AWCACHE_USDHC_MASK);
768+
#else
769+
#if defined(IOMUXC_GPR_GPR13_ARCACHE_USDHC_MASK)
770+
IOMUXC_GPR->GPR13 &= ~(IOMUXC_GPR_GPR13_ARCACHE_USDHC_MASK | IOMUXC_GPR_GPR13_AWCACHE_USDHC_MASK);
771+
#endif
772+
#endif
773+
__DSB(); // Ensure GPR setting completes before USDHC initialization
774+
711775
// Initialize USDHC
712776
const usdhc_config_t config = {
713777
.endianMode = kUSDHC_EndianModeLittle,
@@ -725,6 +789,7 @@ void sdcard_init(mimxrt_sdcard_obj_t *card, uint32_t base_clk) {
725789
.CardRemoved = sdcard_card_removed_callback,
726790
.SdioInterrupt = sdcard_dummy_callback,
727791
.BlockGap = sdcard_dummy_callback,
792+
.TransferComplete = NULL, // Not used - we use blocking transfers
728793
.ReTuning = sdcard_dummy_callback,
729794
};
730795

@@ -779,6 +844,10 @@ bool sdcard_read(mimxrt_sdcard_obj_t *card, uint8_t *buffer, uint32_t block_num,
779844
return false;
780845
}
781846

847+
// Ensure cache is flushed and invalidated so when DMA updates RAM
848+
// from the peripheral, the CPU reads the new data.
849+
MP_HAL_CLEANINVALIDATE_DCACHE(buffer, block_count * card->block_len);
850+
782851
usdhc_data_t data = {
783852
.enableAutoCommand12 = true,
784853
.enableAutoCommand23 = false,
@@ -803,10 +872,13 @@ bool sdcard_read(mimxrt_sdcard_obj_t *card, uint8_t *buffer, uint32_t block_num,
803872
.command = &command,
804873
};
805874

806-
status_t status = sdcard_transfer_blocking(card->usdhc_inst, &card->handle, &transfer, 500);
875+
status_t status = sdcard_transfer_blocking(card->usdhc_inst, &transfer, 500);
807876

808877
if (status == kStatus_Success) {
809878
card->status = command.response[0];
879+
// Invalidate cache again after DMA completes to discard any speculative
880+
// cache line fills that may have occurred during the transfer.
881+
MP_HAL_CLEANINVALIDATE_DCACHE(buffer, block_count * card->block_len);
810882
return true;
811883
} else {
812884
return false;
@@ -818,6 +890,9 @@ bool sdcard_write(mimxrt_sdcard_obj_t *card, uint8_t *buffer, uint32_t block_num
818890
return false;
819891
}
820892

893+
// Ensure cache is flushed to RAM so DMA can read the correct data.
894+
MP_HAL_CLEAN_DCACHE(buffer, block_count * card->block_len);
895+
821896
usdhc_data_t data = {
822897
.enableAutoCommand12 = true,
823898
.enableAutoCommand23 = false,
@@ -842,14 +917,21 @@ bool sdcard_write(mimxrt_sdcard_obj_t *card, uint8_t *buffer, uint32_t block_num
842917
.command = &command,
843918
};
844919

845-
status_t status = sdcard_transfer_blocking(card->usdhc_inst, &card->handle, &transfer, 3000);
920+
status_t status = sdcard_transfer_blocking(card->usdhc_inst, &transfer, 3000);
846921

847-
if (status == kStatus_Success) {
848-
card->status = command.response[0];
849-
return true;
850-
} else {
922+
if (status != kStatus_Success) {
851923
return false;
852924
}
925+
926+
card->status = command.response[0];
927+
928+
// Wait for the card to complete programming the data to flash.
929+
// The transfer above only ensures the data has been sent to the card's buffer.
930+
if (!sdcard_wait_ready(card, 5000)) {
931+
return false;
932+
}
933+
934+
return true;
853935
}
854936

855937
bool sdcard_set_active(mimxrt_sdcard_obj_t *card) {
@@ -873,7 +955,7 @@ bool sdcard_probe_bus_voltage(mimxrt_sdcard_obj_t *card) {
873955
/* Get operating voltage*/
874956
valid_voltage = (((card->oper_cond >> 31U) == 1U) ? true : false);
875957
count++;
876-
ticks_delay_us64(1000);
958+
mp_hal_delay_ms(1);
877959
}
878960

879961
if (count >= SDCARD_MAX_VOLT_TRIAL) {

0 commit comments

Comments
 (0)