From 5b2494f872de4fb3bbc549599cf2e3d3dfbd86f5 Mon Sep 17 00:00:00 2001 From: Uladzimir Pylinski Date: Wed, 18 Feb 2015 13:46:06 +0000 Subject: SDIO. 1) Added MMC support. 2) Added bus clock detection. git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@7686 35acf78f-673a-0410-8e92-d51de3d6d3f4 --- os/hal/include/hal_mmcsd.h | 83 +++- os/hal/ports/STM32/LLD/sdc_lld.c | 212 ++++++++--- os/hal/ports/STM32/LLD/sdc_lld.h | 21 +- os/hal/src/hal_mmcsd.c | 48 ++- os/hal/src/sdc.c | 793 +++++++++++++++++++++++++++++++++++---- 5 files changed, 1021 insertions(+), 136 deletions(-) (limited to 'os/hal') diff --git a/os/hal/include/hal_mmcsd.h b/os/hal/include/hal_mmcsd.h index cd8ec4e53..b310124af 100644 --- a/os/hal/include/hal_mmcsd.h +++ b/os/hal/include/hal_mmcsd.h @@ -75,8 +75,10 @@ #define MMCSD_CMD_ALL_SEND_CID 2 #define MMCSD_CMD_SEND_RELATIVE_ADDR 3 #define MMCSD_CMD_SET_BUS_WIDTH 6 +#define MMCSD_CMD_SWITCH MMCSD_CMD_SET_BUS_WIDTH #define MMCSD_CMD_SEL_DESEL_CARD 7 #define MMCSD_CMD_SEND_IF_COND 8 +#define MMCSD_CMD_SEND_EXT_CSD MMCSD_CMD_SEND_IF_COND #define MMCSD_CMD_SEND_CSD 9 #define MMCSD_CMD_SEND_CID 10 #define MMCSD_CMD_STOP_TRANSMISSION 12 @@ -102,6 +104,41 @@ /** * @brief Slice position of values in CSD register. */ +/* CSD for MMC */ +#define MMCSD_CSD_MMC_CSD_STRUCTURE_SLICE 127,126 +#define MMCSD_CSD_MMC_SPEC_VERS_SLICE 125,122 +#define MMCSD_CSD_MMC_TAAC_SLICE 119,112 +#define MMCSD_CSD_MMC_NSAC_SLICE 111,104 +#define MMCSD_CSD_MMC_TRAN_SPEED_SLICE 103,96 +#define MMCSD_CSD_MMC_CCC_SLICE 95,84 +#define MMCSD_CSD_MMC_READ_BL_LEN_SLICE 83,80 +#define MMCSD_CSD_MMC_READ_BL_PARTIAL_SLICE 79,79 +#define MMCSD_CSD_MMC_WRITE_BLK_MISALIGN_SLICE 78,78 +#define MMCSD_CSD_MMC_READ_BLK_MISALIGN_SLICE 77,77 +#define MMCSD_CSD_MMC_DSR_IMP_SLICE 76,76 +#define MMCSD_CSD_MMC_C_SIZE_SLICE 73,62 +#define MMCSD_CSD_MMC_VDD_R_CURR_MIN_SLICE 61,59 +#define MMCSD_CSD_MMC_VDD_R_CURR_MAX_SLICE 58,56 +#define MMCSD_CSD_MMC_VDD_W_CURR_MIN_SLICE 55,53 +#define MMCSD_CSD_MMC_VDD_W_CURR_MAX_SLICE 52,50 +#define MMCSD_CSD_MMC_C_SIZE_MULT_SLICE 49,47 +#define MMCSD_CSD_MMC_ERASE_GRP_SIZE_SLICE 46,42 +#define MMCSD_CSD_MMC_ERASE_GRP_MULT_SLICE 41,37 +#define MMCSD_CSD_MMC_WP_GRP_SIZE_SLICE 36,32 +#define MMCSD_CSD_MMC_WP_GRP_ENABLE_SLICE 31,31 +#define MMCSD_CSD_MMC_DEFAULT_ECC_SLICE 30,29 +#define MMCSD_CSD_MMC_R2W_FACTOR_SLICE 28,26 +#define MMCSD_CSD_MMC_WRITE_BL_LEN_SLICE 25,22 +#define MMCSD_CSD_MMC_WRITE_BL_PARTIAL_SLICE 21,21 +#define MMCSD_CSD_MMC_CONTENT_PROT_APP_SLICE 16,16 +#define MMCSD_CSD_MMC_FILE_FORMAT_GRP_SLICE 15,15 +#define MMCSD_CSD_MMC_COPY_SLICE 14,14 +#define MMCSD_CSD_MMC_PERM_WRITE_PROTECT_SLICE 13,13 +#define MMCSD_CSD_MMC_TMP_WRITE_PROTECT_SLICE 12,12 +#define MMCSD_CSD_MMC_FILE_FORMAT_SLICE 11,10 +#define MMCSD_CSD_MMC_ECC_SLICE 9,8 +#define MMCSD_CSD_MMC_CRC_SLICE 7,1 + /* CSD version 2.0 */ #define MMCSD_CSD_20_CRC_SLICE 7,1 #define MMCSD_CSD_20_FILE_FORMAT_SLICE 11,10 @@ -126,7 +163,7 @@ #define MMCSD_CSD_20_TRANS_SPEED_SLICE 103,96 #define MMCSD_CSD_20_NSAC_SLICE 111,104 #define MMCSD_CSD_20_TAAC_SLICE 119,112 -#define MMCSD_CSD_20_STRUCTURE_SLICE 127,126 +#define MMCSD_CSD_20_CSD_STRUCTURE_SLICE 127,126 /* CSD version 1.0 */ #define MMCSD_CSD_10_CRC_SLICE MMCSD_CSD_20_CRC_SLICE @@ -157,7 +194,45 @@ #define MMCSD_CSD_10_TRANS_SPEED_SLICE MMCSD_CSD_20_TRANS_SPEED_SLICE #define MMCSD_CSD_10_NSAC_SLICE MMCSD_CSD_20_NSAC_SLICE #define MMCSD_CSD_10_TAAC_SLICE MMCSD_CSD_20_TAAC_SLICE -#define MMCSD_CSD_10_STRUCTURE_SLICE MMCSD_CSD_20_STRUCTURE_SLICE +#define MMCSD_CSD_10_CSD_STRUCTURE_SLICE MMCSD_CSD_20_CSD_STRUCTURE_SLICE +/** @} */ + +/** + * @name CID record offsets + */ +/** + * @brief Slice position of values in CID register. + */ +/* CID for SDC */ +#define MMCSD_CID_SDC_CRC_SLICE 7,1 +#define MMCSD_CID_SDC_MDT_M_SLICE 11,8 +#define MMCSD_CID_SDC_MDT_Y_SLICE 19,12 +#define MMCSD_CID_SDC_PSN_SLICE 55,24 +#define MMCSD_CID_SDC_PRV_M_SLICE 59,56 +#define MMCSD_CID_SDC_PRV_N_SLICE 63,60 +#define MMCSD_CID_SDC_PNM0_SLICE 71,64 +#define MMCSD_CID_SDC_PNM1_SLICE 79,72 +#define MMCSD_CID_SDC_PNM2_SLICE 87,80 +#define MMCSD_CID_SDC_PNM3_SLICE 95,88 +#define MMCSD_CID_SDC_PNM4_SLICE 103,96 +#define MMCSD_CID_SDC_OID_SLICE 119,104 +#define MMCSD_CID_SDC_MID_SLICE 127,120 + +/* CID for MMC */ +#define MMCSD_CID_MMC_CRC_SLICE 7,1 +#define MMCSD_CID_MMC_MDT_Y_SLICE 11,8 +#define MMCSD_CID_MMC_MDT_M_SLICE 15,12 +#define MMCSD_CID_MMC_PSN_SLICE 47,16 +#define MMCSD_CID_MMC_PRV_M_SLICE 51,48 +#define MMCSD_CID_MMC_PRV_N_SLICE 55,52 +#define MMCSD_CID_MMC_PNM0_SLICE 63,56 +#define MMCSD_CID_MMC_PNM1_SLICE 71,64 +#define MMCSD_CID_MMC_PNM2_SLICE 79,72 +#define MMCSD_CID_MMC_PNM3_SLICE 87,80 +#define MMCSD_CID_MMC_PNM4_SLICE 95,88 +#define MMCSD_CID_MMC_PNM5_SLICE 103,96 +#define MMCSD_CID_MMC_OID_SLICE 119,104 +#define MMCSD_CID_MMC_MID_SLICE 127,120 /** @} */ /*===========================================================================*/ @@ -266,7 +341,9 @@ typedef struct { #ifdef __cplusplus extern "C" { #endif - uint32_t mmcsdGetCapacity(uint32_t csd[4]); + uint32_t mmcsd_get_slice(const uint32_t *data, uint32_t end, uint32_t start); + uint32_t mmcsdGetCapacity(const uint32_t *csd); + uint32_t mmcsdGetCapacityMMC(const uint32_t *csd, const uint8_t *ext_csd); #ifdef __cplusplus } #endif diff --git a/os/hal/ports/STM32/LLD/sdc_lld.c b/os/hal/ports/STM32/LLD/sdc_lld.c index b7482bf29..343adf6fc 100644 --- a/os/hal/ports/STM32/LLD/sdc_lld.c +++ b/os/hal/ports/STM32/LLD/sdc_lld.c @@ -65,6 +65,104 @@ static union { /* Driver local functions. */ /*===========================================================================*/ +/** + * @brief Prepares MCU to handle read transaction. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[out] buf pointer to the read buffer + * @param[in] n number of blocks to read + * + * @return The operation status. + * @retval HAL_SUCCESS operation succeeded. + * @retval HAL_FAILED operation failed. + * + * @notapi + */ +static bool sdc_lld_prepare_read_mcu_blocks(SDCDriver *sdcp, + uint8_t *buf, uint32_t blocks) { + + osalDbgCheck(blocks < 0x1000000 / MMCSD_BLOCK_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 HAL_FAILED; + + /* Prepares the DMA channel for writing.*/ + dmaStreamSetMemory0(sdcp->dma, buf); + dmaStreamSetTransactionSize(sdcp->dma, + (blocks * MMCSD_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 = blocks * MMCSD_BLOCK_SIZE; + + /* 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; + + return HAL_SUCCESS; +} + +/** + * @brief Prepares MCU to handle read transaction. + * @details Designed for read special registers from card. + * + * @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 + * + * @return The operation status. + * @retval HAL_SUCCESS operation succeeded. + * @retval HAL_FAILED operation failed. + * + * @notapi + */ +static bool sdc_lld_prepare_read_mcu_bytes(SDCDriver *sdcp, + uint8_t *buf, uint32_t bytes) { + osalDbgCheck(bytes < 0x1000000); + + 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 HAL_FAILED; + + /* Prepares the DMA channel for writing.*/ + dmaStreamSetMemory0(sdcp->dma, buf); + dmaStreamSetTransactionSize(sdcp->dma, bytes / 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 = bytes; + + /* Transaction starts just after DTEN bit setting.*/ + SDIO->DCTRL = SDIO_DCTRL_DTDIR | + SDIO_DCTRL_DTMODE | /* multibyte data transfer */ + SDIO_DCTRL_DMAEN | + SDIO_DCTRL_DTEN; + + return HAL_SUCCESS; +} + /** * @brief Prepares card to handle read transaction. * @@ -79,8 +177,8 @@ static union { * * @notapi */ -static bool sdc_lld_prepare_read(SDCDriver *sdcp, uint32_t startblk, - uint32_t n, uint32_t *resp) { +static bool sdc_lld_prepare_read_card(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.*/ @@ -392,11 +490,16 @@ void sdc_lld_start_clk(SDCDriver *sdcp) { * * @notapi */ -void sdc_lld_set_data_clk(SDCDriver *sdcp) { +void sdc_lld_set_data_clk(SDCDriver *sdcp, sdcbusclk_t clk) { (void)sdcp; - SDIO->CLKCR = (SDIO->CLKCR & 0xFFFFFF00) | STM32_SDIO_DIV_HS; + if (SDC_CLK_50MHz == clk) { + SDIO->CLKCR = (SDIO->CLKCR & 0xFFFFFF00) | STM32_SDIO_DIV_HS + | SDIO_CLKCR_BYPASS; + } + else + SDIO->CLKCR = (SDIO->CLKCR & 0xFFFFFF00) | STM32_SDIO_DIV_HS; } /** @@ -574,7 +677,7 @@ bool sdc_lld_send_cmd_long_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, * @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 + * @param[in] blocks number of blocks to read * * @return The operation status. * @retval HAL_SUCCESS operation succeeded. @@ -583,50 +686,59 @@ bool sdc_lld_send_cmd_long_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, * @notapi */ bool sdc_lld_read_aligned(SDCDriver *sdcp, uint32_t startblk, - uint8_t *buf, uint32_t n) { + uint8_t *buf, uint32_t blocks) { uint32_t resp[1]; - osalDbgCheck(n < 0x1000000 / MMCSD_BLOCK_SIZE); + if(sdc_lld_prepare_read_mcu_blocks(sdcp, buf, blocks)) + goto error; - SDIO->DTIMER = STM32_SDC_READ_TIMEOUT; + if (sdc_lld_prepare_read_card(sdcp, startblk, blocks, resp) == TRUE) + goto error; - /* Checks for errors and waits for the card to be ready for reading.*/ - if (_sdc_wait_for_transfer_state(sdcp)) - return HAL_FAILED; + if (sdc_lld_wait_transaction_end(sdcp, blocks, resp) == TRUE) + goto error; - /* Prepares the DMA channel for writing.*/ - dmaStreamSetMemory0(sdcp->dma, buf); - dmaStreamSetTransactionSize(sdcp->dma, - (n * MMCSD_BLOCK_SIZE) / sizeof (uint32_t)); - dmaStreamSetMode(sdcp->dma, sdcp->dmamode | STM32_DMA_CR_DIR_P2M); - dmaStreamEnable(sdcp->dma); + return HAL_SUCCESS; - /* 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 * MMCSD_BLOCK_SIZE; +error: + sdc_lld_error_cleanup(sdcp, blocks, resp); + return HAL_FAILED; +} - /* 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; +/** + * @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]; - /* Talk to card what we want from it.*/ - if (sdc_lld_prepare_read(sdcp, startblk, n, resp) == TRUE) + if(sdc_lld_prepare_read_mcu_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, n, resp) == TRUE) + + if (sdc_lld_wait_transaction_end(sdcp, 1, resp)) goto error; return HAL_SUCCESS; error: - sdc_lld_error_cleanup(sdcp, n, resp); + sdc_lld_error_cleanup(sdcp, 1, resp); return HAL_FAILED; } @@ -645,10 +757,10 @@ error: * @notapi */ bool sdc_lld_write_aligned(SDCDriver *sdcp, uint32_t startblk, - const uint8_t *buf, uint32_t n) { + const uint8_t *buf, uint32_t blocks) { uint32_t resp[1]; - osalDbgCheck(n < 0x1000000 / MMCSD_BLOCK_SIZE); + osalDbgCheck(blocks < 0x1000000 / MMCSD_BLOCK_SIZE); SDIO->DTIMER = STM32_SDC_WRITE_TIMEOUT; @@ -659,7 +771,7 @@ bool sdc_lld_write_aligned(SDCDriver *sdcp, uint32_t startblk, /* Prepares the DMA channel for writing.*/ dmaStreamSetMemory0(sdcp->dma, buf); dmaStreamSetTransactionSize(sdcp->dma, - (n * MMCSD_BLOCK_SIZE) / sizeof (uint32_t)); + (blocks * MMCSD_BLOCK_SIZE) / sizeof (uint32_t)); dmaStreamSetMode(sdcp->dma, sdcp->dmamode | STM32_DMA_CR_DIR_M2P); dmaStreamEnable(sdcp->dma); @@ -670,10 +782,10 @@ bool sdc_lld_write_aligned(SDCDriver *sdcp, uint32_t startblk, SDIO_MASK_STBITERRIE | SDIO_MASK_TXUNDERRIE | SDIO_MASK_DATAENDIE; - SDIO->DLEN = n * MMCSD_BLOCK_SIZE; + SDIO->DLEN = blocks * MMCSD_BLOCK_SIZE; /* Talk to card what we want from it.*/ - if (sdc_lld_prepare_write(sdcp, startblk, n, resp) == TRUE) + if (sdc_lld_prepare_write(sdcp, startblk, blocks, resp) == TRUE) goto error; /* Transaction starts just after DTEN bit setting.*/ @@ -681,13 +793,13 @@ bool sdc_lld_write_aligned(SDCDriver *sdcp, uint32_t startblk, SDIO_DCTRL_DBLOCKSIZE_0 | SDIO_DCTRL_DMAEN | SDIO_DCTRL_DTEN; - if (sdc_lld_wait_transaction_end(sdcp, n, resp) == TRUE) + if (sdc_lld_wait_transaction_end(sdcp, blocks, resp) == TRUE) goto error; return HAL_SUCCESS; error: - sdc_lld_error_cleanup(sdcp, n, resp); + sdc_lld_error_cleanup(sdcp, blocks, resp); return HAL_FAILED; } @@ -697,7 +809,7 @@ error: * @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 + * @param[in] blocks number of blocks to read * * @return The operation status. * @retval HAL_SUCCESS operation succeeded. @@ -706,12 +818,12 @@ error: * @notapi */ bool sdc_lld_read(SDCDriver *sdcp, uint32_t startblk, - uint8_t *buf, uint32_t n) { + uint8_t *buf, uint32_t blocks) { #if STM32_SDC_SDIO_UNALIGNED_SUPPORT if (((unsigned)buf & 3) != 0) { uint32_t i; - for (i = 0; i < n; i++) { + for (i = 0; i < blocks; i++) { if (sdc_lld_read_aligned(sdcp, startblk, u.buf, 1)) return HAL_FAILED; memcpy(buf, u.buf, MMCSD_BLOCK_SIZE); @@ -721,7 +833,7 @@ bool sdc_lld_read(SDCDriver *sdcp, uint32_t startblk, return HAL_SUCCESS; } #endif /* STM32_SDC_SDIO_UNALIGNED_SUPPORT */ - return sdc_lld_read_aligned(sdcp, startblk, buf, n); + return sdc_lld_read_aligned(sdcp, startblk, buf, blocks); } /** @@ -730,7 +842,7 @@ bool sdc_lld_read(SDCDriver *sdcp, uint32_t startblk, * @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 + * @param[in] blocks number of blocks to write * * @return The operation status. * @retval HAL_SUCCESS operation succeeded. @@ -739,12 +851,12 @@ bool sdc_lld_read(SDCDriver *sdcp, uint32_t startblk, * @notapi */ bool sdc_lld_write(SDCDriver *sdcp, uint32_t startblk, - const uint8_t *buf, uint32_t n) { + const uint8_t *buf, uint32_t blocks) { #if STM32_SDC_SDIO_UNALIGNED_SUPPORT if (((unsigned)buf & 3) != 0) { uint32_t i; - for (i = 0; i < n; i++) { + for (i = 0; i < blocks; i++) { memcpy(u.buf, buf, MMCSD_BLOCK_SIZE); buf += MMCSD_BLOCK_SIZE; if (sdc_lld_write_aligned(sdcp, startblk, u.buf, 1)) @@ -754,7 +866,7 @@ bool sdc_lld_write(SDCDriver *sdcp, uint32_t startblk, return HAL_SUCCESS; } #endif /* STM32_SDC_SDIO_UNALIGNED_SUPPORT */ - return sdc_lld_write_aligned(sdcp, startblk, buf, n); + return sdc_lld_write_aligned(sdcp, startblk, buf, blocks); } /** diff --git a/os/hal/ports/STM32/LLD/sdc_lld.h b/os/hal/ports/STM32/LLD/sdc_lld.h index 5438ff710..9fbd4a3fd 100644 --- a/os/hal/ports/STM32/LLD/sdc_lld.h +++ b/os/hal/ports/STM32/LLD/sdc_lld.h @@ -190,6 +190,14 @@ typedef enum { SDC_MODE_8BIT } sdcbusmode_t; +/** + * @brief Max supported clock. + */ +typedef enum { + SDC_CLK_25MHz = 0, + SDC_CLK_50MHz, +} sdcbusclk_t; + /** * @brief Type of card flags. */ @@ -210,7 +218,10 @@ typedef struct SDCDriver SDCDriver; * @note It could be empty on some architectures. */ typedef struct { - uint32_t dummy; + /** + * @brief Bus width(board specific). + */ + sdcbusmode_t bus_width; } SDCConfig; /** @@ -294,7 +305,7 @@ extern "C" { void sdc_lld_start(SDCDriver *sdcp); void sdc_lld_stop(SDCDriver *sdcp); void sdc_lld_start_clk(SDCDriver *sdcp); - void sdc_lld_set_data_clk(SDCDriver *sdcp); + void sdc_lld_set_data_clk(SDCDriver *sdcp, sdcbusclk_t clk); void sdc_lld_stop_clk(SDCDriver *sdcp); void sdc_lld_set_bus_mode(SDCDriver *sdcp, sdcbusmode_t mode); void sdc_lld_send_cmd_none(SDCDriver *sdcp, uint8_t cmd, uint32_t arg); @@ -304,10 +315,12 @@ extern "C" { uint32_t *resp); bool sdc_lld_send_cmd_long_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, uint32_t *resp); + bool sdc_lld_read_special(SDCDriver *sdcp, uint8_t *buf, size_t bytes, + uint8_t cmd, uint32_t argument); bool sdc_lld_read(SDCDriver *sdcp, uint32_t startblk, - uint8_t *buf, uint32_t n); + uint8_t *buf, uint32_t blocks); bool sdc_lld_write(SDCDriver *sdcp, uint32_t startblk, - const uint8_t *buf, uint32_t n); + const uint8_t *buf, uint32_t blocks); bool sdc_lld_sync(SDCDriver *sdcp); bool sdc_lld_is_card_inserted(SDCDriver *sdcp); bool sdc_lld_is_write_protected(SDCDriver *sdcp); diff --git a/os/hal/src/hal_mmcsd.c b/os/hal/src/hal_mmcsd.c index 37f0d8724..e9ae05966 100644 --- a/os/hal/src/hal_mmcsd.c +++ b/os/hal/src/hal_mmcsd.c @@ -45,6 +45,10 @@ /* Driver local functions. */ /*===========================================================================*/ +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + /** * @brief Gets a bit field from a words array. * @note The bit zero is the LSb of the first word. @@ -57,7 +61,7 @@ * * @notapi */ -static uint32_t mmcsd_get_slice(uint32_t *data, uint32_t end, uint32_t start) { +uint32_t mmcsd_get_slice(const uint32_t *data, uint32_t end, uint32_t start) { unsigned startidx, endidx, startoff; uint32_t endmask; @@ -75,10 +79,6 @@ static uint32_t mmcsd_get_slice(uint32_t *data, uint32_t end, uint32_t start) { return (data[startidx] & endmask) >> startoff; /* One piece case. */ } -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - /** * @brief Extract card capacity from a CSD. * @details The capacity is returned as number of available blocks. @@ -88,10 +88,12 @@ static uint32_t mmcsd_get_slice(uint32_t *data, uint32_t end, uint32_t start) { * @return The card capacity. * @retval 0 CSD format error */ -uint32_t mmcsdGetCapacity(uint32_t csd[4]) { - - switch (csd[3] >> 30) { +uint32_t mmcsdGetCapacity(const uint32_t *csd) { uint32_t a, b, c; + + osalDbgCheck(NULL != csd); + + switch (mmcsd_get_slice(csd, MMCSD_CSD_10_CSD_STRUCTURE_SLICE)) { case 0: /* CSD version 1.0 */ a = mmcsd_get_slice(csd, MMCSD_CSD_10_C_SIZE_SLICE); @@ -107,6 +109,36 @@ uint32_t mmcsdGetCapacity(uint32_t csd[4]) { } } +/** + * @brief Extract MMC card capacity from a CSD or EXT_CSD. + * @details The capacity is returned as number of available blocks. + * + * @param[in] csd the CSD record + * @param[in] ext_csd the extended CSD record + * + * @return The card capacity. + */ +uint32_t mmcsdGetCapacityMMC(const uint32_t *csd, const uint8_t *ext_csd) { + uint32_t a, b, c; + + osalDbgCheck(NULL != csd); + + a = mmcsd_get_slice(csd, MMCSD_CSD_MMC_C_SIZE_SLICE); + if (0xFFF != a) { + b = mmcsd_get_slice(csd, MMCSD_CSD_MMC_C_SIZE_MULT_SLICE); + c = mmcsd_get_slice(csd, MMCSD_CSD_MMC_READ_BL_LEN_SLICE); + return (a + 1) << (b + 2) << (c - 9); /* 2^9 == MMCSD_BLOCK_SIZE. */ + } + else if (NULL != ext_csd) { + return (ext_csd[215] << 24) + + (ext_csd[214] << 16) + + (ext_csd[213] << 8) + + ext_csd[212]; + } + else + return 0; +} + #endif /* HAL_USE_MMC_SPI || HAL_USE_SDC */ /** @} */ diff --git a/os/hal/src/sdc.c b/os/hal/src/sdc.c index 5f676a9fa..1825e2863 100644 --- a/os/hal/src/sdc.c +++ b/os/hal/src/sdc.c @@ -27,12 +27,154 @@ #include "hal.h" +#include "string.h" /* for memset() */ + #if HAL_USE_SDC || defined(__DOXYGEN__) /*===========================================================================*/ /* Driver local definitions. */ /*===========================================================================*/ +#if CH_DBG_ENABLE_CHECKS +typedef struct { + uint8_t mid; + uint16_t oid; + char pnm[5]; + uint8_t prv_n; + uint8_t prv_m; + uint32_t psn; + uint8_t mdt_m; + uint16_t mdt_y; + uint8_t crc; +} unpacked_sdc_cid_t; + +typedef struct { + uint8_t mid; + uint16_t oid; + char pnm[6]; + uint8_t prv_n; + uint8_t prv_m; + uint32_t psn; + uint8_t mdt_m; + uint16_t mdt_y; + uint8_t crc; +} unpacked_mmc_cid_t; + +typedef struct { + uint8_t csd_structure; + uint8_t taac; + uint8_t nsac; + uint8_t tran_speed; + uint16_t ccc; + uint8_t read_bl_len; + uint8_t read_bl_partial; + uint8_t write_blk_misalign; + uint8_t read_blk_misalign; + uint8_t dsr_imp; + uint16_t c_size; + uint8_t vdd_r_curr_min; + uint8_t vdd_r_curr_max; + uint8_t vdd_w_curr_min; + uint8_t vdd_w_curr_max; + uint8_t c_size_mult; + uint8_t erase_blk_en; + uint8_t erase_sector_size; + uint8_t wp_grp_size; + uint8_t wp_grp_enable; + uint8_t r2w_factor; + uint8_t write_bl_len; + uint8_t write_bl_partial; + uint8_t file_format_grp; + uint8_t copy; + uint8_t perm_write_protect; + uint8_t tmp_write_protect; + uint8_t file_format; + uint8_t crc; +} unpacked_sdc_csd_10_t; + +typedef struct { + uint8_t csd_structure; + uint8_t taac; + uint8_t nsac; + uint8_t tran_speed; + uint16_t ccc; + uint8_t read_bl_len; + uint8_t read_bl_partial; + uint8_t write_blk_misalign; + uint8_t read_blk_misalign; + uint8_t dsr_imp; + uint32_t c_size; + uint8_t erase_blk_en; + uint8_t erase_sector_size; + uint8_t wp_grp_size; + uint8_t wp_grp_enable; + uint8_t r2w_factor; + uint8_t write_bl_len; + uint8_t write_bl_partial; + uint8_t file_format_grp; + uint8_t copy; + uint8_t perm_write_protect; + uint8_t tmp_write_protect; + uint8_t file_format; + uint8_t crc; +} unmacked_sdc_csd_20_t; + +typedef struct { + uint8_t csd_structure; + uint8_t spec_vers; + uint8_t taac; + uint8_t nsac; + uint8_t tran_speed; + uint16_t ccc; + uint8_t read_bl_len; + uint8_t read_bl_partial; + uint8_t write_blk_misalign; + uint8_t read_blk_misalign; + uint8_t dsr_imp; + uint16_t c_size; + uint8_t vdd_r_curr_min; + uint8_t vdd_r_curr_max; + uint8_t vdd_w_curr_min; + uint8_t vdd_w_curr_max; + uint8_t c_size_mult; + uint8_t erase_grp_size; + uint8_t erase_grp_mult; + uint8_t wp_grp_size; + uint8_t wp_grp_enable; + uint8_t default_ecc; + uint8_t r2w_factor; + uint8_t write_bl_len; + uint8_t write_bl_partial; + uint8_t content_prot_app; + uint8_t file_format_grp; + uint8_t copy; + uint8_t perm_write_protect; + uint8_t tmp_write_protect; + uint8_t file_format; + uint8_t ecc; + uint8_t crc; +} unpacked_mmc_csd_t; +#endif /* CH_DBG_ENABLE_CHECKS */ + +typedef enum { + MMC_SWITCH_COMMAND_SET = 0, + MMC_SWITCH_SET_BITS = 1, + MMC_SWITCH_CLEAR_BITS = 2, + MMC_SWITCH_WRITE_BYTE = 3, +} mmc_switch_t; + +typedef enum { + SD_SWITCH_CHECK = 0, + SD_SWITCH_SET = 1, +} sd_switch_t; + +typedef enum { + SD_SWITCH_FUNCTION_SPEED = 0, + SD_SWITCH_FUNCTION_CMD_SYSTEM = 1, + SD_SWITCH_FUNCTION_DRIVER_STRENGTH = 2, + SD_SWITCH_FUNCTION_CURRENT_LIMIT = 3, +} sd_switch_function_t; + /*===========================================================================*/ /* Driver exported variables. */ /*===========================================================================*/ @@ -55,10 +197,550 @@ static const struct SDCDriverVMT sdc_vmt = { (bool (*)(void *, BlockDeviceInfo *))sdcGetInfo }; +#if CH_DBG_ENABLE_CHECKS +static unmacked_sdc_csd_20_t csd20; +static unpacked_sdc_csd_10_t csd10; +static unpacked_mmc_csd_t csdmmc; +static unpacked_sdc_cid_t cidsdc; +static unpacked_mmc_cid_t cidmmc; +#endif /* CH_DBG_ENABLE_CHECKS */ + +/** + * @brief Temporal storage for different purposes (extended CSD, etc.). + */ +static uint8_t scratchpad[512]; + /*===========================================================================*/ /* Driver local functions. */ /*===========================================================================*/ +/** + * @brief Unpacks CID array in structure. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * + * @notapi + */ +static void unpack_cid(const SDCDriver *sdcp) { +#if CH_DBG_ENABLE_CHECKS + const uint32_t *cid; + + osalDbgCheck(NULL != sdcp); + cid = sdcp->cid; + + switch(sdcp->cardmode & SDC_MODE_CARDTYPE_MASK){ + case SDC_MODE_CARDTYPE_SDV11: + case SDC_MODE_CARDTYPE_SDV20: + cidsdc.crc = mmcsd_get_slice(cid, MMCSD_CID_SDC_CRC_SLICE); + cidsdc.mdt_y = mmcsd_get_slice(cid, MMCSD_CID_SDC_MDT_Y_SLICE) + 2000; + cidsdc.mdt_m = mmcsd_get_slice(cid, MMCSD_CID_SDC_MDT_M_SLICE); + cidsdc.mid = mmcsd_get_slice(cid, MMCSD_CID_SDC_MID_SLICE); + cidsdc.oid = mmcsd_get_slice(cid, MMCSD_CID_SDC_OID_SLICE); + cidsdc.pnm[4] = mmcsd_get_slice(cid, MMCSD_CID_SDC_PNM0_SLICE); + cidsdc.pnm[3] = mmcsd_get_slice(cid, MMCSD_CID_SDC_PNM1_SLICE); + cidsdc.pnm[2] = mmcsd_get_slice(cid, MMCSD_CID_SDC_PNM2_SLICE); + cidsdc.pnm[1] = mmcsd_get_slice(cid, MMCSD_CID_SDC_PNM3_SLICE); + cidsdc.pnm[0] = mmcsd_get_slice(cid, MMCSD_CID_SDC_PNM4_SLICE); + cidsdc.prv_n = mmcsd_get_slice(cid, MMCSD_CID_SDC_PRV_N_SLICE); + cidsdc.prv_m = mmcsd_get_slice(cid, MMCSD_CID_SDC_PRV_M_SLICE); + cidsdc.psn = mmcsd_get_slice(cid, MMCSD_CID_SDC_PSN_SLICE); + break; + + case SDC_MODE_CARDTYPE_MMC: + cidmmc.crc = mmcsd_get_slice(cid, MMCSD_CID_MMC_CRC_SLICE); + cidmmc.mdt_y = mmcsd_get_slice(cid, MMCSD_CID_MMC_MDT_Y_SLICE) + 1997; + cidmmc.mdt_m = mmcsd_get_slice(cid, MMCSD_CID_MMC_MDT_M_SLICE); + cidmmc.mid = mmcsd_get_slice(cid, MMCSD_CID_MMC_MID_SLICE); + cidmmc.oid = mmcsd_get_slice(cid, MMCSD_CID_MMC_OID_SLICE); + cidmmc.pnm[5] = mmcsd_get_slice(cid, MMCSD_CID_MMC_PNM0_SLICE); + cidmmc.pnm[4] = mmcsd_get_slice(cid, MMCSD_CID_MMC_PNM1_SLICE); + cidmmc.pnm[3] = mmcsd_get_slice(cid, MMCSD_CID_MMC_PNM2_SLICE); + cidmmc.pnm[2] = mmcsd_get_slice(cid, MMCSD_CID_MMC_PNM3_SLICE); + cidmmc.pnm[1] = mmcsd_get_slice(cid, MMCSD_CID_MMC_PNM4_SLICE); + cidmmc.pnm[0] = mmcsd_get_slice(cid, MMCSD_CID_MMC_PNM5_SLICE); + cidmmc.prv_n = mmcsd_get_slice(cid, MMCSD_CID_MMC_PRV_N_SLICE); + cidmmc.prv_m = mmcsd_get_slice(cid, MMCSD_CID_MMC_PRV_M_SLICE); + cidmmc.psn = mmcsd_get_slice(cid, MMCSD_CID_MMC_PSN_SLICE); + break; + + default: + osalSysHalt("Unhanlded case"); + break; + } +#else + (void)sdcp; +#endif /* CH_DBG_ENABLE_CHECKS */ +} + +/** + * @brief Unpacks CSD array in structure. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * + * @notapi + */ +static void unpack_csd(const SDCDriver *sdcp) { +#if CH_DBG_ENABLE_CHECKS + const uint32_t *csd; + + osalDbgCheck(NULL != sdcp); + csd = sdcp->csd; + + if (SDC_MODE_CARDTYPE_MMC == (sdcp->cardmode & SDC_MODE_CARDTYPE_MASK)) { + csdmmc.c_size = mmcsd_get_slice(csd, MMCSD_CSD_MMC_C_SIZE_SLICE); + csdmmc.c_size_mult = mmcsd_get_slice(csd, MMCSD_CSD_MMC_C_SIZE_MULT_SLICE); + csdmmc.ccc = mmcsd_get_slice(csd, MMCSD_CSD_MMC_CCC_SLICE); + csdmmc.copy = mmcsd_get_slice(csd, MMCSD_CSD_MMC_COPY_SLICE); + csdmmc.crc = mmcsd_get_slice(csd, MMCSD_CSD_MMC_CRC_SLICE); + csdmmc.csd_structure = mmcsd_get_slice(csd, MMCSD_CSD_MMC_CSD_STRUCTURE_SLICE); + csdmmc.dsr_imp = mmcsd_get_slice(csd, MMCSD_CSD_MMC_DSR_IMP_SLICE); + csdmmc.ecc = mmcsd_get_slice(csd, MMCSD_CSD_MMC_ECC_SLICE); + csdmmc.erase_grp_mult = mmcsd_get_slice(csd, MMCSD_CSD_MMC_ERASE_GRP_MULT_SLICE); + csdmmc.erase_grp_size = mmcsd_get_slice(csd, MMCSD_CSD_MMC_ERASE_GRP_SIZE_SLICE); + csdmmc.file_format = mmcsd_get_slice(csd, MMCSD_CSD_MMC_FILE_FORMAT_SLICE); + csdmmc.file_format_grp = mmcsd_get_slice(csd, MMCSD_CSD_MMC_FILE_FORMAT_GRP_SLICE); + csdmmc.nsac = mmcsd_get_slice(csd, MMCSD_CSD_MMC_NSAC_SLICE); + csdmmc.perm_write_protect = mmcsd_get_slice(csd, MMCSD_CSD_MMC_PERM_WRITE_PROTECT_SLICE); + csdmmc.r2w_factor = mmcsd_get_slice(csd, MMCSD_CSD_MMC_R2W_FACTOR_SLICE); + csdmmc.read_bl_len = mmcsd_get_slice(csd, MMCSD_CSD_MMC_READ_BL_LEN_SLICE); + csdmmc.read_bl_partial = mmcsd_get_slice(csd, MMCSD_CSD_MMC_READ_BL_PARTIAL_SLICE); + csdmmc.read_blk_misalign = mmcsd_get_slice(csd, MMCSD_CSD_MMC_READ_BLK_MISALIGN_SLICE); + csdmmc.spec_vers = mmcsd_get_slice(csd, MMCSD_CSD_MMC_SPEC_VERS_SLICE); + csdmmc.taac = mmcsd_get_slice(csd, MMCSD_CSD_MMC_TAAC_SLICE); + csdmmc.tmp_write_protect = mmcsd_get_slice(csd, MMCSD_CSD_MMC_TMP_WRITE_PROTECT_SLICE); + csdmmc.tran_speed = mmcsd_get_slice(csd, MMCSD_CSD_MMC_TRAN_SPEED_SLICE); + csdmmc.vdd_r_curr_max = mmcsd_get_slice(csd, MMCSD_CSD_MMC_VDD_R_CURR_MAX_SLICE); + csdmmc.vdd_r_curr_min = mmcsd_get_slice(csd, MMCSD_CSD_MMC_VDD_R_CURR_MIN_SLICE); + csdmmc.vdd_w_curr_max = mmcsd_get_slice(csd, MMCSD_CSD_MMC_VDD_W_CURR_MAX_SLICE); + csdmmc.vdd_w_curr_min = mmcsd_get_slice(csd, MMCSD_CSD_MMC_VDD_W_CURR_MIN_SLICE); + csdmmc.wp_grp_enable = mmcsd_get_slice(csd, MMCSD_CSD_MMC_WP_GRP_ENABLE_SLICE); + csdmmc.wp_grp_size = mmcsd_get_slice(csd, MMCSD_CSD_MMC_WP_GRP_SIZE_SLICE); + csdmmc.write_bl_len = mmcsd_get_slice(csd, MMCSD_CSD_MMC_WRITE_BL_LEN_SLICE); + csdmmc.write_bl_partial = mmcsd_get_slice(csd, MMCSD_CSD_MMC_WRITE_BL_PARTIAL_SLICE); + csdmmc.write_blk_misalign = mmcsd_get_slice(csd, MMCSD_CSD_MMC_WRITE_BLK_MISALIGN_SLICE); + } + else { + switch (mmcsd_get_slice(csd, MMCSD_CSD_10_CSD_STRUCTURE_SLICE)) { + case 0: + /* CSD version 1.0 */ + csd10.c_size = mmcsd_get_slice(csd, MMCSD_CSD_10_C_SIZE_SLICE); + csd10.c_size_mult = mmcsd_get_slice(csd, MMCSD_CSD_10_C_SIZE_MULT_SLICE); + csd10.ccc = mmcsd_get_slice(csd, MMCSD_CSD_10_CCC_SLICE); + csd10.copy = mmcsd_get_slice(csd, MMCSD_CSD_10_COPY_SLICE); + csd10.crc = mmcsd_get_slice(csd, MMCSD_CSD_10_CRC_SLICE); + csd10.csd_structure = mmcsd_get_slice(csd, MMCSD_CSD_10_CSD_STRUCTURE_SLICE); + csd10.dsr_imp = mmcsd_get_slice(csd, MMCSD_CSD_10_DSR_IMP_SLICE); + csd10.erase_blk_en = mmcsd_get_slice(csd, MMCSD_CSD_10_ERASE_BLK_EN_SLICE); + csd10.erase_sector_size = mmcsd_get_slice(csd, MMCSD_CSD_10_ERASE_SECTOR_SIZE_SLICE); + csd10.file_format = mmcsd_get_slice(csd, MMCSD_CSD_10_FILE_FORMAT_SLICE); + csd10.file_format_grp = mmcsd_get_slice(csd, MMCSD_CSD_10_FILE_FORMAT_GRP_SLICE); + csd10.nsac = mmcsd_get_slice(csd, MMCSD_CSD_10_NSAC_SLICE); + csd10.perm_write_protect = mmcsd_get_slice(csd, MMCSD_CSD_10_PERM_WRITE_PROTECT_SLICE); + csd10.r2w_factor = mmcsd_get_slice(csd, MMCSD_CSD_10_R2W_FACTOR_SLICE); + csd10.read_bl_len = mmcsd_get_slice(csd, MMCSD_CSD_10_READ_BL_LEN_SLICE); + csd10.read_bl_partial = mmcsd_get_slice(csd, MMCSD_CSD_10_READ_BL_PARTIAL_SLICE); + csd10.read_blk_misalign = mmcsd_get_slice(csd, MMCSD_CSD_10_READ_BLK_MISALIGN_SLICE); + csd10.taac = mmcsd_get_slice(csd, MMCSD_CSD_10_TAAC_SLICE); + csd10.tmp_write_protect = mmcsd_get_slice(csd, MMCSD_CSD_10_TMP_WRITE_PROTECT_SLICE); + csd10.tran_speed = mmcsd_get_slice(csd, MMCSD_CSD_10_TRANS_SPEED_SLICE); + csd10.wp_grp_enable = mmcsd_get_slice(csd, MMCSD_CSD_10_WP_GRP_ENABLE_SLICE); + csd10.wp_grp_size = mmcsd_get_slice(csd, MMCSD_CSD_10_WP_GRP_SIZE_SLICE); + csd10.write_bl_len = mmcsd_get_slice(csd, MMCSD_CSD_10_WRITE_BL_LEN_SLICE); + csd10.write_bl_partial = mmcsd_get_slice(csd, MMCSD_CSD_10_WRITE_BL_PARTIAL_SLICE); + csd10.write_blk_misalign = mmcsd_get_slice(csd, MMCSD_CSD_10_WRITE_BLK_MISALIGN_SLICE); + break; + case 1: + /* CSD version 2.0.*/ + csd20.c_size = mmcsd_get_slice(csd, MMCSD_CSD_20_C_SIZE_SLICE); + csd20.crc = mmcsd_get_slice(csd, MMCSD_CSD_20_CRC_SLICE); + csd20.ccc = mmcsd_get_slice(csd, MMCSD_CSD_20_CCC_SLICE); + csd20.copy = mmcsd_get_slice(csd, MMCSD_CSD_20_COPY_SLICE); + csd20.csd_structure = mmcsd_get_slice(csd, MMCSD_CSD_20_CSD_STRUCTURE_SLICE); + csd20.dsr_imp = mmcsd_get_slice(csd, MMCSD_CSD_20_DSR_IMP_SLICE); + csd20.erase_blk_en = mmcsd_get_slice(csd, MMCSD_CSD_20_ERASE_BLK_EN_SLICE); + csd20.file_format = mmcsd_get_slice(csd, MMCSD_CSD_20_FILE_FORMAT_SLICE); + csd20.file_format_grp = mmcsd_get_slice(csd, MMCSD_CSD_20_FILE_FORMAT_GRP_SLICE); + csd20.nsac = mmcsd_get_slice(csd, MMCSD_CSD_20_NSAC_SLICE); + csd20.perm_write_protect = mmcsd_get_slice(csd, MMCSD_CSD_20_PERM_WRITE_PROTECT_SLICE); + csd20.r2w_factor = mmcsd_get_slice(csd, MMCSD_CSD_20_R2W_FACTOR_SLICE); + csd20.read_bl_len = mmcsd_get_slice(csd, MMCSD_CSD_20_READ_BL_LEN_SLICE); + csd20.read_bl_partial = mmcsd_get_slice(csd, MMCSD_CSD_20_READ_BL_PARTIAL_SLICE); + csd20.read_blk_misalign = mmcsd_get_slice(csd, MMCSD_CSD_20_READ_BLK_MISALIGN_SLICE); + csd20.erase_sector_size = mmcsd_get_slice(csd, MMCSD_CSD_20_ERASE_SECTOR_SIZE_SLICE); + csd20.taac = mmcsd_get_slice(csd, MMCSD_CSD_20_TAAC_SLICE); + csd20.tmp_write_protect = mmcsd_get_slice(csd, MMCSD_CSD_20_TMP_WRITE_PROTECT_SLICE); + csd20.tran_speed = mmcsd_get_slice(csd, MMCSD_CSD_20_TRANS_SPEED_SLICE); + csd20.wp_grp_enable = mmcsd_get_slice(csd, MMCSD_CSD_20_WP_GRP_ENABLE_SLICE); + csd20.wp_grp_size = mmcsd_get_slice(csd, MMCSD_CSD_20_WP_GRP_SIZE_SLICE); + csd20.write_bl_len = mmcsd_get_slice(csd, MMCSD_CSD_20_WRITE_BL_LEN_SLICE); + csd20.write_bl_partial = mmcsd_get_slice(csd, MMCSD_CSD_20_WRITE_BL_PARTIAL_SLICE); + csd20.write_blk_misalign = mmcsd_get_slice(csd, MMCSD_CSD_20_WRITE_BLK_MISALIGN_SLICE); + break; + default: + /* Reserved value detected.*/ + return; + } + } +#else + (void)sdcp; +#endif /* CH_DBG_ENABLE_CHECKS */ +} + +/** + * @brief Detects card mode. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * + * @return The operation status. + * @retval HAL_SUCCESS operation succeeded. + * @retval HAL_FAILED operation failed. + * + * @notapi + */ +static bool mode_detect(SDCDriver *sdcp) { + uint32_t resp[1]; + + /* V2.0 cards detection.*/ + if (!sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_SEND_IF_COND, + MMCSD_CMD8_PATTERN, resp)) { + sdcp->cardmode = SDC_MODE_CARDTYPE_SDV20; + /* Voltage verification.*/ + if (((resp[0] >> 8) & 0xF) != 1) + return HAL_FAILED; + if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_APP_CMD, 0, resp) || + MMCSD_R1_ERROR(resp[0])) + return HAL_FAILED; + } + else { + /* MMC or SD V1.1 detection.*/ + if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_APP_CMD, 0, resp) || + MMCSD_R1_ERROR(resp[0])) + sdcp->cardmode = SDC_MODE_CARDTYPE_MMC; + else { + sdcp->cardmode = SDC_MODE_CARDTYPE_SDV11; + + /* Reset error flag illegal command.*/ + sdc_lld_send_cmd_none(sdcp, MMCSD_CMD_GO_IDLE_STATE, 0); + } + } + + return HAL_SUCCESS; +} + +/** + * @brief Init procedure for MMC. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * + * @return The operation status. + * @retval HAL_SUCCESS operation succeeded. + * @retval HAL_FAILED operation failed. + * + * @notapi + */ +static bool mmc_init(SDCDriver *sdcp) { + uint32_t ocr; + unsigned i; + uint32_t resp[1]; + + ocr = 0xC0FF8000; + i = 0; + while (true) { + if (sdc_lld_send_cmd_short(sdcp, MMCSD_CMD_INIT, ocr, resp)) + return HAL_FAILED; + if ((resp[0] & 0x80000000) != 0) { + if (resp[0] & 0x40000000) + sdcp->cardmode |= SDC_MODE_HIGH_CAPACITY; + break; + } + if (++i >= SDC_INIT_RETRY) + return HAL_FAILED; + osalThreadSleep(OSAL_MS2ST(10)); + } + + return HAL_SUCCESS; +} + +/** + * @brief Init procedure for SDC. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * + * @return The operation status. + * @retval HAL_SUCCESS operation succeeded. + * @retval HAL_FAILED operation failed. + * + * @notapi + */ +static bool sdc_init(SDCDriver *sdcp) { + unsigned i; + uint32_t ocr; + uint32_t resp[1]; + + if ((sdcp->cardmode & SDC_MODE_CARDTYPE_MASK) == SDC_MODE_CARDTYPE_SDV20) + ocr = 0xC0100000; + else + ocr = 0x80100000; + + i = 0; + while (true) { + if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_APP_CMD, 0, resp) || + MMCSD_R1_ERROR(resp[0])) + return HAL_FAILED; + if (sdc_lld_send_cmd_short(sdcp, MMCSD_CMD_APP_OP_COND, ocr, resp)) + return HAL_FAILED; + if ((resp[0] & 0x80000000) != 0) { + if (resp[0] & 0x40000000) + sdcp->cardmode |= SDC_MODE_HIGH_CAPACITY; + break; + } + if (++i >= SDC_INIT_RETRY) + return HAL_FAILED; + osalThreadSleep(OSAL_MS2ST(10)); + } + + return HAL_SUCCESS; +} + +/** + * @brief Constructs CMD6 argument for MMC. + * + * @param[in] access EXT_CSD access mode + * @param[in] index EXT_CSD byte number + * @param[in] value value to be written in target field + * @param[in] cmd_set switch current command set + * + * @return CMD6 argument. + * + * @notapi + */ +static uint32_t mmc_cmd6_construct(mmc_switch_t access, uint8_t index, + uint8_t value, uint8_t cmd_set) { + + osalDbgAssert((index <= 191), "This field is not writable"); + osalDbgAssert((cmd_set < 8), "This field has only 3 bits"); + + return (access << 24) | (index << 16) | (value << 8) | cmd_set; +} + +/** + * @brief Constructs CMD6 argument for SDC. + * + * @param[in] mode switch/test mode + * @param[in] function function number to be switched + * @param[in] value value to be written in target function + * + * @return CMD6 argument. + * + * @notapi + */ +uint32_t sdc_cmd6_construct(sd_switch_t mode, sd_switch_function_t function, + uint8_t value) { + uint32_t ret = 0xFFFFFF; + + osalDbgAssert((value < 16), "This field has only 4 bits"); + + ret &= ~(0xF << (function * 4)); + ret |= value << (function * 4); + return ret | (mode << 31); +} + +/** + * @brief Extracts information from CMD6 answer. + * + * @param[in] function function number to be switched + * @param[in] buf buffer with answer + * + * @return extracted answer. + * + * @notapi + */ +static uint16_t sdc_cmd6_extract_info(sd_switch_function_t function, + const uint8_t *buf) { + + size_t start = 12 - function * 2; + + return (buf[start] << 8) | buf[start+1]; +} + +/** + * @brief Checks status after switching using CMD6. + * + * @param[in] function function number to be switched + * @param[in] buf buffer with answer + * + * @return The operation status. + * @retval HAL_SUCCESS operation succeeded. + * @retval HAL_FAILED operation failed. + * + * @notapi + */ +static bool sdc_cmd6_check_status(sd_switch_function_t function, + const uint8_t *buf) { + + uint32_t tmp; + uint8_t status; + + tmp = (buf[14] << 16) | (buf[15] << 8) | buf[16]; + status = (tmp >> (function * 4)) & 0xF; + if (0xF != status) + return HAL_SUCCESS; + else + return HAL_FAILED; +} + +/** + * @brief Reads supported bus clock and switch SDC to appropriate mode. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[out] clk pointer to clock enum + * + * @return The operation status. + * @retval HAL_SUCCESS operation succeeded. + * @retval HAL_FAILED operation failed. + * + * @notapi + */ +static bool sdc_detect_bus_clk(SDCDriver *sdcp, sdcbusclk_t *clk) { + uint32_t cmdarg; + + *clk = SDC_CLK_25MHz; /* safe default */ + + cmdarg = 0; + memset(scratchpad, 0x55, sizeof(scratchpad)); + if (sdc_lld_read_special(sdcp, scratchpad, 64, MMCSD_CMD_SWITCH, cmdarg)) + return HAL_FAILED; + + if ((sdc_cmd6_extract_info(SD_SWITCH_FUNCTION_SPEED, scratchpad) & 2) == 2) { + cmdarg = sdc_cmd6_construct(SD_SWITCH_SET, SD_SWITCH_FUNCTION_SPEED, 1); + memset(scratchpad, 0x55, sizeof(scratchpad)); + if (sdc_lld_read_special(sdcp, scratchpad, 64, MMCSD_CMD_SWITCH, cmdarg)) + return HAL_FAILED; + if (HAL_SUCCESS == sdc_cmd6_check_status(SD_SWITCH_FUNCTION_SPEED, scratchpad)) + *clk = SDC_CLK_50MHz; + } + + return HAL_SUCCESS; +} + +/** + * @brief Reads supported bus clock and switch MMC to appropriate mode. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[out] clk pointer to clock enum + * + * @return The operation status. + * @retval HAL_SUCCESS operation succeeded. + * @retval HAL_FAILED operation failed. + * + * @notapi + */ +static sdcbusclk_t mmc_detect_bus_clk(SDCDriver *sdcp, sdcbusclk_t *clk) { + uint32_t cmdarg; + uint32_t resp[1]; + + cmdarg = mmc_cmd6_construct(MMC_SWITCH_WRITE_BYTE, 185, 1, 0); + if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_SWITCH, cmdarg, resp) || + MMCSD_R1_ERROR(resp[0])) + *clk = SDC_CLK_25MHz; + else + *clk = SDC_CLK_50MHz; + + return HAL_SUCCESS; +} + +/** + * @brief Reads supported bus clock and switch card to appropriate mode. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[out] clk pointer to clock enum + * + * @return The operation status. + * @retval HAL_SUCCESS operation succeeded. + * @retval HAL_FAILED operation failed. + * + * @notapi + */ +static bool detect_bus_clk(SDCDriver *sdcp, sdcbusclk_t *clk) { + + if (SDC_MODE_CARDTYPE_MMC == (sdcp->cardmode & SDC_MODE_CARDTYPE_MASK)) + return mmc_detect_bus_clk(sdcp, clk); + else + return sdc_detect_bus_clk(sdcp, clk); +} + +/** + * @brief Sets bus width for SDC. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * + * @return The operation status. + * @retval HAL_SUCCESS operation succeeded. + * @retval HAL_FAILED operation failed. + * + * @notapi + */ +static bool sdc_set_bus_width(SDCDriver *sdcp) { + uint32_t resp[1]; + + if (SDC_MODE_1BIT == sdcp->config->bus_width) { + /* Nothing to do. Bus is already in 1bit mode.*/ + return HAL_SUCCESS; + } + else if (SDC_MODE_4BIT == sdcp->config->bus_width) { + sdc_lld_set_bus_mode(sdcp, SDC_MODE_4BIT); + if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_APP_CMD, sdcp->rca, resp) || + MMCSD_R1_ERROR(resp[0])) + return HAL_FAILED; + if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_SET_BUS_WIDTH, 2, resp) || + MMCSD_R1_ERROR(resp[0])) + return HAL_FAILED; + } + else { + /* SD card does not support 8bit bus.*/ + return HAL_FAILED; + } + + return HAL_SUCCESS; +} + +/** + * @brief Sets bus width for MMC. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * + * @return The operation status. + * @retval HAL_SUCCESS operation succeeded. + * @retval HAL_FAILED operation failed. + * + * @notapi + */ +static bool mmc_set_bus_width(SDCDriver *sdcp) { + uint32_t resp[1]; + uint32_t cmdarg; + + switch(sdcp->config->bus_width){ + case SDC_MODE_1BIT: + /* Nothing to do. Bus is already in 1bit mode.*/ + return HAL_SUCCESS; + break; + case SDC_MODE_4BIT: + cmdarg = mmc_cmd6_construct(MMC_SWITCH_WRITE_BYTE, 183, 1, 0); + break; + case SDC_MODE_8BIT: + cmdarg = mmc_cmd6_construct(MMC_SWITCH_WRITE_BYTE, 183, 2, 0); + break; + } + + sdc_lld_set_bus_mode(sdcp, sdcp->config->bus_width); + if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_SWITCH, cmdarg, resp) || + MMCSD_R1_ERROR(resp[0])) { + return HAL_FAILED; + } + + return HAL_SUCCESS; +} + /** * @brief Wait for the card to complete pending operations. * @@ -188,6 +870,7 @@ void sdcStop(SDCDriver *sdcp) { */ bool sdcConnect(SDCDriver *sdcp) { uint32_t resp[1]; + sdcbusclk_t clk; osalDbgCheck(sdcp != NULL); osalDbgAssert((sdcp->state == BLK_ACTIVE) || (sdcp->state == BLK_READY), @@ -202,72 +885,25 @@ bool sdcConnect(SDCDriver *sdcp) { /* Enforces the initial card state.*/ sdc_lld_send_cmd_none(sdcp, MMCSD_CMD_GO_IDLE_STATE, 0); - /* V2.0 cards detection.*/ - if (!sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_SEND_IF_COND, - MMCSD_CMD8_PATTERN, resp)) { - sdcp->cardmode = SDC_MODE_CARDTYPE_SDV20; - /* Voltage verification.*/ - if (((resp[0] >> 8) & 0xF) != 1) - goto failed; - if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_APP_CMD, 0, resp) || - MMCSD_R1_ERROR(resp[0])) - goto failed; - } - else { -#if SDC_MMC_SUPPORT - /* MMC or SD V1.1 detection.*/ - if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_APP_CMD, 0, resp) || - MMCSD_R1_ERROR(resp[0])) - sdcp->cardmode = SDC_MODE_CARDTYPE_MMC; - else -#endif /* SDC_MMC_SUPPORT */ - { - sdcp->cardmode = SDC_MODE_CARDTYPE_SDV11; - - /* Reset error flag illegal command.*/ - sdc_lld_send_cmd_none(sdcp, MMCSD_CMD_GO_IDLE_STATE, 0); - } - } + /* Detect card type.*/ + if (HAL_FAILED == mode_detect(sdcp)) + goto failed; -#if SDC_MMC_SUPPORT + /* Perform specific initialization procedure.*/ if ((sdcp->cardmode & SDC_MODE_CARDTYPE_MASK) == SDC_MODE_CARDTYPE_MMC) { - /* TODO: MMC initialization.*/ - goto failed; + if (HAL_FAILED == mmc_init(sdcp)) + goto failed; } - else -#endif /* SDC_MMC_SUPPORT */ - { - unsigned i; - uint32_t ocr; - - /* SD initialization.*/ - if ((sdcp->cardmode & SDC_MODE_CARDTYPE_MASK) == SDC_MODE_CARDTYPE_SDV20) - ocr = 0xC0100000; - else - ocr = 0x80100000; - - /* SD-type initialization. */ - i = 0; - while (TRUE) { - if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_APP_CMD, 0, resp) || - MMCSD_R1_ERROR(resp[0])) - goto failed; - if (sdc_lld_send_cmd_short(sdcp, MMCSD_CMD_APP_OP_COND, ocr, resp)) - goto failed; - if ((resp[0] & 0x80000000) != 0) { - if (resp[0] & 0x40000000) - sdcp->cardmode |= SDC_MODE_HIGH_CAPACITY; - break; - } - if (++i >= SDC_INIT_RETRY) - goto failed; - osalThreadSleep(OSAL_MS2ST(10)); - } + else { + if (HAL_FAILED == sdc_init(sdcp)) + goto failed; } /* Reads CID.*/ if (sdc_lld_send_cmd_long_crc(sdcp, MMCSD_CMD_ALL_SEND_CID, 0, sdcp->cid)) goto failed; + else + unpack_cid(sdcp); /* Asks for the RCA.*/ if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_SEND_RELATIVE_ADDR, @@ -278,40 +914,54 @@ bool sdcConnect(SDCDriver *sdcp) { if (sdc_lld_send_cmd_long_crc(sdcp, MMCSD_CMD_SEND_CSD, sdcp->rca, sdcp->csd)) goto failed; - - /* Switches to high speed.*/ - sdc_lld_set_data_clk(sdcp); + else + unpack_csd(sdcp); /* Selects the card for operations.*/ if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_SEL_DESEL_CARD, sdcp->rca, resp)) goto failed; + /* Switches to high speed.*/ + if (HAL_SUCCESS != detect_bus_clk(sdcp, &clk)) + goto failed; + sdc_lld_set_data_clk(sdcp, clk); + + /* Reads extended CSD if needed.*/ + if (SDC_MODE_CARDTYPE_MMC == (sdcp->cardmode & SDC_MODE_CARDTYPE_MASK) && + mmcsd_get_slice(sdcp->csd, MMCSD_CSD_MMC_CSD_STRUCTURE_SLICE) > 1) { + if(sdc_lld_read_special(sdcp, scratchpad, 512, MMCSD_CMD_SEND_EXT_CSD, 0)) + goto failed; + } + /* Block length fixed at 512 bytes.*/ if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_SET_BLOCKLEN, MMCSD_BLOCK_SIZE, resp) || MMCSD_R1_ERROR(resp[0])) goto failed; + /* Determine capacity.*/ + if (SDC_MODE_CARDTYPE_MMC == (sdcp->cardmode & SDC_MODE_CARDTYPE_MASK)) + sdcp->capacity = mmcsdGetCapacityMMC(sdcp->csd, scratchpad); + else + sdcp->capacity = mmcsdGetCapacity(sdcp->csd); + + if (sdcp->capacity == 0) + goto failed; + /* Switches to wide bus mode.*/ switch (sdcp->cardmode & SDC_MODE_CARDTYPE_MASK) { case SDC_MODE_CARDTYPE_SDV11: case SDC_MODE_CARDTYPE_SDV20: - sdc_lld_set_bus_mode(sdcp, SDC_MODE_4BIT); - if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_APP_CMD, sdcp->rca, resp) || - MMCSD_R1_ERROR(resp[0])) + if (HAL_FAILED == sdc_set_bus_width(sdcp)) goto failed; - if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_SET_BUS_WIDTH, 2, resp) || - MMCSD_R1_ERROR(resp[0])) + break; + case SDC_MODE_CARDTYPE_MMC: + if (HAL_FAILED == mmc_set_bus_width(sdcp)) goto failed; break; } - /* Determine capacity.*/ - sdcp->capacity = mmcsdGetCapacity(sdcp->csd); - if (sdcp->capacity == 0) - goto failed; - /* Initialization complete.*/ sdcp->state = BLK_READY; return HAL_SUCCESS; @@ -574,3 +1224,4 @@ failed: #endif /* HAL_USE_SDC */ /** @} */ + -- cgit v1.2.3