/* * Copyright 2022-2023 NXP * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #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, ®_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, ®_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->card_speed = SD_TIMING_SDR104; card->switch_caps.uhs_max_dtr = UHS_SDR104_MAX_DTR; } else if (card->host_props.host_caps.ddr50_support && (card->cccr_flags & SDIO_SUPPORT_DDR50)) { card->card_speed = SD_TIMING_DDR50; card->switch_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR; } else if (card->host_props.host_caps.sdr50_support && (card->cccr_flags & SDIO_SUPPORT_SDR50)) { card->card_speed = SD_TIMING_SDR50; card->switch_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR; } else if (card->host_props.host_caps.high_spd_support && (card->cccr_flags & SDIO_SUPPORT_HS)) { card->card_speed = SD_TIMING_HIGH_SPEED; card->switch_caps.hs_max_dtr = HS_MAX_DTR; } else { card->card_speed = SD_TIMING_DEFAULT; } } /* 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; uint32_t bus_clock; uint8_t speed_reg, target_speed; switch (card->card_speed) { /* Set bus clock speed */ case SD_TIMING_SDR104: bus_clock = MIN(card->host_props.f_max, card->switch_caps.uhs_max_dtr); target_speed = SDIO_CCCR_SPEED_SDR104; timing = SDHC_TIMING_SDR104; break; case SD_TIMING_DDR50: bus_clock = MIN(card->host_props.f_max, card->switch_caps.uhs_max_dtr); target_speed = SDIO_CCCR_SPEED_DDR50; timing = SDHC_TIMING_DDR50; break; case SD_TIMING_SDR50: bus_clock = MIN(card->host_props.f_max, card->switch_caps.uhs_max_dtr); target_speed = SDIO_CCCR_SPEED_SDR50; timing = SDHC_TIMING_SDR50; break; case SD_TIMING_HIGH_SPEED: bus_clock = MIN(card->host_props.f_max, card->switch_caps.hs_max_dtr); target_speed = SDIO_CCCR_SPEED_SDR25; timing = SDHC_TIMING_HS; break; case SD_TIMING_DEFAULT: bus_clock = MIN(card->host_props.f_max, MHZ(25)); target_speed = SDIO_CCCR_SPEED_SDR12; timing = SDHC_TIMING_LEGACY; 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 = bus_clock; 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, ®); 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, ®); 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, ®); 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; }