Skip to content

Commit d18a77e

Browse files
projectgusiabdalkader
authored andcommitted
mimxrt: Workaround DCache invalidation problems with SDCard operations.
- Invalidate the DCache after reads complete, to avoid stale cache lines that were read during the operation (the driver already cleans the cache before the operation). - Disable DMA if the read buffer isn't DCache aligned, to avoid corruption if the CPU writes to the same cache line as the buffer during the operation. The sdcard_dma_align.py test added in the parent commit is fixed by this commit. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton <angus@redyak.com.au>
1 parent 0f831e8 commit d18a77e

1 file changed

Lines changed: 31 additions & 11 deletions

File tree

ports/mimxrt/sdcard.c

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
#include "sdcard.h"
3232
#include "ticks.h"
33+
#include "fsl_cache.h"
3334
#include "fsl_iomuxc.h"
3435
#include "pin.h"
3536

@@ -294,33 +295,52 @@ static void sdcard_error_recovery(USDHC_Type *base) {
294295
static status_t sdcard_transfer_blocking(USDHC_Type *base, usdhc_handle_t *handle, usdhc_transfer_t *transfer, uint32_t timeout_ms) {
295296
status_t status;
296297

297-
usdhc_adma_config_t dma_config;
298-
299-
(void)memset(&dma_config, 0, sizeof(usdhc_adma_config_t));
300-
dma_config.dmaMode = kUSDHC_DmaModeAdma2;
301-
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;
298+
usdhc_adma_config_t dma_config = {
299+
.dmaMode = kUSDHC_DmaModeAdma2,
300+
#if !(defined(FSL_FEATURE_USDHC_HAS_NO_RW_BURST_LEN) && FSL_FEATURE_USDHC_HAS_NO_RW_BURST_LEN)
301+
.burstLen = kUSDHC_EnBurstLenForINCR,
302+
#endif
303+
.admaTable = sdcard_adma_descriptor_table,
304+
.admaTableWords = DMA_DESCRIPTOR_BUFFER_SIZE,
305+
};
306+
usdhc_adma_config_t *p_dma_config = &dma_config;
307+
308+
#if __DCACHE_PRESENT
309+
size_t byte_len = transfer->data->blockCount * transfer->data->blockSize;
310+
if ((uintptr_t)transfer->data->rxData % FSL_FEATURE_L1DCACHE_LINESIZE_BYTE != 0 ||
311+
byte_len % FSL_FEATURE_L1DCACHE_LINESIZE_BYTE != 0) {
312+
// A DMA RX transfer that isn't cache line aligned can be corrupted if the CPU writes the same cache line during
313+
// the read, so make a polling transfer instead.
314+
//
315+
// (Note that the USDHC driver will internally disable DMA if the address isn't 4 byte aligned, so this check only
316+
// changes behaviour for addresses which are word aligned but not cache line aligned!)
317+
p_dma_config = NULL;
318+
}
304319
#endif
305320

306-
dma_config.admaTable = sdcard_adma_descriptor_table;
307-
dma_config.admaTableWords = DMA_DESCRIPTOR_BUFFER_SIZE;
308-
309321
// Wait while the card is busy before a transfer
310322
status = kStatus_Timeout;
311323
for (int i = 0; i < timeout_ms * 100; i++) {
312324
// Wait until Data0 is low any more. Low indicates "Busy".
313325
if (((transfer->data->txData == NULL) && (transfer->data->rxData == NULL)) ||
314326
(USDHC_GetPresentStatusFlags(base) & (uint32_t)kUSDHC_Data0LineLevelFlag) != 0) {
315327
// Not busy anymore or no TX-Data
316-
status = USDHC_TransferBlocking(base, &dma_config, transfer);
328+
status = USDHC_TransferBlocking(base, p_dma_config, transfer);
317329
if (status != kStatus_Success) {
318330
sdcard_error_recovery(base);
319331
}
320332
break;
321333
}
322334
ticks_delay_us64(10);
323335
}
336+
337+
#if __DCACHE_PRESENT
338+
if (p_dma_config && transfer->data->rxData) {
339+
// Invalidate any cache lines that were filled while the transfer was in progress
340+
L1CACHE_InvalidateDCacheByRange((uintptr_t)transfer->data->rxData, byte_len);
341+
}
342+
#endif
343+
324344
return status;
325345

326346
}

0 commit comments

Comments
 (0)