|  | /* | 
|  | * Copyright 2022,2024 NXP | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | #include <zephyr/drivers/disk.h> | 
|  | #include <zephyr/drivers/sdhc.h> | 
|  | #include <zephyr/logging/log.h> | 
|  | #include <zephyr/sd/sd.h> | 
|  | #include <zephyr/sd/sd_spec.h> | 
|  | #include <zephyr/sys/byteorder.h> | 
|  | #include <zephyr/kernel.h> | 
|  |  | 
|  | #include "sd_utils.h" | 
|  |  | 
|  | LOG_MODULE_DECLARE(sd, CONFIG_SD_LOG_LEVEL); | 
|  |  | 
|  | /* Read card status. Return 0 if card is inactive */ | 
|  | int sdmmc_read_status(struct sd_card *card) | 
|  | { | 
|  | struct sdhc_command cmd; | 
|  | int ret; | 
|  |  | 
|  | cmd.opcode = SD_SEND_STATUS; | 
|  | cmd.arg = 0; | 
|  | 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.retries = CONFIG_SD_CMD_RETRIES; | 
|  | 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; | 
|  |  | 
|  | do { | 
|  | if (!sdhc_card_busy(card->sdhc)) { | 
|  | /* Check card status */ | 
|  | ret = sd_retry(sdmmc_read_status, card, CONFIG_SD_RETRY_COUNT); | 
|  | if (ret == 0) { | 
|  | return 0; | 
|  | } | 
|  | if (ret == -ETIMEDOUT) { | 
|  | /* If this check timed out, then the total | 
|  | * time elapsed in microseconds is | 
|  | * SD_CMD_TIMEOUT * SD_RETRY_COUNT * 1000 | 
|  | */ | 
|  | timeout -= (CONFIG_SD_CMD_TIMEOUT * | 
|  | CONFIG_SD_RETRY_COUNT) * 1000; | 
|  | } | 
|  | } | 
|  | /* Delay 125us before polling again */ | 
|  | k_busy_wait(125); | 
|  | timeout -= 125; | 
|  | } while (timeout > 0); | 
|  | return -EBUSY; | 
|  | } | 
|  |  | 
|  | static inline void sdmmc_decode_csd(struct sd_csd *csd, uint32_t *raw_csd, uint32_t *blk_count, | 
|  | uint16_t *blk_size) | 
|  | { | 
|  | uint32_t tmp_blk_count; | 
|  | uint16_t 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); | 
|  | if (raw_csd[2U] & 0x8000U) { | 
|  | csd->flags |= SD_CSD_READ_BLK_PARTIAL_FLAG; | 
|  | } | 
|  | if (raw_csd[2U] & 0x4000U) { | 
|  | csd->flags |= SD_CSD_READ_BLK_PARTIAL_FLAG; | 
|  | } | 
|  | if (raw_csd[2U] & 0x2000U) { | 
|  | csd->flags |= SD_CSD_READ_BLK_MISALIGN_FLAG; | 
|  | } | 
|  | if (raw_csd[2U] & 0x1000U) { | 
|  | csd->flags |= SD_CSD_DSR_IMPLEMENTED_FLAG; | 
|  | } | 
|  |  | 
|  | 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); | 
|  |  | 
|  | /* Get card total block count and block size. */ | 
|  | 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); | 
|  | tmp_blk_size = SDMMC_DEFAULT_BLOCK_SIZE; | 
|  | tmp_blk_count = (tmp_blk_count / tmp_blk_size); | 
|  | } | 
|  | if (blk_count) { | 
|  | *blk_count = tmp_blk_count; | 
|  | } | 
|  | if (blk_size) { | 
|  | *blk_size = tmp_blk_size; | 
|  | } | 
|  | break; | 
|  | 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); | 
|  |  | 
|  | tmp_blk_count = ((csd->device_size + 1U) * 1024U); | 
|  | if (blk_count) { | 
|  | *blk_count = tmp_blk_count; | 
|  | } | 
|  | if (blk_size) { | 
|  | *blk_size = tmp_blk_size; | 
|  | } | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  |  | 
|  | 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); | 
|  | if ((uint8_t)((raw_csd[0U] & 0x200000U) >> 21U)) { | 
|  | csd->flags |= SD_CSD_WRITE_BLK_PARTIAL_FLAG; | 
|  | } | 
|  | if ((uint8_t)((raw_csd[0U] & 0x8000U) >> 15U)) { | 
|  | csd->flags |= SD_CSD_FILE_FMT_GRP_FLAG; | 
|  | } | 
|  | if ((uint8_t)((raw_csd[0U] & 0x4000U) >> 14U)) { | 
|  | csd->flags |= SD_CSD_COPY_FLAG; | 
|  | } | 
|  | if ((uint8_t)((raw_csd[0U] & 0x2000U) >> 13U)) { | 
|  | csd->flags |= SD_CSD_PERMANENT_WRITE_PROTECT_FLAG; | 
|  | } | 
|  | if ((uint8_t)((raw_csd[0U] & 0x1000U) >> 12U)) { | 
|  | csd->flags |= SD_CSD_TMP_WRITE_PROTECT_FLAG; | 
|  | } | 
|  | csd->file_fmt = (uint8_t)((raw_csd[0U] & 0xC00U) >> 10U); | 
|  | } | 
|  |  | 
|  | 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); | 
|  |  | 
|  | cid->name[0U] = (uint8_t)((raw_cid[3U] & 0xFFU)); | 
|  | cid->name[1U] = (uint8_t)((raw_cid[2U] & 0xFF000000U) >> 24U); | 
|  | cid->name[2U] = (uint8_t)((raw_cid[2U] & 0xFF0000U) >> 16U); | 
|  | cid->name[3U] = (uint8_t)((raw_cid[2U] & 0xFF00U) >> 8U); | 
|  | cid->name[4U] = (uint8_t)((raw_cid[2U] & 0xFFU)); | 
|  |  | 
|  | cid->version = (uint8_t)((raw_cid[1U] & 0xFF000000U) >> 24U); | 
|  |  | 
|  | cid->ser_num = (uint32_t)((raw_cid[1U] & 0xFFFFFFU) << 8U); | 
|  | cid->ser_num |= (uint32_t)((raw_cid[0U] & 0xFF000000U) >> 24U); | 
|  |  | 
|  | cid->date = (uint16_t)((raw_cid[0U] & 0xFFF00U) >> 8U); | 
|  | } | 
|  |  | 
|  | /* Reads card id/csd register (in SPI mode) */ | 
|  | static int sdmmc_spi_read_cxd(struct sd_card *card, uint32_t opcode, uint32_t *cxd) | 
|  | { | 
|  | struct sdhc_command cmd; | 
|  | struct sdhc_data data; | 
|  | int ret, i; | 
|  | /* Use internal card buffer for data transfer */ | 
|  | uint32_t *cxd_be = (uint32_t *)card->card_buffer; | 
|  |  | 
|  | cmd.opcode = opcode; | 
|  | cmd.arg = 0; | 
|  | cmd.response_type = SD_SPI_RSP_TYPE_R1; | 
|  | cmd.retries = CONFIG_SD_CMD_RETRIES; | 
|  | cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT; | 
|  |  | 
|  | /* CID/CSD is 16 bytes */ | 
|  | data.block_addr = 0;        /* Unused set to 0 */ | 
|  | data.block_size = 16; | 
|  | data.blocks = 1U; | 
|  | data.data = cxd_be; | 
|  | data.timeout_ms = CONFIG_SD_CMD_TIMEOUT; | 
|  |  | 
|  | ret = sdhc_request(card->sdhc, &cmd, &data); | 
|  | if (ret) { | 
|  | LOG_DBG("CMD%d failed: %d", opcode, ret); | 
|  | } | 
|  | /* Swap endianness of CXD */ | 
|  | for (i = 0; i < 4; 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) | 
|  | { | 
|  | struct sdhc_command cmd; | 
|  | int ret; | 
|  |  | 
|  | cmd.opcode = opcode; | 
|  | cmd.arg = (rca << 16); | 
|  | cmd.response_type = SD_RSP_TYPE_R2; | 
|  | cmd.retries = CONFIG_SD_CMD_RETRIES; | 
|  | cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT; | 
|  |  | 
|  | ret = sdhc_request(card->sdhc, &cmd, NULL); | 
|  | if (ret) { | 
|  | 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) | 
|  | { | 
|  | int ret; | 
|  | uint32_t csd[4]; | 
|  | /* Keep CSD on stack for reduced RAM usage */ | 
|  | struct sd_csd card_csd = {0}; | 
|  |  | 
|  | if (card->host_props.is_spi && IS_ENABLED(CONFIG_SDHC_SUPPORTS_SPI_MODE)) { | 
|  | ret = sdmmc_spi_read_cxd(card, SD_SEND_CSD, csd); | 
|  | } else if (IS_ENABLED(CONFIG_SDHC_SUPPORTS_NATIVE_MODE)) { | 
|  | ret = sdmmc_read_cxd(card, SD_SEND_CSD, card->relative_addr, csd); | 
|  | } else { | 
|  | /* The host controller must run in either native or SPI mode */ | 
|  | return -ENOTSUP; | 
|  | } | 
|  | 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); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Reads card identification register, and decodes it */ | 
|  | 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 = {0}; | 
|  | #endif | 
|  |  | 
|  | if (card->host_props.is_spi && IS_ENABLED(CONFIG_SDHC_SUPPORTS_SPI_MODE)) { | 
|  | ret = sdmmc_spi_read_cxd(card, SD_SEND_CID, cid); | 
|  | } else if (IS_ENABLED(CONFIG_SDHC_SUPPORTS_NATIVE_MODE)) { | 
|  | ret = sdmmc_read_cxd(card, SD_ALL_SEND_CID, 0, cid); | 
|  | } else { | 
|  | /* The host controller must run in either native or SPI mode */ | 
|  | return -ENOTSUP; | 
|  | } | 
|  | if (ret) { | 
|  | 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]); | 
|  | #endif | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Implements signal voltage switch procedure described in section 3.6.1 of | 
|  | * SD host controller specification. | 
|  | */ | 
|  | int sdmmc_switch_voltage(struct sd_card *card) | 
|  | { | 
|  | int ret, sd_clock; | 
|  | struct sdhc_command cmd; | 
|  |  | 
|  | /* Check to make sure card supports 1.8V */ | 
|  | if (!(card->flags & SD_1800MV_FLAG)) { | 
|  | /* Do not attempt to switch voltages */ | 
|  | LOG_WRN("SD card reports as SDHC/SDXC, but does not support 1.8V"); | 
|  | return 0; | 
|  | } | 
|  | /* Send CMD11 to request a voltage switch */ | 
|  | cmd.opcode = SD_VOL_SWITCH; | 
|  | cmd.arg = 0U; | 
|  | cmd.response_type = SD_RSP_TYPE_R1; | 
|  | cmd.retries = CONFIG_SD_CMD_RETRIES; | 
|  | cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT; | 
|  | ret = sdhc_request(card->sdhc, &cmd, NULL); | 
|  | if (ret) { | 
|  | LOG_DBG("CMD11 failed"); | 
|  | return ret; | 
|  | } | 
|  | /* Check R1 response for error */ | 
|  | ret = sd_check_response(&cmd); | 
|  | if (ret) { | 
|  | LOG_DBG("SD response to CMD11 indicates error"); | 
|  | return ret; | 
|  | } | 
|  | /* | 
|  | * Card should drive CMD and DAT[3:0] signals low at the next clock | 
|  | * cycle. Some cards will only drive these | 
|  | * lines low briefly, so we should check as soon as possible | 
|  | */ | 
|  | if (!(sdhc_card_busy(card->sdhc))) { | 
|  | /* Delay 1ms to allow card to drive lines low */ | 
|  | sd_delay(1); | 
|  | if (!sdhc_card_busy(card->sdhc)) { | 
|  | /* Card did not drive CMD and DAT lines low */ | 
|  | LOG_DBG("Card did not drive DAT lines low"); | 
|  | return -EAGAIN; | 
|  | } | 
|  | } | 
|  | /* | 
|  | * Per SD spec (section "Timing to Switch Signal Voltage"), | 
|  | * host must gate clock at least 5ms. | 
|  | */ | 
|  | sd_clock = card->bus_io.clock; | 
|  | card->bus_io.clock = 0; | 
|  | ret = sdhc_set_io(card->sdhc, &card->bus_io); | 
|  | if (ret) { | 
|  | LOG_DBG("Failed to gate SD clock"); | 
|  | return ret; | 
|  | } | 
|  | /* Now that clock is gated, change signal voltage */ | 
|  | card->bus_io.signal_voltage = SD_VOL_1_8_V; | 
|  | ret = sdhc_set_io(card->sdhc, &card->bus_io); | 
|  | if (ret) { | 
|  | LOG_DBG("Failed to switch SD host to 1.8V"); | 
|  | return ret; | 
|  | } | 
|  | sd_delay(10); /* Gate for 10ms, even though spec requires 5 */ | 
|  | /* Restart the clock */ | 
|  | card->bus_io.clock = sd_clock; | 
|  | ret = sdhc_set_io(card->sdhc, &card->bus_io); | 
|  | if (ret) { | 
|  | LOG_ERR("Failed to restart SD clock"); | 
|  | return ret; | 
|  | } | 
|  | /* | 
|  | * If SD does not drive at least one of | 
|  | * DAT[3:0] high within 1ms, switch failed | 
|  | */ | 
|  | sd_delay(1); | 
|  | if (sdhc_card_busy(card->sdhc)) { | 
|  | LOG_DBG("Card failed to switch voltages"); | 
|  | return -EAGAIN; | 
|  | } | 
|  | card->card_voltage = SD_VOL_1_8_V; | 
|  | LOG_INF("Card switched to 1.8V signaling"); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Requests card to publish a new relative card address, and move from | 
|  | * identification to data mode | 
|  | */ | 
|  | int sdmmc_request_rca(struct sd_card *card) | 
|  | { | 
|  | struct sdhc_command cmd; | 
|  | int ret; | 
|  |  | 
|  | cmd.opcode = SD_SEND_RELATIVE_ADDR; | 
|  | cmd.arg = 0; | 
|  | cmd.response_type = SD_RSP_TYPE_R6; | 
|  | cmd.retries = CONFIG_SD_CMD_RETRIES; | 
|  | cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT; | 
|  | /* Issue CMD3 until card responds with nonzero RCA */ | 
|  | do { | 
|  | ret = sdhc_request(card->sdhc, &cmd, NULL); | 
|  | if (ret) { | 
|  | LOG_DBG("CMD3 failed"); | 
|  | return ret; | 
|  | } | 
|  | /* Card RCA is in upper 16 bits of response */ | 
|  | card->relative_addr = ((cmd.response[0U] & 0xFFFF0000) >> 16U); | 
|  | } while (card->relative_addr == 0U); | 
|  | LOG_DBG("Card relative addr: %d", card->relative_addr); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Selects card, moving it into data transfer mode | 
|  | */ | 
|  | int sdmmc_select_card(struct sd_card *card) | 
|  | { | 
|  | struct sdhc_command cmd; | 
|  | int ret; | 
|  |  | 
|  | cmd.opcode = SD_SELECT_CARD; | 
|  | cmd.arg = ((card->relative_addr) << 16U); | 
|  | cmd.response_type = SD_RSP_TYPE_R1; | 
|  | cmd.retries = CONFIG_SD_CMD_RETRIES; | 
|  | cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT; | 
|  |  | 
|  | ret = sdhc_request(card->sdhc, &cmd, NULL); | 
|  | if (ret) { | 
|  | LOG_DBG("CMD7 failed"); | 
|  | return ret; | 
|  | } | 
|  | ret = sd_check_response(&cmd); | 
|  | if (ret) { | 
|  | LOG_DBG("CMD7 reports error"); | 
|  | return ret; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Helper to send SD app command */ | 
|  | int card_app_command(struct sd_card *card, int relative_card_address) | 
|  | { | 
|  | 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.retries = CONFIG_SD_CMD_RETRIES; | 
|  | 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; | 
|  | struct sdhc_data data; | 
|  |  | 
|  | /* | 
|  | * 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.retries = CONFIG_SD_DATA_RETRIES; | 
|  | cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT; | 
|  |  | 
|  | 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"); | 
|  | 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_MSEC(CONFIG_SD_DATA_TIMEOUT)); | 
|  | 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; | 
|  | struct sdhc_data data; | 
|  | 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.retries = CONFIG_SD_CMD_RETRIES; | 
|  | cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT; | 
|  |  | 
|  | data.block_addr = 0;        /* Unused set to 0 */ | 
|  | 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; | 
|  | struct sdhc_data data; | 
|  |  | 
|  | /* | 
|  | * 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.retries = CONFIG_SD_DATA_RETRIES; | 
|  | cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT; | 
|  |  | 
|  | 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_MSEC(CONFIG_SD_DATA_TIMEOUT)); | 
|  | 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) | 
|  | { | 
|  | int ret; | 
|  |  | 
|  | ret = k_mutex_lock(&card->lock, K_MSEC(CONFIG_SD_DATA_TIMEOUT)); | 
|  | if (ret) { | 
|  | LOG_WRN("Could not get SD card mutex"); | 
|  | return ret; | 
|  | } | 
|  | 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 | 
|  | */ | 
|  | ret = sdmmc_wait_ready(card); | 
|  | break; | 
|  | case DISK_IOCTL_CTRL_DEINIT: | 
|  | /* Ensure card is not busy with data write */ | 
|  | ret = sdmmc_wait_ready(card); | 
|  | if (ret < 0) { | 
|  | LOG_WRN("Card busy when powering off"); | 
|  | } | 
|  | /* Power down the card */ | 
|  | card->bus_io.power_mode = SDHC_POWER_OFF; | 
|  | ret = sdhc_set_io(card->sdhc, &card->bus_io); | 
|  | break; | 
|  | default: | 
|  | ret = -ENOTSUP; | 
|  | } | 
|  | k_mutex_unlock(&card->lock); | 
|  | return ret; | 
|  | } |