sd: Changes framework to support MMC
- Adds mmc.c - Edits sd.c to init and probe MMC - Adds mmc init to sd_init - Some functions from sdmmc.c should be in sd_ops because they can be used by both sdmmc and mmc. Signed-off-by: Declan Snyder <declan.snyder@nxp.com>
This commit is contained in:
parent
3bc095a810
commit
cad243d59e
7 changed files with 559 additions and 453 deletions
|
@ -7,4 +7,6 @@ if (CONFIG_SD_STACK)
|
||||||
zephyr_library_sources(sd.c sd_ops.c)
|
zephyr_library_sources(sd.c sd_ops.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_SDMMC_STACK sdmmc.c)
|
zephyr_library_sources_ifdef(CONFIG_SDMMC_STACK sdmmc.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_SDIO_STACK sdio.c)
|
zephyr_library_sources_ifdef(CONFIG_SDIO_STACK sdio.c)
|
||||||
|
zephyr_library_sources_ifdef(CONFIG_MMC_STACK mmc.c)
|
||||||
|
|
||||||
endif()
|
endif()
|
||||||
|
|
40
subsys/sd/mmc.c
Normal file
40
subsys/sd/mmc.c
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2022 NXP
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zephyr/drivers/sdhc.h>
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
#include <zephyr/sd/mmc.h>
|
||||||
|
#include <zephyr/sd/sd.h>
|
||||||
|
#include <zephyr/sd/sd_spec.h>
|
||||||
|
#include <zephyr/zephyr.h>
|
||||||
|
|
||||||
|
#include "sd_ops.h"
|
||||||
|
#include "sd_utils.h"
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize MMC card for use with subsystem
|
||||||
|
*/
|
||||||
|
int mmc_card_init(struct sd_card *card)
|
||||||
|
{
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
|
@ -15,6 +15,7 @@
|
||||||
#include "sd_utils.h"
|
#include "sd_utils.h"
|
||||||
#include "sd_init.h"
|
#include "sd_init.h"
|
||||||
|
|
||||||
|
|
||||||
LOG_MODULE_REGISTER(sd, CONFIG_SD_LOG_LEVEL);
|
LOG_MODULE_REGISTER(sd, CONFIG_SD_LOG_LEVEL);
|
||||||
|
|
||||||
/* Idle all cards on bus. Can be used to clear errors on cards */
|
/* Idle all cards on bus. Can be used to clear errors on cards */
|
||||||
|
@ -24,6 +25,7 @@ static inline int sd_idle(struct sd_card *card)
|
||||||
|
|
||||||
/* Reset card with CMD0 */
|
/* Reset card with CMD0 */
|
||||||
cmd.opcode = SD_GO_IDLE_STATE;
|
cmd.opcode = SD_GO_IDLE_STATE;
|
||||||
|
cmd.arg = 0x0;
|
||||||
cmd.response_type = (SD_RSP_TYPE_NONE | SD_SPI_RSP_TYPE_R1);
|
cmd.response_type = (SD_RSP_TYPE_NONE | SD_SPI_RSP_TYPE_R1);
|
||||||
cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT;
|
cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT;
|
||||||
return sdhc_request(card->sdhc, &cmd, NULL);
|
return sdhc_request(card->sdhc, &cmd, NULL);
|
||||||
|
@ -164,19 +166,25 @@ static int sd_init_io(struct sd_card *card)
|
||||||
static int sd_command_init(struct sd_card *card)
|
static int sd_command_init(struct sd_card *card)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We must wait 74 clock cycles, per SD spec, to use card after power
|
* We must wait 74 clock cycles, per SD spec, to use card after power
|
||||||
* on. At 400000KHz, this is a 185us delay. Wait 1ms to be safe.
|
* on. At 400000KHz, this is a 185us delay. Wait 1ms to be safe.
|
||||||
*/
|
*/
|
||||||
sd_delay(1);
|
sd_delay(1);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Start card initialization and identification
|
* Start card initialization and identification
|
||||||
* flow described in section 3.6 of SD specification
|
* flow described in section 3.6 of SD specification
|
||||||
|
* Common to SDIO and SDMMC. Some eMMC chips break the
|
||||||
|
* specification and expect something like this too.
|
||||||
*/
|
*/
|
||||||
ret = sd_common_init(card);
|
ret = sd_common_init(card);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_SDIO_STACK
|
#ifdef CONFIG_SDIO_STACK
|
||||||
/* Attempt to initialize SDIO card */
|
/* Attempt to initialize SDIO card */
|
||||||
if (!sdio_card_init(card)) {
|
if (!sdio_card_init(card)) {
|
||||||
|
@ -189,6 +197,16 @@ static int sd_command_init(struct sd_card *card)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_SDIO_STACK */
|
#endif /* CONFIG_SDIO_STACK */
|
||||||
|
#ifdef CONFIG_MMC_STACK
|
||||||
|
ret = sd_idle(card);
|
||||||
|
if (ret) {
|
||||||
|
LOG_ERR("Card error on CMD0");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (!mmc_card_init(card)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_MMC_STACK */
|
||||||
/* Unknown card type */
|
/* Unknown card type */
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,4 +15,6 @@ int sdio_card_init(struct sd_card *card);
|
||||||
|
|
||||||
int sdmmc_card_init(struct sd_card *card);
|
int sdmmc_card_init(struct sd_card *card);
|
||||||
|
|
||||||
|
int mmc_card_init(struct sd_card *card);
|
||||||
|
|
||||||
#endif /* ZEPHYR_SUBSYS_SD_INIT_PRIV_H_ */
|
#endif /* ZEPHYR_SUBSYS_SD_INIT_PRIV_H_ */
|
||||||
|
|
|
@ -4,34 +4,93 @@
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <zephyr/zephyr.h>
|
#include <zephyr/drivers/disk.h>
|
||||||
#include <zephyr/drivers/sdhc.h>
|
#include <zephyr/drivers/sdhc.h>
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
#include <zephyr/sd/sd.h>
|
#include <zephyr/sd/sd.h>
|
||||||
#include <zephyr/sd/sd_spec.h>
|
#include <zephyr/sd/sd_spec.h>
|
||||||
#include <zephyr/logging/log.h>
|
|
||||||
#include <zephyr/sys/byteorder.h>
|
#include <zephyr/sys/byteorder.h>
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
|
||||||
#include "sd_utils.h"
|
#include "sd_utils.h"
|
||||||
|
|
||||||
LOG_MODULE_DECLARE(sd, CONFIG_SD_LOG_LEVEL);
|
LOG_MODULE_DECLARE(sd, CONFIG_SD_LOG_LEVEL);
|
||||||
|
|
||||||
static inline void sdmmc_decode_csd(struct sd_csd *csd,
|
/* Read card status. Return 0 if card is inactive */
|
||||||
uint32_t *raw_csd, uint32_t *blk_count, uint32_t *blk_size)
|
int sdmmc_read_status(struct sd_card *card)
|
||||||
|
{
|
||||||
|
struct sdhc_command cmd = {0};
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
cmd.opcode = SD_SEND_STATUS;
|
||||||
|
if (!card->host_props.is_spi) {
|
||||||
|
cmd.arg = (card->relative_addr << 16U);
|
||||||
|
}
|
||||||
|
cmd.response_type = (SD_RSP_TYPE_R1 | SD_SPI_RSP_TYPE_R2);
|
||||||
|
cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT;
|
||||||
|
|
||||||
|
ret = sdhc_request(card->sdhc, &cmd, NULL);
|
||||||
|
if (ret) {
|
||||||
|
return SD_RETRY;
|
||||||
|
}
|
||||||
|
if (card->host_props.is_spi) {
|
||||||
|
/* Check R2 response bits */
|
||||||
|
if ((cmd.response[0U] & SDHC_SPI_R2_CARD_LOCKED) ||
|
||||||
|
(cmd.response[0U] & SDHC_SPI_R2_UNLOCK_FAIL)) {
|
||||||
|
return -EACCES;
|
||||||
|
} else if ((cmd.response[0U] & SDHC_SPI_R2_WP_VIOLATION) ||
|
||||||
|
(cmd.response[0U] & SDHC_SPI_R2_ERASE_PARAM) ||
|
||||||
|
(cmd.response[0U] & SDHC_SPI_R2_OUT_OF_RANGE)) {
|
||||||
|
return -EINVAL;
|
||||||
|
} else if ((cmd.response[0U] & SDHC_SPI_R2_ERR) ||
|
||||||
|
(cmd.response[0U] & SDHC_SPI_R2_CC_ERR) ||
|
||||||
|
(cmd.response[0U] & SDHC_SPI_R2_ECC_FAIL)) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
/* Otherwise, no error in R2 response */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* Otherwise, check native card response */
|
||||||
|
if ((cmd.response[0U] & SD_R1_RDY_DATA) &&
|
||||||
|
(SD_R1_CURRENT_STATE(cmd.response[0U]) == SDMMC_R1_TRANSFER)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* Valid response, the card is busy */
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Waits for SD card to be ready for data. Returns 0 if card is ready */
|
||||||
|
int sdmmc_wait_ready(struct sd_card *card)
|
||||||
|
{
|
||||||
|
int ret, timeout = CONFIG_SD_DATA_TIMEOUT * 1000;
|
||||||
|
bool busy = true;
|
||||||
|
|
||||||
|
do {
|
||||||
|
busy = sdhc_card_busy(card->sdhc);
|
||||||
|
if (!busy) {
|
||||||
|
/* Check card status */
|
||||||
|
ret = sd_retry(sdmmc_read_status, card, CONFIG_SD_RETRY_COUNT);
|
||||||
|
busy = (ret != 0);
|
||||||
|
} else {
|
||||||
|
/* Delay 125us before polling again */
|
||||||
|
k_busy_wait(125);
|
||||||
|
timeout -= 125;
|
||||||
|
}
|
||||||
|
} while (busy && (timeout > 0));
|
||||||
|
return busy;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void sdmmc_decode_csd(struct sd_csd *csd, uint32_t *raw_csd, uint32_t *blk_count,
|
||||||
|
uint32_t *blk_size)
|
||||||
{
|
{
|
||||||
uint32_t tmp_blk_count, tmp_blk_size;
|
uint32_t tmp_blk_count, tmp_blk_size;
|
||||||
|
|
||||||
csd->csd_structure = (uint8_t)((raw_csd[3U] &
|
csd->csd_structure = (uint8_t)((raw_csd[3U] & 0xC0000000U) >> 30U);
|
||||||
0xC0000000U) >> 30U);
|
csd->read_time1 = (uint8_t)((raw_csd[3U] & 0xFF0000U) >> 16U);
|
||||||
csd->read_time1 = (uint8_t)((raw_csd[3U] &
|
csd->read_time2 = (uint8_t)((raw_csd[3U] & 0xFF00U) >> 8U);
|
||||||
0xFF0000U) >> 16U);
|
csd->xfer_rate = (uint8_t)(raw_csd[3U] & 0xFFU);
|
||||||
csd->read_time2 = (uint8_t)((raw_csd[3U] &
|
csd->cmd_class = (uint16_t)((raw_csd[2U] & 0xFFF00000U) >> 20U);
|
||||||
0xFF00U) >> 8U);
|
csd->read_blk_len = (uint8_t)((raw_csd[2U] & 0xF0000U) >> 16U);
|
||||||
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) {
|
if (raw_csd[2U] & 0x8000U) {
|
||||||
csd->flags |= SD_CSD_READ_BLK_PARTIAL_FLAG;
|
csd->flags |= SD_CSD_READ_BLK_PARTIAL_FLAG;
|
||||||
}
|
}
|
||||||
|
@ -47,24 +106,16 @@ static inline void sdmmc_decode_csd(struct sd_csd *csd,
|
||||||
|
|
||||||
switch (csd->csd_structure) {
|
switch (csd->csd_structure) {
|
||||||
case 0:
|
case 0:
|
||||||
csd->device_size = (uint32_t)((raw_csd[2U] &
|
csd->device_size = (uint32_t)((raw_csd[2U] & 0x3FFU) << 2U);
|
||||||
0x3FFU) << 2U);
|
csd->device_size |= (uint32_t)((raw_csd[1U] & 0xC0000000U) >> 30U);
|
||||||
csd->device_size |= (uint32_t)((raw_csd[1U] &
|
csd->read_current_min = (uint8_t)((raw_csd[1U] & 0x38000000U) >> 27U);
|
||||||
0xC0000000U) >> 30U);
|
csd->read_current_max = (uint8_t)((raw_csd[1U] & 0x7000000U) >> 24U);
|
||||||
csd->read_current_min = (uint8_t)((raw_csd[1U] &
|
csd->write_current_min = (uint8_t)((raw_csd[1U] & 0xE00000U) >> 20U);
|
||||||
0x38000000U) >> 27U);
|
csd->write_current_max = (uint8_t)((raw_csd[1U] & 0x1C0000U) >> 18U);
|
||||||
csd->read_current_max = (uint8_t)((raw_csd[1U] &
|
csd->dev_size_mul = (uint8_t)((raw_csd[1U] & 0x38000U) >> 15U);
|
||||||
0x7000000U) >> 24U);
|
|
||||||
csd->write_current_min = (uint8_t)((raw_csd[1U] &
|
|
||||||
0xE00000U) >> 20U);
|
|
||||||
csd->write_current_max = (uint8_t)((raw_csd[1U] &
|
|
||||||
0x1C0000U) >> 18U);
|
|
||||||
csd->dev_size_mul = (uint8_t)((raw_csd[1U] &
|
|
||||||
0x38000U) >> 15U);
|
|
||||||
|
|
||||||
/* Get card total block count and block size. */
|
/* Get card total block count and block size. */
|
||||||
tmp_blk_count = ((csd->device_size + 1U) <<
|
tmp_blk_count = ((csd->device_size + 1U) << (csd->dev_size_mul + 2U));
|
||||||
(csd->dev_size_mul + 2U));
|
|
||||||
tmp_blk_size = (1U << (csd->read_blk_len));
|
tmp_blk_size = (1U << (csd->read_blk_len));
|
||||||
if (tmp_blk_size != SDMMC_DEFAULT_BLOCK_SIZE) {
|
if (tmp_blk_size != SDMMC_DEFAULT_BLOCK_SIZE) {
|
||||||
tmp_blk_count = (tmp_blk_count * tmp_blk_size);
|
tmp_blk_count = (tmp_blk_count * tmp_blk_size);
|
||||||
|
@ -81,10 +132,8 @@ static inline void sdmmc_decode_csd(struct sd_csd *csd,
|
||||||
case 1:
|
case 1:
|
||||||
tmp_blk_size = SDMMC_DEFAULT_BLOCK_SIZE;
|
tmp_blk_size = SDMMC_DEFAULT_BLOCK_SIZE;
|
||||||
|
|
||||||
csd->device_size = (uint32_t)((raw_csd[2U] &
|
csd->device_size = (uint32_t)((raw_csd[2U] & 0x3FU) << 16U);
|
||||||
0x3FU) << 16U);
|
csd->device_size |= (uint32_t)((raw_csd[1U] & 0xFFFF0000U) >> 16U);
|
||||||
csd->device_size |= (uint32_t)((raw_csd[1U] &
|
|
||||||
0xFFFF0000U) >> 16U);
|
|
||||||
|
|
||||||
tmp_blk_count = ((csd->device_size + 1U) * 1024U);
|
tmp_blk_count = ((csd->device_size + 1U) * 1024U);
|
||||||
if (blk_count) {
|
if (blk_count) {
|
||||||
|
@ -101,14 +150,10 @@ static inline void sdmmc_decode_csd(struct sd_csd *csd,
|
||||||
if ((uint8_t)((raw_csd[1U] & 0x4000U) >> 14U)) {
|
if ((uint8_t)((raw_csd[1U] & 0x4000U) >> 14U)) {
|
||||||
csd->flags |= SD_CSD_ERASE_BLK_EN_FLAG;
|
csd->flags |= SD_CSD_ERASE_BLK_EN_FLAG;
|
||||||
}
|
}
|
||||||
csd->erase_size = (uint8_t)((raw_csd[1U] &
|
csd->erase_size = (uint8_t)((raw_csd[1U] & 0x3F80U) >> 7U);
|
||||||
0x3F80U) >> 7U);
|
csd->write_prtect_size = (uint8_t)(raw_csd[1U] & 0x7FU);
|
||||||
csd->write_prtect_size = (uint8_t)(raw_csd[1U] &
|
csd->write_speed_factor = (uint8_t)((raw_csd[0U] & 0x1C000000U) >> 26U);
|
||||||
0x7FU);
|
csd->write_blk_len = (uint8_t)((raw_csd[0U] & 0x3C00000U) >> 22U);
|
||||||
csd->write_speed_factor = (uint8_t)((raw_csd[0U] &
|
|
||||||
0x1C000000U) >> 26U);
|
|
||||||
csd->write_blk_len = (uint8_t)((raw_csd[0U] &
|
|
||||||
0x3C00000U) >> 22U);
|
|
||||||
if ((uint8_t)((raw_csd[0U] & 0x200000U) >> 21U)) {
|
if ((uint8_t)((raw_csd[0U] & 0x200000U) >> 21U)) {
|
||||||
csd->flags |= SD_CSD_WRITE_BLK_PARTIAL_FLAG;
|
csd->flags |= SD_CSD_WRITE_BLK_PARTIAL_FLAG;
|
||||||
}
|
}
|
||||||
|
@ -127,8 +172,7 @@ static inline void sdmmc_decode_csd(struct sd_csd *csd,
|
||||||
csd->file_fmt = (uint8_t)((raw_csd[0U] & 0xC00U) >> 10U);
|
csd->file_fmt = (uint8_t)((raw_csd[0U] & 0xC00U) >> 10U);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void sdmmc_decode_cid(struct sd_cid *cid,
|
static inline void sdmmc_decode_cid(struct sd_cid *cid, uint32_t *raw_cid)
|
||||||
uint32_t *raw_cid)
|
|
||||||
{
|
{
|
||||||
cid->manufacturer = (uint8_t)((raw_cid[3U] & 0xFF000000U) >> 24U);
|
cid->manufacturer = (uint8_t)((raw_cid[3U] & 0xFF000000U) >> 24U);
|
||||||
cid->application = (uint16_t)((raw_cid[3U] & 0xFFFF00U) >> 8U);
|
cid->application = (uint16_t)((raw_cid[3U] & 0xFFFF00U) >> 8U);
|
||||||
|
@ -148,8 +192,7 @@ static inline void sdmmc_decode_cid(struct sd_cid *cid,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reads card id/csd register (in SPI mode) */
|
/* Reads card id/csd register (in SPI mode) */
|
||||||
static int sdmmc_spi_read_cxd(struct sd_card *card,
|
static int sdmmc_spi_read_cxd(struct sd_card *card, uint32_t opcode, uint32_t *cxd)
|
||||||
uint32_t opcode, uint32_t *cxd)
|
|
||||||
{
|
{
|
||||||
struct sdhc_command cmd = {0};
|
struct sdhc_command cmd = {0};
|
||||||
struct sdhc_data data = {0};
|
struct sdhc_data data = {0};
|
||||||
|
@ -174,14 +217,13 @@ static int sdmmc_spi_read_cxd(struct sd_card *card,
|
||||||
}
|
}
|
||||||
/* Swap endianness of CXD */
|
/* Swap endianness of CXD */
|
||||||
for (i = 0; i < 4; i++) {
|
for (i = 0; i < 4; i++) {
|
||||||
cxd[3-i] = sys_be32_to_cpu(cxd_be[i]);
|
cxd[3 - i] = sys_be32_to_cpu(cxd_be[i]);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reads card id/csd register (native SD mode */
|
/* Reads card id/csd register (native SD mode */
|
||||||
static int sdmmc_read_cxd(struct sd_card *card,
|
static int sdmmc_read_cxd(struct sd_card *card, uint32_t opcode, uint32_t rca, uint32_t *cxd)
|
||||||
uint32_t opcode, uint32_t rca, uint32_t *cxd)
|
|
||||||
{
|
{
|
||||||
struct sdhc_command cmd = {0};
|
struct sdhc_command cmd = {0};
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -196,13 +238,11 @@ static int sdmmc_read_cxd(struct sd_card *card,
|
||||||
LOG_DBG("CMD%d failed: %d", opcode, ret);
|
LOG_DBG("CMD%d failed: %d", opcode, ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* CSD/CID is 16 bytes */
|
/* CSD/CID is 16 bytes */
|
||||||
memcpy(cxd, cmd.response, 16);
|
memcpy(cxd, cmd.response, 16);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Read card specific data register */
|
/* Read card specific data register */
|
||||||
int sdmmc_read_csd(struct sd_card *card)
|
int sdmmc_read_csd(struct sd_card *card)
|
||||||
{
|
{
|
||||||
|
@ -222,21 +262,20 @@ int sdmmc_read_csd(struct sd_card *card)
|
||||||
if (ret) {
|
if (ret) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
sdmmc_decode_csd(&card_csd, csd, &card->block_count, &card->block_size);
|
||||||
sdmmc_decode_csd(&card_csd, csd,
|
LOG_DBG("Card block count %d, block size %d", card->block_count, card->block_size);
|
||||||
&card->block_count, &card->block_size);
|
|
||||||
LOG_DBG("Card block count %d, block size %d",
|
|
||||||
card->block_count, card->block_size);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reads card identification register, and decodes it */
|
/* Reads card identification register, and decodes it */
|
||||||
int sdmmc_read_cid(struct sd_card *card)
|
int card_read_cid(struct sd_card *card)
|
||||||
{
|
{
|
||||||
uint32_t cid[4];
|
uint32_t cid[4];
|
||||||
int ret;
|
int ret;
|
||||||
|
#if defined(CONFIG_SDMMC_STACK) || defined(CONFIG_SDIO_STACK)
|
||||||
/* Keep CID on stack for reduced RAM usage */
|
/* Keep CID on stack for reduced RAM usage */
|
||||||
struct sd_cid card_cid;
|
struct sd_cid card_cid;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (card->host_props.is_spi && IS_ENABLED(CONFIG_SDHC_SUPPORTS_SPI_MODE)) {
|
if (card->host_props.is_spi && IS_ENABLED(CONFIG_SDHC_SUPPORTS_SPI_MODE)) {
|
||||||
ret = sdmmc_spi_read_cxd(card, SD_SEND_CID, cid);
|
ret = sdmmc_spi_read_cxd(card, SD_SEND_CID, cid);
|
||||||
|
@ -250,11 +289,19 @@ int sdmmc_read_cid(struct sd_card *card)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(CONFIG_MMC_STACK)
|
||||||
|
if (card->type == CARD_MMC) {
|
||||||
|
LOG_INF("CID decoding not supported for MMC");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if defined(CONFIG_SDMMC_STACK) || defined(CONFIG_SDIO_STACK)
|
||||||
/* Decode SD CID */
|
/* Decode SD CID */
|
||||||
sdmmc_decode_cid(&card_cid, cid);
|
sdmmc_decode_cid(&card_cid, cid);
|
||||||
LOG_DBG("Card MID: 0x%x, OID: %c%c", card_cid.manufacturer,
|
LOG_DBG("Card MID: 0x%x, OID: %c%c", card_cid.manufacturer,
|
||||||
((char *)&card_cid.application)[0],
|
((char *)&card_cid.application)[0], ((char *)&card_cid.application)[1]);
|
||||||
((char *)&card_cid.application)[1]);
|
#endif
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -379,7 +426,7 @@ int sdmmc_select_card(struct sd_card *card)
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
cmd.opcode = SD_SELECT_CARD;
|
cmd.opcode = SD_SELECT_CARD;
|
||||||
cmd.arg = (card->relative_addr << 16U);
|
cmd.arg = ((card->relative_addr) << 16U);
|
||||||
cmd.response_type = SD_RSP_TYPE_R1;
|
cmd.response_type = SD_RSP_TYPE_R1;
|
||||||
cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT;
|
cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT;
|
||||||
|
|
||||||
|
@ -395,3 +442,335 @@ int sdmmc_select_card(struct sd_card *card)
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Helper to send SD app command */
|
||||||
|
int card_app_command(struct sd_card *card, int relative_card_address)
|
||||||
|
{
|
||||||
|
struct sdhc_command cmd = {0};
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
cmd.opcode = SD_APP_CMD;
|
||||||
|
cmd.arg = relative_card_address << 16U;
|
||||||
|
cmd.response_type = (SD_RSP_TYPE_R1 | SD_SPI_RSP_TYPE_R1);
|
||||||
|
cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT;
|
||||||
|
ret = sdhc_request(card->sdhc, &cmd, NULL);
|
||||||
|
if (ret) {
|
||||||
|
/* We want to retry transmission */
|
||||||
|
return SD_RETRY;
|
||||||
|
}
|
||||||
|
ret = sd_check_response(&cmd);
|
||||||
|
if (ret) {
|
||||||
|
LOG_WRN("SD app command failed with R1 response of 0x%X", cmd.response[0]);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
/* Check application command flag to determine if card is ready for APP CMD */
|
||||||
|
if ((!card->host_props.is_spi) && !(cmd.response[0U] & SD_R1_APP_CMD)) {
|
||||||
|
/* Command succeeded, but card not ready for app command. No APP CMD support */
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int card_read(struct sd_card *card, uint8_t *rbuf, uint32_t start_block, uint32_t num_blocks)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct sdhc_command cmd = {0};
|
||||||
|
struct sdhc_data data = {0};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note: The SD specification allows for CMD23 to be sent before a
|
||||||
|
* transfer in order to set the block length (often preferable).
|
||||||
|
* The specification also requires that CMD12 be sent to stop a transfer.
|
||||||
|
* However, the host specification defines support for "Auto CMD23" and
|
||||||
|
* "Auto CMD12", where the host sends CMD23 and CMD12 automatically to
|
||||||
|
* remove the overhead of interrupts in software from sending these
|
||||||
|
* commands. Therefore, we will not handle CMD12 or CMD23 at this layer.
|
||||||
|
* The host SDHC driver is expected to recognize CMD17, CMD18, CMD24,
|
||||||
|
* and CMD25 as special read/write commands and handle CMD23 and
|
||||||
|
* CMD12 appropriately.
|
||||||
|
*/
|
||||||
|
cmd.opcode = (num_blocks == 1U) ? SD_READ_SINGLE_BLOCK : SD_READ_MULTIPLE_BLOCK;
|
||||||
|
if (!(card->flags & SD_HIGH_CAPACITY_FLAG)) {
|
||||||
|
/* SDSC cards require block size in bytes, not blocks */
|
||||||
|
cmd.arg = start_block * card->block_size;
|
||||||
|
} else {
|
||||||
|
cmd.arg = start_block;
|
||||||
|
}
|
||||||
|
cmd.response_type = (SD_RSP_TYPE_R1 | SD_SPI_RSP_TYPE_R1);
|
||||||
|
cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT;
|
||||||
|
cmd.retries = CONFIG_SD_DATA_RETRIES;
|
||||||
|
|
||||||
|
data.block_addr = start_block;
|
||||||
|
data.block_size = card->block_size;
|
||||||
|
data.blocks = num_blocks;
|
||||||
|
data.data = rbuf;
|
||||||
|
data.timeout_ms = CONFIG_SD_DATA_TIMEOUT;
|
||||||
|
|
||||||
|
LOG_DBG("READ: Sector = %u, Count = %u", start_block, num_blocks);
|
||||||
|
|
||||||
|
ret = sdhc_request(card->sdhc, &cmd, &data);
|
||||||
|
if (ret) {
|
||||||
|
LOG_ERR("Failed to read from SDMMC %d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Verify card is back in transfer state after read */
|
||||||
|
ret = sdmmc_wait_ready(card);
|
||||||
|
if (ret) {
|
||||||
|
LOG_ERR("Card did not return to ready state");
|
||||||
|
k_mutex_unlock(&card->lock);
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reads data from SD card memory card */
|
||||||
|
int card_read_blocks(struct sd_card *card, uint8_t *rbuf, uint32_t start_block, uint32_t num_blocks)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
uint32_t rlen;
|
||||||
|
uint32_t sector;
|
||||||
|
uint8_t *buf_offset;
|
||||||
|
|
||||||
|
if ((start_block + num_blocks) > card->block_count) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (card->type == CARD_SDIO) {
|
||||||
|
LOG_WRN("SDIO does not support MMC commands");
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
ret = k_mutex_lock(&card->lock, K_NO_WAIT);
|
||||||
|
if (ret) {
|
||||||
|
LOG_WRN("Could not get SD card mutex");
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the buffer we are provided with is aligned, we can use it
|
||||||
|
* directly. Otherwise, we need to use the card's internal buffer
|
||||||
|
* and memcpy the data back out
|
||||||
|
*/
|
||||||
|
if ((((uintptr_t)rbuf) & (CONFIG_SDHC_BUFFER_ALIGNMENT - 1)) != 0) {
|
||||||
|
/* lower bits of address are set, not aligned. Use internal buffer */
|
||||||
|
LOG_DBG("Unaligned buffer access to SD card may incur performance penalty");
|
||||||
|
if (sizeof(card->card_buffer) < card->block_size) {
|
||||||
|
LOG_ERR("Card buffer size needs to be increased for "
|
||||||
|
"unaligned writes to work");
|
||||||
|
k_mutex_unlock(&card->lock);
|
||||||
|
return -ENOBUFS;
|
||||||
|
}
|
||||||
|
rlen = sizeof(card->card_buffer) / card->block_size;
|
||||||
|
sector = 0;
|
||||||
|
buf_offset = rbuf;
|
||||||
|
while (sector < num_blocks) {
|
||||||
|
/* Read from disk to card buffer */
|
||||||
|
ret = card_read(card, card->card_buffer, sector + start_block, rlen);
|
||||||
|
if (ret) {
|
||||||
|
LOG_ERR("Write failed");
|
||||||
|
k_mutex_unlock(&card->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
/* Copy data from card buffer */
|
||||||
|
memcpy(buf_offset, card->card_buffer, rlen * card->block_size);
|
||||||
|
/* Increase sector count and buffer offset */
|
||||||
|
sector += rlen;
|
||||||
|
buf_offset += rlen * card->block_size;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Aligned buffers can be used directly */
|
||||||
|
ret = card_read(card, rbuf, start_block, num_blocks);
|
||||||
|
if (ret) {
|
||||||
|
LOG_ERR("Card read failed");
|
||||||
|
k_mutex_unlock(&card->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
k_mutex_unlock(&card->lock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sends ACMD22 (number of written blocks) to see how many blocks were written
|
||||||
|
* to a card
|
||||||
|
*/
|
||||||
|
static int card_query_written(struct sd_card *card, uint32_t *num_written)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct sdhc_command cmd = {0};
|
||||||
|
struct sdhc_data data = {0};
|
||||||
|
uint32_t *blocks = (uint32_t *)card->card_buffer;
|
||||||
|
|
||||||
|
ret = card_app_command(card, card->relative_addr);
|
||||||
|
if (ret) {
|
||||||
|
LOG_DBG("App CMD for ACMD22 failed");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.opcode = SD_APP_SEND_NUM_WRITTEN_BLK;
|
||||||
|
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 = 4U;
|
||||||
|
data.blocks = 1U;
|
||||||
|
data.data = blocks;
|
||||||
|
data.timeout_ms = CONFIG_SD_DATA_TIMEOUT;
|
||||||
|
|
||||||
|
ret = sdhc_request(card->sdhc, &cmd, &data);
|
||||||
|
if (ret) {
|
||||||
|
LOG_DBG("ACMD22 failed: %d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
ret = sd_check_response(&cmd);
|
||||||
|
if (ret) {
|
||||||
|
LOG_DBG("ACMD22 reports error");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Decode blocks */
|
||||||
|
*num_written = sys_be32_to_cpu(blocks[0]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int card_write(struct sd_card *card, const uint8_t *wbuf, uint32_t start_block,
|
||||||
|
uint32_t num_blocks)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
uint32_t blocks;
|
||||||
|
struct sdhc_command cmd = {0};
|
||||||
|
struct sdhc_data data = {0};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* See the note in card_read() above. We will not issue CMD23
|
||||||
|
* or CMD12, and expect the host to handle those details.
|
||||||
|
*/
|
||||||
|
cmd.opcode = (num_blocks == 1) ? SD_WRITE_SINGLE_BLOCK : SD_WRITE_MULTIPLE_BLOCK;
|
||||||
|
if (!(card->flags & SD_HIGH_CAPACITY_FLAG)) {
|
||||||
|
/* SDSC cards require block size in bytes, not blocks */
|
||||||
|
cmd.arg = start_block * card->block_size;
|
||||||
|
} else {
|
||||||
|
cmd.arg = start_block;
|
||||||
|
}
|
||||||
|
cmd.response_type = (SD_RSP_TYPE_R1 | SD_SPI_RSP_TYPE_R1);
|
||||||
|
cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT;
|
||||||
|
cmd.retries = CONFIG_SD_DATA_RETRIES;
|
||||||
|
|
||||||
|
data.block_addr = start_block;
|
||||||
|
data.block_size = card->block_size;
|
||||||
|
data.blocks = num_blocks;
|
||||||
|
data.data = (uint8_t *)wbuf;
|
||||||
|
data.timeout_ms = CONFIG_SD_DATA_TIMEOUT;
|
||||||
|
|
||||||
|
LOG_DBG("WRITE: Sector = %u, Count = %u", start_block, num_blocks);
|
||||||
|
|
||||||
|
ret = sdhc_request(card->sdhc, &cmd, &data);
|
||||||
|
if (ret) {
|
||||||
|
LOG_DBG("Write failed: %d", ret);
|
||||||
|
ret = sdmmc_wait_ready(card);
|
||||||
|
if (ret) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
/* Query card to see how many blocks were actually written */
|
||||||
|
ret = card_query_written(card, &blocks);
|
||||||
|
if (ret) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
LOG_ERR("Only %d blocks of %d were written", blocks, num_blocks);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
/* Verify card is back in transfer state after write */
|
||||||
|
ret = sdmmc_wait_ready(card);
|
||||||
|
if (ret) {
|
||||||
|
LOG_ERR("Card did not return to ready state");
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Writes data to SD card memory card */
|
||||||
|
int card_write_blocks(struct sd_card *card, const uint8_t *wbuf, uint32_t start_block,
|
||||||
|
uint32_t num_blocks)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
uint32_t wlen;
|
||||||
|
uint32_t sector;
|
||||||
|
const uint8_t *buf_offset;
|
||||||
|
|
||||||
|
if ((start_block + num_blocks) > card->block_count) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (card->type == CARD_SDIO) {
|
||||||
|
LOG_WRN("SDIO does not support MMC commands");
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
ret = k_mutex_lock(&card->lock, K_NO_WAIT);
|
||||||
|
if (ret) {
|
||||||
|
LOG_WRN("Could not get SD card mutex");
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* If the buffer we are provided with is aligned, we can use it
|
||||||
|
* directly. Otherwise, we need to use the card's internal buffer
|
||||||
|
* and memcpy the data back out
|
||||||
|
*/
|
||||||
|
if ((((uintptr_t)wbuf) & (CONFIG_SDHC_BUFFER_ALIGNMENT - 1)) != 0) {
|
||||||
|
/* lower bits of address are set, not aligned. Use internal buffer */
|
||||||
|
LOG_DBG("Unaligned buffer access to SD card may incur performance penalty");
|
||||||
|
if (sizeof(card->card_buffer) < card->block_size) {
|
||||||
|
LOG_ERR("Card buffer size needs to be increased for "
|
||||||
|
"unaligned writes to work");
|
||||||
|
k_mutex_unlock(&card->lock);
|
||||||
|
return -ENOBUFS;
|
||||||
|
}
|
||||||
|
wlen = sizeof(card->card_buffer) / card->block_size;
|
||||||
|
sector = 0;
|
||||||
|
buf_offset = wbuf;
|
||||||
|
while (sector < num_blocks) {
|
||||||
|
/* Copy data into card buffer */
|
||||||
|
memcpy(card->card_buffer, buf_offset, wlen * card->block_size);
|
||||||
|
/* Write card buffer to disk */
|
||||||
|
ret = card_write(card, card->card_buffer, sector + start_block, wlen);
|
||||||
|
if (ret) {
|
||||||
|
LOG_ERR("Write failed");
|
||||||
|
k_mutex_unlock(&card->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
/* Increase sector count and buffer offset */
|
||||||
|
sector += wlen;
|
||||||
|
buf_offset += wlen * card->block_size;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* We can use aligned buffers directly */
|
||||||
|
ret = card_write(card, wbuf, start_block, num_blocks);
|
||||||
|
if (ret) {
|
||||||
|
LOG_ERR("Write failed");
|
||||||
|
k_mutex_unlock(&card->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
k_mutex_unlock(&card->lock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* IO Control handler for SD MMC */
|
||||||
|
int card_ioctl(struct sd_card *card, uint8_t cmd, void *buf)
|
||||||
|
{
|
||||||
|
switch (cmd) {
|
||||||
|
case DISK_IOCTL_GET_SECTOR_COUNT:
|
||||||
|
(*(uint32_t *)buf) = card->block_count;
|
||||||
|
break;
|
||||||
|
case DISK_IOCTL_GET_SECTOR_SIZE:
|
||||||
|
case DISK_IOCTL_GET_ERASE_BLOCK_SZ:
|
||||||
|
(*(uint32_t *)buf) = card->block_size;
|
||||||
|
break;
|
||||||
|
case DISK_IOCTL_CTRL_SYNC:
|
||||||
|
/* Ensure card is not busy with data write.
|
||||||
|
* Note that SD stack does not support enabling caching, so
|
||||||
|
* cache flush is not required here
|
||||||
|
*/
|
||||||
|
return sdmmc_wait_ready(card);
|
||||||
|
default:
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ int sdmmc_switch_voltage(struct sd_card *card);
|
||||||
/*
|
/*
|
||||||
* Reads card identification register, and decodes it
|
* Reads card identification register, and decodes it
|
||||||
*/
|
*/
|
||||||
int sdmmc_read_cid(struct sd_card *card);
|
int card_read_cid(struct sd_card *card);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Read card specific data register
|
* Read card specific data register
|
||||||
|
@ -45,4 +45,18 @@ static inline int sdmmc_host_uhs(struct sdhc_host_props *props)
|
||||||
& (props->host_caps.vol_180_support);
|
& (props->host_caps.vol_180_support);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int card_ioctl(struct sd_card *card, uint8_t cmd, void *buf);
|
||||||
|
|
||||||
|
int card_read_blocks(struct sd_card *card, uint8_t *rbuf,
|
||||||
|
uint32_t start_block, uint32_t num_blocks);
|
||||||
|
|
||||||
|
int card_write_blocks(struct sd_card *card, const uint8_t *wbuf,
|
||||||
|
uint32_t start_block, uint32_t num_blocks);
|
||||||
|
|
||||||
|
int card_app_command(struct sd_card *card, int relative_card_address);
|
||||||
|
|
||||||
|
int sdmmc_read_status(struct sd_card *card);
|
||||||
|
|
||||||
|
int sdmmc_wait_ready(struct sd_card *card);
|
||||||
|
|
||||||
#endif /* ZEPHYR_SUBSYS_SD_SD_OPS_H_ */
|
#endif /* ZEPHYR_SUBSYS_SD_SD_OPS_H_ */
|
||||||
|
|
|
@ -63,30 +63,23 @@ static inline void sdmmc_decode_scr(struct sd_scr *scr,
|
||||||
/* Helper to send SD app command */
|
/* Helper to send SD app command */
|
||||||
static int sdmmc_app_command(struct sd_card *card, int relative_card_address)
|
static int sdmmc_app_command(struct sd_card *card, int relative_card_address)
|
||||||
{
|
{
|
||||||
struct sdhc_command cmd = {0};
|
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;
|
int ret;
|
||||||
|
|
||||||
cmd.opcode = SD_APP_CMD;
|
cmd.opcode = SD_SPI_READ_OCR;
|
||||||
cmd.arg = relative_card_address << 16U;
|
cmd.arg = arg;
|
||||||
cmd.response_type = (SD_RSP_TYPE_R1 | SD_SPI_RSP_TYPE_R1);
|
cmd.response_type = SD_SPI_RSP_TYPE_R3;
|
||||||
cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT;
|
|
||||||
ret = sdhc_request(card->sdhc, &cmd, NULL);
|
ret = sdhc_request(card->sdhc, &cmd, NULL);
|
||||||
if (ret) {
|
|
||||||
/* We want to retry transmission */
|
card->ocr = cmd.response[1];
|
||||||
return SD_RETRY;
|
return ret;
|
||||||
}
|
|
||||||
ret = sd_check_response(&cmd);
|
|
||||||
if (ret) {
|
|
||||||
LOG_WRN("SD app command failed with R1 response of 0x%X",
|
|
||||||
cmd.response[0]);
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
/* Check application command flag to determine if card is ready for APP CMD */
|
|
||||||
if ((!card->host_props.is_spi) && !(cmd.response[0U] & SD_R1_APP_CMD)) {
|
|
||||||
/* Command succeeded, but card not ready for app command. No APP CMD support */
|
|
||||||
return -ENOTSUP;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sends OCR to card using ACMD41 */
|
/* Sends OCR to card using ACMD41 */
|
||||||
|
@ -557,11 +550,16 @@ int sdmmc_card_init(struct sd_card *card)
|
||||||
int ret;
|
int ret;
|
||||||
uint32_t ocr_arg = 0U;
|
uint32_t ocr_arg = 0U;
|
||||||
|
|
||||||
/* First send a probing OCR using ACMD41. Note that SPI cards also
|
/* First send a probing OCR */
|
||||||
* accept CMD58 at this point, but we skip this command as it is not
|
if (card->host_props.is_spi && IS_ENABLED(CONFIG_SDHC_SUPPORTS_SPI_MODE)) {
|
||||||
* required by the spec.
|
/* Probe SPI card with CMD58*/
|
||||||
*/
|
ret = sdmmc_spi_send_ocr(card, ocr_arg);
|
||||||
ret = sdmmc_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) {
|
if (ret) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -569,9 +567,11 @@ int sdmmc_card_init(struct sd_card *card)
|
||||||
card->type = CARD_SDMMC;
|
card->type = CARD_SDMMC;
|
||||||
|
|
||||||
if (card->flags & SD_SDHC_FLAG) {
|
if (card->flags & SD_SDHC_FLAG) {
|
||||||
/* High capacity card. See if host supports 1.8V */
|
if (IS_ENABLED(CONFIG_SDHC_SUPPORTS_NATIVE_MODE)) {
|
||||||
if (card->host_props.host_caps.vol_180_support) {
|
/* High capacity card. See if host supports 1.8V */
|
||||||
ocr_arg |= SD_OCR_SWITCH_18_REQ_FLAG;
|
if (card->host_props.host_caps.vol_180_support) {
|
||||||
|
ocr_arg |= SD_OCR_SWITCH_18_REQ_FLAG;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/* Set host high capacity support flag */
|
/* Set host high capacity support flag */
|
||||||
ocr_arg |= SD_OCR_HOST_CAP_FLAG;
|
ocr_arg |= SD_OCR_HOST_CAP_FLAG;
|
||||||
|
@ -594,6 +594,13 @@ int sdmmc_card_init(struct sd_card *card)
|
||||||
LOG_ERR("Failed to query card OCR");
|
LOG_ERR("Failed to query card OCR");
|
||||||
return ret;
|
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 */
|
/* Check SD high capacity and 1.8V support flags */
|
||||||
if (card->ocr & SD_OCR_CARD_CAP_FLAG) {
|
if (card->ocr & SD_OCR_CARD_CAP_FLAG) {
|
||||||
card->flags |= SD_HIGH_CAPACITY_FLAG;
|
card->flags |= SD_HIGH_CAPACITY_FLAG;
|
||||||
|
@ -629,7 +636,7 @@ int sdmmc_card_init(struct sd_card *card)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Read the card's CID (card identification register) */
|
/* Read the card's CID (card identification register) */
|
||||||
ret = sdmmc_read_cid(card);
|
ret = card_read_cid(card);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -713,375 +720,19 @@ int sdmmc_card_init(struct sd_card *card)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read card status. Return 0 if card is inactive */
|
int sdmmc_ioctl(struct sd_card *card, uint8_t cmd, void *buf)
|
||||||
static int sdmmc_read_status(struct sd_card *card)
|
|
||||||
{
|
{
|
||||||
struct sdhc_command cmd = {0};
|
return card_ioctl(card, cmd, buf);
|
||||||
int ret;
|
|
||||||
|
|
||||||
cmd.opcode = SD_SEND_STATUS;
|
|
||||||
if (!card->host_props.is_spi) {
|
|
||||||
cmd.arg = (card->relative_addr << 16U);
|
|
||||||
}
|
|
||||||
cmd.response_type = (SD_RSP_TYPE_R1 | SD_SPI_RSP_TYPE_R2);
|
|
||||||
cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT;
|
|
||||||
|
|
||||||
ret = sdhc_request(card->sdhc, &cmd, NULL);
|
|
||||||
if (ret) {
|
|
||||||
return SD_RETRY;
|
|
||||||
}
|
|
||||||
if (card->host_props.is_spi) {
|
|
||||||
/* Check R2 response bits */
|
|
||||||
if ((cmd.response[0U] & SDHC_SPI_R2_CARD_LOCKED) ||
|
|
||||||
(cmd.response[0U] & SDHC_SPI_R2_UNLOCK_FAIL)) {
|
|
||||||
return -EACCES;
|
|
||||||
} else if ((cmd.response[0U] & SDHC_SPI_R2_WP_VIOLATION) ||
|
|
||||||
(cmd.response[0U] & SDHC_SPI_R2_ERASE_PARAM) ||
|
|
||||||
(cmd.response[0U] & SDHC_SPI_R2_OUT_OF_RANGE)) {
|
|
||||||
return -EINVAL;
|
|
||||||
} else if ((cmd.response[0U] & SDHC_SPI_R2_ERR) ||
|
|
||||||
(cmd.response[0U] & SDHC_SPI_R2_CC_ERR) ||
|
|
||||||
(cmd.response[0U] & SDHC_SPI_R2_ECC_FAIL)) {
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
/* Otherwise, no error in R2 response */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
/* Otherwise, check native card response */
|
|
||||||
if ((cmd.response[0U] & SD_R1_RDY_DATA) &&
|
|
||||||
(SD_R1_CURRENT_STATE(cmd.response[0U]) == SDMMC_R1_TRANSFER)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
/* Valid response, the card is busy */
|
|
||||||
return -EBUSY;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Waits for SD card to be ready for data. Returns 0 if card is ready */
|
|
||||||
static int sdmmc_wait_ready(struct sd_card *card)
|
|
||||||
{
|
|
||||||
int ret, timeout = CONFIG_SD_DATA_TIMEOUT * 1000;
|
|
||||||
bool busy = true;
|
|
||||||
|
|
||||||
do {
|
|
||||||
busy = sdhc_card_busy(card->sdhc);
|
|
||||||
if (!busy) {
|
|
||||||
/* Check card status */
|
|
||||||
ret = sd_retry(sdmmc_read_status, card, CONFIG_SD_RETRY_COUNT);
|
|
||||||
busy = (ret != 0);
|
|
||||||
} else {
|
|
||||||
/* Delay 125us before polling again */
|
|
||||||
k_busy_wait(125);
|
|
||||||
timeout -= 125;
|
|
||||||
}
|
|
||||||
} while (busy && (timeout > 0));
|
|
||||||
return busy;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sdmmc_read(struct sd_card *card, uint8_t *rbuf,
|
|
||||||
uint32_t start_block, uint32_t num_blocks)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
struct sdhc_command cmd = {0};
|
|
||||||
struct sdhc_data data = {0};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Note: The SD specification allows for CMD23 to be sent before a
|
|
||||||
* transfer in order to set the block length (often preferable).
|
|
||||||
* The specification also requires that CMD12 be sent to stop a transfer.
|
|
||||||
* However, the host specification defines support for "Auto CMD23" and
|
|
||||||
* "Auto CMD12", where the host sends CMD23 and CMD12 automatically to
|
|
||||||
* remove the overhead of interrupts in software from sending these
|
|
||||||
* commands. Therefore, we will not handle CMD12 or CMD23 at this layer.
|
|
||||||
* The host SDHC driver is expected to recognize CMD17, CMD18, CMD24,
|
|
||||||
* and CMD25 as special read/write commands and handle CMD23 and
|
|
||||||
* CMD12 appropriately.
|
|
||||||
*/
|
|
||||||
cmd.opcode = (num_blocks == 1U) ? SD_READ_SINGLE_BLOCK : SD_READ_MULTIPLE_BLOCK;
|
|
||||||
if (!(card->flags & SD_HIGH_CAPACITY_FLAG)) {
|
|
||||||
/* SDSC cards require block size in bytes, not blocks */
|
|
||||||
cmd.arg = start_block * card->block_size;
|
|
||||||
} else {
|
|
||||||
cmd.arg = start_block;
|
|
||||||
}
|
|
||||||
cmd.response_type = (SD_RSP_TYPE_R1 | SD_SPI_RSP_TYPE_R1);
|
|
||||||
cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT;
|
|
||||||
cmd.retries = CONFIG_SD_DATA_RETRIES;
|
|
||||||
|
|
||||||
data.block_addr = start_block;
|
|
||||||
data.block_size = card->block_size;
|
|
||||||
data.blocks = num_blocks;
|
|
||||||
data.data = rbuf;
|
|
||||||
data.timeout_ms = CONFIG_SD_DATA_TIMEOUT;
|
|
||||||
|
|
||||||
LOG_DBG("READ: Sector = %u, Count = %u", start_block, num_blocks);
|
|
||||||
|
|
||||||
ret = sdhc_request(card->sdhc, &cmd, &data);
|
|
||||||
if (ret) {
|
|
||||||
LOG_ERR("Failed to read from SDMMC %d", ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Verify card is back in transfer state after read */
|
|
||||||
ret = sdmmc_wait_ready(card);
|
|
||||||
if (ret) {
|
|
||||||
LOG_ERR("Card did not return to ready state");
|
|
||||||
k_mutex_unlock(&card->lock);
|
|
||||||
return -ETIMEDOUT;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reads data from SD card memory card */
|
|
||||||
int sdmmc_read_blocks(struct sd_card *card, uint8_t *rbuf,
|
int sdmmc_read_blocks(struct sd_card *card, uint8_t *rbuf,
|
||||||
uint32_t start_block, uint32_t num_blocks)
|
uint32_t start_block, uint32_t num_blocks)
|
||||||
{
|
{
|
||||||
int ret;
|
return card_read_blocks(card, rbuf, start_block, num_blocks);
|
||||||
uint32_t rlen;
|
|
||||||
uint32_t sector;
|
|
||||||
uint8_t *buf_offset;
|
|
||||||
|
|
||||||
if ((start_block + num_blocks) > card->block_count) {
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
if (card->type == CARD_SDIO) {
|
|
||||||
LOG_WRN("SDIO does not support MMC commands");
|
|
||||||
return -ENOTSUP;
|
|
||||||
}
|
|
||||||
ret = k_mutex_lock(&card->lock, K_NO_WAIT);
|
|
||||||
if (ret) {
|
|
||||||
LOG_WRN("Could not get SD card mutex");
|
|
||||||
return -EBUSY;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If the buffer we are provided with is aligned, we can use it
|
|
||||||
* directly. Otherwise, we need to use the card's internal buffer
|
|
||||||
* and memcpy the data back out
|
|
||||||
*/
|
|
||||||
if ((((uintptr_t)rbuf) & (CONFIG_SDHC_BUFFER_ALIGNMENT - 1)) != 0) {
|
|
||||||
/* lower bits of address are set, not aligned. Use internal buffer */
|
|
||||||
LOG_DBG("Unaligned buffer access to SD card may incur performance penalty");
|
|
||||||
if (sizeof(card->card_buffer) < card->block_size) {
|
|
||||||
LOG_ERR("Card buffer size needs to be increased for "
|
|
||||||
"unaligned writes to work");
|
|
||||||
k_mutex_unlock(&card->lock);
|
|
||||||
return -ENOBUFS;
|
|
||||||
}
|
|
||||||
rlen = sizeof(card->card_buffer) / card->block_size;
|
|
||||||
sector = 0;
|
|
||||||
buf_offset = rbuf;
|
|
||||||
while (sector < num_blocks) {
|
|
||||||
/* Read from disk to card buffer */
|
|
||||||
ret = sdmmc_read(card, card->card_buffer,
|
|
||||||
sector + start_block, rlen);
|
|
||||||
if (ret) {
|
|
||||||
LOG_ERR("Write failed");
|
|
||||||
k_mutex_unlock(&card->lock);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
/* Copy data from card buffer */
|
|
||||||
memcpy(buf_offset, card->card_buffer, rlen * card->block_size);
|
|
||||||
/* Increase sector count and buffer offset */
|
|
||||||
sector += rlen;
|
|
||||||
buf_offset += rlen * card->block_size;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* Aligned buffers can be used directly */
|
|
||||||
ret = sdmmc_read(card, rbuf, start_block, num_blocks);
|
|
||||||
if (ret) {
|
|
||||||
LOG_ERR("Card read failed");
|
|
||||||
k_mutex_unlock(&card->lock);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
k_mutex_unlock(&card->lock);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Sends ACMD22 (number of written blocks) to see how many blocks were written
|
|
||||||
* to a card
|
|
||||||
*/
|
|
||||||
static int sdmmc_query_written(struct sd_card *card, uint32_t *num_written)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
struct sdhc_command cmd = {0};
|
|
||||||
struct sdhc_data data = {0};
|
|
||||||
uint32_t *blocks = (uint32_t *)card->card_buffer;
|
|
||||||
|
|
||||||
ret = sdmmc_app_command(card, card->relative_addr);
|
|
||||||
if (ret) {
|
|
||||||
LOG_DBG("App CMD for ACMD22 failed");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.opcode = SD_APP_SEND_NUM_WRITTEN_BLK;
|
|
||||||
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 = 4U;
|
|
||||||
data.blocks = 1U;
|
|
||||||
data.data = blocks;
|
|
||||||
data.timeout_ms = CONFIG_SD_DATA_TIMEOUT;
|
|
||||||
|
|
||||||
ret = sdhc_request(card->sdhc, &cmd, &data);
|
|
||||||
if (ret) {
|
|
||||||
LOG_DBG("ACMD22 failed: %d", ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
ret = sd_check_response(&cmd);
|
|
||||||
if (ret) {
|
|
||||||
LOG_DBG("ACMD22 reports error");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Decode blocks */
|
|
||||||
*num_written = sys_be32_to_cpu(blocks[0]);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sdmmc_write(struct sd_card *card, const uint8_t *wbuf,
|
|
||||||
uint32_t start_block, uint32_t num_blocks)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
uint32_t blocks;
|
|
||||||
struct sdhc_command cmd = {0};
|
|
||||||
struct sdhc_data data = {0};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* See the note in sdmmc_read() above. We will not issue CMD23
|
|
||||||
* or CMD12, and expect the host to handle those details.
|
|
||||||
*/
|
|
||||||
cmd.opcode = (num_blocks == 1) ? SD_WRITE_SINGLE_BLOCK : SD_WRITE_MULTIPLE_BLOCK;
|
|
||||||
if (!(card->flags & SD_HIGH_CAPACITY_FLAG)) {
|
|
||||||
/* SDSC cards require block size in bytes, not blocks */
|
|
||||||
cmd.arg = start_block * card->block_size;
|
|
||||||
} else {
|
|
||||||
cmd.arg = start_block;
|
|
||||||
}
|
|
||||||
cmd.response_type = (SD_RSP_TYPE_R1 | SD_SPI_RSP_TYPE_R1);
|
|
||||||
cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT;
|
|
||||||
cmd.retries = CONFIG_SD_DATA_RETRIES;
|
|
||||||
|
|
||||||
data.block_addr = start_block;
|
|
||||||
data.block_size = card->block_size;
|
|
||||||
data.blocks = num_blocks;
|
|
||||||
data.data = (uint8_t *)wbuf;
|
|
||||||
data.timeout_ms = CONFIG_SD_DATA_TIMEOUT;
|
|
||||||
|
|
||||||
LOG_DBG("WRITE: Sector = %u, Count = %u", start_block, num_blocks);
|
|
||||||
|
|
||||||
ret = sdhc_request(card->sdhc, &cmd, &data);
|
|
||||||
if (ret) {
|
|
||||||
LOG_DBG("Write failed: %d", ret);
|
|
||||||
ret = sdmmc_wait_ready(card);
|
|
||||||
if (ret) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
/* Query card to see how many blocks were actually written */
|
|
||||||
ret = sdmmc_query_written(card, &blocks);
|
|
||||||
if (ret) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
LOG_ERR("Only %d blocks of %d were written", blocks, num_blocks);
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
/* Verify card is back in transfer state after write */
|
|
||||||
ret = sdmmc_wait_ready(card);
|
|
||||||
if (ret) {
|
|
||||||
LOG_ERR("Card did not return to ready state");
|
|
||||||
return -ETIMEDOUT;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Writes data to SD card memory card */
|
|
||||||
int sdmmc_write_blocks(struct sd_card *card, const uint8_t *wbuf,
|
int sdmmc_write_blocks(struct sd_card *card, const uint8_t *wbuf,
|
||||||
uint32_t start_block, uint32_t num_blocks)
|
uint32_t start_block, uint32_t num_blocks)
|
||||||
{
|
{
|
||||||
int ret;
|
return card_write_blocks(card, wbuf, start_block, num_blocks);
|
||||||
uint32_t wlen;
|
|
||||||
uint32_t sector;
|
|
||||||
const uint8_t *buf_offset;
|
|
||||||
|
|
||||||
if ((start_block + num_blocks) > card->block_count) {
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
if (card->type == CARD_SDIO) {
|
|
||||||
LOG_WRN("SDIO does not support MMC commands");
|
|
||||||
return -ENOTSUP;
|
|
||||||
}
|
|
||||||
ret = k_mutex_lock(&card->lock, K_NO_WAIT);
|
|
||||||
if (ret) {
|
|
||||||
LOG_WRN("Could not get SD card mutex");
|
|
||||||
return -EBUSY;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* If the buffer we are provided with is aligned, we can use it
|
|
||||||
* directly. Otherwise, we need to use the card's internal buffer
|
|
||||||
* and memcpy the data back out
|
|
||||||
*/
|
|
||||||
if ((((uintptr_t)wbuf) & (CONFIG_SDHC_BUFFER_ALIGNMENT - 1)) != 0) {
|
|
||||||
/* lower bits of address are set, not aligned. Use internal buffer */
|
|
||||||
LOG_DBG("Unaligned buffer access to SD card may incur performance penalty");
|
|
||||||
if (sizeof(card->card_buffer) < card->block_size) {
|
|
||||||
LOG_ERR("Card buffer size needs to be increased for "
|
|
||||||
"unaligned writes to work");
|
|
||||||
k_mutex_unlock(&card->lock);
|
|
||||||
return -ENOBUFS;
|
|
||||||
}
|
|
||||||
wlen = sizeof(card->card_buffer) / card->block_size;
|
|
||||||
sector = 0;
|
|
||||||
buf_offset = wbuf;
|
|
||||||
while (sector < num_blocks) {
|
|
||||||
/* Copy data into card buffer */
|
|
||||||
memcpy(card->card_buffer, buf_offset, wlen * card->block_size);
|
|
||||||
/* Write card buffer to disk */
|
|
||||||
ret = sdmmc_write(card, card->card_buffer,
|
|
||||||
sector + start_block, wlen);
|
|
||||||
if (ret) {
|
|
||||||
LOG_ERR("Write failed");
|
|
||||||
k_mutex_unlock(&card->lock);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
/* Increase sector count and buffer offset */
|
|
||||||
sector += wlen;
|
|
||||||
buf_offset += wlen * card->block_size;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* We can use aligned buffers directly */
|
|
||||||
ret = sdmmc_write(card, wbuf, start_block, num_blocks);
|
|
||||||
if (ret) {
|
|
||||||
LOG_ERR("Write failed");
|
|
||||||
k_mutex_unlock(&card->lock);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
k_mutex_unlock(&card->lock);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* IO Control handler for SD MMC */
|
|
||||||
int sdmmc_ioctl(struct sd_card *card, uint8_t cmd, void *buf)
|
|
||||||
{
|
|
||||||
switch (cmd) {
|
|
||||||
case DISK_IOCTL_GET_SECTOR_COUNT:
|
|
||||||
(*(uint32_t *)buf) = card->block_count;
|
|
||||||
break;
|
|
||||||
case DISK_IOCTL_GET_SECTOR_SIZE:
|
|
||||||
case DISK_IOCTL_GET_ERASE_BLOCK_SZ:
|
|
||||||
(*(uint32_t *)buf) = card->block_size;
|
|
||||||
break;
|
|
||||||
case DISK_IOCTL_CTRL_SYNC:
|
|
||||||
/* Ensure card is not busy with data write.
|
|
||||||
* Note that SD stack does not support enabling caching, so
|
|
||||||
* cache flush is not required here
|
|
||||||
*/
|
|
||||||
return sdmmc_wait_ready(card);
|
|
||||||
default:
|
|
||||||
return -ENOTSUP;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue