zephyr/subsys/sd/mmc.c
Paul Alvin c897adb1c6 sd: mmc: Remove unwanted request to card for reading OCR content
As part of the MMC card initialization sequence, CMD1 command send
multiple times to the card, first time to identify the card type and
second time to check the requested voltage window is supported or not.
There is a chance that after issuing the CMD1 for first time, card will
move to the ready state before issuing the CMD1 for second time. In this
case, card will not respond to the CMD1 send for the second time as card
is already moved to ready state and this leads to failures. Hence remove
the separate card identification logic and call mmc_send_op_cond only
once to check both supported voltage window and card type
identification.

Signed-off-by: Paul Alvin <alvin.paulp@amd.com>
2025-02-26 22:03:23 +00:00

636 lines
18 KiB
C

/*
* Copyright 2022 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/drivers/sdhc.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/sd/mmc.h>
#include <zephyr/sd/sd.h>
#include <zephyr/sd/sd_spec.h>
#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;
}
/* 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_DBG("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 (retries == 0) {
/* Card is MMC card if no error
* (Only MMC protocol supports CMD1)
*/
card->type = CARD_MMC;
}
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;
}