sd: add stub for SDIO support

Add stub code for SDIO support, capable of verifying card responds to CMD5.
This commit also changes the architecture of the SDIO probe step to make
adding new protocol support more streamlined, and enable compiling out
support for undesired protocols.

Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
This commit is contained in:
Daniel DeGrasse 2022-05-24 11:35:06 -05:00 committed by Carles Cufí
commit b7cd970493
8 changed files with 152 additions and 149 deletions

View file

@ -4,5 +4,7 @@ if (CONFIG_SD_STACK)
zephyr_interface_library_named(SD)
zephyr_library()
zephyr_library_sources (sd.c sdmmc.c)
zephyr_library_sources(sd.c)
zephyr_library_sources_ifdef(CONFIG_SDMMC_STACK sdmmc.c)
zephyr_library_sources_ifdef(CONFIG_SDIO_STACK sdio.c)
endif()

View file

@ -15,6 +15,17 @@ module = SD
module-str = SD stack
source "subsys/logging/Kconfig.template.log_config"
config SDMMC_STACK
bool "SDMMC protocol support"
help
Enable SDMMC protocol support. Required for SD memory cards to
function.
config SDIO_STACK
bool "SDIO protocol support"
help
Enable SDIO protocol support. Required for SD I/O cards to function.
config SD_INIT_TIMEOUT
int "Timeout while initializing SD card"
default 1500

View file

@ -14,6 +14,7 @@
#include "sd_utils.h"
#include "sdmmc_priv.h"
#include "sdio_priv.h"
LOG_MODULE_REGISTER(sd, CONFIG_SD_LOG_LEVEL);
@ -159,71 +160,6 @@ static int sd_init_io(struct sd_card *card)
return 0;
}
/*
* Sends CMD5 to SD card, and uses response to determine if card
* is SDIO or SDMMC card. Return 0 if SDIO card, positive value if not, or
* negative errno on error
*/
int sd_test_sdio(struct sd_card *card)
{
struct sdhc_command cmd = {0};
int ret;
cmd.opcode = SDIO_SEND_OP_COND;
cmd.arg = 0;
cmd.response_type = (SD_RSP_TYPE_R4 | SD_SPI_RSP_TYPE_R4);
cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT;
ret = sdhc_request(card->sdhc, &cmd, NULL);
if (ret) {
/*
* We are just probing card, and it is likely an SD.
* return error
*/
card->type = CARD_SDMMC;
return SD_NOT_SDIO;
}
/* Check the number of I/O 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. */
card->type = CARD_SDMMC;
return SD_NOT_SDIO;
}
/* Card is not a valid SD device. We do not support it */
return -ENOTSUP;
}
/* Since we got a valid OCR response,
* we know this card is an SDIO card.
*/
card->type = CARD_SDIO;
return 0;
}
/*
* Check SD card type
* Uses SDIO OCR response to determine what type of card is present.
*/
static int sd_check_card_type(struct sd_card *card)
{
int ret;
/* Test if the card response to CMD5 (only SDIO cards will) */
/* Note that CMD5 can take many retries */
ret = sd_test_sdio(card);
if ((ret == SD_NOT_SDIO) && card->type == CARD_SDMMC) {
LOG_INF("Detected SD card");
return 0;
} else if ((ret == 0) && card->type == CARD_SDIO) {
LOG_INF("Detected SDIO card");
return 0;
}
LOG_ERR("No usable card type was found");
return -ENOTSUP;
}
/*
* Performs init flow described in section 3.6 of SD specification.
*/
@ -243,33 +179,20 @@ static int sd_command_init(struct sd_card *card)
if (ret) {
return ret;
}
/* Use CMD5 to determine card type */
ret = sd_check_card_type(card);
if (ret) {
LOG_ERR("Unusable card");
return -ENOTSUP;
#ifdef CONFIG_SDIO_STACK
/* Attempt to initialize SDIO card */
if (!sdio_card_init(card)) {
return 0;
}
if (card->type == CARD_SDMMC) {
/*
* Reset the card first- CMD5 sent to see if it is SDIO card
* may have left it in error state
*/
ret = sd_common_init(card);
if (ret) {
LOG_ERR("Init after CMD5 failed");
return ret;
}
/* Perform memory card initialization */
ret = sdmmc_card_init(card);
} else if (card->type == CARD_SDIO) {
LOG_ERR("SDIO cards not currently supported");
return -ENOTSUP;
#endif /* CONFIG_SDIO_STACK */
#ifdef CONFIG_SDMMC_STACK
/* Attempt to initialize SDMMC card */
if (!sdmmc_card_init(card)) {
return 0;
}
if (ret) {
LOG_ERR("Card init failed");
return ret;
}
return 0;
#endif /* CONFIG_SDIO_STACK */
/* Unknown card type */
return -ENOTSUP;
}
/* Initializes SD/SDIO card */

78
subsys/sd/sdio.c Normal file
View file

@ -0,0 +1,78 @@
/*
* Copyright 2022 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/zephyr.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_utils.h"
LOG_MODULE_DECLARE(sd, CONFIG_SD_LOG_LEVEL);
/*
* Send SDIO OCR using CMD5
*/
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;
}
}
}
/*
* Initialize an SDIO card for use with subsystem
*/
int sdio_card_init(struct sd_card *card)
{
int ret;
/* Probe card with SDIO OCR CM5 */
ret = sdio_send_ocr(card, 0);
if (ret) {
return ret;
}
/* Card responded to ACMD41, type is SDIO */
card->type = CARD_SDIO;
/* No support for SDIO */
return -ENOTSUP;
}

