From 690fd6364bd682ade14f27e86cb3821c84524d78 Mon Sep 17 00:00:00 2001 From: barthess Date: Mon, 5 Mar 2012 16:44:56 +0000 Subject: [PATCH] SDC. Code merged to fresh branch. git-svn-id: svn://svn.code.sf.net/p/chibios/svn/branches/sdc_dev2@4021 35acf78f-673a-0410-8e92-d51de3d6d3f4 --- os/hal/include/sdc.h | 97 +++- os/hal/platforms/STM32/sdc_lld.c | 647 +++++++++++++------------ os/hal/platforms/STM32/sdc_lld.h | 85 +++- os/hal/platforms/STM32F4xx/hal_lld.h | 4 + os/hal/platforms/STM32F4xx/platform.mk | 5 +- os/hal/platforms/STM32F4xx/stm32_rcc.h | 33 ++ os/hal/src/sdc.c | 134 +++-- 7 files changed, 644 insertions(+), 361 deletions(-) diff --git a/os/hal/include/sdc.h b/os/hal/include/sdc.h index 52bef66f0..9be833dd3 100644 --- a/os/hal/include/sdc.h +++ b/os/hal/include/sdc.h @@ -1,6 +1,6 @@ /* ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010, - 2011,2012 Giovanni Di Sirio. + 2011 Giovanni Di Sirio. This file is part of ChibiOS/RT. @@ -88,6 +88,27 @@ #define SDC_CMD_LOCK_UNLOCK 42 #define SDC_CMD_APP_CMD 55 +/** + * @brief Returning status. + */ +#define SDC_SUCCESS FALSE +#define SDC_FAILED TRUE + +/** + * @name SDC bus error conditions + * @{ + */ +#define SDC_NO_ERROR 0 /**< @brief No error. */ +#define SDC_CMD_CRC_ERROR 1 /**< @brief Command CRC error. */ +#define SDC_DATA_CRC_ERROR 2 /**< @brief Data CRC error. */ +#define SDC_DATA_TIMEOUT 4 /**< @brief Hardware write timeout.*/ +#define SDC_COMMAND_TIMEOUT 8 /**< @brief Hardware read timeout. */ +#define SDC_TX_UNDERRUN 16 /**< @brief TX buffer underrun. */ +#define SDC_RX_OVERRUN 32 /**< @brief RX buffer overrun. */ +#define SDC_STARTBIT_ERROR 64 /**< @brief Start bit not detected.*/ +#define SDC_OVERFLOW_ERROR 128 /**< @brief Card overflow error. */ +#define SDC_UNHANDLED_ERROR 0xFFFFFFFF + /*===========================================================================*/ /* Driver pre-compile time settings. */ /*===========================================================================*/ @@ -122,6 +143,20 @@ #if !defined(SDC_NICE_WAITING) || defined(__DOXYGEN__) #define SDC_NICE_WAITING TRUE #endif + +/** + * @brief Write timeout in milliseconds. + */ +#if !defined(SDC_WRITE_TIMEOUT_MS) || defined(__DOXYGEN__) +#define SDC_WRITE_TIMEOUT_MS 250 +#endif + +/** + * @brief Write timeout in milliseconds. + */ +#if !defined(SDC_READ_TIMEOUT_MS) || defined(__DOXYGEN__) +#define SDC_READ_TIMEOUT_MS 5 +#endif /** @} */ /*===========================================================================*/ @@ -223,6 +258,66 @@ typedef enum { * @api */ #define sdcIsWriteProtected(sdcp) (sdc_lld_is_write_protected(sdcp)) + +/** + * @brief Slice position of values in CSD register. + */ +/* CSD version 1.0 */ +#define SDC_CSD_20_CRC_SLICE 7,1 +#define SDC_CSD_20_FILE_FORMAT_SLICE 11,10 +#define SDC_CSD_20_TMP_WRITE_PROTECT_SLICE 12,12 +#define SDC_CSD_20_PERM_WRITE_PROTECT_SLICE 13,13 +#define SDC_CSD_20_COPY_SLICE 14,14 +#define SDC_CSD_20_FILE_FORMAT_GRP_SLICE 15,15 +#define SDC_CSD_20_WRITE_BL_PARTIAL_SLICE 21,21 +#define SDC_CSD_20_WRITE_BL_LEN_SLICE 25,12 +#define SDC_CSD_20_R2W_FACTOR_SLICE 28,26 +#define SDC_CSD_20_WP_GRP_ENABLE_SLICE 31,31 +#define SDC_CSD_20_WP_GRP_SIZE_SLICE 38,32 +#define SDC_CSD_20_ERASE_SECTOR_SIZE_SLICE 45,39 +#define SDC_CSD_20_ERASE_BLK_EN_SLICE 46,46 +#define SDC_CSD_20_C_SIZE_SLICE 69,48 +#define SDC_CSD_20_DSR_IMP_SLICE 76,76 +#define SDC_CSD_20_READ_BLK_MISALIGN_SLICE 77,77 +#define SDC_CSD_20_WRITE_BLK_MISALIGN_SLICE 78,78 +#define SDC_CSD_20_READ_BL_PARTIAL_SLICE 79,79 +#define SDC_CSD_20_READ_BL_LEN_SLICE 83,80 +#define SDC_CSD_20_CCC_SLICE 95,84 +#define SDC_CSD_20_TRANS_SPEED_SLICE 103,96 +#define SDC_CSD_20_NSAC_SLICE 111,104 +#define SDC_CSD_20_TAAC_SLICE 119,112 +#define SDC_CSD_20_STRUCTURE_SLICE 127,126 + +/* CSD version 2.0 */ +#define SDC_CSD_10_CRC_SLICE SDC_CSD_20_CRC_SLICE +#define SDC_CSD_10_FILE_FORMAT_SLICE SDC_CSD_20_FILE_FORMAT_SLICE +#define SDC_CSD_10_TMP_WRITE_PROTECT_SLICE SDC_CSD_20_TMP_WRITE_PROTECT_SLICE +#define SDC_CSD_10_PERM_WRITE_PROTECT_SLICE SDC_CSD_20_PERM_WRITE_PROTECT_SLICE +#define SDC_CSD_10_COPY_SLICE SDC_CSD_20_COPY_SLICE +#define SDC_CSD_10_FILE_FORMAT_GRP_SLICE SDC_CSD_20_FILE_FORMAT_GRP_SLICE +#define SDC_CSD_10_WRITE_BL_PARTIAL_SLICE SDC_CSD_20_WRITE_BL_PARTIAL_SLICE +#define SDC_CSD_10_WRITE_BL_LEN_SLICE SDC_CSD_20_WRITE_BL_LEN_SLICE +#define SDC_CSD_10_R2W_FACTOR_SLICE SDC_CSD_20_R2W_FACTOR_SLICE +#define SDC_CSD_10_WP_GRP_ENABLE_SLICE SDC_CSD_20_WP_GRP_ENABLE_SLICE +#define SDC_CSD_10_WP_GRP_SIZE_SLICE SDC_CSD_20_WP_GRP_SIZE_SLICE +#define SDC_CSD_10_ERASE_SECTOR_SIZE_SLICE SDC_CSD_20_ERASE_SECTOR_SIZE_SLICE +#define SDC_CSD_10_ERASE_BLK_EN_SLICE SDC_CSD_20_ERASE_BLK_EN_SLICE +#define SDC_CSD_10_C_SIZE_MULT_SLICE 49,47 +#define SDC_CSD_10_VDD_W_CURR_MAX_SLICE 52,50 +#define SDC_CSD_10_VDD_W_CURR_MIN_SLICE 55,53 +#define SDC_CSD_10_VDD_R_CURR_MAX_SLICE 58,56 +#define SDC_CSD_10_VDD_R_CURR_MIX_SLICE 61,59 +#define SDC_CSD_10_C_SIZE_SLICE 73,62 +#define SDC_CSD_10_DSR_IMP_SLICE SDC_CSD_20_DSR_IMP_SLICE +#define SDC_CSD_10_READ_BLK_MISALIGN_SLICE SDC_CSD_20_READ_BLK_MISALIGN_SLICE +#define SDC_CSD_10_WRITE_BLK_MISALIGN_SLICE SDC_CSD_20_WRITE_BLK_MISALIGN_SLICE +#define SDC_CSD_10_READ_BL_PARTIAL_SLICE SDC_CSD_20_READ_BL_PARTIAL_SLICE +#define SDC_CSD_10_READ_BL_LEN_SLICE 83, 80 +#define SDC_CSD_10_CCC_SLICE SDC_CSD_20_CCC_SLICE +#define SDC_CSD_10_TRANS_SPEED_SLICE SDC_CSD_20_TRANS_SPEED_SLICE +#define SDC_CSD_10_NSAC_SLICE SDC_CSD_20_NSAC_SLICE +#define SDC_CSD_10_TAAC_SLICE SDC_CSD_20_TAAC_SLICE +#define SDC_CSD_10_STRUCTURE_SLICE SDC_CSD_20_STRUCTURE_SLICE /** @} */ /*===========================================================================*/ diff --git a/os/hal/platforms/STM32/sdc_lld.c b/os/hal/platforms/STM32/sdc_lld.c index 42883e3b0..f74d06c9e 100644 --- a/os/hal/platforms/STM32/sdc_lld.c +++ b/os/hal/platforms/STM32/sdc_lld.c @@ -1,6 +1,6 @@ /* ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010, - 2011,2012 Giovanni Di Sirio. + 2011 Giovanni Di Sirio. This file is part of ChibiOS/RT. @@ -26,6 +26,10 @@ * @{ */ +/* + TODO: Try preerase blocks before writing (ACMD23). + */ + #include #include "ch.h" @@ -33,6 +37,14 @@ #if HAL_USE_SDC || defined(__DOXYGEN__) +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#define DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_SDC_SDIO_DMA_STREAM, \ + STM32_SDC_SDIO_DMA_CHN) + /*===========================================================================*/ /* Driver exported variables. */ /*===========================================================================*/ @@ -59,330 +71,193 @@ static union { /*===========================================================================*/ /** - * @brief Reads one or more blocks. + * @brief Prepares card to handle read transaction. * * @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 + * @param[in] resp pointer to the response buffer + * * @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. + * @retval SDC_SUCCESS operation succeeded. + * @retval SDC_FAILED operation failed. * * @notapi */ -static bool_t sdc_lld_read_multiple(SDCDriver *sdcp, uint32_t startblk, - uint8_t *buf, uint32_t n) { - uint32_t resp[1]; +static bool_t sdc_lld_prepare_read(SDCDriver *sdcp, uint32_t startblk, + uint32_t n, uint32_t *resp){ - /* 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) + /* 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 *= 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 (n > 1){ + /* Send read multiple blocks command to card.*/ + if (sdc_lld_send_cmd_short_crc(sdcp, SDC_CMD_READ_MULTIPLE_BLOCK, + startblk, resp) || SDC_R1_ERROR(resp[0])) + return SDC_FAILED; } - if ((SDIO->STA & SDIO_STA_DATAEND) == 0) { - chSysUnlock(); - goto error; + else{ + /* Send read single block command.*/ + if (sdc_lld_send_cmd_short_crc(sdcp, SDC_CMD_READ_SINGLE_BLOCK, + startblk, resp) || SDC_R1_ERROR(resp[0])) + return SDC_FAILED; } - 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; + return SDC_SUCCESS; } /** - * @brief Reads one block. + * @brief Prepares card to handle write transaction. * * @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 write + * @param[in] resp pointer to the response buffer + * * @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. + * @retval SDC_SUCCESS operation succeeded. + * @retval SDC_FAILED operation failed. * * @notapi */ -static bool_t sdc_lld_read_single(SDCDriver *sdcp, uint32_t startblk, - uint8_t *buf) { - uint32_t resp[1]; +static bool_t sdc_lld_prepare_write(SDCDriver *sdcp, uint32_t startblk, + uint32_t n, uint32_t *resp){ - /* 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, - 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 = 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 single block command.*/ - if ((sdcp->cardmode & SDC_MODE_HIGH_CAPACITY) == 0) + /* 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 *= SDC_BLOCK_SIZE; - if (sdc_lld_send_cmd_short_crc(sdcp, SDC_CMD_READ_SINGLE_BLOCK, - startblk, resp) || - SDC_R1_ERROR(resp[0])) - goto error; - chSysLock(); - if (SDIO->MASK != 0) { - chDbgAssert(sdcp->thread == NULL, - "sdc_lld_read_single(), #1", "not NULL"); - sdcp->thread = chThdSelf(); - chSchGoSleepS(THD_STATE_SUSPENDED); - chDbgAssert(sdcp->thread == NULL, - "sdc_lld_read_single(), #2", "not NULL"); + if (n > 1){ + /* Write multiple blocks command.*/ + if (sdc_lld_send_cmd_short_crc(sdcp, SDC_CMD_WRITE_MULTIPLE_BLOCK, + startblk, resp) || SDC_R1_ERROR(resp[0])) + return SDC_FAILED; } - if ((SDIO->STA & SDIO_STA_DATAEND) == 0) { - chSysUnlock(); - goto error; + else{ + /* Write single block command.*/ + if (sdc_lld_send_cmd_short_crc(sdcp, SDC_CMD_WRITE_BLOCK, + startblk, resp) || SDC_R1_ERROR(resp[0])) + return SDC_FAILED; } - 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; + return SDC_SUCCESS; } /** - * @brief Writes one or more blocks. + * @brief Wait end of data transaction and performs finalizations. * * @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. + * @param[in] n number of blocks in transaction + * @param[in] resp pointer to the response buffer * - * @notapi + * @return The operation status. + * @retval SDC_SUCCESS operation succeeded. + * @retval SDC_FAILED operation failed. */ -static bool_t sdc_lld_write_multiple(SDCDriver *sdcp, uint32_t startblk, - const uint8_t *buf, uint32_t n) { - 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, - (n * 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 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_WRITE_MULTIPLE_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 = n * 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); +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_write_multiple(), #1", "not NULL"); + "sdc_lld_start_data_transaction(), #1", "not NULL"); sdcp->thread = chThdSelf(); chSchGoSleepS(THD_STATE_SUSPENDED); chDbgAssert(sdcp->thread == NULL, - "sdc_lld_write_multiple(), #2", "not NULL"); + "sdc_lld_start_data_transaction(), #2", "not NULL"); } if ((SDIO->STA & SDIO_STA_DATAEND) == 0) { chSysUnlock(); - goto error; + return SDC_FAILED; } - dmaStreamDisable(STM32_DMA2_STREAM4); - SDIO->ICR = 0xFFFFFFFF; + + /* 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(); - 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; + /* Wait until interrupt flags to be cleared.*/ + while (((DMA2->LISR) >> (sdcp->dma->ishift)) & STM32_DMA_ISR_TCIF) + dmaStreamClearInterrupt(sdcp->dma); + + /* Finalize transaction.*/ + if (n > 1) + return sdc_lld_send_cmd_short_crc(sdcp, SDC_CMD_STOP_TRANSMISSION, 0, resp); + else + return SDC_SUCCESS; } /** - * @brief Writes one block. + * @brief Gets SDC errors. * * @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]; +static void sdc_lld_collect_errors(SDCDriver *sdcp) { + uint32_t errors = SDC_NO_ERROR; - /* 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_CCRCFAIL){ + SDIO->ICR |= SDIO_ICR_CCRCFAILC; + errors |= SDC_CMD_CRC_ERROR; } - if ((SDIO->STA & SDIO_STA_DATAEND) == 0) { - chSysUnlock(); - goto error; + if (SDIO->STA & SDIO_STA_DCRCFAIL){ + SDIO->ICR |= SDIO_ICR_DCRCFAILC; + errors |= SDC_DATA_CRC_ERROR; + } + if (SDIO->STA & SDIO_STA_CTIMEOUT){ + SDIO->ICR |= SDIO_ICR_CTIMEOUTC; + errors |= SDC_COMMAND_TIMEOUT; + } + if (SDIO->STA & SDIO_STA_DTIMEOUT){ + SDIO->ICR |= SDIO_ICR_CTIMEOUTC; + errors |= SDC_DATA_TIMEOUT; + } + if (SDIO->STA & SDIO_STA_TXUNDERR){ + SDIO->ICR |= SDIO_ICR_TXUNDERRC; + errors |= SDC_TX_UNDERRUN; + } + if (SDIO->STA & SDIO_STA_RXOVERR){ + SDIO->ICR |= SDIO_ICR_RXOVERRC; + errors |= SDC_RX_OVERRUN; + } + if (SDIO->STA & SDIO_STA_STBITERR){ + SDIO->ICR |= SDIO_ICR_STBITERRC; + errors |= SDC_STARTBIT_ERROR; } - dmaStreamDisable(STM32_DMA2_STREAM4); - SDIO->ICR = 0xFFFFFFFF; - SDIO->DCTRL = 0; - chSysUnlock(); - return FALSE; -error: - dmaStreamDisable(STM32_DMA2_STREAM4); - SDIO->ICR = 0xFFFFFFFF; + sdcp->errors |= errors; +} + +/** + * @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; - return TRUE; + sdc_lld_collect_errors(sdcp); + if (n > 1) + sdc_lld_send_cmd_short_crc(sdcp, SDC_CMD_STOP_TRANSMISSION, 0, resp); } /*===========================================================================*/ @@ -391,6 +266,8 @@ error: /** * @brief SDIO IRQ handler. + * @details It just wakes transaction thread. All error handling performs in + * that thread. * * @isr */ @@ -398,17 +275,18 @@ CH_IRQ_HANDLER(SDIO_IRQHandler) { CH_IRQ_PROLOGUE(); - chSysLockFromIsr(); - if (SDCD1.thread != NULL) { - chSchReadyI(SDCD1.thread); - SDCD1.thread = NULL; - } - chSysUnlockFromIsr(); + chSysLockFromIsr() /* Disables the source but the status flags are not reset because the - read/write functions need to check them.*/ + read/write functions needs to check them.*/ SDIO->MASK = 0; + if (SDCD1.thread != NULL) { + chSchReadyI(SDCD1.thread); + SDCD1.thread = NULL; } + + chSysUnlockFromIsr(); + CH_IRQ_EPILOGUE(); } @@ -425,31 +303,52 @@ void sdc_lld_init(void) { sdcObjectInit(&SDCD1); SDCD1.thread = NULL; + SDCD1.dma = STM32_DMA_STREAM(STM32_SDC_SDIO_DMA_STREAM); +#if CH_DBG_ENABLE_ASSERTS + SDCD1.sdio = SDIO; +#endif } /** * @brief Configures and activates the SDC peripheral. * - * @param[in] sdcp pointer to the @p SDCDriver object, must be @p NULL, - * this driver does not require any configuration + * @param[in] sdcp pointer to the @p SDCDriver object * * @notapi */ void sdc_lld_start(SDCDriver *sdcp) { + sdcp->dmamode = STM32_DMA_CR_CHSEL(DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_SDC_SDIO_DMA_PRIORITY) | + STM32_DMA_CR_PSIZE_WORD | + STM32_DMA_CR_MSIZE_WORD | + STM32_DMA_CR_MINC; + + #if (defined(STM32F4XX) || defined(STM32F2XX)) + sdcp->dmamode |= STM32_DMA_CR_PFCTRL | + STM32_DMA_CR_PBURST_INCR4 | + STM32_DMA_CR_MBURST_INCR4; + #endif + if (sdcp->state == SDC_STOP) { /* Note, the DMA must be enabled before the IRQs.*/ - dmaStreamAllocate(STM32_DMA2_STREAM4, 0, NULL, NULL); - dmaStreamSetPeripheral(STM32_DMA2_STREAM4, &SDIO->FIFO); + bool_t b; + b = dmaStreamAllocate(sdcp->dma, STM32_SDC_SDIO_IRQ_PRIORITY, NULL, NULL); + chDbgAssert(!b, "i2c_lld_start(), #3", "stream already allocated"); + dmaStreamSetPeripheral(sdcp->dma, &SDIO->FIFO); + #if (defined(STM32F4XX) || defined(STM32F2XX)) + dmaStreamSetFIFO(sdcp->dma, STM32_DMA_FCR_DMDIS | STM32_DMA_FCR_FTH_FULL); + #endif nvicEnableVector(SDIO_IRQn, CORTEX_PRIORITY_MASK(STM32_SDC_SDIO_IRQ_PRIORITY)); rccEnableSDIO(FALSE); } + /* Configuration, card clock is initially stopped.*/ SDIO->POWER = 0; SDIO->CLKCR = 0; SDIO->DCTRL = 0; - SDIO->DTIMER = STM32_SDC_DATATIMEOUT; + SDIO->DTIMER = 0; } /** @@ -469,7 +368,7 @@ void sdc_lld_stop(SDCDriver *sdcp) { /* Clock deactivation.*/ nvicDisableVector(SDIO_IRQn); - dmaStreamRelease(STM32_DMA2_STREAM4); + dmaStreamRelease(sdcp->dma); rccDisableSDIO(FALSE); } } @@ -538,6 +437,7 @@ void sdc_lld_set_bus_mode(SDCDriver *sdcp, sdcbusmode_t mode) { break; case SDC_MODE_8BIT: SDIO->CLKCR = clk | SDIO_CLKCR_WIDBUS_1; + break; } } @@ -568,10 +468,10 @@ void sdc_lld_send_cmd_none(SDCDriver *sdcp, uint8_t cmd, uint32_t arg) { * @param[in] cmd card command * @param[in] arg command argument * @param[out] resp pointer to the response buffer (one word) + * * @return The operation status. - * @retval FALSE the operation succeeded. - * @retval TRUE the operation failed because timeout, CRC check or - * other errors. + * @retval SDC_SUCCESS operation succeeded. + * @retval SDC_FAILED operation failed. * * @notapi */ @@ -586,10 +486,12 @@ bool_t sdc_lld_send_cmd_short(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, SDIO_STA_CCRCFAIL)) == 0) ; SDIO->ICR = SDIO_ICR_CMDRENDC | SDIO_ICR_CTIMEOUTC | SDIO_ICR_CCRCFAILC; - if ((sta & (SDIO_STA_CTIMEOUT)) != 0) - return TRUE; + if ((sta & (SDIO_STA_CTIMEOUT)) != 0){ + sdc_lld_collect_errors(sdcp); + return SDC_FAILED; + } *resp = SDIO->RESP1; - return FALSE; + return SDC_SUCCESS; } /** @@ -599,10 +501,10 @@ bool_t sdc_lld_send_cmd_short(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, * @param[in] cmd card command * @param[in] arg command argument * @param[out] resp pointer to the response buffer (one word) + * * @return The operation status. - * @retval FALSE the operation succeeded. - * @retval TRUE the operation failed because timeout, CRC check or - * other errors. + * @retval SDC_SUCCESS operation succeeded. + * @retval SDC_FAILED operation failed. * * @notapi */ @@ -617,10 +519,12 @@ bool_t sdc_lld_send_cmd_short_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, SDIO_STA_CCRCFAIL)) == 0) ; SDIO->ICR = SDIO_ICR_CMDRENDC | SDIO_ICR_CTIMEOUTC | SDIO_ICR_CCRCFAILC; - if ((sta & (SDIO_STA_CTIMEOUT | SDIO_STA_CCRCFAIL)) != 0) - return TRUE; + if ((sta & (SDIO_STA_CTIMEOUT | SDIO_STA_CCRCFAIL)) != 0){ + sdc_lld_collect_errors(sdcp); + return SDC_FAILED; + } *resp = SDIO->RESP1; - return FALSE; + return SDC_SUCCESS; } /** @@ -630,10 +534,10 @@ bool_t sdc_lld_send_cmd_short_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, * @param[in] cmd card command * @param[in] arg command argument * @param[out] resp pointer to the response buffer (four words) + * * @return The operation status. - * @retval FALSE the operation succeeded. - * @retval TRUE the operation failed because timeout, CRC check or - * other errors. + * @retval SDC_SUCCESS operation succeeded. + * @retval SDC_FAILED operation failed. * * @notapi */ @@ -650,10 +554,16 @@ bool_t sdc_lld_send_cmd_long_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, SDIO_STA_CCRCFAIL)) == 0) ; SDIO->ICR = SDIO_ICR_CMDRENDC | SDIO_ICR_CTIMEOUTC | SDIO_ICR_CCRCFAILC; - if ((sta & (SDIO_STA_CTIMEOUT | SDIO_STA_CCRCFAIL)) != 0) - return TRUE; - *resp = SDIO->RESP1; - return FALSE; + if ((sta & (STM32_SDIO_STA_ERROR_MASK)) != 0){ + sdc_lld_collect_errors(sdcp); + return SDC_FAILED; + } + /* save bytes in reverse order because MSB in response comes first */ + *resp++ = SDIO->RESP4; + *resp++ = SDIO->RESP3; + *resp++ = SDIO->RESP2; + *resp = SDIO->RESP1; + return SDC_SUCCESS; } /** @@ -663,10 +573,133 @@ bool_t sdc_lld_send_cmd_long_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, * @param[in] startblk first block to read * @param[out] buf pointer to the read buffer * @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. + * @retval SDC_SUCCESS operation succeeded. + * @retval SDC_FAILED operation failed. + * + * @notapi + */ +bool_t sdc_lld_read_aligned(SDCDriver *sdcp, uint32_t startblk, + uint8_t *buf, uint32_t n) { + uint32_t resp[1]; + + chDbgCheck((n < (0x1000000 / SDC_BLOCK_SIZE)), "max transaction size"); + + SDIO->DTIMER = STM32_SDC_READ_TIMEOUT; + + /* Checks for errors and waits for the card to be ready for reading.*/ + if (_sdc_wait_for_transfer_state(sdcp)) + return SDC_FAILED; + + /* Prepares the DMA channel for writing.*/ + dmaStreamSetMemory0(sdcp->dma, buf); + dmaStreamSetTransactionSize(sdcp->dma, + (n * SDC_BLOCK_SIZE) / sizeof (uint32_t)); + dmaStreamSetMode(sdcp->dma, sdcp->dmamode | STM32_DMA_CR_DIR_P2M); + dmaStreamEnable(sdcp->dma); + + /* Setting up data transfer.*/ + SDIO->ICR = STM32_SDIO_ICR_ALL_FLAGS; + SDIO->MASK = SDIO_MASK_DCRCFAILIE | + SDIO_MASK_DTIMEOUTIE | + SDIO_MASK_STBITERRIE | + SDIO_MASK_RXOVERRIE | + SDIO_MASK_DATAENDIE; + SDIO->DLEN = n * SDC_BLOCK_SIZE; + + /* Talk to card what we want from it.*/ + if (sdc_lld_prepare_read(sdcp, startblk, n, resp) == SDC_FAILED) + goto error; + + /* Transaction starts just after DTEN bit setting.*/ + SDIO->DCTRL = SDIO_DCTRL_DTDIR | + SDIO_DCTRL_DBLOCKSIZE_3 | + SDIO_DCTRL_DBLOCKSIZE_0 | + SDIO_DCTRL_DMAEN | + SDIO_DCTRL_DTEN; + if (sdc_lld_wait_transaction_end(sdcp, n, resp) == SDC_FAILED) + goto error; + else + return SDC_SUCCESS; + +error: + sdc_lld_error_cleanup(sdcp, n, resp); + return SDC_FAILED; +} + +/** + * @brief Writes one or more blocks. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[in] startblk first block to write + * @param[out] buf pointer to the write buffer + * @param[in] n number of blocks to write + * + * @return The operation status. + * @retval SDC_SUCCESS operation succeeded. + * @retval SDC_FAILED operation failed. + * + * @notapi + */ +bool_t sdc_lld_write_aligned(SDCDriver *sdcp, uint32_t startblk, + const uint8_t *buf, uint32_t n) { + uint32_t resp[1]; + + chDbgCheck((n < (0x1000000 / SDC_BLOCK_SIZE)), "max transaction size"); + + SDIO->DTIMER = STM32_SDC_WRITE_TIMEOUT; + + /* Checks for errors and waits for the card to be ready for writing.*/ + if (_sdc_wait_for_transfer_state(sdcp)) + return SDC_FAILED; + + /* Prepares the DMA channel for writing.*/ + dmaStreamSetMemory0(sdcp->dma, buf); + dmaStreamSetTransactionSize(sdcp->dma, + (n * SDC_BLOCK_SIZE) / sizeof (uint32_t)); + dmaStreamSetMode(sdcp->dma, sdcp->dmamode | STM32_DMA_CR_DIR_M2P); + dmaStreamEnable(sdcp->dma); + + /* Setting up data transfer.*/ + SDIO->ICR = STM32_SDIO_ICR_ALL_FLAGS; + SDIO->MASK = SDIO_MASK_DCRCFAILIE | + SDIO_MASK_DTIMEOUTIE | + SDIO_MASK_STBITERRIE | + SDIO_MASK_TXUNDERRIE | + SDIO_MASK_DATAENDIE; + SDIO->DLEN = n * SDC_BLOCK_SIZE; + + /* Talk to card what we want from it.*/ + if (sdc_lld_prepare_write(sdcp, startblk, n, resp) == SDC_FAILED) + goto error; + + /* Transaction starts just after DTEN bit setting.*/ + SDIO->DCTRL = SDIO_DCTRL_DBLOCKSIZE_3 | + SDIO_DCTRL_DBLOCKSIZE_0 | + SDIO_DCTRL_DMAEN | + SDIO_DCTRL_DTEN; + if (sdc_lld_wait_transaction_end(sdcp, n, resp) == SDC_FAILED) + goto error; + else + return SDC_SUCCESS; + +error: + sdc_lld_error_cleanup(sdcp, n, resp); + return SDC_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 + * @param[in] n number of blocks to read + * + * @return The operation status. + * @retval SDC_SUCCESS operation succeeded. + * @retval SDC_FAILED operation failed. * * @notapi */ @@ -677,18 +710,16 @@ bool_t sdc_lld_read(SDCDriver *sdcp, uint32_t startblk, if (((unsigned)buf & 3) != 0) { uint32_t i; for (i = 0; i < n; i++) { - if (sdc_lld_read_single(sdcp, startblk, u.buf)) - return TRUE; + if (sdc_lld_read_aligned(sdcp, startblk, u.buf, 1)) + return SDC_FAILED; memcpy(buf, u.buf, SDC_BLOCK_SIZE); buf += SDC_BLOCK_SIZE; startblk++; } - return FALSE; + return SDC_SUCCESS; } #endif - if (n == 1) - return sdc_lld_read_single(sdcp, startblk, buf); - return sdc_lld_read_multiple(sdcp, startblk, buf, n); + return sdc_lld_read_aligned(sdcp, startblk, buf, n); } /** @@ -698,32 +729,30 @@ bool_t sdc_lld_read(SDCDriver *sdcp, uint32_t startblk, * @param[in] startblk first block to write * @param[out] buf pointer to the write buffer * @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. + * @retval SDC_SUCCESS operation succeeded. + * @retval SDC_FAILED operation failed. * * @notapi */ bool_t sdc_lld_write(SDCDriver *sdcp, uint32_t startblk, const uint8_t *buf, uint32_t n) { -#if STM32_SDC_UNALIGNED_SUPPORT + #if STM32_SDC_UNALIGNED_SUPPORT if (((unsigned)buf & 3) != 0) { uint32_t i; for (i = 0; i < n; i++) { memcpy(u.buf, buf, SDC_BLOCK_SIZE); buf += SDC_BLOCK_SIZE; - if (sdc_lld_write_single(sdcp, startblk, u.buf)) - return TRUE; + if (sdc_lld_write_aligned(sdcp, startblk, u.buf, 1)) + return SDC_FAILED; startblk++; } - return FALSE; + return SDC_SUCCESS; } #endif - if (n == 1) - return sdc_lld_write_single(sdcp, startblk, buf); - return sdc_lld_write_multiple(sdcp, startblk, buf, n); + return sdc_lld_write_aligned(sdcp, startblk, buf, n); } #endif /* HAL_USE_SDC */ diff --git a/os/hal/platforms/STM32/sdc_lld.h b/os/hal/platforms/STM32/sdc_lld.h index 1d4f21034..51db5b2aa 100644 --- a/os/hal/platforms/STM32/sdc_lld.h +++ b/os/hal/platforms/STM32/sdc_lld.h @@ -1,6 +1,6 @@ /* ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010, - 2011,2012 Giovanni Di Sirio. + 2011 Giovanni Di Sirio. This file is part of ChibiOS/RT. @@ -35,6 +35,23 @@ /* Driver constants. */ /*===========================================================================*/ +/** + * @brief Value to clear all interrupts flag at once. + */ +#define STM32_SDIO_ICR_ALL_FLAGS (SDIO_ICR_CCRCFAILC | SDIO_ICR_DCRCFAILC | \ + SDIO_ICR_CTIMEOUTC | SDIO_ICR_DTIMEOUTC | \ + SDIO_ICR_TXUNDERRC | SDIO_ICR_RXOVERRC | \ + SDIO_ICR_CMDRENDC | SDIO_ICR_CMDSENTC | \ + SDIO_ICR_DATAENDC | SDIO_ICR_STBITERRC | \ + SDIO_ICR_DBCKENDC | SDIO_ICR_SDIOITC | \ + SDIO_ICR_CEATAENDC) + +/** + * @brief Mask of error flags in STA register. + */ +#define STM32_SDIO_STA_ERROR_MASK (SDIO_STA_CCRCFAIL | SDIO_STA_DCRCFAIL | \ + SDIO_STA_CTIMEOUT | SDIO_STA_DTIMEOUT | \ + SDIO_STA_TXUNDERR | SDIO_STA_RXOVERR) /*===========================================================================*/ /* Driver pre-compile time settings. */ @@ -44,13 +61,6 @@ * @name Configuration options * @{ */ -/** - * @brief SDIO data timeout in SDIO clock cycles. - */ -#if !defined(STM32_SDC_DATATIMEOUT) || defined(__DOXYGEN__) -#define STM32_SDC_DATATIMEOUT 0x000FFFFF -#endif - /** * @brief SDIO DMA priority (0..3|lowest..highest). */ @@ -65,12 +75,6 @@ #define STM32_SDC_SDIO_IRQ_PRIORITY 9 #endif -/** - * @brief SDIO support for unaligned transfers. - */ -#if !defined(STM32_SDC_UNALIGNED_SUPPORT) || defined(__DOXYGEN__) -#define STM32_SDC_UNALIGNED_SUPPORT TRUE -#endif /** @} */ /*===========================================================================*/ @@ -88,14 +92,34 @@ /* * SDIO clock divider. */ -#if STM32_HCLK > 48000000 -#define STM32_SDIO_DIV_HS 0x01 -#define STM32_SDIO_DIV_LS 0xB2 +#if (defined(STM32F4XX) || defined(STM32F2XX)) + #define STM32_SDIO_DIV_HS 0 + #define STM32_SDIO_DIV_LS 120 +#elif STM32_HCLK > 48000000 + #define STM32_SDIO_DIV_HS 1 + #define STM32_SDIO_DIV_LS 178 #else -#define STM32_SDIO_DIV_HS 0x00 -#define STM32_SDIO_DIV_LS 0x76 + #define STM32_SDIO_DIV_HS 0 + #define STM32_SDIO_DIV_LS 118 #endif +/** + * @brief SDIO data timeouts in SDIO clock cycles. + */ +#if (defined(STM32F4XX) || defined(STM32F2XX)) + #define STM32_SDC_WRITE_TIMEOUT \ + (((48000000 / (STM32_SDIO_DIV_HS + 2)) / 1000) * SDC_WRITE_TIMEOUT_MS) + #define STM32_SDC_READ_TIMEOUT \ + (((48000000 / (STM32_SDIO_DIV_HS + 2)) / 1000) * SDC_READ_TIMEOUT_MS) +#else + #define STM32_SDC_WRITE_TIMEOUT \ + (((STM32_HCLK /((STM32_SDIO_DIV_HS + 2)) / 1000) * SDC_WRITE_TIMEOUT_MS) + #define STM32_SDC_READ_TIMEOUT \ + (((STM32_HCLK /((STM32_SDIO_DIV_HS + 2)) / 1000) * SDC_READ_TIMEOUT_MS) +#endif + + + /*===========================================================================*/ /* Driver data structures and types. */ /*===========================================================================*/ @@ -143,6 +167,10 @@ struct SDCDriver { * @brief Various flags regarding the mounted card. */ sdcmode_t cardmode; + /** + * @brief Errors flags. + */ + uint32_t errors; /** * @brief Card CID. */ @@ -155,11 +183,30 @@ struct SDCDriver { * @brief Card RCA. */ uint32_t rca; + /** + * @brief Total number of blocks in card. + */ + uint32_t capacity; /* End of the mandatory fields.*/ /** * @brief Thread waiting for I/O completion IRQ. */ Thread *thread; + /** + * @brief DMA mode bit mask. + */ + uint32_t dmamode; + /** + * @brief Transmit DMA channel. + */ + const stm32_dma_stream_t *dma; + /** + * @brief Pointer to the SDIO registers block. + * @note Used only for dubugging purpose. + */ +#if CH_DBG_ENABLE_ASSERTS + SDIO_TypeDef *sdio; +#endif }; /*===========================================================================*/ diff --git a/os/hal/platforms/STM32F4xx/hal_lld.h b/os/hal/platforms/STM32F4xx/hal_lld.h index c88ac3a96..a058e848a 100644 --- a/os/hal/platforms/STM32F4xx/hal_lld.h +++ b/os/hal/platforms/STM32F4xx/hal_lld.h @@ -339,6 +339,9 @@ /* SDIO attributes.*/ #define STM32_HAS_SDIO TRUE +#define STM32_SDC_SDIO_DMA_MSK (STM32_DMA_STREAM_ID_MSK(2, 3) | \ + STM32_DMA_STREAM_ID_MSK(2, 6)) +#define STM32_SDC_SDIO_DMA_CHN 0x04004000 /* SPI attributes.*/ #define STM32_HAS_SPI1 TRUE @@ -492,6 +495,7 @@ #define TIM8_CC_IRQHandler VectorF8 /**< TIM8 Capture Compare. */ #define DMA1_Stream7_IRQHandler VectorFC /**< DMA1 Stream 7. */ #define FSMC_IRQHandler Vector100 /**< FSMC. */ +#define SDIO_IRQHandler Vector104 /**< SDIO. */ #define TIM5_IRQHandler Vector108 /**< TIM5. */ #define SPI3_IRQHandler Vector10C /**< SPI3. */ #define UART4_IRQHandler Vector110 /**< UART4. */ diff --git a/os/hal/platforms/STM32F4xx/platform.mk b/os/hal/platforms/STM32F4xx/platform.mk index 475a5b35c..fa9caef41 100644 --- a/os/hal/platforms/STM32F4xx/platform.mk +++ b/os/hal/platforms/STM32F4xx/platform.mk @@ -11,10 +11,11 @@ PLATFORMSRC = ${CHIBIOS}/os/hal/platforms/STM32F4xx/stm32_dma.c \ ${CHIBIOS}/os/hal/platforms/STM32/spi_lld.c \ ${CHIBIOS}/os/hal/platforms/STM32/uart_lld.c \ ${CHIBIOS}/os/hal/platforms/STM32/GPIOv2/pal_lld.c \ - ${CHIBIOS}/os/hal/platforms/STM32/RTCv2/rtc_lld.c + ${CHIBIOS}/os/hal/platforms/STM32/RTCv2/rtc_lld.c \ + ${CHIBIOS}/os/hal/platforms/STM32/RTCv2/sdc_lld.c # Required include directories PLATFORMINC = ${CHIBIOS}/os/hal/platforms/STM32F4xx \ ${CHIBIOS}/os/hal/platforms/STM32 \ ${CHIBIOS}/os/hal/platforms/STM32/GPIOv2 \ - ${CHIBIOS}/os/hal/platforms/STM32/RTCv2 \ No newline at end of file + ${CHIBIOS}/os/hal/platforms/STM32/RTCv2 diff --git a/os/hal/platforms/STM32F4xx/stm32_rcc.h b/os/hal/platforms/STM32F4xx/stm32_rcc.h index c7f5c8a51..181a2547c 100644 --- a/os/hal/platforms/STM32F4xx/stm32_rcc.h +++ b/os/hal/platforms/STM32F4xx/stm32_rcc.h @@ -502,6 +502,39 @@ #define rccResetI2C3() rccResetAPB1(RCC_APB1RSTR_I2C3RST) /** @} */ +/** + * @name SDIO peripheral specific RCC operations + * @{ + */ +/** + * @brief Enables the SDIO peripheral clock. + * @note The @p lp parameter is ignored in this family. + * + * @param[in] lp low power enable flag + * + * @api + */ +#define rccEnableSDIO(lp) rccEnableAPB2(RCC_APB2ENR_SDIOEN, lp) + +/** + * @brief Disables the SDIO peripheral clock. + * @note The @p lp parameter is ignored in this family. + * + * @param[in] lp low power enable flag + * + * @api + */ +#define rccDisableSDIO(lp) rccDisableAPB2(RCC_APB2ENR_SDIOEN, lp) + +/** + * @brief Resets the SDIO peripheral. + * @note Not supported in this family, does nothing. + * + * @api + */ +#define rccResetSDIO() rccResetAPB2(RCC_APB2RSTR_SDIORST) +/** @} */ + /** * @name SPI peripherals specific RCC operations * @{ diff --git a/os/hal/src/sdc.c b/os/hal/src/sdc.c index e8894a597..62f3cb45a 100644 --- a/os/hal/src/sdc.c +++ b/os/hal/src/sdc.c @@ -1,6 +1,6 @@ /* ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010, - 2011,2012 Giovanni Di Sirio. + 2011 Giovanni Di Sirio. This file is part of ChibiOS/RT. @@ -47,14 +47,50 @@ /* Driver local functions. */ /*===========================================================================*/ +/** + * @brief Get slice with data from uint32_t[4] array. + * + * @notapi + */ +static uint32_t _sdc_get_slice(uint32_t *data, int8_t end, int8_t start) { + uint32_t word = 0; + uint32_t mask = 0; + + chDbgCheck(((start >=0) && (end >=0) && (end >= start)), "sdc_get_slice"); + + while ((start - 32 * word) > 31){ + word++; + data++; + } + + end -= 32 * word; + start -= 32 * word; + + if (end < 31){ + /* Value lays in one word.*/ + mask = (1 << (end - start + 1)) - 1; + return (*data >> start) & mask; + } + else{ + /* Value spread on separate words.*/ + uint32_t lsb, msb; + lsb = *data >> start; + data++; + mask = (1 << (end - 32 + 1)) - 1; + msb = *data & mask; + msb = msb << (32 - start); + return (msb | lsb); + } +} + /** * @brief Wait for the card to complete pending operations. * * @param[in] sdcp pointer to the @p SDCDriver object + * * @return The operation status. - * @retval FALSE the card is now in transfer state. - * @retval TRUE an error occurred while waiting or the card is in an - * unexpected state. + * @retval SDC_SUCCESS operation succeeded. + * @retval SDC_FAILED operation failed. * * @notapi */ @@ -65,10 +101,10 @@ bool_t _sdc_wait_for_transfer_state(SDCDriver *sdcp) { if (sdc_lld_send_cmd_short_crc(sdcp, SDC_CMD_SEND_STATUS, sdcp->rca, resp) || SDC_R1_ERROR(resp[0])) - return TRUE; + return SDC_FAILED; switch (SDC_R1_STS(resp[0])) { case SDC_STS_TRAN: - return FALSE; + return SDC_SUCCESS; case SDC_STS_DATA: case SDC_STS_RCV: case SDC_STS_PRG: @@ -79,9 +115,11 @@ bool_t _sdc_wait_for_transfer_state(SDCDriver *sdcp) { default: /* The card should have been initialized so any other state is not valid and is reported as an error.*/ - return TRUE; + return SDC_FAILED; } } + /* If something going too wrong.*/ + return SDC_FAILED; } /*===========================================================================*/ @@ -110,7 +148,9 @@ void sdcInit(void) { void sdcObjectInit(SDCDriver *sdcp) { sdcp->state = SDC_STOP; + sdcp->errors = SDC_NO_ERROR; sdcp->config = NULL; + sdcp->capacity = 0; } /** @@ -162,10 +202,10 @@ void sdcStop(SDCDriver *sdcp) { * to perform read and write operations. * * @param[in] sdcp pointer to the @p SDCDriver object + * * @return The operation status. - * @retval FALSE operation succeeded, the driver is now - * in the @p SDC_ACTIVE state. - * @retval TRUE operation failed. + * @retval SDC_SUCCESS operation succeeded. + * @retval SDC_FAILED operation failed. * * @api */ @@ -282,24 +322,48 @@ bool_t sdcConnect(SDCDriver *sdcp) { if (sdc_lld_send_cmd_short_crc(sdcp, SDC_CMD_SET_BUS_WIDTH, 2, resp) || SDC_R1_ERROR(resp[0])) goto failed; + break; } + /* Determine capacity.*/ + switch (sdcp->csd[3] >> 30) { + uint32_t a; + uint8_t b, c; + case 0: + /* CSD version 1.0 */ + a = _sdc_get_slice(sdcp->csd, SDC_CSD_10_C_SIZE_SLICE); + b = _sdc_get_slice(sdcp->csd, SDC_CSD_10_C_SIZE_MULT_SLICE); + c = _sdc_get_slice(sdcp->csd, SDC_CSD_10_READ_BL_LEN_SLICE); + sdcp->capacity = ((a + 1) << (b + 2) << c) / 512; + break; + case 1: + /* CSD version 2.0 */ + a = _sdc_get_slice(sdcp->csd, SDC_CSD_20_C_SIZE_SLICE); + sdcp->capacity = 1024 * (a + 1); + break; + } + if (sdcp->capacity == 0) + goto failed; + + /* Initialization complete.*/ sdcp->state = SDC_ACTIVE; - return FALSE; + return SDC_SUCCESS; + + /* Initialization failed.*/ failed: sdc_lld_stop_clk(sdcp); sdcp->state = SDC_READY; - return TRUE; + return SDC_FAILED; } /** * @brief Brings the driver in a state safe for card removal. * * @param[in] sdcp pointer to the @p SDCDriver object + * * @return The operation status. - * @retval FALSE the operation succeeded and the driver is now - * in the @p SDC_READY state. - * @retval TRUE the operation failed. + * @retval SDC_SUCCESS operation succeeded. + * @retval SDC_FAILED operation failed. * * @api */ @@ -312,20 +376,20 @@ bool_t sdcDisconnect(SDCDriver *sdcp) { "sdcDisconnect(), #1", "invalid state"); if (sdcp->state == SDC_READY) { chSysUnlock(); - return FALSE; + return SDC_SUCCESS; } sdcp->state = SDC_DISCONNECTING; chSysUnlock(); /* Waits for eventual pending operations completion.*/ if (_sdc_wait_for_transfer_state(sdcp)) - return TRUE; + return SDC_FAILED; /* Card clock stopped.*/ sdc_lld_stop_clk(sdcp); sdcp->state = SDC_READY; - return FALSE; + return SDC_SUCCESS; } /** @@ -337,27 +401,32 @@ bool_t sdcDisconnect(SDCDriver *sdcp) { * @param[in] startblk first block to read * @param[out] buf pointer to the read buffer * @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. + * @retval SDC_SUCCESS operation succeeded. + * @retval SDC_FAILED operation failed. * * @api */ bool_t sdcRead(SDCDriver *sdcp, uint32_t startblk, uint8_t *buf, uint32_t n) { - bool_t err; + bool_t status; chDbgCheck((sdcp != NULL) && (buf != NULL) && (n > 0), "sdcRead"); + if ((startblk + n - 1) > sdcp->capacity){ + sdcp->errors |= SDC_OVERFLOW_ERROR; + return SDC_FAILED; + } + chSysLock(); chDbgAssert(sdcp->state == SDC_ACTIVE, "sdcRead(), #1", "invalid state"); sdcp->state = SDC_READING; chSysUnlock(); - err = sdc_lld_read(sdcp, startblk, buf, n); + status = sdc_lld_read(sdcp, startblk, buf, n); sdcp->state = SDC_ACTIVE; - return err; + return status; } /** @@ -369,27 +438,32 @@ bool_t sdcRead(SDCDriver *sdcp, uint32_t startblk, * @param[in] startblk first block to write * @param[out] buf pointer to the write buffer * @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. + * @retval SDC_SUCCESS operation succeeded. + * @retval SDC_FAILED operation failed. * * @api */ bool_t sdcWrite(SDCDriver *sdcp, uint32_t startblk, const uint8_t *buf, uint32_t n) { - bool_t err; + bool_t status; chDbgCheck((sdcp != NULL) && (buf != NULL) && (n > 0), "sdcWrite"); + if ((startblk + n - 1) > sdcp->capacity){ + sdcp->errors |= SDC_OVERFLOW_ERROR; + return SDC_FAILED; + } + chSysLock(); chDbgAssert(sdcp->state == SDC_ACTIVE, "sdcWrite(), #1", "invalid state"); sdcp->state = SDC_WRITING; chSysUnlock(); - err = sdc_lld_write(sdcp, startblk, buf, n); + status = sdc_lld_write(sdcp, startblk, buf, n); sdcp->state = SDC_ACTIVE; - return err; + return status; } #endif /* HAL_USE_SDC */