From cad243d59e6430fbdefd3b66da43f6d952d0cb3a Mon Sep 17 00:00:00 2001 From: Declan Snyder Date: Tue, 30 Aug 2022 11:13:05 -0500 Subject: [PATCH] sd: Changes framework to support MMC - Adds mmc.c - Edits sd.c to init and probe MMC - Adds mmc init to sd_init - Some functions from sdmmc.c should be in sd_ops because they can be used by both sdmmc and mmc. Signed-off-by: Declan Snyder --- subsys/sd/CMakeLists.txt | 2 + subsys/sd/mmc.c | 40 ++++ subsys/sd/sd.c | 18 ++ subsys/sd/sd_init.h | 2 + subsys/sd/sd_ops.c | 503 ++++++++++++++++++++++++++++++++++----- subsys/sd/sd_ops.h | 16 +- subsys/sd/sdmmc.c | 431 ++++----------------------------- 7 files changed, 559 insertions(+), 453 deletions(-) create mode 100644 subsys/sd/mmc.c diff --git a/subsys/sd/CMakeLists.txt b/subsys/sd/CMakeLists.txt index fc138130759..a6c644049f5 100644 --- a/subsys/sd/CMakeLists.txt +++ b/subsys/sd/CMakeLists.txt @@ -7,4 +7,6 @@ if (CONFIG_SD_STACK) zephyr_library_sources(sd.c sd_ops.c) zephyr_library_sources_ifdef(CONFIG_SDMMC_STACK sdmmc.c) zephyr_library_sources_ifdef(CONFIG_SDIO_STACK sdio.c) + zephyr_library_sources_ifdef(CONFIG_MMC_STACK mmc.c) + endif() diff --git a/subsys/sd/mmc.c b/subsys/sd/mmc.c new file mode 100644 index 00000000000..166cd7956f1 --- /dev/null +++ b/subsys/sd/mmc.c @@ -0,0 +1,40 @@ +/* + * Copyright 2022 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +#include "sd_ops.h" +#include "sd_utils.h" + +inline int mmc_write_blocks(struct sd_card *card, const uint8_t *wbuf, uint32_t start_block, + uint32_t num_blocks) +{ + return card_write_blocks(card, wbuf, start_block, num_blocks); +} + +inline int mmc_read_blocks(struct sd_card *card, uint8_t *rbuf, uint32_t start_block, + uint32_t num_blocks) +{ + return card_read_blocks(card, rbuf, start_block, num_blocks); +} + +inline int mmc_ioctl(struct sd_card *card, uint8_t cmd, void *buf) +{ + return card_ioctl(card, cmd, buf); +} + +/* + * Initialize MMC card for use with subsystem + */ +int mmc_card_init(struct sd_card *card) +{ + return -ENOTSUP; +} diff --git a/subsys/sd/sd.c b/subsys/sd/sd.c index 494cf5cec8f..ca00c8bcca4 100644 --- a/subsys/sd/sd.c +++ b/subsys/sd/sd.c @@ -15,6 +15,7 @@ #include "sd_utils.h" #include "sd_init.h" + LOG_MODULE_REGISTER(sd, CONFIG_SD_LOG_LEVEL); /* Idle all cards on bus. Can be used to clear errors on cards */ @@ -24,6 +25,7 @@ static inline int sd_idle(struct sd_card *card) /* Reset card with CMD0 */ cmd.opcode = SD_GO_IDLE_STATE; + cmd.arg = 0x0; cmd.response_type = (SD_RSP_TYPE_NONE | SD_SPI_RSP_TYPE_R1); cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT; return sdhc_request(card->sdhc, &cmd, NULL); @@ -164,19 +166,25 @@ static int sd_init_io(struct sd_card *card) static int sd_command_init(struct sd_card *card) { int ret; + /* * We must wait 74 clock cycles, per SD spec, to use card after power * on. At 400000KHz, this is a 185us delay. Wait 1ms to be safe. */ sd_delay(1); + + /* * Start card initialization and identification * flow described in section 3.6 of SD specification + * Common to SDIO and SDMMC. Some eMMC chips break the + * specification and expect something like this too. */ ret = sd_common_init(card); if (ret) { return ret; } + #ifdef CONFIG_SDIO_STACK /* Attempt to initialize SDIO card */ if (!sdio_card_init(card)) { @@ -189,6 +197,16 @@ static int sd_command_init(struct sd_card *card) return 0; } #endif /* CONFIG_SDIO_STACK */ +#ifdef CONFIG_MMC_STACK + ret = sd_idle(card); + if (ret) { + LOG_ERR("Card error on CMD0"); + return ret; + } + if (!mmc_card_init(card)) { + return 0; + } +#endif /* CONFIG_MMC_STACK */ /* Unknown card type */ return -ENOTSUP; } diff --git a/subsys/sd/sd_init.h b/subsys/sd/sd_init.h index 481f0183754..2ffbb8551a1 100644 --- a/subsys/sd/sd_init.h +++ b/subsys/sd/sd_init.h @@ -15,4 +15,6 @@ int sdio_card_init(struct sd_card *card); int sdmmc_card_init(struct sd_card *card); +int mmc_card_init(struct sd_card *card); + #endif /* ZEPHYR_SUBSYS_SD_INIT_PRIV_H_ */ diff --git a/subsys/sd/sd_ops.c b/subsys/sd/sd_ops.c index 590ff5a554f..007ff54c758 100644 --- a/subsys/sd/sd_ops.c +++ b/subsys/sd/sd_ops.c @@ -4,34 +4,93 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include +#include #include +#include #include #include -#include #include +#include #include "sd_utils.h" LOG_MODULE_DECLARE(sd, CONFIG_SD_LOG_LEVEL); -static inline void sdmmc_decode_csd(struct sd_csd *csd, - uint32_t *raw_csd, uint32_t *blk_count, uint32_t *blk_size) +/* Read card status. Return 0 if card is inactive */ +int sdmmc_read_status(struct sd_card *card) +{ + struct sdhc_command cmd = {0}; + int ret; + + cmd.opcode = SD_SEND_STATUS; + if (!card->host_props.is_spi) { + cmd.arg = (card->relative_addr << 16U); + } + cmd.response_type = (SD_RSP_TYPE_R1 | SD_SPI_RSP_TYPE_R2); + cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT; + + ret = sdhc_request(card->sdhc, &cmd, NULL); + if (ret) { + return SD_RETRY; + } + if (card->host_props.is_spi) { + /* Check R2 response bits */ + if ((cmd.response[0U] & SDHC_SPI_R2_CARD_LOCKED) || + (cmd.response[0U] & SDHC_SPI_R2_UNLOCK_FAIL)) { + return -EACCES; + } else if ((cmd.response[0U] & SDHC_SPI_R2_WP_VIOLATION) || + (cmd.response[0U] & SDHC_SPI_R2_ERASE_PARAM) || + (cmd.response[0U] & SDHC_SPI_R2_OUT_OF_RANGE)) { + return -EINVAL; + } else if ((cmd.response[0U] & SDHC_SPI_R2_ERR) || + (cmd.response[0U] & SDHC_SPI_R2_CC_ERR) || + (cmd.response[0U] & SDHC_SPI_R2_ECC_FAIL)) { + return -EIO; + } + /* Otherwise, no error in R2 response */ + return 0; + } + /* Otherwise, check native card response */ + if ((cmd.response[0U] & SD_R1_RDY_DATA) && + (SD_R1_CURRENT_STATE(cmd.response[0U]) == SDMMC_R1_TRANSFER)) { + return 0; + } + /* Valid response, the card is busy */ + return -EBUSY; +} + +/* Waits for SD card to be ready for data. Returns 0 if card is ready */ +int sdmmc_wait_ready(struct sd_card *card) +{ + int ret, timeout = CONFIG_SD_DATA_TIMEOUT * 1000; + bool busy = true; + + do { + busy = sdhc_card_busy(card->sdhc); + if (!busy) { + /* Check card status */ + ret = sd_retry(sdmmc_read_status, card, CONFIG_SD_RETRY_COUNT); + busy = (ret != 0); + } else { + /* Delay 125us before polling again */ + k_busy_wait(125); + timeout -= 125; + } + } while (busy && (timeout > 0)); + return busy; +} + +static inline void sdmmc_decode_csd(struct sd_csd *csd, uint32_t *raw_csd, uint32_t *blk_count, + uint32_t *blk_size) { uint32_t tmp_blk_count, tmp_blk_size; - csd->csd_structure = (uint8_t)((raw_csd[3U] & - 0xC0000000U) >> 30U); - csd->read_time1 = (uint8_t)((raw_csd[3U] & - 0xFF0000U) >> 16U); - csd->read_time2 = (uint8_t)((raw_csd[3U] & - 0xFF00U) >> 8U); - csd->xfer_rate = (uint8_t)(raw_csd[3U] & - 0xFFU); - csd->cmd_class = (uint16_t)((raw_csd[2U] & - 0xFFF00000U) >> 20U); - csd->read_blk_len = (uint8_t)((raw_csd[2U] & - 0xF0000U) >> 16U); + csd->csd_structure = (uint8_t)((raw_csd[3U] & 0xC0000000U) >> 30U); + csd->read_time1 = (uint8_t)((raw_csd[3U] & 0xFF0000U) >> 16U); + csd->read_time2 = (uint8_t)((raw_csd[3U] & 0xFF00U) >> 8U); + csd->xfer_rate = (uint8_t)(raw_csd[3U] & 0xFFU); + csd->cmd_class = (uint16_t)((raw_csd[2U] & 0xFFF00000U) >> 20U); + csd->read_blk_len = (uint8_t)((raw_csd[2U] & 0xF0000U) >> 16U); if (raw_csd[2U] & 0x8000U) { csd->flags |= SD_CSD_READ_BLK_PARTIAL_FLAG; } @@ -47,24 +106,16 @@ static inline void sdmmc_decode_csd(struct sd_csd *csd, switch (csd->csd_structure) { case 0: - csd->device_size = (uint32_t)((raw_csd[2U] & - 0x3FFU) << 2U); - csd->device_size |= (uint32_t)((raw_csd[1U] & - 0xC0000000U) >> 30U); - csd->read_current_min = (uint8_t)((raw_csd[1U] & - 0x38000000U) >> 27U); - csd->read_current_max = (uint8_t)((raw_csd[1U] & - 0x7000000U) >> 24U); - csd->write_current_min = (uint8_t)((raw_csd[1U] & - 0xE00000U) >> 20U); - csd->write_current_max = (uint8_t)((raw_csd[1U] & - 0x1C0000U) >> 18U); - csd->dev_size_mul = (uint8_t)((raw_csd[1U] & - 0x38000U) >> 15U); + csd->device_size = (uint32_t)((raw_csd[2U] & 0x3FFU) << 2U); + csd->device_size |= (uint32_t)((raw_csd[1U] & 0xC0000000U) >> 30U); + csd->read_current_min = (uint8_t)((raw_csd[1U] & 0x38000000U) >> 27U); + csd->read_current_max = (uint8_t)((raw_csd[1U] & 0x7000000U) >> 24U); + csd->write_current_min = (uint8_t)((raw_csd[1U] & 0xE00000U) >> 20U); + csd->write_current_max = (uint8_t)((raw_csd[1U] & 0x1C0000U) >> 18U); + csd->dev_size_mul = (uint8_t)((raw_csd[1U] & 0x38000U) >> 15U); /* Get card total block count and block size. */ - tmp_blk_count = ((csd->device_size + 1U) << - (csd->dev_size_mul + 2U)); + tmp_blk_count = ((csd->device_size + 1U) << (csd->dev_size_mul + 2U)); tmp_blk_size = (1U << (csd->read_blk_len)); if (tmp_blk_size != SDMMC_DEFAULT_BLOCK_SIZE) { tmp_blk_count = (tmp_blk_count * tmp_blk_size); @@ -81,10 +132,8 @@ static inline void sdmmc_decode_csd(struct sd_csd *csd, case 1: tmp_blk_size = SDMMC_DEFAULT_BLOCK_SIZE; - csd->device_size = (uint32_t)((raw_csd[2U] & - 0x3FU) << 16U); - csd->device_size |= (uint32_t)((raw_csd[1U] & - 0xFFFF0000U) >> 16U); + csd->device_size = (uint32_t)((raw_csd[2U] & 0x3FU) << 16U); + csd->device_size |= (uint32_t)((raw_csd[1U] & 0xFFFF0000U) >> 16U); tmp_blk_count = ((csd->device_size + 1U) * 1024U); if (blk_count) { @@ -101,14 +150,10 @@ static inline void sdmmc_decode_csd(struct sd_csd *csd, if ((uint8_t)((raw_csd[1U] & 0x4000U) >> 14U)) { csd->flags |= SD_CSD_ERASE_BLK_EN_FLAG; } - csd->erase_size = (uint8_t)((raw_csd[1U] & - 0x3F80U) >> 7U); - csd->write_prtect_size = (uint8_t)(raw_csd[1U] & - 0x7FU); - csd->write_speed_factor = (uint8_t)((raw_csd[0U] & - 0x1C000000U) >> 26U); - csd->write_blk_len = (uint8_t)((raw_csd[0U] & - 0x3C00000U) >> 22U); + csd->erase_size = (uint8_t)((raw_csd[1U] & 0x3F80U) >> 7U); + csd->write_prtect_size = (uint8_t)(raw_csd[1U] & 0x7FU); + csd->write_speed_factor = (uint8_t)((raw_csd[0U] & 0x1C000000U) >> 26U); + csd->write_blk_len = (uint8_t)((raw_csd[0U] & 0x3C00000U) >> 22U); if ((uint8_t)((raw_csd[0U] & 0x200000U) >> 21U)) { csd->flags |= SD_CSD_WRITE_BLK_PARTIAL_FLAG; } @@ -127,8 +172,7 @@ static inline void sdmmc_decode_csd(struct sd_csd *csd, csd->file_fmt = (uint8_t)((raw_csd[0U] & 0xC00U) >> 10U); } -static inline void sdmmc_decode_cid(struct sd_cid *cid, - uint32_t *raw_cid) +static inline void sdmmc_decode_cid(struct sd_cid *cid, uint32_t *raw_cid) { cid->manufacturer = (uint8_t)((raw_cid[3U] & 0xFF000000U) >> 24U); cid->application = (uint16_t)((raw_cid[3U] & 0xFFFF00U) >> 8U); @@ -148,8 +192,7 @@ static inline void sdmmc_decode_cid(struct sd_cid *cid, } /* Reads card id/csd register (in SPI mode) */ -static int sdmmc_spi_read_cxd(struct sd_card *card, - uint32_t opcode, uint32_t *cxd) +static int sdmmc_spi_read_cxd(struct sd_card *card, uint32_t opcode, uint32_t *cxd) { struct sdhc_command cmd = {0}; struct sdhc_data data = {0}; @@ -174,14 +217,13 @@ static int sdmmc_spi_read_cxd(struct sd_card *card, } /* Swap endianness of CXD */ for (i = 0; i < 4; i++) { - cxd[3-i] = sys_be32_to_cpu(cxd_be[i]); + cxd[3 - i] = sys_be32_to_cpu(cxd_be[i]); } return 0; } /* Reads card id/csd register (native SD mode */ -static int sdmmc_read_cxd(struct sd_card *card, - uint32_t opcode, uint32_t rca, uint32_t *cxd) +static int sdmmc_read_cxd(struct sd_card *card, uint32_t opcode, uint32_t rca, uint32_t *cxd) { struct sdhc_command cmd = {0}; int ret; @@ -196,13 +238,11 @@ static int sdmmc_read_cxd(struct sd_card *card, LOG_DBG("CMD%d failed: %d", opcode, ret); return ret; } - /* CSD/CID is 16 bytes */ memcpy(cxd, cmd.response, 16); return 0; } - /* Read card specific data register */ int sdmmc_read_csd(struct sd_card *card) { @@ -222,21 +262,20 @@ int sdmmc_read_csd(struct sd_card *card) if (ret) { return ret; } - - sdmmc_decode_csd(&card_csd, csd, - &card->block_count, &card->block_size); - LOG_DBG("Card block count %d, block size %d", - card->block_count, card->block_size); + sdmmc_decode_csd(&card_csd, csd, &card->block_count, &card->block_size); + LOG_DBG("Card block count %d, block size %d", card->block_count, card->block_size); return 0; } /* Reads card identification register, and decodes it */ -int sdmmc_read_cid(struct sd_card *card) +int card_read_cid(struct sd_card *card) { uint32_t cid[4]; int ret; +#if defined(CONFIG_SDMMC_STACK) || defined(CONFIG_SDIO_STACK) /* Keep CID on stack for reduced RAM usage */ struct sd_cid card_cid; +#endif if (card->host_props.is_spi && IS_ENABLED(CONFIG_SDHC_SUPPORTS_SPI_MODE)) { ret = sdmmc_spi_read_cxd(card, SD_SEND_CID, cid); @@ -250,11 +289,19 @@ int sdmmc_read_cid(struct sd_card *card) return ret; } +#if defined(CONFIG_MMC_STACK) + if (card->type == CARD_MMC) { + LOG_INF("CID decoding not supported for MMC"); + return 0; + } +#endif +#if defined(CONFIG_SDMMC_STACK) || defined(CONFIG_SDIO_STACK) /* Decode SD CID */ sdmmc_decode_cid(&card_cid, cid); LOG_DBG("Card MID: 0x%x, OID: %c%c", card_cid.manufacturer, - ((char *)&card_cid.application)[0], - ((char *)&card_cid.application)[1]); + ((char *)&card_cid.application)[0], ((char *)&card_cid.application)[1]); +#endif + return 0; } @@ -379,7 +426,7 @@ int sdmmc_select_card(struct sd_card *card) int ret; cmd.opcode = SD_SELECT_CARD; - cmd.arg = (card->relative_addr << 16U); + cmd.arg = ((card->relative_addr) << 16U); cmd.response_type = SD_RSP_TYPE_R1; cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT; @@ -395,3 +442,335 @@ int sdmmc_select_card(struct sd_card *card) } return 0; } + +/* Helper to send SD app command */ +int card_app_command(struct sd_card *card, int relative_card_address) +{ + struct sdhc_command cmd = {0}; + int ret; + + cmd.opcode = SD_APP_CMD; + cmd.arg = relative_card_address << 16U; + cmd.response_type = (SD_RSP_TYPE_R1 | SD_SPI_RSP_TYPE_R1); + cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT; + ret = sdhc_request(card->sdhc, &cmd, NULL); + if (ret) { + /* We want to retry transmission */ + return SD_RETRY; + } + ret = sd_check_response(&cmd); + if (ret) { + LOG_WRN("SD app command failed with R1 response of 0x%X", cmd.response[0]); + return -EIO; + } + /* Check application command flag to determine if card is ready for APP CMD */ + if ((!card->host_props.is_spi) && !(cmd.response[0U] & SD_R1_APP_CMD)) { + /* Command succeeded, but card not ready for app command. No APP CMD support */ + return -ENOTSUP; + } + return 0; +} + +static int card_read(struct sd_card *card, uint8_t *rbuf, uint32_t start_block, uint32_t num_blocks) +{ + int ret; + struct sdhc_command cmd = {0}; + struct sdhc_data data = {0}; + + /* + * Note: The SD specification allows for CMD23 to be sent before a + * transfer in order to set the block length (often preferable). + * The specification also requires that CMD12 be sent to stop a transfer. + * However, the host specification defines support for "Auto CMD23" and + * "Auto CMD12", where the host sends CMD23 and CMD12 automatically to + * remove the overhead of interrupts in software from sending these + * commands. Therefore, we will not handle CMD12 or CMD23 at this layer. + * The host SDHC driver is expected to recognize CMD17, CMD18, CMD24, + * and CMD25 as special read/write commands and handle CMD23 and + * CMD12 appropriately. + */ + cmd.opcode = (num_blocks == 1U) ? SD_READ_SINGLE_BLOCK : SD_READ_MULTIPLE_BLOCK; + if (!(card->flags & SD_HIGH_CAPACITY_FLAG)) { + /* SDSC cards require block size in bytes, not blocks */ + cmd.arg = start_block * card->block_size; + } else { + cmd.arg = start_block; + } + cmd.response_type = (SD_RSP_TYPE_R1 | SD_SPI_RSP_TYPE_R1); + cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT; + cmd.retries = CONFIG_SD_DATA_RETRIES; + + data.block_addr = start_block; + data.block_size = card->block_size; + data.blocks = num_blocks; + data.data = rbuf; + data.timeout_ms = CONFIG_SD_DATA_TIMEOUT; + + LOG_DBG("READ: Sector = %u, Count = %u", start_block, num_blocks); + + ret = sdhc_request(card->sdhc, &cmd, &data); + if (ret) { + LOG_ERR("Failed to read from SDMMC %d", ret); + return ret; + } + + /* Verify card is back in transfer state after read */ + ret = sdmmc_wait_ready(card); + if (ret) { + LOG_ERR("Card did not return to ready state"); + k_mutex_unlock(&card->lock); + return -ETIMEDOUT; + } + return 0; +} + +/* Reads data from SD card memory card */ +int card_read_blocks(struct sd_card *card, uint8_t *rbuf, uint32_t start_block, uint32_t num_blocks) +{ + int ret; + uint32_t rlen; + uint32_t sector; + uint8_t *buf_offset; + + if ((start_block + num_blocks) > card->block_count) { + return -EINVAL; + } + if (card->type == CARD_SDIO) { + LOG_WRN("SDIO does not support MMC commands"); + return -ENOTSUP; + } + ret = k_mutex_lock(&card->lock, K_NO_WAIT); + if (ret) { + LOG_WRN("Could not get SD card mutex"); + return -EBUSY; + } + + /* + * If the buffer we are provided with is aligned, we can use it + * directly. Otherwise, we need to use the card's internal buffer + * and memcpy the data back out + */ + if ((((uintptr_t)rbuf) & (CONFIG_SDHC_BUFFER_ALIGNMENT - 1)) != 0) { + /* lower bits of address are set, not aligned. Use internal buffer */ + LOG_DBG("Unaligned buffer access to SD card may incur performance penalty"); + if (sizeof(card->card_buffer) < card->block_size) { + LOG_ERR("Card buffer size needs to be increased for " + "unaligned writes to work"); + k_mutex_unlock(&card->lock); + return -ENOBUFS; + } + rlen = sizeof(card->card_buffer) / card->block_size; + sector = 0; + buf_offset = rbuf; + while (sector < num_blocks) { + /* Read from disk to card buffer */ + ret = card_read(card, card->card_buffer, sector + start_block, rlen); + if (ret) { + LOG_ERR("Write failed"); + k_mutex_unlock(&card->lock); + return ret; + } + /* Copy data from card buffer */ + memcpy(buf_offset, card->card_buffer, rlen * card->block_size); + /* Increase sector count and buffer offset */ + sector += rlen; + buf_offset += rlen * card->block_size; + } + } else { + /* Aligned buffers can be used directly */ + ret = card_read(card, rbuf, start_block, num_blocks); + if (ret) { + LOG_ERR("Card read failed"); + k_mutex_unlock(&card->lock); + return ret; + } + } + k_mutex_unlock(&card->lock); + return 0; +} + +/* + * Sends ACMD22 (number of written blocks) to see how many blocks were written + * to a card + */ +static int card_query_written(struct sd_card *card, uint32_t *num_written) +{ + int ret; + struct sdhc_command cmd = {0}; + struct sdhc_data data = {0}; + uint32_t *blocks = (uint32_t *)card->card_buffer; + + ret = card_app_command(card, card->relative_addr); + if (ret) { + LOG_DBG("App CMD for ACMD22 failed"); + return ret; + } + + cmd.opcode = SD_APP_SEND_NUM_WRITTEN_BLK; + cmd.arg = 0; + cmd.response_type = (SD_RSP_TYPE_R1 | SD_SPI_RSP_TYPE_R1); + cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT; + + data.block_size = 4U; + data.blocks = 1U; + data.data = blocks; + data.timeout_ms = CONFIG_SD_DATA_TIMEOUT; + + ret = sdhc_request(card->sdhc, &cmd, &data); + if (ret) { + LOG_DBG("ACMD22 failed: %d", ret); + return ret; + } + ret = sd_check_response(&cmd); + if (ret) { + LOG_DBG("ACMD22 reports error"); + return ret; + } + + /* Decode blocks */ + *num_written = sys_be32_to_cpu(blocks[0]); + return 0; +} + +static int card_write(struct sd_card *card, const uint8_t *wbuf, uint32_t start_block, + uint32_t num_blocks) +{ + int ret; + uint32_t blocks; + struct sdhc_command cmd = {0}; + struct sdhc_data data = {0}; + + /* + * See the note in card_read() above. We will not issue CMD23 + * or CMD12, and expect the host to handle those details. + */ + cmd.opcode = (num_blocks == 1) ? SD_WRITE_SINGLE_BLOCK : SD_WRITE_MULTIPLE_BLOCK; + if (!(card->flags & SD_HIGH_CAPACITY_FLAG)) { + /* SDSC cards require block size in bytes, not blocks */ + cmd.arg = start_block * card->block_size; + } else { + cmd.arg = start_block; + } + cmd.response_type = (SD_RSP_TYPE_R1 | SD_SPI_RSP_TYPE_R1); + cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT; + cmd.retries = CONFIG_SD_DATA_RETRIES; + + data.block_addr = start_block; + data.block_size = card->block_size; + data.blocks = num_blocks; + data.data = (uint8_t *)wbuf; + data.timeout_ms = CONFIG_SD_DATA_TIMEOUT; + + LOG_DBG("WRITE: Sector = %u, Count = %u", start_block, num_blocks); + + ret = sdhc_request(card->sdhc, &cmd, &data); + if (ret) { + LOG_DBG("Write failed: %d", ret); + ret = sdmmc_wait_ready(card); + if (ret) { + return ret; + } + /* Query card to see how many blocks were actually written */ + ret = card_query_written(card, &blocks); + if (ret) { + return ret; + } + LOG_ERR("Only %d blocks of %d were written", blocks, num_blocks); + return -EIO; + } + /* Verify card is back in transfer state after write */ + ret = sdmmc_wait_ready(card); + if (ret) { + LOG_ERR("Card did not return to ready state"); + return -ETIMEDOUT; + } + return 0; +} + +/* Writes data to SD card memory card */ +int card_write_blocks(struct sd_card *card, const uint8_t *wbuf, uint32_t start_block, + uint32_t num_blocks) +{ + int ret; + uint32_t wlen; + uint32_t sector; + const uint8_t *buf_offset; + + if ((start_block + num_blocks) > card->block_count) { + return -EINVAL; + } + if (card->type == CARD_SDIO) { + LOG_WRN("SDIO does not support MMC commands"); + return -ENOTSUP; + } + ret = k_mutex_lock(&card->lock, K_NO_WAIT); + if (ret) { + LOG_WRN("Could not get SD card mutex"); + return -EBUSY; + } + /* + * If the buffer we are provided with is aligned, we can use it + * directly. Otherwise, we need to use the card's internal buffer + * and memcpy the data back out + */ + if ((((uintptr_t)wbuf) & (CONFIG_SDHC_BUFFER_ALIGNMENT - 1)) != 0) { + /* lower bits of address are set, not aligned. Use internal buffer */ + LOG_DBG("Unaligned buffer access to SD card may incur performance penalty"); + if (sizeof(card->card_buffer) < card->block_size) { + LOG_ERR("Card buffer size needs to be increased for " + "unaligned writes to work"); + k_mutex_unlock(&card->lock); + return -ENOBUFS; + } + wlen = sizeof(card->card_buffer) / card->block_size; + sector = 0; + buf_offset = wbuf; + while (sector < num_blocks) { + /* Copy data into card buffer */ + memcpy(card->card_buffer, buf_offset, wlen * card->block_size); + /* Write card buffer to disk */ + ret = card_write(card, card->card_buffer, sector + start_block, wlen); + if (ret) { + LOG_ERR("Write failed"); + k_mutex_unlock(&card->lock); + return ret; + } + /* Increase sector count and buffer offset */ + sector += wlen; + buf_offset += wlen * card->block_size; + } + } else { + /* We can use aligned buffers directly */ + ret = card_write(card, wbuf, start_block, num_blocks); + if (ret) { + LOG_ERR("Write failed"); + k_mutex_unlock(&card->lock); + return ret; + } + } + k_mutex_unlock(&card->lock); + return 0; +} + +/* IO Control handler for SD MMC */ +int card_ioctl(struct sd_card *card, uint8_t cmd, void *buf) +{ + switch (cmd) { + case DISK_IOCTL_GET_SECTOR_COUNT: + (*(uint32_t *)buf) = card->block_count; + break; + case DISK_IOCTL_GET_SECTOR_SIZE: + case DISK_IOCTL_GET_ERASE_BLOCK_SZ: + (*(uint32_t *)buf) = card->block_size; + break; + case DISK_IOCTL_CTRL_SYNC: + /* Ensure card is not busy with data write. + * Note that SD stack does not support enabling caching, so + * cache flush is not required here + */ + return sdmmc_wait_ready(card); + default: + return -ENOTSUP; + } + return 0; +} diff --git a/subsys/sd/sd_ops.h b/subsys/sd/sd_ops.h index 4a89aec2a10..ecd04f48a62 100644 --- a/subsys/sd/sd_ops.h +++ b/subsys/sd/sd_ops.h @@ -17,7 +17,7 @@ int sdmmc_switch_voltage(struct sd_card *card); /* * Reads card identification register, and decodes it */ -int sdmmc_read_cid(struct sd_card *card); +int card_read_cid(struct sd_card *card); /* * Read card specific data register @@ -45,4 +45,18 @@ static inline int sdmmc_host_uhs(struct sdhc_host_props *props) & (props->host_caps.vol_180_support); } +int card_ioctl(struct sd_card *card, uint8_t cmd, void *buf); + +int card_read_blocks(struct sd_card *card, uint8_t *rbuf, + uint32_t start_block, uint32_t num_blocks); + +int card_write_blocks(struct sd_card *card, const uint8_t *wbuf, + uint32_t start_block, uint32_t num_blocks); + +int card_app_command(struct sd_card *card, int relative_card_address); + +int sdmmc_read_status(struct sd_card *card); + +int sdmmc_wait_ready(struct sd_card *card); + #endif /* ZEPHYR_SUBSYS_SD_SD_OPS_H_ */ diff --git a/subsys/sd/sdmmc.c b/subsys/sd/sdmmc.c index ae72d811b41..084df891e46 100644 --- a/subsys/sd/sdmmc.c +++ b/subsys/sd/sdmmc.c @@ -63,30 +63,23 @@ static inline void sdmmc_decode_scr(struct sd_scr *scr, /* Helper to send SD app command */ static int sdmmc_app_command(struct sd_card *card, int relative_card_address) { - struct sdhc_command cmd = {0}; + return card_app_command(card, relative_card_address); +} + +/* Reads OCR from SPI mode card using CMD58 */ +static int sdmmc_spi_send_ocr(struct sd_card *card, uint32_t arg) +{ + struct sdhc_command cmd; int ret; - cmd.opcode = SD_APP_CMD; - cmd.arg = relative_card_address << 16U; - cmd.response_type = (SD_RSP_TYPE_R1 | SD_SPI_RSP_TYPE_R1); - cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT; + cmd.opcode = SD_SPI_READ_OCR; + cmd.arg = arg; + cmd.response_type = SD_SPI_RSP_TYPE_R3; + ret = sdhc_request(card->sdhc, &cmd, NULL); - if (ret) { - /* We want to retry transmission */ - return SD_RETRY; - } - ret = sd_check_response(&cmd); - if (ret) { - LOG_WRN("SD app command failed with R1 response of 0x%X", - cmd.response[0]); - return -EIO; - } - /* Check application command flag to determine if card is ready for APP CMD */ - if ((!card->host_props.is_spi) && !(cmd.response[0U] & SD_R1_APP_CMD)) { - /* Command succeeded, but card not ready for app command. No APP CMD support */ - return -ENOTSUP; - } - return 0; + + card->ocr = cmd.response[1]; + return ret; } /* Sends OCR to card using ACMD41 */ @@ -557,11 +550,16 @@ int sdmmc_card_init(struct sd_card *card) int ret; uint32_t ocr_arg = 0U; - /* First send a probing OCR using ACMD41. Note that SPI cards also - * accept CMD58 at this point, but we skip this command as it is not - * required by the spec. - */ - ret = sdmmc_send_ocr(card, ocr_arg); + /* First send a probing OCR */ + if (card->host_props.is_spi && IS_ENABLED(CONFIG_SDHC_SUPPORTS_SPI_MODE)) { + /* Probe SPI card with CMD58*/ + ret = sdmmc_spi_send_ocr(card, ocr_arg); + } else if (IS_ENABLED(CONFIG_SDHC_SUPPORTS_NATIVE_MODE)) { + /* Probe Native card with ACMD41 */ + ret = sdmmc_send_ocr(card, ocr_arg); + } else { + return -ENOTSUP; + } if (ret) { return ret; } @@ -569,9 +567,11 @@ int sdmmc_card_init(struct sd_card *card) card->type = CARD_SDMMC; if (card->flags & SD_SDHC_FLAG) { - /* High capacity card. See if host supports 1.8V */ - if (card->host_props.host_caps.vol_180_support) { - ocr_arg |= SD_OCR_SWITCH_18_REQ_FLAG; + if (IS_ENABLED(CONFIG_SDHC_SUPPORTS_NATIVE_MODE)) { + /* High capacity card. See if host supports 1.8V */ + if (card->host_props.host_caps.vol_180_support) { + ocr_arg |= SD_OCR_SWITCH_18_REQ_FLAG; + } } /* Set host high capacity support flag */ ocr_arg |= SD_OCR_HOST_CAP_FLAG; @@ -594,6 +594,13 @@ int sdmmc_card_init(struct sd_card *card) LOG_ERR("Failed to query card OCR"); return ret; } + if (card->host_props.is_spi && IS_ENABLED(CONFIG_SDHC_SUPPORTS_SPI_MODE)) { + /* Send second CMD58 to get CCS bit */ + ret = sdmmc_spi_send_ocr(card, ocr_arg); + if (ret) { + return ret; + } + } /* Check SD high capacity and 1.8V support flags */ if (card->ocr & SD_OCR_CARD_CAP_FLAG) { card->flags |= SD_HIGH_CAPACITY_FLAG; @@ -629,7 +636,7 @@ int sdmmc_card_init(struct sd_card *card) } } /* Read the card's CID (card identification register) */ - ret = sdmmc_read_cid(card); + ret = card_read_cid(card); if (ret) { return ret; } @@ -713,375 +720,19 @@ int sdmmc_card_init(struct sd_card *card) return ret; } -/* Read card status. Return 0 if card is inactive */ -static int sdmmc_read_status(struct sd_card *card) +int sdmmc_ioctl(struct sd_card *card, uint8_t cmd, void *buf) { - struct sdhc_command cmd = {0}; - int ret; - - cmd.opcode = SD_SEND_STATUS; - if (!card->host_props.is_spi) { - cmd.arg = (card->relative_addr << 16U); - } - cmd.response_type = (SD_RSP_TYPE_R1 | SD_SPI_RSP_TYPE_R2); - cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT; - - ret = sdhc_request(card->sdhc, &cmd, NULL); - if (ret) { - return SD_RETRY; - } - if (card->host_props.is_spi) { - /* Check R2 response bits */ - if ((cmd.response[0U] & SDHC_SPI_R2_CARD_LOCKED) || - (cmd.response[0U] & SDHC_SPI_R2_UNLOCK_FAIL)) { - return -EACCES; - } else if ((cmd.response[0U] & SDHC_SPI_R2_WP_VIOLATION) || - (cmd.response[0U] & SDHC_SPI_R2_ERASE_PARAM) || - (cmd.response[0U] & SDHC_SPI_R2_OUT_OF_RANGE)) { - return -EINVAL; - } else if ((cmd.response[0U] & SDHC_SPI_R2_ERR) || - (cmd.response[0U] & SDHC_SPI_R2_CC_ERR) || - (cmd.response[0U] & SDHC_SPI_R2_ECC_FAIL)) { - return -EIO; - } - /* Otherwise, no error in R2 response */ - return 0; - } - /* Otherwise, check native card response */ - if ((cmd.response[0U] & SD_R1_RDY_DATA) && - (SD_R1_CURRENT_STATE(cmd.response[0U]) == SDMMC_R1_TRANSFER)) { - return 0; - } - /* Valid response, the card is busy */ - return -EBUSY; + return card_ioctl(card, cmd, buf); } -/* Waits for SD card to be ready for data. Returns 0 if card is ready */ -static int sdmmc_wait_ready(struct sd_card *card) -{ - int ret, timeout = CONFIG_SD_DATA_TIMEOUT * 1000; - bool busy = true; - - do { - busy = sdhc_card_busy(card->sdhc); - if (!busy) { - /* Check card status */ - ret = sd_retry(sdmmc_read_status, card, CONFIG_SD_RETRY_COUNT); - busy = (ret != 0); - } else { - /* Delay 125us before polling again */ - k_busy_wait(125); - timeout -= 125; - } - } while (busy && (timeout > 0)); - return busy; -} - -static int sdmmc_read(struct sd_card *card, uint8_t *rbuf, - uint32_t start_block, uint32_t num_blocks) -{ - int ret; - struct sdhc_command cmd = {0}; - struct sdhc_data data = {0}; - - /* - * Note: The SD specification allows for CMD23 to be sent before a - * transfer in order to set the block length (often preferable). - * The specification also requires that CMD12 be sent to stop a transfer. - * However, the host specification defines support for "Auto CMD23" and - * "Auto CMD12", where the host sends CMD23 and CMD12 automatically to - * remove the overhead of interrupts in software from sending these - * commands. Therefore, we will not handle CMD12 or CMD23 at this layer. - * The host SDHC driver is expected to recognize CMD17, CMD18, CMD24, - * and CMD25 as special read/write commands and handle CMD23 and - * CMD12 appropriately. - */ - cmd.opcode = (num_blocks == 1U) ? SD_READ_SINGLE_BLOCK : SD_READ_MULTIPLE_BLOCK; - if (!(card->flags & SD_HIGH_CAPACITY_FLAG)) { - /* SDSC cards require block size in bytes, not blocks */ - cmd.arg = start_block * card->block_size; - } else { - cmd.arg = start_block; - } - cmd.response_type = (SD_RSP_TYPE_R1 | SD_SPI_RSP_TYPE_R1); - cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT; - cmd.retries = CONFIG_SD_DATA_RETRIES; - - data.block_addr = start_block; - data.block_size = card->block_size; - data.blocks = num_blocks; - data.data = rbuf; - data.timeout_ms = CONFIG_SD_DATA_TIMEOUT; - - LOG_DBG("READ: Sector = %u, Count = %u", start_block, num_blocks); - - ret = sdhc_request(card->sdhc, &cmd, &data); - if (ret) { - LOG_ERR("Failed to read from SDMMC %d", ret); - return ret; - } - - /* Verify card is back in transfer state after read */ - ret = sdmmc_wait_ready(card); - if (ret) { - LOG_ERR("Card did not return to ready state"); - k_mutex_unlock(&card->lock); - return -ETIMEDOUT; - } - return 0; -} - -/* Reads data from SD card memory card */ int sdmmc_read_blocks(struct sd_card *card, uint8_t *rbuf, uint32_t start_block, uint32_t num_blocks) { - int ret; - uint32_t rlen; - uint32_t sector; - uint8_t *buf_offset; - - if ((start_block + num_blocks) > card->block_count) { - return -EINVAL; - } - if (card->type == CARD_SDIO) { - LOG_WRN("SDIO does not support MMC commands"); - return -ENOTSUP; - } - ret = k_mutex_lock(&card->lock, K_NO_WAIT); - if (ret) { - LOG_WRN("Could not get SD card mutex"); - return -EBUSY; - } - - /* - * If the buffer we are provided with is aligned, we can use it - * directly. Otherwise, we need to use the card's internal buffer - * and memcpy the data back out - */ - if ((((uintptr_t)rbuf) & (CONFIG_SDHC_BUFFER_ALIGNMENT - 1)) != 0) { - /* lower bits of address are set, not aligned. Use internal buffer */ - LOG_DBG("Unaligned buffer access to SD card may incur performance penalty"); - if (sizeof(card->card_buffer) < card->block_size) { - LOG_ERR("Card buffer size needs to be increased for " - "unaligned writes to work"); - k_mutex_unlock(&card->lock); - return -ENOBUFS; - } - rlen = sizeof(card->card_buffer) / card->block_size; - sector = 0; - buf_offset = rbuf; - while (sector < num_blocks) { - /* Read from disk to card buffer */ - ret = sdmmc_read(card, card->card_buffer, - sector + start_block, rlen); - if (ret) { - LOG_ERR("Write failed"); - k_mutex_unlock(&card->lock); - return ret; - } - /* Copy data from card buffer */ - memcpy(buf_offset, card->card_buffer, rlen * card->block_size); - /* Increase sector count and buffer offset */ - sector += rlen; - buf_offset += rlen * card->block_size; - } - } else { - /* Aligned buffers can be used directly */ - ret = sdmmc_read(card, rbuf, start_block, num_blocks); - if (ret) { - LOG_ERR("Card read failed"); - k_mutex_unlock(&card->lock); - return ret; - } - } - k_mutex_unlock(&card->lock); - return 0; + return card_read_blocks(card, rbuf, start_block, num_blocks); } -/* - * Sends ACMD22 (number of written blocks) to see how many blocks were written - * to a card - */ -static int sdmmc_query_written(struct sd_card *card, uint32_t *num_written) -{ - int ret; - struct sdhc_command cmd = {0}; - struct sdhc_data data = {0}; - uint32_t *blocks = (uint32_t *)card->card_buffer; - - ret = sdmmc_app_command(card, card->relative_addr); - if (ret) { - LOG_DBG("App CMD for ACMD22 failed"); - return ret; - } - - cmd.opcode = SD_APP_SEND_NUM_WRITTEN_BLK; - cmd.arg = 0; - cmd.response_type = (SD_RSP_TYPE_R1 | SD_SPI_RSP_TYPE_R1); - cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT; - - data.block_size = 4U; - data.blocks = 1U; - data.data = blocks; - data.timeout_ms = CONFIG_SD_DATA_TIMEOUT; - - ret = sdhc_request(card->sdhc, &cmd, &data); - if (ret) { - LOG_DBG("ACMD22 failed: %d", ret); - return ret; - } - ret = sd_check_response(&cmd); - if (ret) { - LOG_DBG("ACMD22 reports error"); - return ret; - } - - /* Decode blocks */ - *num_written = sys_be32_to_cpu(blocks[0]); - return 0; -} - -static int sdmmc_write(struct sd_card *card, const uint8_t *wbuf, - uint32_t start_block, uint32_t num_blocks) -{ - int ret; - uint32_t blocks; - struct sdhc_command cmd = {0}; - struct sdhc_data data = {0}; - - /* - * See the note in sdmmc_read() above. We will not issue CMD23 - * or CMD12, and expect the host to handle those details. - */ - cmd.opcode = (num_blocks == 1) ? SD_WRITE_SINGLE_BLOCK : SD_WRITE_MULTIPLE_BLOCK; - if (!(card->flags & SD_HIGH_CAPACITY_FLAG)) { - /* SDSC cards require block size in bytes, not blocks */ - cmd.arg = start_block * card->block_size; - } else { - cmd.arg = start_block; - } - cmd.response_type = (SD_RSP_TYPE_R1 | SD_SPI_RSP_TYPE_R1); - cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT; - cmd.retries = CONFIG_SD_DATA_RETRIES; - - data.block_addr = start_block; - data.block_size = card->block_size; - data.blocks = num_blocks; - data.data = (uint8_t *)wbuf; - data.timeout_ms = CONFIG_SD_DATA_TIMEOUT; - - LOG_DBG("WRITE: Sector = %u, Count = %u", start_block, num_blocks); - - ret = sdhc_request(card->sdhc, &cmd, &data); - if (ret) { - LOG_DBG("Write failed: %d", ret); - ret = sdmmc_wait_ready(card); - if (ret) { - return ret; - } - /* Query card to see how many blocks were actually written */ - ret = sdmmc_query_written(card, &blocks); - if (ret) { - return ret; - } - LOG_ERR("Only %d blocks of %d were written", blocks, num_blocks); - return -EIO; - } - /* Verify card is back in transfer state after write */ - ret = sdmmc_wait_ready(card); - if (ret) { - LOG_ERR("Card did not return to ready state"); - return -ETIMEDOUT; - } - return 0; -} - -/* Writes data to SD card memory card */ int sdmmc_write_blocks(struct sd_card *card, const uint8_t *wbuf, uint32_t start_block, uint32_t num_blocks) { - int ret; - uint32_t wlen; - uint32_t sector; - const uint8_t *buf_offset; - - if ((start_block + num_blocks) > card->block_count) { - return -EINVAL; - } - if (card->type == CARD_SDIO) { - LOG_WRN("SDIO does not support MMC commands"); - return -ENOTSUP; - } - ret = k_mutex_lock(&card->lock, K_NO_WAIT); - if (ret) { - LOG_WRN("Could not get SD card mutex"); - return -EBUSY; - } - /* - * If the buffer we are provided with is aligned, we can use it - * directly. Otherwise, we need to use the card's internal buffer - * and memcpy the data back out - */ - if ((((uintptr_t)wbuf) & (CONFIG_SDHC_BUFFER_ALIGNMENT - 1)) != 0) { - /* lower bits of address are set, not aligned. Use internal buffer */ - LOG_DBG("Unaligned buffer access to SD card may incur performance penalty"); - if (sizeof(card->card_buffer) < card->block_size) { - LOG_ERR("Card buffer size needs to be increased for " - "unaligned writes to work"); - k_mutex_unlock(&card->lock); - return -ENOBUFS; - } - wlen = sizeof(card->card_buffer) / card->block_size; - sector = 0; - buf_offset = wbuf; - while (sector < num_blocks) { - /* Copy data into card buffer */ - memcpy(card->card_buffer, buf_offset, wlen * card->block_size); - /* Write card buffer to disk */ - ret = sdmmc_write(card, card->card_buffer, - sector + start_block, wlen); - if (ret) { - LOG_ERR("Write failed"); - k_mutex_unlock(&card->lock); - return ret; - } - /* Increase sector count and buffer offset */ - sector += wlen; - buf_offset += wlen * card->block_size; - } - } else { - /* We can use aligned buffers directly */ - ret = sdmmc_write(card, wbuf, start_block, num_blocks); - if (ret) { - LOG_ERR("Write failed"); - k_mutex_unlock(&card->lock); - return ret; - } - } - k_mutex_unlock(&card->lock); - return 0; -} - - -/* IO Control handler for SD MMC */ -int sdmmc_ioctl(struct sd_card *card, uint8_t cmd, void *buf) -{ - switch (cmd) { - case DISK_IOCTL_GET_SECTOR_COUNT: - (*(uint32_t *)buf) = card->block_count; - break; - case DISK_IOCTL_GET_SECTOR_SIZE: - case DISK_IOCTL_GET_ERASE_BLOCK_SZ: - (*(uint32_t *)buf) = card->block_size; - break; - case DISK_IOCTL_CTRL_SYNC: - /* Ensure card is not busy with data write. - * Note that SD stack does not support enabling caching, so - * cache flush is not required here - */ - return sdmmc_wait_ready(card); - default: - return -ENOTSUP; - } - return 0; + return card_write_blocks(card, wbuf, start_block, num_blocks); }