16
subsys/sd/sdio_priv.h Normal file
View file

@ -0,0 +1,16 @@
/*
* Copyright 2022 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_SUBSYS_SD_SDIO_PRIV_H_
#define ZEPHYR_SUBSYS_SD_SDIO_PRIV_H_
#include <zephyr/zephyr.h>
#include <zephyr/sd/sd.h>
int sdio_card_init(struct sd_card *card);
#endif /* ZEPHYR_SUBSYS_SD_SDIO_PRIV_H_ */

View file

@ -230,31 +230,15 @@ static int sdmmc_app_command(struct sd_card *card, int relative_card_address)
return 0;
}
/* Reads OCR from SPI mode card using CMD58 */
static int sdmmc_spi_send_ocr(struct sd_card *card, uint32_t arg)
{
struct sdhc_command cmd;
int ret;
cmd.opcode = SD_SPI_READ_OCR;
cmd.arg = arg;
cmd.response_type = SD_SPI_RSP_TYPE_R3;
ret = sdhc_request(card->sdhc, &cmd, NULL);
card->ocr = cmd.response[1];
return ret;
}
/* Sends OCR to card using ACMD41 */
static int sdmmc_send_ocr(struct sd_card *card, int ocr_arg)
static int sdmmc_send_ocr(struct sd_card *card, int ocr)
{
struct sdhc_command cmd;
int ret;
int retries;
cmd.opcode = SD_APP_SEND_OP_COND;
cmd.arg = ocr_arg;
cmd.arg = ocr;
cmd.response_type = (SD_RSP_TYPE_R3 | SD_SPI_RSP_TYPE_R1);
cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT;
/* Send initialization ACMD41 */
@ -271,7 +255,7 @@ static int sdmmc_send_ocr(struct sd_card *card, int ocr_arg)
/* OCR failed */
return ret;
}
if (ocr_arg == 0) {
if (ocr == 0) {
/* Just probing, don't wait for card to exit busy state */
return 0;
}
@ -970,52 +954,39 @@ int sdmmc_card_init(struct sd_card *card)
int ret;
uint32_t ocr_arg = 0U;
if (card->host_props.is_spi && IS_ENABLED(CONFIG_SDHC_SUPPORTS_SPI_MODE)) {
/* SD card needs CMD58 before ACMD41 to read OCR */
ret = sdmmc_spi_send_ocr(card, 0);
if (ret) {
/* Card is not an SD card */
return ret;
}
if (card->flags & SD_SDHC_FLAG) {
ocr_arg |= SD_OCR_HOST_CAP_FLAG;
}
ret = sdmmc_send_ocr(card, ocr_arg);
if (ret) {
return ret;
}
/* Send second CMD58 to get CCS bit */
ret = sdmmc_spi_send_ocr(card, ocr_arg);
} else if (IS_ENABLED(CONFIG_SDHC_SUPPORTS_NATIVE_MODE)) {
/* Send initial probing OCR */
ret = sdmmc_send_ocr(card, 0);
if (ret) {
/* Card is not an SD card */
return ret;
}
if (card->flags & SD_SDHC_FLAG) {
/* High capacity card. See if host supports 1.8V */
if (card->host_props.host_caps.vol_180_support) {
ocr_arg |= SD_OCR_SWITCH_18_REQ_FLAG;
}
/* Set host high capacity support flag */
ocr_arg |= SD_OCR_HOST_CAP_FLAG;
/* First send a probing OCR using ACMD41. Note that SPI cards also
* accept CMD58 at this point, but we skip this command as it is not
* required by the spec.
*/
ret = sdmmc_send_ocr(card, ocr_arg);
if (ret) {
return ret;
}
/* Card responded to ACMD41, type is SDMMC */
card->type = CARD_SDMMC;
if (card->flags & SD_SDHC_FLAG) {
/* High capacity card. See if host supports 1.8V */
if (card->host_props.host_caps.vol_180_support) {
ocr_arg |= SD_OCR_SWITCH_18_REQ_FLAG;
}
/* Set host high capacity support flag */
ocr_arg |= SD_OCR_HOST_CAP_FLAG;
}
if (IS_ENABLED(CONFIG_SDHC_SUPPORTS_NATIVE_MODE)) {
/* Set voltage window */
if (card->host_props.host_caps.vol_300_support) {
ocr_arg |= SD_OCR_VDD29_30FLAG;
}
ocr_arg |= (SD_OCR_VDD32_33FLAG | SD_OCR_VDD33_34FLAG);
/* Momentary delay before initialization OCR. Some cards will
* never leave busy state if init OCR is sent too soon after
* probing OCR
*/
k_busy_wait(100);
/* Send SD OCR to card to initialize it */
ret = sdmmc_send_ocr(card, ocr_arg);
} else {
return -ENOTSUP;
}
/* Momentary delay before initialization OCR. Some cards will
* never leave busy state if init OCR is sent too soon after
* probing OCR
*/
k_busy_wait(100);
/* Send SD OCR to card to initialize it */
ret = sdmmc_send_ocr(card, ocr_arg);
if (ret) {
LOG_ERR("Failed to query card OCR");
return ret;