|
38 | 38 | #define SDCARD_SWITCH_1_8V_CAPACITY ((uint32_t)0x01000000U) |
39 | 39 | #define SDCARD_MAX_VOLT_TRIAL ((uint32_t)0x000000FFU) |
40 | 40 |
|
| 41 | +// Transfer status flags |
| 42 | +#define SDCARD_TRANSFER_CMD_COMPLETE (1U << 0) |
| 43 | +#define SDCARD_TRANSFER_DATA_COMPLETE (1U << 1) |
| 44 | +#define SDCARD_TRANSFER_ERROR (1U << 2) |
| 45 | + |
41 | 46 | // Error |
42 | 47 | #define SDCARD_STATUS_OUT_OF_RANGE_SHIFT (31U) |
43 | 48 | #define SDCARD_STATUS_ADDRESS_ERROR_SHIFT (30U) |
@@ -275,6 +280,20 @@ void sdcard_dummy_callback(USDHC_Type *base, void *userData) { |
275 | 280 | return; |
276 | 281 | } |
277 | 282 |
|
| 283 | +void sdcard_transfer_complete_callback(USDHC_Type *base, usdhc_handle_t *handle, status_t status, void *userData) { |
| 284 | + mimxrt_sdcard_obj_t *card = (mimxrt_sdcard_obj_t *)userData; |
| 285 | + |
| 286 | + if (status == kStatus_USDHC_TransferDataComplete) { |
| 287 | + card->state->transfer_status |= SDCARD_TRANSFER_DATA_COMPLETE; |
| 288 | + } else if (status == kStatus_USDHC_SendCommandSuccess) { |
| 289 | + card->state->transfer_status |= SDCARD_TRANSFER_CMD_COMPLETE; |
| 290 | + } else if (status != kStatus_USDHC_BusyTransferring) { |
| 291 | + card->state->transfer_error = status; |
| 292 | + __DSB(); // Ensure error is visible before status flag |
| 293 | + card->state->transfer_status |= SDCARD_TRANSFER_ERROR; |
| 294 | + } |
| 295 | +} |
| 296 | + |
278 | 297 | static void sdcard_error_recovery(USDHC_Type *base) { |
279 | 298 | uint32_t status = 0U; |
280 | 299 | /* get host present status */ |
@@ -306,23 +325,46 @@ static status_t sdcard_transfer_blocking(USDHC_Type *base, usdhc_handle_t *handl |
306 | 325 | dma_config.admaTable = sdcard_adma_descriptor_table; |
307 | 326 | dma_config.admaTableWords = DMA_DESCRIPTOR_BUFFER_SIZE; |
308 | 327 |
|
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); |
| 328 | + // Get the card object from handle's userData |
| 329 | + mimxrt_sdcard_obj_t *card = (mimxrt_sdcard_obj_t *)handle->userData; |
| 330 | + |
| 331 | + // Clear transfer status flags |
| 332 | + card->state->transfer_status = 0; |
| 333 | + card->state->transfer_error = kStatus_Success; |
| 334 | + |
| 335 | + // Determine expected completion flags |
| 336 | + uint32_t expected_flags = SDCARD_TRANSFER_CMD_COMPLETE; |
| 337 | + if (transfer->data != NULL) { |
| 338 | + expected_flags |= SDCARD_TRANSFER_DATA_COMPLETE; |
| 339 | + } |
| 340 | + |
| 341 | + // Start non-blocking transfer |
| 342 | + status = USDHC_TransferNonBlocking(base, handle, &dma_config, transfer); |
| 343 | + if (status != kStatus_Success) { |
| 344 | + return status; |
| 345 | + } |
| 346 | + |
| 347 | + // Wait for transfer completion with timeout |
| 348 | + // Read transfer_status into local variable to avoid race with ISR between condition checks |
| 349 | + uint32_t start = mp_hal_ticks_ms(); |
| 350 | + uint32_t xfer_status; |
| 351 | + while (((xfer_status = card->state->transfer_status) != expected_flags) && |
| 352 | + !(xfer_status & SDCARD_TRANSFER_ERROR) && |
| 353 | + (mp_hal_ticks_ms() - start) < timeout_ms) { |
| 354 | + MICROPY_EVENT_POLL_HOOK; |
| 355 | + } |
| 356 | + |
| 357 | + if (xfer_status == 0) { |
| 358 | + // Timeout - no status flags set |
| 359 | + sdcard_error_recovery(base); |
| 360 | + return kStatus_Timeout; |
| 361 | + } else if (xfer_status != expected_flags) { |
| 362 | + // Error occurred |
| 363 | + sdcard_error_recovery(base); |
| 364 | + return card->state->transfer_error; |
323 | 365 | } |
324 | | - return status; |
325 | 366 |
|
| 367 | + return kStatus_Success; |
326 | 368 | } |
327 | 369 |
|
328 | 370 | static void sdcard_decode_csd(mimxrt_sdcard_obj_t *card, csd_t *csd) { |
@@ -725,10 +767,11 @@ void sdcard_init(mimxrt_sdcard_obj_t *card, uint32_t base_clk) { |
725 | 767 | .CardRemoved = sdcard_card_removed_callback, |
726 | 768 | .SdioInterrupt = sdcard_dummy_callback, |
727 | 769 | .BlockGap = sdcard_dummy_callback, |
| 770 | + .TransferComplete = sdcard_transfer_complete_callback, |
728 | 771 | .ReTuning = sdcard_dummy_callback, |
729 | 772 | }; |
730 | 773 |
|
731 | | - USDHC_TransferCreateHandle(card->usdhc_inst, &card->handle, &callbacks, NULL); |
| 774 | + USDHC_TransferCreateHandle(card->usdhc_inst, &card->handle, &callbacks, card); |
732 | 775 | } |
733 | 776 |
|
734 | 777 | void sdcard_deinit(mimxrt_sdcard_obj_t *card) { |
@@ -987,7 +1030,7 @@ bool sdcard_power_on(mimxrt_sdcard_obj_t *card) { |
987 | 1030 |
|
988 | 1031 | bool sdcard_power_off(mimxrt_sdcard_obj_t *card) { |
989 | 1032 | // Only send GO_IDLE_STATE command if card was successfully initialized |
990 | | - // Sending commands to a non-existent card will deadlock waiting for response |
| 1033 | + // Sending commands to a non-existent card can timeout/hang waiting for response |
991 | 1034 | if (card->state->initialized) { |
992 | 1035 | (void)sdcard_cmd_go_idle_state(card); |
993 | 1036 | } |
|
0 commit comments