/* * Copyright 2022 NXP * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include "sd_ops.h" #include "sd_utils.h" /* These are used as specific arguments to commands */ /* For switch commands, the argument structure is: * [31:26] : Set to 0 * [25:24] : Access * [23:16] : Index (byte address in EXT_CSD) * [15:8] : Value (data) * [7:3] : Set to 0 * [2:0] : Cmd Set */ #define MMC_SWITCH_8_BIT_DDR_BUS_ARG \ (0xFC000000 & (0U << 26)) + (0x03000000 & (0b11 << 24)) + (0x00FF0000 & (183U << 16)) + \ (0x0000FF00 & (6U << 8)) + (0x000000F7 & (0U << 3)) + (0x00000000 & (3U << 0)) #define MMC_SWITCH_8_BIT_BUS_ARG \ (0xFC000000 & (0U << 26)) + (0x03000000 & (0b11 << 24)) + (0x00FF0000 & (183U << 16)) + \ (0x0000FF00 & (2U << 8)) + (0x000000F7 & (0U << 3)) + (0x00000000 & (3U << 0)) #define MMC_SWITCH_4_BIT_BUS_ARG \ (0xFC000000 & (0U << 26)) + (0x03000000 & (0b11 << 24)) + (0x00FF0000 & (183U << 16)) + \ (0x0000FF00 & (1U << 8)) + (0x000000F7 & (0U << 3)) + (0x00000000 & (3U << 0)) #define MMC_SWITCH_HS_TIMING_ARG \ (0xFC000000 & (0U << 26)) + (0x03000000 & (0b11 << 24)) + (0x00FF0000 & (185U << 16)) + \ (0x0000FF00 & (1U << 8)) + (0x000000F7 & (0U << 3)) + (0x00000000 & (3U << 0)) #define MMC_SWITCH_HS400_TIMING_ARG \ (0xFC000000 & (0U << 26)) + (0x03000000 & (0b11 << 24)) + (0x00FF0000 & (185U << 16)) + \ (0x0000FF00 & (3U << 8)) + (0x000000F7 & (0U << 3)) + (0x00000000 & (3U << 0)) #define MMC_SWITCH_HS200_TIMING_ARG \ (0xFC000000 & (0U << 26)) + (0x03000000 & (0b11 << 24)) + (0x00FF0000 & (185U << 16)) + \ (0x0000FF00 & (2U << 8)) + (0x000000F7 & (0U << 3)) + (0x00000000 & (3U << 0)) #define MMC_RCA_ARG (CONFIG_MMC_RCA << 16U) #define MMC_REL_ADR_ARG (card->relative_addr << 16U) #define MMC_SWITCH_PWR_CLASS_ARG \ (0xFC000000 & (0U << 26)) + (0x03000000 & (0b11 << 24)) + (0x00FF0000 & (187U << 16)) + \ (0x0000FF00 & (0U << 8)) + (0x000000F7 & (0U << 3)) + (0x00000000 & (3U << 0)) #define MMC_SWITCH_CACHE_ON_ARG \ (0xFC000000 & (0U << 26)) + (0x03000000 & (0b11 << 24)) + (0x00FF0000 & (33U << 16)) + \ (0x0000FF00 & (1U << 8)) + (0x000000F7 & (0U << 3)) + (0x00000000 & (3U << 0)) LOG_MODULE_DECLARE(sd, CONFIG_SD_LOG_LEVEL); 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); } /* Sends CMD1 */ static int mmc_send_op_cond(struct sd_card *card, int ocr); /* Sends CMD3 */ static int mmc_set_rca(struct sd_card *card); /* Sends CMD9 */ static int mmc_read_csd(struct sd_card *card, struct sd_csd *card_csd); static inline void mmc_decode_csd(struct sd_csd *csd, uint32_t *raw_csd); /* Sends CMD8 */ static int mmc_read_ext_csd(struct sd_card *card, struct mmc_ext_csd *card_ext_csd); static inline void mmc_decode_ext_csd(struct mmc_ext_csd *ext_csd, uint8_t *raw); /* Sets SDHC max frequency in legacy timing */ static inline int mmc_set_max_freq(struct sd_card *card, struct sd_csd *card_csd); /* Sends CMD6 to switch bus width*/ static int mmc_set_bus_width(struct sd_card *card); /* Sets card to the fastest timing mode (using CMD6) and SDHC to max frequency */ static int mmc_set_timing(struct sd_card *card, struct mmc_ext_csd *card_ext_csd); /* Enable cache for emmc if applicable */ static int mmc_set_cache(struct sd_card *card, struct mmc_ext_csd *card_ext_csd); /* * Initialize MMC card for use with subsystem */ int mmc_card_init(struct sd_card *card) { int ret = 0; uint32_t ocr_arg = 0U; /* Keep CSDs on stack for reduced RAM usage */ struct sd_csd card_csd = {0}; struct mmc_ext_csd card_ext_csd = {0}; /* SPI is not supported for MMC */ if (card->host_props.is_spi) { return -EINVAL; } /* Probe to see if card is an MMC card */ ret = mmc_send_op_cond(card, ocr_arg); if (ret) { return ret; } /* Card is MMC card if no error * (Only MMC protocol supports CMD1) */ card->type = CARD_MMC; /* Set OCR Arguments */ if (card->host_props.host_caps.vol_180_support) { ocr_arg |= MMC_OCR_VDD170_195FLAG; } if (card->host_props.host_caps.vol_330_support || card->host_props.host_caps.vol_300_support) { ocr_arg |= MMC_OCR_VDD27_36FLAG; } /* Modern SDHC always at least supports 512 byte block sizes, * which is enough to support sectors */ ocr_arg |= MMC_OCR_SECTOR_MODE; /* CMD1 */ ret = mmc_send_op_cond(card, ocr_arg); if (ret) { LOG_ERR("Failed to query card OCR"); return ret; } /* CMD2 */ ret = card_read_cid(card); if (ret) { return ret; } /* CMD3 */ ret = mmc_set_rca(card); if (ret) { LOG_ERR("Failed on sending RCA to card"); return ret; } /* CMD9 */ ret = mmc_read_csd(card, &card_csd); if (ret) { return ret; } /* Set max bus clock in legacy timing to speed up initialization * Currently only eMMC is supported for this command * Legacy MMC cards will initialize slowly */ ret = mmc_set_max_freq(card, &card_csd); if (ret) { return ret; } /* CMD7 */ ret = sdmmc_select_card(card); if (ret) { return ret; } /* CMD6: Set bus width to max supported*/ ret = mmc_set_bus_width(card); if (ret) { return ret; } /* CMD8 */ ret = mmc_read_ext_csd(card, &card_ext_csd); if (ret) { return ret; } /* Set timing to fastest supported */ ret = mmc_set_timing(card, &card_ext_csd); if (ret) { return ret; } /* Turn on cache if it exists */ ret = mmc_set_cache(card, &card_ext_csd); if (ret) { return ret; } return 0; } static int mmc_send_op_cond(struct sd_card *card, int ocr) { struct sdhc_command cmd = {0}; int ret = 0; int retries; cmd.opcode = MMC_SEND_OP_COND; cmd.arg = ocr; cmd.response_type = SD_RSP_TYPE_R3; cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT; for (retries = 0; retries < CONFIG_SD_OCR_RETRY_COUNT && !(cmd.response[0] & SD_OCR_PWR_BUSY_FLAG); retries++) { ret = sdhc_request(card->sdhc, &cmd, NULL); if (ret) { /* OCR failed */ return ret; } if (ocr == 0) { /* Just probing */ return 0; } sd_delay(10); } if (retries >= CONFIG_SD_OCR_RETRY_COUNT) { /* OCR timed out */ LOG_ERR("Card never left busy state"); return -ETIMEDOUT; } LOG_DBG("MMC responded to CMD1 after %d attempts", retries); if (cmd.response[0] & MMC_OCR_VDD170_195FLAG) { card->flags |= SD_1800MV_FLAG; } if (cmd.response[0] & MMC_OCR_VDD27_36FLAG) { card->flags |= SD_3000MV_FLAG; } /* Switch to 1.8V */ if (card->host_props.host_caps.vol_180_support && (card->flags & SD_1800MV_FLAG)) { 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 MMC host to 1.8V"); return ret; } sd_delay(10); card->card_voltage = SD_VOL_1_8_V; LOG_INF("Card switched to 1.8V signaling"); } /* SD high Capacity is >2GB, the same as sector supporting MMC cards. */ if (cmd.response[0] & MMC_OCR_SECTOR_MODE) { card->flags |= SD_HIGH_CAPACITY_FLAG; } return 0; } static int mmc_set_rca(struct sd_card *card) { struct sdhc_command cmd = {0}; int ret; cmd.opcode = MMC_SEND_RELATIVE_ADDR; cmd.arg = MMC_RCA_ARG; cmd.response_type = SD_RSP_TYPE_R1; cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT; ret = sdhc_request(card->sdhc, &cmd, NULL); if (ret) { return ret; } ret = sd_check_response(&cmd); if (ret) { return ret; } card->relative_addr = CONFIG_MMC_RCA; return 0; } static int mmc_read_csd(struct sd_card *card, struct sd_csd *card_csd) { int ret; struct sdhc_command cmd = {0}; cmd.opcode = SD_SEND_CSD; cmd.arg = MMC_REL_ADR_ARG; cmd.response_type = SD_RSP_TYPE_R2; cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT; ret = sdhc_request(card->sdhc, &cmd, NULL); if (ret) { LOG_DBG("CMD9 failed: %d", ret); return ret; } mmc_decode_csd(card_csd, cmd.response); if (card_csd->csd_structure < 2) { LOG_ERR("Legacy MMC cards are not supported."); return -ENOTSUP; } return 0; } static inline void mmc_decode_csd(struct sd_csd *csd, uint32_t *raw_csd) { 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; } csd->device_size = (uint16_t)(((raw_csd[2U] & 0x3FFU) << 2U) + ((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] & 0x07000000U) >> 24U); csd->write_current_min = (uint8_t)((raw_csd[1U] & 0x00E00000U) >> 21U); csd->write_current_max = (uint8_t)((raw_csd[1U] & 0x001C0000U) >> 18U); csd->dev_size_mul = (uint8_t)((raw_csd[1U] & 0x00038000U) >> 15U); csd->erase_size = (uint8_t)((raw_csd[1U] & 0x00007C00U) >> 10U); csd->write_prtect_size = (uint8_t)(raw_csd[1U] & 0x0000001FU); csd->write_speed_factor = (uint8_t)((raw_csd[0U] & 0x1C000000U) >> 26U); csd->write_blk_len = (uint8_t)((raw_csd[0U] & 0x03C00000U) >> 22U); csd->file_fmt = (uint8_t)((raw_csd[0U] & 0x00000C00U) >> 10U); } static inline int mmc_set_max_freq(struct sd_card *card, struct sd_csd *card_csd) { int ret; enum mmc_csd_freq frequency_code = (card_csd->xfer_rate & 0x7); enum mmc_csd_freq multiplier_code = (card_csd->xfer_rate & 0x78); /* 4.3 - 5.1 emmc spec says 26 MHz */ if (frequency_code == MMC_MAXFREQ_10MHZ && multiplier_code == MMC_MAXFREQ_MULT_26) { card->bus_io.clock = 26000000U; card->bus_io.timing = SDHC_TIMING_LEGACY; } /* 4.0 - 4.2 emmc spec says 20 MHz */ else if (frequency_code == MMC_MAXFREQ_10MHZ && multiplier_code == MMC_MAXFREQ_MULT_20) { card->bus_io.clock = 20000000U; card->bus_io.timing = SDHC_TIMING_LEGACY; } else { LOG_INF("Using Legacy MMC will have slow initialization"); return 0; } ret = sdhc_set_io(card->sdhc, &card->bus_io); if (ret) { LOG_ERR("Error seetting initial clock frequency"); return ret; } return 0; } static int mmc_set_bus_width(struct sd_card *card) { int ret; struct sdhc_command cmd = {0}; if (card->host_props.host_caps.bus_8_bit_support && card->bus_width == 8) { cmd.arg = MMC_SWITCH_8_BIT_BUS_ARG; card->bus_io.bus_width = SDHC_BUS_WIDTH8BIT; } else if (card->host_props.host_caps.bus_4_bit_support && card->bus_width >= 4) { cmd.arg = MMC_SWITCH_4_BIT_BUS_ARG; card->bus_io.bus_width = SDHC_BUS_WIDTH4BIT; } else { /* If only 1 bit bus is supported, nothing to be done */ return 0; } /* Set Card Bus Width */ cmd.opcode = SD_SWITCH; cmd.response_type = SD_RSP_TYPE_R1b; cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT; ret = sdhc_request(card->sdhc, &cmd, NULL); sdmmc_wait_ready(card); if (ret) { LOG_ERR("Setting card data bus width failed: %d", ret); return ret; } /* Set host controller bus width */ ret = sdhc_set_io(card->sdhc, &card->bus_io); if (ret) { LOG_ERR("Setting SDHC data bus width failed: %d", ret); return ret; } return ret; } static int mmc_set_hs_timing(struct sd_card *card) { int ret = 0; struct sdhc_command cmd = {0}; /* Change Card Timing Mode */ cmd.opcode = SD_SWITCH; cmd.arg = MMC_SWITCH_HS_TIMING_ARG; cmd.response_type = SD_RSP_TYPE_R1b; cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT; ret = sdhc_request(card->sdhc, &cmd, NULL); if (ret) { LOG_DBG("Error setting bus timing: %d", ret); return ret; } sdmmc_wait_ready(card); /* Max frequency in HS mode is 52 MHz */ card->bus_io.clock = MMC_CLOCK_52MHZ; card->bus_io.timing = SDHC_TIMING_HS; /* Change SDHC bus timing */ ret = sdhc_set_io(card->sdhc, &card->bus_io); if (ret) { return ret; } return ret; } static int mmc_set_power_class_HS200(struct sd_card *card, struct mmc_ext_csd *ext) { int ret = 0; struct sdhc_command cmd = {0}; cmd.opcode = SD_SWITCH; cmd.arg = ((MMC_SWITCH_PWR_CLASS_ARG) | (ext->pwr_class_200MHZ_VCCQ195 << 8)); cmd.response_type = SD_RSP_TYPE_R1b; cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT; ret = sdhc_request(card->sdhc, &cmd, NULL); sdmmc_wait_ready(card); return ret; } static int mmc_set_timing(struct sd_card *card, struct mmc_ext_csd *ext) { int ret = 0; struct sdhc_command cmd = {0}; /* Timing depends on EXT_CSD register information */ if ((ext->device_type.MMC_HS200_SDR_1200MV || ext->device_type.MMC_HS200_SDR_1800MV) && (card->host_props.host_caps.hs200_support) && (card->bus_io.signal_voltage == SD_VOL_1_8_V) && (card->bus_io.bus_width >= SDHC_BUS_WIDTH4BIT)) { ret = mmc_set_hs_timing(card); if (ret) { return ret; } cmd.arg = MMC_SWITCH_HS200_TIMING_ARG; card->bus_io.clock = MMC_CLOCK_HS200; card->bus_io.timing = SDHC_TIMING_HS200; } else if (ext->device_type.MMC_HS_52_DV) { return mmc_set_hs_timing(card); } else if (ext->device_type.MMC_HS_26_DV) { /* Nothing to do, card is already configured for this */ return 0; } else { return -ENOTSUP; } /* Set card timing mode */ cmd.opcode = SD_SWITCH; cmd.response_type = SD_RSP_TYPE_R1b; cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT; ret = sdhc_request(card->sdhc, &cmd, NULL); if (ret) { LOG_DBG("Error setting bus timing: %d", ret); return ret; } ret = sdmmc_wait_ready(card); if (ret) { return ret; } card->card_speed = ((cmd.arg & 0xFF << 8) >> 8); /* Set power class to match timing mode */ if (card->card_speed == MMC_HS200_TIMING) { ret = mmc_set_power_class_HS200(card, ext); if (ret) { return ret; } } /* Set SDHC bus io parameters */ ret = sdhc_set_io(card->sdhc, &card->bus_io); if (ret) { return ret; } /* Execute Tuning for HS200 */ if (card->card_speed == MMC_HS200_TIMING) { ret = sdhc_execute_tuning(card->sdhc); if (ret) { LOG_ERR("MMC Tuning failed: %d", ret); return ret; } } /* Switch to HS400 if applicable */ if ((ext->device_type.MMC_HS400_DDR_1200MV || ext->device_type.MMC_HS400_DDR_1800MV) && (card->host_props.host_caps.hs400_support) && (card->bus_io.bus_width == SDHC_BUS_WIDTH8BIT)) { /* Switch back to regular HS timing */ ret = mmc_set_hs_timing(card); if (ret) { LOG_ERR("Switching MMC back to HS from HS200 during HS400 init failed."); return ret; } /* Set bus width to DDR 8 bit */ cmd.opcode = SD_SWITCH; cmd.arg = MMC_SWITCH_8_BIT_DDR_BUS_ARG; cmd.response_type = SD_RSP_TYPE_R1b; cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT; ret = sdhc_request(card->sdhc, &cmd, NULL); sdmmc_wait_ready(card); if (ret) { LOG_ERR("Setting DDR data bus width failed during HS400 init: %d", ret); return ret; } /* Set card timing mode to HS400 */ cmd.opcode = SD_SWITCH; cmd.arg = MMC_SWITCH_HS400_TIMING_ARG; cmd.response_type = SD_RSP_TYPE_R1b; cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT; ret = sdhc_request(card->sdhc, &cmd, NULL); if (ret) { LOG_DBG("Error setting card to HS400 bus timing: %d", ret); return ret; } ret = sdmmc_wait_ready(card); if (ret) { return ret; } /* Set SDHC bus io parameters */ card->bus_io.clock = MMC_CLOCK_HS400; card->bus_io.timing = SDHC_TIMING_HS400; ret = sdhc_set_io(card->sdhc, &card->bus_io); if (ret) { return ret; } card->card_speed = ((cmd.arg & 0xFF << 8) >> 8); } return ret; } static int mmc_read_ext_csd(struct sd_card *card, struct mmc_ext_csd *card_ext_csd) { int ret; struct sdhc_command cmd = {0}; struct sdhc_data data = {0}; cmd.opcode = MMC_SEND_EXT_CSD; cmd.arg = 0; cmd.response_type = SD_RSP_TYPE_R1; cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT; data.block_size = MMC_EXT_CSD_BYTES; data.blocks = 1; data.data = card->card_buffer; data.timeout_ms = CONFIG_SD_DATA_TIMEOUT; ret = sdhc_request(card->sdhc, &cmd, &data); if (ret) { LOG_ERR("CMD8 (send_ext_csd) failed: %d", ret); return ret; } mmc_decode_ext_csd(card_ext_csd, card->card_buffer); card->block_count = card_ext_csd->sec_count; card->block_size = SDMMC_DEFAULT_BLOCK_SIZE; LOG_INF("Card block count is %d, block size is %d", card->block_count, card->block_size); return 0; } static inline void mmc_decode_ext_csd(struct mmc_ext_csd *ext, uint8_t *raw) { ext->sec_count = (raw[215U] << 24U) + (raw[214U] << 16U) + (raw[213U] << 8U) + (raw[212U] << 0U); ext->bus_width = raw[183U]; ext->hs_timing = raw[185U]; ext->device_type.MMC_HS400_DDR_1200MV = ((1 << 7U) & raw[196U]); ext->device_type.MMC_HS400_DDR_1800MV = ((1 << 6U) & raw[196U]); ext->device_type.MMC_HS200_SDR_1200MV = ((1 << 5U) & raw[196U]); ext->device_type.MMC_HS200_SDR_1800MV = ((1 << 4U) & raw[196U]); ext->device_type.MMC_HS_DDR_1200MV = ((1 << 3U) & raw[196U]); ext->device_type.MMC_HS_DDR_1800MV = ((1 << 2U) & raw[196U]); ext->device_type.MMC_HS_52_DV = ((1 << 1U) & raw[196U]); ext->device_type.MMC_HS_26_DV = ((1 << 0U) & raw[196U]); ext->rev = raw[192U]; ext->power_class = (raw[187] & 0x0F); ext->mmc_driver_strengths = raw[197U]; ext->pwr_class_200MHZ_VCCQ195 = raw[237U]; ext->cache_size = (raw[252] << 24U) + (raw[251] << 16U) + (raw[250] << 8U) + (raw[249] << 0U); } static int mmc_set_cache(struct sd_card *card, struct mmc_ext_csd *card_ext_csd) { int ret = 0; struct sdhc_command cmd = {0}; /* If there is no cache, don't use cache */ if (card_ext_csd->cache_size == 0) { return 0; } /* CMD6 to write to EXT CSD to turn on cache */ cmd.opcode = SD_SWITCH; cmd.arg = MMC_SWITCH_CACHE_ON_ARG; cmd.response_type = SD_RSP_TYPE_R1b; cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT; ret = sdhc_request(card->sdhc, &cmd, NULL); if (ret) { LOG_DBG("Error turning on card cache: %d", ret); return ret; } ret = sdmmc_wait_ready(card); return ret; }