/** * @brief Wait end of data transaction and performs finalizations. * * @param[in] sdcp pointer to the @p SDCDriver object * @param[in] n number of blocks in transaction * @param[in] resp pointer to the response buffer * * @return The operation status. * @retval HAL_SUCCESS operation succeeded. * @retval HAL_FAILED operation failed. */ static bool sdc_lld_wait_transaction_end(SDCDriver *sdcp, uint32_t n, uint32_t *resp) { /* Note the mask is checked before going to sleep because the interrupt may have occurred before reaching the critical zone.*/ osalSysLock(); if (sdcp->sdmmc->MASK != 0) osalThreadSuspendS(&sdcp->thread); if ((sdcp->sdmmc->STA & SDMMC_STA_DATAEND) == 0) { osalSysUnlock(); return HAL_FAILED; } /* Wait until DMA channel enabled to be sure that all data transferred.*/ while (sdcp->dma->stream->CR & STM32_DMA_CR_EN) ; /* DMA event flags must be manually cleared.*/ dmaStreamClearInterrupt(sdcp->dma); sdcp->sdmmc->ICR = SDMMC_ICR_ALL_FLAGS; sdcp->sdmmc->DCTRL = 0; osalSysUnlock(); /* Finalize transaction.*/ if (n > 1) return sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_STOP_TRANSMISSION, 0, resp); return HAL_SUCCESS; }
/** * @brief Wait end of data transaction and performs finalizations. * * @param[in] sdcp pointer to the @p SDCDriver object * @param[in] n number of blocks in transaction * @param[in] resp pointer to the response buffer * * @return The operation status. * @retval HAL_SUCCESS operation succeeded. * @retval HAL_FAILED operation failed. */ static bool sdc_lld_wait_transaction_end(SDCDriver *sdcp, uint32_t n, uint32_t *resp) { /* Note the mask is checked before going to sleep because the interrupt may have occurred before reaching the critical zone.*/ osalSysLock(); if (sdcp->sdmmc->MASK != 0) osalThreadSuspendS(&sdcp->thread); if ((sdcp->sdmmc->STA & SDMMC_STA_DATAEND) == 0) { osalSysUnlock(); return HAL_FAILED; } /* Waits for transfer completion at DMA level, then the stream is disabled and cleared.*/ dmaWaitCompletion(sdcp->dma); sdcp->sdmmc->ICR = SDMMC_ICR_ALL_FLAGS; sdcp->sdmmc->DCTRL = 0; osalSysUnlock(); /* Finalize transaction.*/ if (n > 1) return sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_STOP_TRANSMISSION, 0, resp); return HAL_SUCCESS; }
/** * @brief Performs clean transaction stopping in case of errors. * * @param[in] sdcp pointer to the @p SDCDriver object * @param[in] n number of blocks in transaction * @param[in] resp pointer to the response buffer * * @notapi */ static void sdc_lld_error_cleanup(SDCDriver *sdcp, uint32_t n, uint32_t *resp) { dmaStreamClearInterrupt(sdcp->dma); dmaStreamDisable(sdcp->dma); SDIO->ICR = STM32_SDIO_ICR_ALL_FLAGS; SDIO->MASK = 0; SDIO->DCTRL = 0; sdc_lld_collect_errors(sdcp); if (n > 1) sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_STOP_TRANSMISSION, 0, resp); }
/** * @brief Wait end of data transaction and performs finalizations. * * @param[in] sdcp pointer to the @p SDCDriver object * @param[in] n number of blocks in transaction * @param[in] resp pointer to the response buffer * * @return The operation status. * @retval CH_SUCCESS operation succeeded. * @retval CH_FAILED operation failed. */ static bool_t sdc_lld_wait_transaction_end(SDCDriver *sdcp, uint32_t n, uint32_t *resp) { /* Note the mask is checked before going to sleep because the interrupt may have occurred before reaching the critical zone.*/ chSysLock(); if (SDIO->MASK != 0) { chDbgAssert(sdcp->thread == NULL, "sdc_lld_start_data_transaction(), #1", "not NULL"); sdcp->thread = chThdSelf(); chSchGoSleepS(THD_STATE_SUSPENDED); chDbgAssert(sdcp->thread == NULL, "sdc_lld_start_data_transaction(), #2", "not NULL"); } if ((SDIO->STA & SDIO_STA_DATAEND) == 0) { chSysUnlock(); return CH_FAILED; } #if (defined(STM32F4XX) || defined(STM32F2XX)) /* Wait until DMA channel enabled to be sure that all data transferred.*/ while (sdcp->dma->stream->CR & STM32_DMA_CR_EN) ; /* DMA event flags must be manually cleared.*/ dmaStreamClearInterrupt(sdcp->dma); SDIO->ICR = STM32_SDIO_ICR_ALL_FLAGS; SDIO->DCTRL = 0; chSysUnlock(); /* Wait until interrupt flags to be cleared.*/ /*while (((DMA2->LISR) >> (sdcp->dma->ishift)) & STM32_DMA_ISR_TCIF) dmaStreamClearInterrupt(sdcp->dma);*/ #else /* Waits for transfer completion at DMA level, the the stream is disabled and cleared.*/ dmaWaitCompletion(sdcp->dma); SDIO->ICR = STM32_SDIO_ICR_ALL_FLAGS; SDIO->DCTRL = 0; chSysUnlock(); #endif /* Finalize transaction.*/ if (n > 1) return sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_STOP_TRANSMISSION, 0, resp); return CH_SUCCESS; }
/** * @brief Prepares card to handle read transaction. * * @param[in] sdcp pointer to the @p SDCDriver object * @param[in] startblk first block to read * @param[in] n number of blocks to read * @param[in] resp pointer to the response buffer * * @return The operation status. * @retval CH_SUCCESS operation succeeded. * @retval CH_FAILED operation failed. * * @notapi */ static bool_t sdc_lld_prepare_read(SDCDriver *sdcp, uint32_t startblk, uint32_t n, uint32_t *resp) { /* Driver handles data in 512 bytes blocks (just like HC cards). But if we have not HC card than we must convert address from blocks to bytes.*/ if (!(sdcp->cardmode & SDC_MODE_HIGH_CAPACITY)) startblk *= MMCSD_BLOCK_SIZE; if (n > 1) { /* Send read multiple blocks command to card.*/ if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_READ_MULTIPLE_BLOCK, startblk, resp) || MMCSD_R1_ERROR(resp[0])) return CH_FAILED; } else{ /* Send read single block command.*/ if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_READ_SINGLE_BLOCK, startblk, resp) || MMCSD_R1_ERROR(resp[0])) return CH_FAILED; } return CH_SUCCESS; }
/** * @brief Performs clean transaction stopping in case of errors. * * @param[in] sdcp pointer to the @p SDCDriver object * @param[in] n number of blocks in transaction * @param[in] resp pointer to the response buffer * * @notapi */ static void sdc_lld_error_cleanup(SDCDriver *sdcp, uint32_t n, uint32_t *resp) { uint32_t sta = sdcp->sdmmc->STA; dmaStreamDisable(sdcp->dma); sdcp->sdmmc->ICR = SDMMC_ICR_ALL_FLAGS; sdcp->sdmmc->MASK = 0; sdcp->sdmmc->DCTRL = 0; sdc_lld_collect_errors(sdcp, sta); if (n > 1) sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_STOP_TRANSMISSION, 0, resp); }
/** * @brief Wait end of data transaction and performs finalizations. * * @param[in] sdcp pointer to the @p SDCDriver object * @param[in] n number of blocks in transaction * @param[in] resp pointer to the response buffer * * @return The operation status. * @retval HAL_SUCCESS operation succeeded. * @retval HAL_FAILED operation failed. */ static bool sdc_lld_wait_transaction_end(SDCDriver *sdcp, uint32_t n, uint32_t *resp) { /* Note the mask is checked before going to sleep because the interrupt may have occurred before reaching the critical zone.*/ osalSysLock(); if (sdcp->sdio->MASK != 0) osalThreadSuspendS(&sdcp->thread); if ((sdcp->sdio->STA & SDIO_STA_DATAEND) == 0) { osalSysUnlock(); return HAL_FAILED; } #if (defined(STM32F4XX) || defined(STM32F2XX)) /* Wait until DMA channel enabled to be sure that all data transferred.*/ while (sdcp->dma->stream->CR & STM32_DMA_CR_EN) ; /* DMA event flags must be manually cleared.*/ dmaStreamClearInterrupt(sdcp->dma); sdcp->sdio->ICR = STM32_SDIO_ICR_ALL_FLAGS; sdcp->sdio->DCTRL = 0; osalSysUnlock(); /* Wait until interrupt flags to be cleared.*/ /*while (((DMA2->LISR) >> (sdcp->dma->ishift)) & STM32_DMA_ISR_TCIF) dmaStreamClearInterrupt(sdcp->dma);*/ #else /* Waits for transfer completion at DMA level, then the stream is disabled and cleared.*/ dmaWaitCompletion(sdcp->dma); sdcp->sdio->ICR = STM32_SDIO_ICR_ALL_FLAGS; sdcp->sdio->DCTRL = 0; osalSysUnlock(); #endif /* Finalize transaction.*/ if (n > 1) return sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_STOP_TRANSMISSION, 0, resp); return HAL_SUCCESS; }
/** * @brief Reads special registers using data bus. * @details Needs only during card detection procedure. * * @param[in] sdcp pointer to the @p SDCDriver object * @param[out] buf pointer to the read buffer * @param[in] bytes number of bytes to read * @param[in] cmd card command * @param[in] arg argument for command * * @return The operation status. * @retval HAL_SUCCESS operation succeeded. * @retval HAL_FAILED operation failed. * * @notapi */ bool sdc_lld_read_special(SDCDriver *sdcp, uint8_t *buf, size_t bytes, uint8_t cmd, uint32_t arg) { uint32_t resp[1]; if(sdc_lld_prepare_read_bytes(sdcp, buf, bytes)) goto error; if (sdc_lld_send_cmd_short_crc(sdcp, cmd, arg, resp) || MMCSD_R1_ERROR(resp[0])) goto error; if (sdc_lld_wait_transaction_end(sdcp, 1, resp)) goto error; return HAL_SUCCESS; error: sdc_lld_error_cleanup(sdcp, 1, resp); return HAL_FAILED; }
/** * @brief Reads one or more blocks. * * @param[in] sdcp pointer to the @p SDCDriver object * @param[in] startblk first block to read * @param[out] buf pointer to the read buffer, it must be aligned to * four bytes boundary * @param[in] n number of blocks to read * @return The operation status. * @retval FALSE operation succeeded, the requested blocks have been * read. * @retval TRUE operation failed, the state of the buffer is uncertain. * * @notapi */ static bool_t sdc_lld_read_multiple(SDCDriver *sdcp, uint32_t startblk, uint8_t *buf, uint32_t n) { uint32_t resp[1]; /* Checks for errors and waits for the card to be ready for reading.*/ if (_sdc_wait_for_transfer_state(sdcp)) return TRUE; /* Prepares the DMA channel for reading.*/ dmaStreamSetMemory0(STM32_DMA2_STREAM4, buf); dmaStreamSetTransactionSize(STM32_DMA2_STREAM4, (n * SDC_BLOCK_SIZE) / sizeof (uint32_t)); dmaStreamSetMode(STM32_DMA2_STREAM4, STM32_DMA_CR_PL(STM32_SDC_SDIO_DMA_PRIORITY) | STM32_DMA_CR_DIR_P2M | STM32_DMA_CR_PSIZE_WORD | STM32_DMA_CR_MSIZE_WORD | STM32_DMA_CR_MINC); /* Setting up data transfer. Options: Card to Controller, Block mode, DMA mode, 512 bytes blocks.*/ SDIO->ICR = 0xFFFFFFFF; SDIO->MASK = SDIO_MASK_DCRCFAILIE | SDIO_MASK_DTIMEOUTIE | SDIO_MASK_DATAENDIE | SDIO_MASK_STBITERRIE; SDIO->DLEN = n * SDC_BLOCK_SIZE; SDIO->DCTRL = SDIO_DCTRL_DTDIR | SDIO_DCTRL_DBLOCKSIZE_3 | SDIO_DCTRL_DBLOCKSIZE_0 | SDIO_DCTRL_DMAEN | SDIO_DCTRL_DTEN; /* DMA channel activation.*/ dmaStreamEnable(STM32_DMA2_STREAM4); /* Read multiple blocks command.*/ if ((sdcp->cardmode & SDC_MODE_HIGH_CAPACITY) == 0) startblk *= SDC_BLOCK_SIZE; if (sdc_lld_send_cmd_short_crc(sdcp, SDC_CMD_READ_MULTIPLE_BLOCK, startblk, resp) || SDC_R1_ERROR(resp[0])) goto error; chSysLock(); if (SDIO->MASK != 0) { chDbgAssert(sdcp->thread == NULL, "sdc_lld_read_multiple(), #1", "not NULL"); sdcp->thread = chThdSelf(); chSchGoSleepS(THD_STATE_SUSPENDED); chDbgAssert(sdcp->thread == NULL, "sdc_lld_read_multiple(), #2", "not NULL"); } if ((SDIO->STA & SDIO_STA_DATAEND) == 0) { chSysUnlock(); goto error; } dmaStreamDisable(STM32_DMA2_STREAM4); SDIO->ICR = 0xFFFFFFFF; SDIO->DCTRL = 0; chSysUnlock(); return sdc_lld_send_cmd_short_crc(sdcp, SDC_CMD_STOP_TRANSMISSION, 0, resp); error: dmaStreamDisable(STM32_DMA2_STREAM4); SDIO->ICR = 0xFFFFFFFF; SDIO->MASK = 0; SDIO->DCTRL = 0; return TRUE; }
/** * @brief Writes one block. * * @param[in] sdcp pointer to the @p SDCDriver object * @param[in] startblk first block to write * @param[out] buf pointer to the write buffer, it must be aligned to * four bytes boundary * @param[in] n number of blocks to write * @return The operation status. * @retval FALSE operation succeeded, the requested blocks have been * written. * @retval TRUE operation failed. * * @notapi */ static bool_t sdc_lld_write_single(SDCDriver *sdcp, uint32_t startblk, const uint8_t *buf) { uint32_t resp[1]; /* Checks for errors and waits for the card to be ready for writing.*/ if (_sdc_wait_for_transfer_state(sdcp)) return TRUE; /* Prepares the DMA channel for writing.*/ dmaStreamSetMemory0(STM32_DMA2_STREAM4, buf); dmaStreamSetTransactionSize(STM32_DMA2_STREAM4, SDC_BLOCK_SIZE / sizeof (uint32_t)); dmaStreamSetMode(STM32_DMA2_STREAM4, STM32_DMA_CR_PL(STM32_SDC_SDIO_DMA_PRIORITY) | STM32_DMA_CR_DIR_M2P | STM32_DMA_CR_PSIZE_WORD | STM32_DMA_CR_MSIZE_WORD | STM32_DMA_CR_MINC); /* Write single block command.*/ if ((sdcp->cardmode & SDC_MODE_HIGH_CAPACITY) == 0) startblk *= SDC_BLOCK_SIZE; if (sdc_lld_send_cmd_short_crc(sdcp, SDC_CMD_WRITE_BLOCK, startblk, resp) || SDC_R1_ERROR(resp[0])) return TRUE; /* Setting up data transfer. Options: Controller to Card, Block mode, DMA mode, 512 bytes blocks.*/ SDIO->ICR = 0xFFFFFFFF; SDIO->MASK = SDIO_MASK_DCRCFAILIE | SDIO_MASK_DTIMEOUTIE | SDIO_MASK_DATAENDIE | SDIO_MASK_TXUNDERRIE | SDIO_MASK_STBITERRIE; SDIO->DLEN = SDC_BLOCK_SIZE; SDIO->DCTRL = SDIO_DCTRL_DBLOCKSIZE_3 | SDIO_DCTRL_DBLOCKSIZE_0 | SDIO_DCTRL_DMAEN | SDIO_DCTRL_DTEN; /* DMA channel activation.*/ dmaStreamEnable(STM32_DMA2_STREAM4); /* Note the mask is checked before going to sleep because the interrupt may have occurred before reaching the critical zone.*/ chSysLock(); if (SDIO->MASK != 0) { chDbgAssert(sdcp->thread == NULL, "sdc_lld_write_single(), #1", "not NULL"); sdcp->thread = chThdSelf(); chSchGoSleepS(THD_STATE_SUSPENDED); chDbgAssert(sdcp->thread == NULL, "sdc_lld_write_single(), #2", "not NULL"); } if ((SDIO->STA & SDIO_STA_DATAEND) == 0) { chSysUnlock(); goto error; } dmaStreamDisable(STM32_DMA2_STREAM4); SDIO->ICR = 0xFFFFFFFF; SDIO->DCTRL = 0; chSysUnlock(); return FALSE; error: dmaStreamDisable(STM32_DMA2_STREAM4); SDIO->ICR = 0xFFFFFFFF; SDIO->MASK = 0; SDIO->DCTRL = 0; return TRUE; }