blob: 64c813b1090b090b3b801d275bc058fcb1739fbf [file] [log] [blame]
/*
* Copyright 2022-2023 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 "sd_ops.h"
#include "sd_utils.h"
LOG_MODULE_DECLARE(sd, CONFIG_SD_LOG_LEVEL);
uint8_t cis_tuples[] = {
SDIO_TPL_CODE_MANIFID,
SDIO_TPL_CODE_FUNCID,
SDIO_TPL_CODE_FUNCE,
};
/*
* Send SDIO OCR using CMD5
*/
static int sdio_send_ocr(struct sd_card *card, uint32_t ocr)
{
struct sdhc_command cmd = {0};
int ret;
int retries;
cmd.opcode = SDIO_SEND_OP_COND;
cmd.arg = ocr;
cmd.response_type = (SD_RSP_TYPE_R4 | SD_SPI_RSP_TYPE_R4);
cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT;
/* Send OCR5 to initialize card */
for (retries = 0; retries < CONFIG_SD_OCR_RETRY_COUNT; retries++) {
ret = sdhc_request(card->sdhc, &cmd, NULL);
if (ret) {
if (ocr == 0) {
/* Just probing card, likely not SDIO */
return SD_NOT_SDIO;
}
return ret;
}
if (ocr == 0) {
/* We are probing card, check number of IO functions */
card->num_io = (cmd.response[0] & SDIO_OCR_IO_NUMBER)
>> SDIO_OCR_IO_NUMBER_SHIFT;
if ((card->num_io == 0) ||
((cmd.response[0] & SDIO_IO_OCR_MASK) == 0)) {
if (cmd.response[0] & SDIO_OCR_MEM_PRESENT_FLAG) {
/* Card is not an SDIO card */
return SD_NOT_SDIO;
}
/* Card is not a supported SD device */
return -ENOTSUP;
}
/* Card has IO present, return zero to
* indicate SDIO card
*/
return 0;
}
/* Check to see if card is busy with power up */
if (cmd.response[0] & SD_OCR_PWR_BUSY_FLAG) {
break;
}
/* Delay before retrying command */
sd_delay(10);
}
if (retries >= CONFIG_SD_OCR_RETRY_COUNT) {
/* OCR timed out */
LOG_ERR("Card never left busy state");
return -ETIMEDOUT;
}
LOG_DBG("SDIO responded to CMD5 after %d attempts", retries);
if (!card->host_props.is_spi) {
/* Save OCR */
card->ocr = cmd.response[0U];
}
return 0;
}
static int sdio_io_rw_direct(struct sd_card *card,
enum sdio_io_dir direction,
enum sdio_func_num func,
uint32_t reg_addr,
uint8_t data_in,
uint8_t *data_out)
{
int ret;
struct sdhc_command cmd = {0};
cmd.opcode = SDIO_RW_DIRECT;
cmd.arg = (func << SDIO_CMD_ARG_FUNC_NUM_SHIFT) |
((reg_addr & SDIO_CMD_ARG_REG_ADDR_MASK) << SDIO_CMD_ARG_REG_ADDR_SHIFT);
if (direction == SDIO_IO_WRITE) {
cmd.arg |= data_in & SDIO_DIRECT_CMD_DATA_MASK;
cmd.arg |= BIT(SDIO_CMD_ARG_RW_SHIFT);
if (data_out) {
cmd.arg |= BIT(SDIO_DIRECT_CMD_ARG_RAW_SHIFT);
}
}
cmd.response_type = (SD_RSP_TYPE_R5 | SD_SPI_RSP_TYPE_R5);
cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT;
ret = sdhc_request(card->sdhc, &cmd, NULL);
if (ret) {
return ret;
}
if (data_out) {
if (card->host_props.is_spi) {
*data_out = (cmd.response[0U] >> 8) & SDIO_DIRECT_CMD_DATA_MASK;
} else {
*data_out = cmd.response[0U] & SDIO_DIRECT_CMD_DATA_MASK;
}
}
return ret;
}
static int sdio_io_rw_extended(struct sd_card *card,
enum sdio_io_dir direction,
enum sdio_func_num func,
uint32_t reg_addr,
bool increment,
uint8_t *buf,
uint32_t blocks,
uint32_t block_size)
{
struct sdhc_command cmd = {0};
struct sdhc_data data = {0};
cmd.opcode = SDIO_RW_EXTENDED;
cmd.arg = (func << SDIO_CMD_ARG_FUNC_NUM_SHIFT) |
((reg_addr & SDIO_CMD_ARG_REG_ADDR_MASK) << SDIO_CMD_ARG_REG_ADDR_SHIFT);
cmd.arg |= (direction == SDIO_IO_WRITE) ? BIT(SDIO_CMD_ARG_RW_SHIFT) : 0;
cmd.arg |= increment ? BIT(SDIO_EXTEND_CMD_ARG_OP_CODE_SHIFT) : 0;
cmd.response_type = (SD_RSP_TYPE_R5 | SD_SPI_RSP_TYPE_R5);
cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT;
if (blocks == 0) {
/* Byte mode */
cmd.arg |= (block_size == 512) ? 0 : block_size;
} else {
/* Block mode */
cmd.arg |= BIT(SDIO_EXTEND_CMD_ARG_BLK_SHIFT) | blocks;
}
data.block_size = block_size;
/* Host expects blocks to be at least 1 */
data.blocks = blocks ? blocks : 1;
data.data = buf;
data.timeout_ms = CONFIG_SD_DATA_TIMEOUT;
return sdhc_request(card->sdhc, &cmd, &data);
}
/*
* Helper for extended r/w. Splits the transfer into the minimum possible
* number of block r/w, then uses byte transfers for remaining data
*/
static int sdio_io_rw_extended_helper(struct sdio_func *func,
enum sdio_io_dir direction,
uint32_t reg_addr,
bool increment,
uint8_t *buf,
uint32_t len)
{
int ret;
int remaining = len;
uint32_t blocks, size;
if (func->num > SDIO_MAX_IO_NUMS) {
return -EINVAL;
}
if ((func->card->cccr_flags & SDIO_SUPPORT_MULTIBLOCK) &&
((len > func->block_size))) {
/* Use block I/O for r/w where possible */
while (remaining >= func->block_size) {
blocks = remaining / func->block_size;
size = blocks * func->block_size;
ret = sdio_io_rw_extended(func->card, direction,
func->num, reg_addr, increment, buf, blocks,
func->block_size);
if (ret) {
return ret;
}
/* Update remaining length and buffer pointer */
remaining -= size;
buf += size;
if (increment) {
reg_addr += size;
}
}
}
/* Remaining data must be written using byte I/O */
while (remaining > 0) {
size = MIN(remaining, func->cis.max_blk_size);
ret = sdio_io_rw_extended(func->card, direction, func->num,
reg_addr, increment, buf, 0, size);
if (ret) {
return ret;
}
remaining -= size;
buf += size;
if (increment) {
reg_addr += size;
}
}
return 0;
}
/*
* Read card capability register to determine features card supports.
*/
static int sdio_read_cccr(struct sd_card *card)
{
int ret;
uint8_t data;
uint32_t cccr_ver;
ret = sdio_io_rw_direct(card, SDIO_IO_READ, SDIO_FUNC_NUM_0,
SDIO_CCCR_CCCR, 0, &data);
if (ret) {
LOG_DBG("CCCR read failed: %d", ret);
return ret;
}
cccr_ver = (data & SDIO_CCCR_CCCR_REV_MASK) >>
SDIO_CCCR_CCCR_REV_SHIFT;
LOG_DBG("SDIO cccr revision %u", cccr_ver);
/* Read SD spec version */
ret = sdio_io_rw_direct(card, SDIO_IO_READ, SDIO_FUNC_NUM_0,
SDIO_CCCR_SD, 0, &data);
if (ret) {
return ret;
}
card->sd_version = (data & SDIO_CCCR_SD_SPEC_MASK) >> SDIO_CCCR_SD_SPEC_SHIFT;
/* Read CCCR capability flags */
ret = sdio_io_rw_direct(card, SDIO_IO_READ, SDIO_FUNC_NUM_0,
SDIO_CCCR_CAPS, 0, &data);
if (ret) {
return ret;
}
card->cccr_flags = 0;
if (data & SDIO_CCCR_CAPS_BLS) {
card->cccr_flags |= SDIO_SUPPORT_4BIT_LS_BUS;
}
if (data & SDIO_CCCR_CAPS_SMB) {
card->cccr_flags |= SDIO_SUPPORT_MULTIBLOCK;
}
if (cccr_ver >= SDIO_CCCR_CCCR_REV_2_00) {
/* Read high speed properties */
ret = sdio_io_rw_direct(card, SDIO_IO_READ, SDIO_FUNC_NUM_0,
SDIO_CCCR_SPEED, 0, &data);
if (ret) {
return ret;
}
if (data & SDIO_CCCR_SPEED_SHS) {
card->cccr_flags |= SDIO_SUPPORT_HS;
}
}
if (cccr_ver >= SDIO_CCCR_CCCR_REV_3_00 &&
(card->flags & SD_1800MV_FLAG)) {
/* Read UHS properties */
ret = sdio_io_rw_direct(card, SDIO_IO_READ, SDIO_FUNC_NUM_0,
SDIO_CCCR_UHS, 0, &data);
if (ret) {
return ret;
}
if (sdmmc_host_uhs(&card->host_props)) {
if (data & SDIO_CCCR_UHS_SDR50) {
card->cccr_flags |= SDIO_SUPPORT_SDR50;
}
if (data & SDIO_CCCR_UHS_SDR104) {
card->cccr_flags |= SDIO_SUPPORT_SDR104;
}
if (data & SDIO_CCCR_UHS_DDR50) {
card->cccr_flags |= SDIO_SUPPORT_DDR50;
}
}
ret = sdio_io_rw_direct(card, SDIO_IO_READ, SDIO_FUNC_NUM_0,
SDIO_CCCR_DRIVE_STRENGTH, 0, &data);
if (ret) {
return ret;
}
card->switch_caps.sd_drv_type = 0;
if (data & SDIO_CCCR_DRIVE_STRENGTH_A) {
card->switch_caps.sd_drv_type |= SD_DRIVER_TYPE_A;
}
if (data & SDIO_CCCR_DRIVE_STRENGTH_C) {
card->switch_caps.sd_drv_type |= SD_DRIVER_TYPE_C;
}
if (data & SDIO_CCCR_DRIVE_STRENGTH_D) {
card->switch_caps.sd_drv_type |= SD_DRIVER_TYPE_D;
}
}
return 0;
}
static void sdio_decode_cis(struct sdio_cis *cis, enum sdio_func_num func,
uint8_t *data, uint8_t tpl_code, uint8_t tpl_link)
{
switch (tpl_code) {
case SDIO_TPL_CODE_MANIFID:
cis->manf_id = data[0] | ((uint16_t)data[1] << 8);
cis->manf_code = data[2] | ((uint16_t)data[3] << 8);
break;
case SDIO_TPL_CODE_FUNCID:
cis->func_id = data[0];
break;
case SDIO_TPL_CODE_FUNCE:
if (func == 0) {
cis->max_blk_size = data[1] | ((uint16_t)data[2] << 8);
cis->max_speed = data[3];
} else {
cis->max_blk_size = data[12] | ((uint16_t)data[13] << 8);
cis->rdy_timeout = data[28] | ((uint16_t)data[29] << 8);
}
break;
default:
LOG_WRN("Unknown CIS tuple %d", tpl_code);
break;
}
}
/*
* Read CIS for a given SDIO function.
* Tuples provides a list of tuples that should be decoded.
*/
static int sdio_read_cis(struct sdio_func *func,
uint8_t *tuples,
uint32_t tuple_count)
{
int ret;
char *data = func->card->card_buffer;
uint32_t cis_ptr = 0, num = 0;
uint8_t tpl_code, tpl_link;
bool match_tpl = false;
memset(&func->cis, 0, sizeof(struct sdio_cis));
/* First find the CIS pointer for this function */
for (int i = 0; i < 3; i++) {
ret = sdio_io_rw_direct(func->card, SDIO_IO_READ, SDIO_FUNC_NUM_0,
SDIO_FBR_BASE(func->num) + SDIO_FBR_CIS + i, 0, data);
if (ret) {
return ret;
}
cis_ptr |= *data << (i * 8);
}
/* Read CIS tuples until we have read all requested CIS tuple codes */
do {
/* Read tuple code */
ret = sdio_io_rw_direct(func->card, SDIO_IO_READ, SDIO_FUNC_NUM_0,
cis_ptr++, 0, &tpl_code);
if (ret) {
return ret;
}
if (tpl_code == SDIO_TPL_CODE_END) {
/* End of tuple chain */
break;
}
if (tpl_code == SDIO_TPL_CODE_NULL) {
/* Skip NULL tuple */
continue;
}
/* Read tuple link */
ret = sdio_io_rw_direct(func->card, SDIO_IO_READ, SDIO_FUNC_NUM_0,
cis_ptr++, 0, &tpl_link);
if (ret) {
return ret;
}
if (tpl_link == SDIO_TPL_CODE_END) {
/* End of tuple chain */
break;
}
/* Check to see if read tuple matches any we should look for */
for (int i = 0; i < tuple_count; i++) {
if (tpl_code == tuples[i]) {
match_tpl = true;
break;
}
}
if (match_tpl) {
/* tuple chains may be maximum of 255 bytes long */
memset(data, 0, 255);
for (int i = 0; i < tpl_link; i++) {
ret = sdio_io_rw_direct(func->card, SDIO_IO_READ,
SDIO_FUNC_NUM_0, cis_ptr++, 0, data + i);
if (ret) {
return ret;
}
}
num++;
match_tpl = false;
/* Decode the CIS data we read */
sdio_decode_cis(&func->cis, func->num, data,
tpl_code, tpl_link);
} else {
/* Advance CIS pointer */
cis_ptr += tpl_link;
}
} while (num < tuple_count);
LOG_DBG("SDIO CIS max block size for func %d: %d", func->num,
func->cis.max_blk_size);
return ret;
}
static int sdio_set_bus_width(struct sd_card *card, enum sdhc_bus_width width)
{
uint8_t reg_bus_interface = 0U;
int ret;
ret = sdio_io_rw_direct(card, SDIO_IO_READ, SDIO_FUNC_NUM_0,
SDIO_CCCR_BUS_IF, 0, &reg_bus_interface);
if (ret) {
return ret;
}
reg_bus_interface &= ~SDIO_CCCR_BUS_IF_WIDTH_MASK;
switch (width) {
case SDHC_BUS_WIDTH1BIT:
reg_bus_interface |= SDIO_CCCR_BUS_IF_WIDTH_1_BIT;
break;
case SDHC_BUS_WIDTH4BIT:
reg_bus_interface |= SDIO_CCCR_BUS_IF_WIDTH_4_BIT;
break;
case SDHC_BUS_WIDTH8BIT:
reg_bus_interface |= SDIO_CCCR_BUS_IF_WIDTH_8_BIT;
break;
default:
return -ENOTSUP;
}
ret = sdio_io_rw_direct(card, SDIO_IO_WRITE, SDIO_FUNC_NUM_0,
SDIO_CCCR_BUS_IF, reg_bus_interface, &reg_bus_interface);
if (ret) {
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;
}
static inline void sdio_select_bus_speed(struct sd_card *card)
{
if (card->host_props.host_caps.sdr104_support &&
(card->cccr_flags & SDIO_SUPPORT_SDR104) &&
(card->host_props.f_max >= SD_CLOCK_208MHZ)) {
card->card_speed = SD_TIMING_SDR104;
} else if (card->host_props.host_caps.ddr50_support &&
(card->cccr_flags & SDIO_SUPPORT_DDR50) &&
(card->host_props.f_max >= SD_CLOCK_50MHZ)) {
card->card_speed = SD_TIMING_DDR50;
} else if (card->host_props.host_caps.sdr50_support &&
(card->cccr_flags & SDIO_SUPPORT_SDR50) &&
(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 & SDIO_SUPPORT_HS) &&
(card->host_props.f_max >= SD_CLOCK_50MHZ)) {
card->card_speed = SD_TIMING_SDR25;
} else {
card->card_speed = SD_TIMING_SDR12;
}
}
/* Applies selected card bus speed to card and host */
static int sdio_set_bus_speed(struct sd_card *card)
{
int ret, timing, retries = CONFIG_SD_RETRY_COUNT;
uint8_t speed_reg, target_speed;
switch (card->card_speed) {
/* Set bus clock speed */
case SD_TIMING_SDR104:
card->switch_caps.uhs_max_dtr = SD_CLOCK_208MHZ;
target_speed = SDIO_CCCR_SPEED_SDR104;
timing = SDHC_TIMING_SDR104;
break;
case SD_TIMING_DDR50:
card->switch_caps.uhs_max_dtr = SD_CLOCK_50MHZ;
target_speed = SDIO_CCCR_SPEED_DDR50;
timing = SDHC_TIMING_DDR50;
break;
case SD_TIMING_SDR50:
card->switch_caps.uhs_max_dtr = SD_CLOCK_100MHZ;
target_speed = SDIO_CCCR_SPEED_SDR50;
timing = SDHC_TIMING_SDR50;
break;
case SD_TIMING_SDR25:
card->switch_caps.uhs_max_dtr = SD_CLOCK_50MHZ;
target_speed = SDIO_CCCR_SPEED_SDR25;
timing = SDHC_TIMING_SDR25;
break;
case SD_TIMING_SDR12:
card->switch_caps.uhs_max_dtr = SD_CLOCK_25MHZ;
target_speed = SDIO_CCCR_SPEED_SDR12;
timing = SDHC_TIMING_SDR12;
break;
default:
/* No need to change bus speed */
return 0;
}
/* Read the bus speed register */
ret = sdio_io_rw_direct(card, SDIO_IO_READ, SDIO_FUNC_NUM_0,
SDIO_CCCR_SPEED, 0, &speed_reg);
if (ret) {
return ret;
}
/* Attempt to set speed several times */
do {
/* Set new speed */
speed_reg &= ~SDIO_CCCR_SPEED_MASK;
speed_reg |= (target_speed << SDIO_CCCR_SPEED_SHIFT);
ret = sdio_io_rw_direct(card, SDIO_IO_WRITE, SDIO_FUNC_NUM_0,
SDIO_CCCR_SPEED, speed_reg, &speed_reg);
if (ret) {
return ret;
}
} while (((speed_reg & target_speed) != target_speed) && retries-- > 0);
if (retries == 0) {
/* Don't error out, as card can still work */
LOG_WRN("Could not set target SDIO speed");
} else {
/* Set card bus clock and timing */
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 ret;
}
/*
* Initialize an SDIO card for use with subsystem
*/
int sdio_card_init(struct sd_card *card)
{
int ret;
uint32_t ocr_arg = 0U;
/* Probe card with SDIO OCR CM5 */
ret = sdio_send_ocr(card, ocr_arg);
if (ret) {
return ret;
}
/* Card responded to CMD5, type is SDIO */
card->type = CARD_SDIO;
/* 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);
if (IS_ENABLED(CONFIG_SDHC_SUPPORTS_NATIVE_MODE) &&
card->host_props.host_caps.vol_180_support) {
/* See if the card also supports 1.8V */
ocr_arg |= SD_OCR_SWITCH_18_REQ_FLAG;
}
ret = sdio_send_ocr(card, ocr_arg);
if (ret) {
return ret;
}
if (card->ocr & SD_OCR_SWITCH_18_ACCEPT_FLAG) {
LOG_DBG("Card supports 1.8V signalling");
card->flags |= SD_1800MV_FLAG;
}
/* Check OCR voltage window */
if (card->ocr & SD_OCR_VDD29_30FLAG) {
card->flags |= SD_3000MV_FLAG;
}
/* Check mem present flag */
if (card->ocr & SDIO_OCR_MEM_PRESENT_FLAG) {
card->flags |= SD_MEM_PRESENT_FLAG;
}
/* Following steps are only required when using native SD mode */
if (IS_ENABLED(CONFIG_SDHC_SUPPORTS_NATIVE_MODE)) {
/*
* If card and host support 1.8V, perform voltage switch sequence now.
* note that we skip this switch if the UHS protocol is not enabled.
*/
if ((card->flags & SD_1800MV_FLAG) &&
(!card->host_props.is_spi) &&
(card->host_props.host_caps.vol_180_support) &&
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;
}
}
if ((card->flags & SD_MEM_PRESENT_FLAG) &&
((card->flags & SD_SDHC_FLAG) == 0)) {
/* We must send CMD2 to get card cid */
ret = card_read_cid(card);
if (ret) {
return ret;
}
}
/* Send CMD3 to get card relative address */
ret = sdmmc_request_rca(card);
if (ret) {
return ret;
}
/* Move the card to transfer state (with CMD7) to run
* remaining commands
*/
ret = sdmmc_select_card(card);
if (ret) {
return ret;
}
}
/* Read SDIO card common control register */
ret = sdio_read_cccr(card);
if (ret) {
return ret;
}
/* Initialize internal card function 0 structure */
card->func0.num = SDIO_FUNC_NUM_0;
card->func0.card = card;
ret = sdio_read_cis(&card->func0, cis_tuples,
ARRAY_SIZE(cis_tuples));
if (ret) {
return ret;
}
/* If card and host support 4 bit bus, enable it */
if (IS_ENABLED(CONFIG_SDHC_SUPPORTS_NATIVE_MODE) &&
((card->cccr_flags & SDIO_SUPPORT_HS) ||
(card->cccr_flags & SDIO_SUPPORT_4BIT_LS_BUS))) {
/* Raise bus width to 4 bits */
ret = sdio_set_bus_width(card, SDHC_BUS_WIDTH4BIT);
if (ret) {
return ret;
}
LOG_DBG("Raised card bus width to 4 bits");
}
/* Select and set bus speed */
sdio_select_bus_speed(card);
ret = sdio_set_bus_speed(card);
if (ret) {
return ret;
}
if (card->card_speed == SD_TIMING_SDR50 ||
card->card_speed == SD_TIMING_SDR104) {
/* SDR104, SDR50, and DDR50 mode need tuning */
ret = sdhc_execute_tuning(card->sdhc);
if (ret) {
LOG_ERR("SD tuning failed: %d", ret);
}
}
return ret;
}
/**
* @brief Initialize SDIO function.
*
* Initializes SDIO card function. The card function will not be enabled,
* but after this call returns the SDIO function structure can be used to read
* and write data from the card.
* @param func: function structure to initialize
* @param card: SD card to enable function on
* @param num: function number to initialize
* @retval 0 function was initialized successfully
* @retval -EIO: I/O error
*/
int sdio_init_func(struct sd_card *card, struct sdio_func *func,
enum sdio_func_num num)
{
/* Initialize function structure */
func->num = num;
func->card = card;
func->block_size = 0;
/* Read function properties from CCCR */
return sdio_read_cis(func, cis_tuples, ARRAY_SIZE(cis_tuples));
}
/**
* @brief Enable SDIO function
*
* Enables SDIO card function. @ref sdio_init_func must be called to
* initialized the function structure before enabling it in the card.
* @param func: function to enable
* @retval 0 function was enabled successfully
* @retval -ETIMEDOUT: card I/O timed out
* @retval -EIO: I/O error
*/
int sdio_enable_func(struct sdio_func *func)
{
int ret;
uint8_t reg;
uint16_t retries = CONFIG_SD_RETRY_COUNT;
/* Enable the I/O function */
ret = sdio_io_rw_direct(func->card, SDIO_IO_READ, SDIO_FUNC_NUM_0,
SDIO_CCCR_IO_EN, 0, &reg);
if (ret) {
return ret;
}
reg |= BIT(func->num);
ret = sdio_io_rw_direct(func->card, SDIO_IO_WRITE, SDIO_FUNC_NUM_0,
SDIO_CCCR_IO_EN, reg, &reg);
if (ret) {
return ret;
}
/* Wait for I/O ready to be set */
if (func->cis.rdy_timeout) {
retries = 1U;
}
do {
/* Timeout is in units of 10ms */
sd_delay(((uint32_t)func->cis.rdy_timeout) * 10U);
ret = sdio_io_rw_direct(func->card, SDIO_IO_READ,
SDIO_FUNC_NUM_0, SDIO_CCCR_IO_RD, 0, &reg);
if (ret) {
return ret;
}
if (reg & BIT(func->num)) {
return 0;
}
} while (retries-- != 0);
return -ETIMEDOUT;
}
/**
* @brief Set block size of SDIO function
*
* Set desired block size for SDIO function, used by block transfers
* to SDIO registers.
* @param func: function to set block size for
* @param bsize: block size
* @retval 0 block size was set
* @retval -EINVAL: unsupported/invalid block size
* @retval -EIO: I/O error
*/
int sdio_set_block_size(struct sdio_func *func, uint16_t bsize)
{
int ret;
uint8_t reg;
if (func->cis.max_blk_size < bsize) {
return -EINVAL;
}
for (int i = 0; i < 2; i++) {
reg = (bsize >> (i * 8));
ret = sdio_io_rw_direct(func->card, SDIO_IO_WRITE, SDIO_FUNC_NUM_0,
SDIO_FBR_BASE(func->num) + SDIO_FBR_BLK_SIZE + i, reg, NULL);
if (ret) {
return ret;
}
}
func->block_size = bsize;
return 0;
}
/**
* @brief Read byte from SDIO register
*
* Reads byte from SDIO register
* @param func: function to read from
* @param reg: register address to read from
* @param val: filled with byte value read from register
* @retval 0 read succeeded
* @retval -EBUSY: card is busy with another request
* @retval -ETIMEDOUT: card read timed out
* @retval -EIO: I/O error
*/
int sdio_read_byte(struct sdio_func *func, uint32_t reg, uint8_t *val)
{
int ret;
if ((func->card->type != CARD_SDIO) && (func->card->type != CARD_COMBO)) {
LOG_WRN("Card does not support SDIO commands");
return -ENOTSUP;
}
ret = k_mutex_lock(&func->card->lock, K_MSEC(CONFIG_SD_DATA_TIMEOUT));
if (ret) {
LOG_WRN("Could not get SD card mutex");
return -EBUSY;
}
ret = sdio_io_rw_direct(func->card, SDIO_IO_READ, func->num, reg, 0, val);
k_mutex_unlock(&func->card->lock);
return ret;
}
/**
* @brief Write byte to SDIO register
*
* Writes byte to SDIO register
* @param func: function to write to
* @param reg: register address to write to
* @param write_val: value to write to register
* @retval 0 write succeeded
* @retval -EBUSY: card is busy with another request
* @retval -ETIMEDOUT: card write timed out
* @retval -EIO: I/O error
*/
int sdio_write_byte(struct sdio_func *func, uint32_t reg, uint8_t write_val)
{
int ret;
if ((func->card->type != CARD_SDIO) && (func->card->type != CARD_COMBO)) {
LOG_WRN("Card does not support SDIO commands");
return -ENOTSUP;
}
ret = k_mutex_lock(&func->card->lock, K_MSEC(CONFIG_SD_DATA_TIMEOUT));
if (ret) {
LOG_WRN("Could not get SD card mutex");
return -EBUSY;
}
ret = sdio_io_rw_direct(func->card, SDIO_IO_WRITE, func->num, reg,
write_val, NULL);
k_mutex_unlock(&func->card->lock);
return ret;
}
/**
* @brief Write byte to SDIO register, and read result
*
* Writes byte to SDIO register, and reads the register after write
* @param func: function to write to
* @param reg: register address to write to
* @param write_val: value to write to register
* @param read_val: filled with value read from register
* @retval 0 write succeeded
* @retval -EBUSY: card is busy with another request
* @retval -ETIMEDOUT: card write timed out
* @retval -EIO: I/O error
*/
int sdio_rw_byte(struct sdio_func *func, uint32_t reg, uint8_t write_val,
uint8_t *read_val)
{
int ret;
if ((func->card->type != CARD_SDIO) && (func->card->type != CARD_COMBO)) {
LOG_WRN("Card does not support SDIO commands");
return -ENOTSUP;
}
ret = k_mutex_lock(&func->card->lock, K_MSEC(CONFIG_SD_DATA_TIMEOUT));
if (ret) {
LOG_WRN("Could not get SD card mutex");
return -EBUSY;
}
ret = sdio_io_rw_direct(func->card, SDIO_IO_WRITE, func->num, reg,
write_val, read_val);
k_mutex_unlock(&func->card->lock);
return ret;
}
/**
* @brief Read bytes from SDIO fifo
*
* Reads bytes from SDIO register, treating it as a fifo. Reads will
* all be done from same address.
* @param func: function to read from
* @param reg: register address of fifo
* @param data: filled with data read from fifo
* @param len: length of data to read from card
* @retval 0 read succeeded
* @retval -EBUSY: card is busy with another request
* @retval -ETIMEDOUT: card read timed out
* @retval -EIO: I/O error
*/
int sdio_read_fifo(struct sdio_func *func, uint32_t reg, uint8_t *data,
uint32_t len)
{
int ret;
if ((func->card->type != CARD_SDIO) && (func->card->type != CARD_COMBO)) {
LOG_WRN("Card does not support SDIO commands");
return -ENOTSUP;
}
ret = k_mutex_lock(&func->card->lock, K_MSEC(CONFIG_SD_DATA_TIMEOUT));
if (ret) {
LOG_WRN("Could not get SD card mutex");
return -EBUSY;
}
ret = sdio_io_rw_extended_helper(func, SDIO_IO_READ, reg, false,
data, len);
k_mutex_unlock(&func->card->lock);
return ret;
}
/**
* @brief Write bytes to SDIO fifo
*
* Writes bytes to SDIO register, treating it as a fifo. Writes will
* all be done to same address.
* @param func: function to write to
* @param reg: register address of fifo
* @param data: data to write to fifo
* @param len: length of data to write to card
* @retval 0 write succeeded
* @retval -EBUSY: card is busy with another request
* @retval -ETIMEDOUT: card write timed out
* @retval -EIO: I/O error
*/
int sdio_write_fifo(struct sdio_func *func, uint32_t reg, uint8_t *data,
uint32_t len)
{
int ret;
if ((func->card->type != CARD_SDIO) && (func->card->type != CARD_COMBO)) {
LOG_WRN("Card does not support SDIO commands");
return -ENOTSUP;
}
ret = k_mutex_lock(&func->card->lock, K_MSEC(CONFIG_SD_DATA_TIMEOUT));
if (ret) {
LOG_WRN("Could not get SD card mutex");
return -EBUSY;
}
ret = sdio_io_rw_extended_helper(func, SDIO_IO_WRITE, reg, false,
data, len);
k_mutex_unlock(&func->card->lock);
return ret;
}
/**
* @brief Read blocks from SDIO fifo
*
* Reads blocks from SDIO register, treating it as a fifo. Reads will
* all be done from same address.
* @param func: function to read from
* @param reg: register address of fifo
* @param data: filled with data read from fifo
* @param blocks: number of blocks to read from fifo
* @retval 0 read succeeded
* @retval -EBUSY: card is busy with another request
* @retval -ETIMEDOUT: card read timed out
* @retval -EIO: I/O error
*/
int sdio_read_blocks_fifo(struct sdio_func *func, uint32_t reg, uint8_t *data,
uint32_t blocks)
{
int ret;
if ((func->card->type != CARD_SDIO) && (func->card->type != CARD_COMBO)) {
LOG_WRN("Card does not support SDIO commands");
return -ENOTSUP;
}
ret = k_mutex_lock(&func->card->lock, K_MSEC(CONFIG_SD_DATA_TIMEOUT));
if (ret) {
LOG_WRN("Could not get SD card mutex");
return -EBUSY;
}
ret = sdio_io_rw_extended(func->card, SDIO_IO_READ, func->num, reg,
false, data, blocks, func->block_size);
k_mutex_unlock(&func->card->lock);
return ret;
}
/**
* @brief Write blocks to SDIO fifo
*
* Writes blocks from SDIO register, treating it as a fifo. Writes will
* all be done to same address.
* @param func: function to write to
* @param reg: register address of fifo
* @param data: data to write to fifo
* @param blocks: number of blocks to write to fifo
* @retval 0 write succeeded
* @retval -EBUSY: card is busy with another request
* @retval -ETIMEDOUT: card write timed out
* @retval -EIO: I/O error
*/
int sdio_write_blocks_fifo(struct sdio_func *func, uint32_t reg, uint8_t *data,
uint32_t blocks)
{
int ret;
if ((func->card->type != CARD_SDIO) && (func->card->type != CARD_COMBO)) {
LOG_WRN("Card does not support SDIO commands");
return -ENOTSUP;
}
ret = k_mutex_lock(&func->card->lock, K_MSEC(CONFIG_SD_DATA_TIMEOUT));
if (ret) {
LOG_WRN("Could not get SD card mutex");
return -EBUSY;
}
ret = sdio_io_rw_extended(func->card, SDIO_IO_WRITE, func->num, reg,
false, data, blocks, func->block_size);
k_mutex_unlock(&func->card->lock);
return ret;
}
/**
* @brief Copy bytes from an SDIO card
*
* Copies bytes from an SDIO card, starting from provided address.
* @param func: function to read from
* @param reg: register address to start copy at
* @param data: buffer to copy data into
* @param len: length of data to read
* @retval 0 read succeeded
* @retval -EBUSY: card is busy with another request
* @retval -ETIMEDOUT: card read timed out
* @retval -EIO: I/O error
*/
int sdio_read_addr(struct sdio_func *func, uint32_t reg, uint8_t *data,
uint32_t len)
{
int ret;
if ((func->card->type != CARD_SDIO) && (func->card->type != CARD_COMBO)) {
LOG_WRN("Card does not support SDIO commands");
return -ENOTSUP;
}
ret = k_mutex_lock(&func->card->lock, K_MSEC(CONFIG_SD_DATA_TIMEOUT));
if (ret) {
LOG_WRN("Could not get SD card mutex");
return -EBUSY;
}
ret = sdio_io_rw_extended_helper(func, SDIO_IO_READ, reg, true,
data, len);
k_mutex_unlock(&func->card->lock);
return ret;
}
/**
* @brief Copy bytes to an SDIO card
*
* Copies bytes to an SDIO card, starting from provided address.
*
* @param func: function to write to
* @param reg: register address to start copy at
* @param data: buffer to copy data from
* @param len: length of data to write
* @retval 0 write succeeded
* @retval -EBUSY: card is busy with another request
* @retval -ETIMEDOUT: card write timed out
* @retval -EIO: I/O error
*/
int sdio_write_addr(struct sdio_func *func, uint32_t reg, uint8_t *data,
uint32_t len)
{
int ret;
if ((func->card->type != CARD_SDIO) && (func->card->type != CARD_COMBO)) {
LOG_WRN("Card does not support SDIO commands");
return -ENOTSUP;
}
ret = k_mutex_lock(&func->card->lock, K_MSEC(CONFIG_SD_DATA_TIMEOUT));
if (ret) {
LOG_WRN("Could not get SD card mutex");
return -EBUSY;
}
ret = sdio_io_rw_extended_helper(func, SDIO_IO_WRITE, reg, true,
data, len);
k_mutex_unlock(&func->card->lock);
return ret;
}