blob: 084df891e460b41afbf5fbb765b1235dd47989fb [file] [log] [blame]
/*
* Copyright 2022 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/drivers/sdhc.h>
#include <zephyr/sd/sd.h>
#include <zephyr/sd/sdmmc.h>
#include <zephyr/sd/sd_spec.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/drivers/disk.h>
#include "sd_utils.h"
#include "sd_ops.h"
LOG_MODULE_DECLARE(sd, CONFIG_SD_LOG_LEVEL);
static inline void sdmmc_decode_scr(struct sd_scr *scr,
uint32_t *raw_scr, uint32_t *version)
{
uint32_t tmp_version = 0;
scr->flags = 0U;
scr->scr_structure = (uint8_t)((raw_scr[0U] & 0xF0000000U) >> 28U);
scr->sd_spec = (uint8_t)((raw_scr[0U] & 0xF000000U) >> 24U);
if ((uint8_t)((raw_scr[0U] & 0x800000U) >> 23U)) {
scr->flags |= SD_SCR_DATA_STATUS_AFTER_ERASE;
}
scr->sd_sec = (uint8_t)((raw_scr[0U] & 0x700000U) >> 20U);
scr->sd_width = (uint8_t)((raw_scr[0U] & 0xF0000U) >> 16U);
if ((uint8_t)((raw_scr[0U] & 0x8000U) >> 15U)) {
scr->flags |= SD_SCR_SPEC3;
}
scr->sd_ext_sec = (uint8_t)((raw_scr[0U] & 0x7800U) >> 10U);
scr->cmd_support = (uint8_t)(raw_scr[0U] & 0x3U);
scr->rsvd = raw_scr[1U];
/* Get specification version. */
switch (scr->sd_spec) {
case 0U:
tmp_version = SD_SPEC_VER1_0;
break;
case 1U:
tmp_version = SD_SPEC_VER1_1;
break;
case 2U:
tmp_version = SD_SPEC_VER2_0;
if (scr->flags & SD_SCR_SPEC3) {
tmp_version = SD_SPEC_VER3_0;
}
break;
default:
break;
}
if (version && tmp_version) {
*version = tmp_version;
}
}
/* Helper to send SD app command */
static int sdmmc_app_command(struct sd_card *card, int relative_card_address)
{
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_SPI_READ_OCR;
cmd.arg = arg;
cmd.response_type = SD_SPI_RSP_TYPE_R3;
ret = sdhc_request(card->sdhc, &cmd, NULL);
card->ocr = cmd.response[1];
return ret;
}
/* Sends OCR to card using ACMD41 */
static int sdmmc_send_ocr(struct sd_card *card, int ocr)
{
struct sdhc_command cmd;
int ret;
int retries;
cmd.opcode = SD_APP_SEND_OP_COND;
cmd.arg = ocr;
cmd.response_type = (SD_RSP_TYPE_R3 | SD_SPI_RSP_TYPE_R1);
cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT;
/* Send initialization ACMD41 */
for (retries = 0; retries < CONFIG_SD_OCR_RETRY_COUNT; retries++) {
ret = sdmmc_app_command(card, 0U);
if (ret == SD_RETRY) {
/* Retry */
continue;
} else if (ret) {
return ret;
}
ret = sdhc_request(card->sdhc, &cmd, NULL);
if (ret) {
/* OCR failed */
return ret;
}
if (ocr == 0) {
/* Just probing, don't wait for card to exit busy state */
return 0;
}
/*
* Check to see if card is busy with power up. PWR_BUSY
* flag will be cleared when card finishes power up sequence
*/
if (card->host_props.is_spi) {
if (!(cmd.response[0] & SD_SPI_R1IDLE_STATE)) {
break;
}
} else {
if ((cmd.response[0U] & SD_OCR_PWR_BUSY_FLAG)) {
break;
}
}
sd_delay(10);
}
if (retries >= CONFIG_SD_OCR_RETRY_COUNT) {
/* OCR timed out */
LOG_ERR("Card never left busy state");
return -ETIMEDOUT;
}
LOG_DBG("SDMMC responded to ACMD41 after %d attempts", retries);
if (!card->host_props.is_spi) {
/* Save OCR */
card->ocr = cmd.response[0U];
}
return 0;
}
/* Reads SD configuration register */
static int sdmmc_read_scr(struct sd_card *card)
{
struct sdhc_command cmd = {0};
struct sdhc_data data = {0};
/* Place SCR struct on stack to reduce flash usage */
struct sd_scr card_scr;
int ret;
/* DMA onto stack is unsafe, so we use an internal card buffer */
uint32_t *scr = (uint32_t *)card->card_buffer;
uint32_t raw_scr[2];
ret = sdmmc_app_command(card, card->relative_addr);
if (ret) {
LOG_DBG("SD app command failed for SD SCR");
return ret;
}
cmd.opcode = SD_APP_SEND_SCR;
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 = 8U;
data.blocks = 1U;
data.data = scr;
data.timeout_ms = CONFIG_SD_DATA_TIMEOUT;
ret = sdhc_request(card->sdhc, &cmd, &data);
if (ret) {
LOG_DBG("ACMD51 failed: %d", ret);
return ret;
}
/* Decode SCR */
raw_scr[0] = sys_be32_to_cpu(scr[0]);
raw_scr[1] = sys_be32_to_cpu(scr[1]);
sdmmc_decode_scr(&card_scr, raw_scr, &card->sd_version);
LOG_DBG("SD reports specification version %d", card->sd_version);
/* Check card supported bus width */
if (card_scr.sd_width & 0x4U) {
card->flags |= SD_4BITS_WIDTH;
}
/* Check if card supports speed class command (CMD20) */
if (card_scr.cmd_support & 0x1U) {
card->flags |= SD_SPEED_CLASS_CONTROL_FLAG;
}
/* Check for set block count (CMD 23) support */
if (card_scr.cmd_support & 0x2U) {
card->flags |= SD_CMD23_FLAG;
}
return 0;
}
/* Sets block length of SD card */
static int sdmmc_set_blocklen(struct sd_card *card, uint32_t block_len)
{
struct sdhc_command cmd = {0};
cmd.opcode = SD_SET_BLOCK_SIZE;
cmd.arg = block_len;
cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT;
cmd.response_type = (SD_RSP_TYPE_R1 | SD_SPI_RSP_TYPE_R1);
return sdhc_request(card->sdhc, &cmd, NULL);
}
/*
* Sets bus width of host and card, following section 3.4 of
* SD host controller specification
*/
static int sdmmc_set_bus_width(struct sd_card *card, enum sdhc_bus_width width)
{
struct sdhc_command cmd = {0};
int ret;
/*
* The specification strictly requires card interrupts to be masked, but
* Linux does not do so, so we won't either.
*/
/* Send ACMD6 to change bus width */
ret = sdmmc_app_command(card, card->relative_addr);
if (ret) {
LOG_DBG("SD app command failed for ACMD6");
return ret;
}
cmd.opcode = SD_APP_SET_BUS_WIDTH;
cmd.response_type = SD_RSP_TYPE_R1;
cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT;
switch (width) {
case SDHC_BUS_WIDTH1BIT:
cmd.arg = 0U;
break;
case SDHC_BUS_WIDTH4BIT:
cmd.arg = 2U;
break;
default:
return -ENOTSUP;
}
/* Send app command */
ret = sdhc_request(card->sdhc, &cmd, NULL);
if (ret) {
LOG_DBG("Error on ACMD6: %d", ret);
return ret;
}
ret = sd_check_response(&cmd);
if (ret) {
LOG_DBG("ACMD6 reports error, response 0x%x", cmd.response[0U]);
return ret;
}
/* Card now has changed bus width. Change host bus width */
card->bus_io.bus_width = width;
ret = sdhc_set_io(card->sdhc, &card->bus_io);
if (ret) {
LOG_DBG("Could not change host bus width");
}
return ret;
}
/*
* Sends SD switch function CMD6.
* See table 4-32 in SD physical specification for argument details.
* When setting a function, we should set the 4 bit block of the command
* argument corresponding to that function to "value", and all other 4 bit
* blocks should be left as 0xF (no effect on current function)
*/
static int sdmmc_switch(struct sd_card *card, enum sd_switch_arg mode,
enum sd_group_num group, uint8_t value, uint8_t *response)
{
struct sdhc_command cmd = {0};
struct sdhc_data data = {0};
cmd.opcode = SD_SWITCH;
cmd.arg = ((mode & 0x1) << 31) | 0x00FFFFFF;
cmd.arg &= ~(0xFU << (group * 4));
cmd.arg |= (value & 0xF) << (group * 4);
cmd.response_type = (SD_RSP_TYPE_R1 | SD_SPI_RSP_TYPE_R1);
cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT;
data.block_size = 64U;
data.blocks = 1;
data.data = response;
data.timeout_ms = CONFIG_SD_DATA_TIMEOUT;
return sdhc_request(card->sdhc, &cmd, &data);
}
static int sdmmc_read_switch(struct sd_card *card)
{
uint8_t *status;
int ret;
if (card->sd_version < SD_SPEC_VER1_1) {
/* Switch not supported */
LOG_INF("SD spec 1.01 does not support CMD6");
return 0;
}
/* Use card internal buffer to read 64 byte switch data */
status = card->card_buffer;
/*
* Setting switch to zero will read card's support values,
* otherwise known as SD "check function"
*/
ret = sdmmc_switch(card, SD_SWITCH_CHECK, 0, 0, status);
if (ret) {
LOG_DBG("CMD6 failed %d", ret);
return ret;
}
/*
* See table 4-11 and 4.3.10.4 of physical layer specification for
* bit definitions. Note that response is big endian, so index 13 will
* read bits 400-408.
* Bit n being set in support bit field indicates support for function
* number n on the card. (So 0x3 indicates support for functions 0 and 1)
*/
if (status[13] & HIGH_SPEED_BUS_SPEED) {
card->switch_caps.hs_max_dtr = HS_MAX_DTR;
}
if (card->sd_version >= SD_SPEC_VER3_0) {
card->switch_caps.bus_speed = status[13];
card->switch_caps.sd_drv_type = status[9];
card->switch_caps.sd_current_limit = status[7];
}
return 0;
}
static inline void sdmmc_select_bus_speed(struct sd_card *card)
{
/*
* Note that function support is defined using bitfields, but function
* selection is defined using values 0x0-0xF.
*/
if (card->host_props.host_caps.sdr104_support &&
(card->switch_caps.bus_speed & UHS_SDR104_BUS_SPEED) &&
(card->host_props.f_max >= SD_CLOCK_208MHZ)) {
card->card_speed = SD_TIMING_SDR104;
} else if (card->host_props.host_caps.ddr50_support &&
(card->switch_caps.bus_speed & UHS_DDR50_BUS_SPEED) &&
(card->host_props.f_max >= SD_CLOCK_50MHZ)) {
card->card_speed = SD_TIMING_DDR50;
} else if (card->host_props.host_caps.sdr50_support &&
(card->switch_caps.bus_speed & UHS_SDR50_BUS_SPEED) &&
(card->host_props.f_max >= SD_CLOCK_100MHZ)) {
card->card_speed = SD_TIMING_SDR50;
} else if (card->host_props.host_caps.high_spd_support &&
(card->switch_caps.bus_speed & UHS_SDR12_BUS_SPEED) &&
(card->host_props.f_max >= SD_CLOCK_25MHZ)) {
card->card_speed = SD_TIMING_SDR12;
}
}
/* Selects driver type for SD card */
static int sdmmc_select_driver_type(struct sd_card *card)
{
int ret = 0;
uint8_t *status = card->card_buffer;
/*
* We will only attempt to use driver type C over the default of type B,
* since it should result in lower current consumption if supported.
*/
if (card->host_props.host_caps.drv_type_c_support &&
(card->switch_caps.sd_drv_type & SD_DRIVER_TYPE_C)) {
card->bus_io.driver_type = SD_DRIVER_TYPE_C;
/* Change drive strength */
ret = sdmmc_switch(card, SD_SWITCH_SET,
SD_GRP_DRIVER_STRENGTH_MODE,
(find_msb_set(SD_DRIVER_TYPE_C) - 1), status);
}
return ret;
}
/* Sets current limit for SD card */
static int sdmmc_set_current_limit(struct sd_card *card)
{
int ret;
int max_current = -1;
uint8_t *status = card->card_buffer;
if ((card->card_speed != SD_TIMING_SDR50) &&
(card->card_speed != SD_TIMING_SDR104) &&
(card->card_speed != SD_TIMING_DDR50)) {
return 0; /* Cannot set current limit */
} else if (card->host_props.max_current_180 >= 800 &&
(card->switch_caps.sd_current_limit & SD_MAX_CURRENT_800MA)) {
max_current = SD_SET_CURRENT_800MA;
} else if (card->host_props.max_current_180 >= 600 &&
(card->switch_caps.sd_current_limit & SD_MAX_CURRENT_600MA)) {
max_current = SD_SET_CURRENT_600MA;
} else if (card->host_props.max_current_180 >= 400 &&
(card->switch_caps.sd_current_limit & SD_MAX_CURRENT_400MA)) {
max_current = SD_SET_CURRENT_400MA;
} else if (card->host_props.max_current_180 >= 200 &&
(card->switch_caps.sd_current_limit & SD_MAX_CURRENT_200MA)) {
max_current = SD_SET_CURRENT_200MA;
}
if (max_current != -1) {
LOG_DBG("Changing SD current limit: %d", max_current);
/* Switch SD current */
ret = sdmmc_switch(card, SD_SWITCH_SET, SD_GRP_CURRENT_LIMIT_MODE,
max_current, status);
if (ret) {
LOG_DBG("Failed to set SD current limit");
return ret;
}
if (((status[15] >> 4) & 0x0F) != max_current) {
/* Status response indicates card did not select request limit */
LOG_WRN("Card did not accept current limit");
}
}
return 0;
}
/* Applies selected card bus speed to card and host */
static int sdmmc_set_bus_speed(struct sd_card *card)
{
int ret;
int timing = 0;
uint8_t *status = card->card_buffer;
switch (card->card_speed) {
/* Set bus clock speed */
case SD_TIMING_SDR104:
card->switch_caps.uhs_max_dtr = SD_CLOCK_208MHZ;
timing = SDHC_TIMING_SDR104;
break;
case SD_TIMING_DDR50:
card->switch_caps.uhs_max_dtr = SD_CLOCK_50MHZ;
timing = SDHC_TIMING_DDR50;
break;
case SD_TIMING_SDR50:
card->switch_caps.uhs_max_dtr = SD_CLOCK_100MHZ;
timing = SDHC_TIMING_SDR50;
break;
case SD_TIMING_SDR25:
card->switch_caps.uhs_max_dtr = SD_CLOCK_50MHZ;
timing = SDHC_TIMING_SDR25;
break;
case SD_TIMING_SDR12:
card->switch_caps.uhs_max_dtr = SD_CLOCK_25MHZ;
timing = SDHC_TIMING_SDR12;
break;
default:
/* No need to change bus speed */
return 0;
}
/* Switch bus speed */
ret = sdmmc_switch(card, SD_SWITCH_SET, SD_GRP_TIMING_MODE,
card->card_speed, status);
if (ret) {
LOG_DBG("Failed to switch SD card speed");
return ret;
}
if ((status[16] & 0xF) != card->card_speed) {
LOG_WRN("Card did not accept new speed");
} else {
/* Change host bus speed */
card->bus_io.timing = timing;
card->bus_io.clock = card->switch_caps.uhs_max_dtr;
LOG_DBG("Setting bus clock to: %d", card->bus_io.clock);
ret = sdhc_set_io(card->sdhc, &card->bus_io);
if (ret) {
LOG_ERR("Failed to change host bus speed");
return ret;
}
}
return 0;
}
/*
* Init UHS capable SD card. Follows figure 3-16 in physical layer specification.
*/
static int sdmmc_init_uhs(struct sd_card *card)
{
int ret;
/* Raise bus width to 4 bits */
ret = sdmmc_set_bus_width(card, SDHC_BUS_WIDTH4BIT);
if (ret) {
LOG_ERR("Failed to change card bus width to 4 bits");
return ret;
}
/* Select bus speed for card depending on host and card capability*/
sdmmc_select_bus_speed(card);
/* Now, set the driver strength for the card */
ret = sdmmc_select_driver_type(card);
if (ret) {
LOG_DBG("Failed to select new driver type");
return ret;
}
ret = sdmmc_set_current_limit(card);
if (ret) {
LOG_DBG("Failed to set card current limit");
return ret;
}
/* Apply the bus speed selected earlier */
ret = sdmmc_set_bus_speed(card);
if (ret) {
LOG_DBG("Failed to set card bus speed");
return ret;
}
if (card->card_speed == SD_TIMING_SDR50 ||
card->card_speed == SD_TIMING_SDR104 ||
card->card_speed == SD_TIMING_DDR50) {
/* SDR104, SDR50, and DDR50 mode need tuning */
ret = sdhc_execute_tuning(card->sdhc);
if (ret) {
LOG_ERR("SD tuning failed: %d", ret);
}
}
return ret;
}
/* Performs initialization for SD high speed cards */
static int sdmmc_init_hs(struct sd_card *card)
{
int ret;
if ((!card->host_props.host_caps.high_spd_support) ||
(card->sd_version < SD_SPEC_VER1_1) ||
(card->switch_caps.hs_max_dtr == 0)) {
/* No high speed support. Leave card untouched */
return 0;
}
card->card_speed = SD_TIMING_SDR25;
ret = sdmmc_set_bus_speed(card);
if (ret) {
LOG_ERR("Failed to switch card to HS mode");
return ret;
}
if (card->flags & SD_4BITS_WIDTH) {
/* Raise bus width to 4 bits */
ret = sdmmc_set_bus_width(card, SDHC_BUS_WIDTH4BIT);
if (ret) {
LOG_ERR("Failed to change card bus width to 4 bits");
return ret;
}
}
return 0;
}
/*
* Initializes SDMMC card. Note that the common SD function has already
* sent CMD0 and CMD8 to the card at function entry.
*/
int sdmmc_card_init(struct sd_card *card)
{
int ret;
uint32_t ocr_arg = 0U;
/* 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;
}
/* Card responded to ACMD41, type is SDMMC */
card->type = CARD_SDMMC;
if (card->flags & SD_SDHC_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;
}
if (IS_ENABLED(CONFIG_SDHC_SUPPORTS_NATIVE_MODE)) {
/* Set voltage window */
if (card->host_props.host_caps.vol_300_support) {
ocr_arg |= SD_OCR_VDD29_30FLAG;
}
ocr_arg |= (SD_OCR_VDD32_33FLAG | SD_OCR_VDD33_34FLAG);
}
/* Momentary delay before initialization OCR. Some cards will
* never leave busy state if init OCR is sent too soon after
* probing OCR
*/
k_busy_wait(100);
/* Send SD OCR to card to initialize it */
ret = sdmmc_send_ocr(card, ocr_arg);
if (ret) {
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;
}
if (card->ocr & SD_OCR_SWITCH_18_ACCEPT_FLAG) {
LOG_DBG("Card supports 1.8V signaling");
card->flags |= SD_1800MV_FLAG;
}
/* Check OCR voltage window */
if (card->ocr & SD_OCR_VDD29_30FLAG) {
card->flags |= SD_3000MV_FLAG;
}
/*
* If card is high capacity (SDXC or SDHC), and supports 1.8V signaling,
* switch to new signal voltage using "signal voltage switch procedure"
* described in SD specification
*/
if ((card->flags & SD_1800MV_FLAG) &&
(card->host_props.host_caps.vol_180_support) &&
(!card->host_props.is_spi) &&
IS_ENABLED(CONFIG_SD_UHS_PROTOCOL)) {
ret = sdmmc_switch_voltage(card);
if (ret) {
/* Disable host support for 1.8 V */
card->host_props.host_caps.vol_180_support = false;
/*
* The host or SD card may have already switched to
* 1.8V. Return SD_RESTART to indicate
* negotiation should be restarted.
*/
card->status = CARD_ERROR;
return SD_RESTART;
}
}
/* Read the card's CID (card identification register) */
ret = card_read_cid(card);
if (ret) {
return ret;
}
if (!card->host_props.is_spi && IS_ENABLED(CONFIG_SDHC_SUPPORTS_NATIVE_MODE)) {
/*
* Request new relative card address. This moves the card from
* identification mode to data transfer mode
*/
ret = sdmmc_request_rca(card);
if (ret) {
return ret;
}
}
/* Card has entered data transfer mode. Get card specific data register */
ret = sdmmc_read_csd(card);
if (ret) {
return ret;
}
if (!card->host_props.is_spi && IS_ENABLED(CONFIG_SDHC_SUPPORTS_NATIVE_MODE)) {
/* Move the card to transfer state (with CMD7) to run remaining commands */
ret = sdmmc_select_card(card);
if (ret) {
return ret;
}
}
/*
* With card in data transfer state, we can set SD clock to maximum
* frequency for non high speed mode (25Mhz)
*/
if (card->host_props.f_max < SD_CLOCK_25MHZ) {
LOG_INF("Maximum SD clock is under 25MHz, using clock of %dHz",
card->host_props.f_max);
card->bus_io.clock = card->host_props.f_max;
} else {
card->bus_io.clock = SD_CLOCK_25MHZ;
}
ret = sdhc_set_io(card->sdhc, &card->bus_io);
if (ret) {
LOG_ERR("Failed to raise bus frequency to 25MHz");
return ret;
}
/* Read SD SCR (SD configuration register),
* to get supported bus width
*/
ret = sdmmc_read_scr(card);
if (ret) {
return ret;
}
/* Read switch capabilities to determine what speeds card supports */
if (!card->host_props.is_spi && IS_ENABLED(CONFIG_SDHC_SUPPORTS_NATIVE_MODE)) {
ret = sdmmc_read_switch(card);
if (ret) {
LOG_ERR("Failed to read card functions");
return ret;
}
}
if ((card->flags & SD_1800MV_FLAG) &&
sdmmc_host_uhs(&card->host_props) &&
!(card->host_props.is_spi) &&
IS_ENABLED(CONFIG_SD_UHS_PROTOCOL)) {
ret = sdmmc_init_uhs(card);
if (ret) {
LOG_ERR("UHS card init failed");
}
} else {
if ((card->flags & SD_HIGH_CAPACITY_FLAG) == 0) {
/* Standard capacity SDSC card. set block length to 512 */
ret = sdmmc_set_blocklen(card, SDMMC_DEFAULT_BLOCK_SIZE);
if (ret) {
LOG_ERR("Could not set SD blocklen to 512");
return ret;
}
card->block_size = 512;
}
/* Card is not UHS. Try to use high speed mode */
ret = sdmmc_init_hs(card);
if (ret) {
LOG_ERR("HS card init failed");
}
}
return ret;
}
int sdmmc_ioctl(struct sd_card *card, uint8_t cmd, void *buf)
{
return card_ioctl(card, cmd, buf);
}
int sdmmc_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);
}
int sdmmc_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);
}