Skip to content

Commit ac078e5

Browse files
tpwrulestridge
authored andcommitted
SDMMCv1: fix system hang issues
1. SDMMCv1: unlock before waiting for DMA completion Avoids system hanging forever when an error interrupt is signaled and the controller is in a state where the DMA can never complete. 2. SDMMCv1: wait for DMA completion only when it will complete In certain error states (e.g. CRC error in the middle of a multi-block transmission), the DMA may never complete. Therefore, only wait for its completion if the transfer was successful. We do need to wait for completion on read as some words may still be in flight from the receive FIFO to RAM. The STM32F7 reference manual (RM0385 Rev 9 Section 35.3.2 SDMMC APB2 interface) mentions that it is possible for a receive FIFO overflow on the last word to be signaled only after the DATAEND flag is set. Therefore we also have to check that the FIFO does not have data in it and confirm there is no RXOVERR error too. 3. SDMMCv1: listen for reserved start bit error Sometimes this is the only interrupt that gets signaled in certain error cases. We need to listen for it so we don't sleep forever waiting for an interrupt from the controller. The STM32F7 reference manual RM0385 documents this bit as reserved but evidently it can still be triggered in some cases. The logic has been copied from the SDIOv1 driver where it is not reserved.
1 parent 2e60c06 commit ac078e5

1 file changed

Lines changed: 35 additions & 10 deletions

File tree

os/hal/ports/STM32/LLD/SDMMCv1/hal_sdc_lld.c

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,19 @@
4949
STM32_DMA_GETCHANNEL(STM32_SDC_SDMMC2_DMA_STREAM, \
5050
STM32_SDC_SDMMC2_DMA_CHN)
5151

52+
/*
53+
* Despite allegedly being reserved, the start bit error 9 was evidently
54+
* inherited from SDIOv1 and will sometimes be asserted on error, so we need to
55+
* detect it to avoid a forever wait for a transaction to complete.
56+
*/
57+
#if !defined(SDMMC_STA_STBITERR)
58+
#define SDMMC_STA_STBITERR (0x1UL << 9U)
59+
#endif
60+
61+
#if !defined(SDMMC_MASK_STBITERRIE)
62+
#define SDMMC_MASK_STBITERRIE (0x1UL << 9U)
63+
#endif
64+
5265
/*===========================================================================*/
5366
/* Driver exported variables. */
5467
/*===========================================================================*/
@@ -171,6 +184,7 @@ static bool sdc_lld_prepare_read_bytes(SDCDriver *sdcp,
171184
sdcp->sdmmc->ICR = SDMMC_ICR_ALL_FLAGS;
172185
sdcp->sdmmc->MASK = SDMMC_MASK_DCRCFAILIE |
173186
SDMMC_MASK_DTIMEOUTIE |
187+
SDMMC_MASK_STBITERRIE |
174188
SDMMC_MASK_RXOVERRIE |
175189
SDMMC_MASK_DATAENDIE;
176190
sdcp->sdmmc->DLEN = bytes;
@@ -286,22 +300,31 @@ static bool sdc_lld_wait_transaction_end(SDCDriver *sdcp, uint32_t n,
286300
osalThreadSuspendS(&sdcp->thread);
287301
}
288302

289-
/* Stopping operations, waiting for transfer completion at DMA level, then
290-
the stream is disabled and cleared.*/
291-
dmaWaitCompletion(sdcp->dma);
292-
sdcp->sdmmc->MASK = 0U;
293-
sdcp->sdmmc->DCTRL = 0U;
303+
/* Mask has now been set to zero by interrupt handler. */
304+
osalSysUnlock();
294305

306+
/* Data transfer not complete, let error cleanup stop DMA.*/
295307
if ((sdcp->sdmmc->STA & SDMMC_STA_DATAEND) == 0U) {
296-
osalSysUnlock();
297308
return HAL_FAILED;
298309
}
299310

311+
/* RXOVERR may be late, wait for RX FIFO to not have data (or be disabled).*/
312+
while ((sdcp->sdmmc->STA & SDMMC_STA_RXDAVL) != 0U)
313+
;
314+
315+
/* RX data overflow, let error cleanup stop DMA.*/
316+
if ((sdcp->sdmmc->STA & SDMMC_STA_RXOVERR) != 0U) {
317+
return HAL_FAILED;
318+
}
319+
320+
/* Waiting for transfer completion at DMA level, then the stream is disabled
321+
and cleared.*/
322+
dmaWaitCompletion(sdcp->dma);
323+
sdcp->sdmmc->DCTRL = 0U;
324+
300325
/* Clearing status.*/
301326
sdcp->sdmmc->ICR = SDMMC_ICR_ALL_FLAGS;
302327

303-
osalSysUnlock();
304-
305328
/* Finalize transaction.*/
306329
if (n > 1U)
307330
return sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_STOP_TRANSMISSION, 0, resp);
@@ -332,8 +355,8 @@ static void sdc_lld_collect_errors(SDCDriver *sdcp, uint32_t sta) {
332355
errors |= SDC_TX_UNDERRUN;
333356
if (sta & SDMMC_STA_RXOVERR)
334357
errors |= SDC_RX_OVERRUN;
335-
/* if (sta & SDMMC_STA_STBITERR)
336-
errors |= SDC_STARTBIT_ERROR;*/
358+
if (sta & SDMMC_STA_STBITERR)
359+
errors |= SDC_STARTBIT_ERROR;
337360

338361
sdcp->errors |= errors;
339362
}
@@ -859,6 +882,7 @@ bool sdc_lld_read_aligned(SDCDriver *sdcp, uint32_t startblk,
859882
sdcp->sdmmc->ICR = SDMMC_ICR_ALL_FLAGS;
860883
sdcp->sdmmc->MASK = SDMMC_MASK_DCRCFAILIE |
861884
SDMMC_MASK_DTIMEOUTIE |
885+
SDMMC_MASK_STBITERRIE |
862886
SDMMC_MASK_RXOVERRIE |
863887
SDMMC_MASK_DATAENDIE;
864888
sdcp->sdmmc->DLEN = blocks * MMCSD_BLOCK_SIZE;
@@ -927,6 +951,7 @@ bool sdc_lld_write_aligned(SDCDriver *sdcp, uint32_t startblk,
927951
sdcp->sdmmc->ICR = SDMMC_ICR_ALL_FLAGS;
928952
sdcp->sdmmc->MASK = SDMMC_MASK_DCRCFAILIE |
929953
SDMMC_MASK_DTIMEOUTIE |
954+
SDMMC_MASK_STBITERRIE |
930955
SDMMC_MASK_TXUNDERRIE |
931956
SDMMC_MASK_DATAENDIE;
932957
sdcp->sdmmc->DLEN = blocks * MMCSD_BLOCK_SIZE;

0 commit comments

Comments
 (0)