644 lines
18 KiB
C
644 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;
|
|
}
|
|
|
|
/* 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) {
|
|
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) {
|
|
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;
|
|
}
|