sdmmc: add support for SPI protocol commands

This commit is contained in:
Ivan Grokhotkov 2017-07-04 12:52:40 +08:00
parent 3b96771feb
commit 5f8785eaec
2 changed files with 179 additions and 33 deletions

View file

@ -30,6 +30,7 @@
#define MMC_SELECT_CARD 7 /* R1 */
#define MMC_SEND_EXT_CSD 8 /* R1 */
#define MMC_SEND_CSD 9 /* R2 */
#define MMC_SEND_CID 10 /* R1 */
#define MMC_STOP_TRANSMISSION 12 /* R1B */
#define MMC_SEND_STATUS 13 /* R1 */
#define MMC_SET_BLOCKLEN 16 /* R1 */
@ -44,9 +45,12 @@
#define SD_SEND_RELATIVE_ADDR 3 /* R6 */
#define SD_SEND_SWITCH_FUNC 6 /* R1 */
#define SD_SEND_IF_COND 8 /* R7 */
#define SD_READ_OCR 58 /* R3 */
#define SD_CRC_ON_OFF 59 /* R1 */
/* SD application commands */ /* response type */
#define SD_APP_SET_BUS_WIDTH 6 /* R1 */
#define SD_APP_SD_STATUS 13 /* R2 */
#define SD_APP_OP_COND 41 /* R3 */
#define SD_APP_SEND_SCR 51 /* R1 */
@ -76,16 +80,26 @@
#define SD_OCR_SDHC_CAP (1<<30)
#define SD_OCR_VOL_MASK 0xFF8000 /* bits 23:15 */
/* R1 response type bits */
/* SD mode R1 response type bits */
#define MMC_R1_READY_FOR_DATA (1<<8) /* ready for next transfer */
#define MMC_R1_APP_CMD (1<<5) /* app. commands supported */
/* SPI mode R1 response type bits */
#define SD_SPI_R1_IDLE_STATE (1<<0)
#define SD_SPI_R1_CMD_CRC_ERR (1<<3)
/* 48-bit response decoding (32 bits w/o CRC) */
#define MMC_R1(resp) ((resp)[0])
#define MMC_R3(resp) ((resp)[0])
#define SD_R6(resp) ((resp)[0])
#define MMC_R1_CURRENT_STATE(resp) (((resp)[0] >> 9) & 0xf)
/* SPI mode response decoding */
#define SD_SPI_R1(resp) ((resp)[0] & 0xff)
#define SD_SPI_R2(resp) ((resp)[0] & 0xffff)
#define SD_SPI_R3(resp) ((resp)[0])
#define SD_SPI_R7(resp) ((resp)[0])
/* RCA argument and response */
#define MMC_ARG_RCA(rca) ((rca) << 16)
#define SD_R6_RCA(resp) (SD_R6((resp)) >> 16)

View file

@ -23,8 +23,9 @@
#include "driver/sdmmc_defs.h"
#include "driver/sdmmc_types.h"
#include "sdmmc_cmd.h"
#include "sys/param.h"
#define MIN(a,b) (((a)<(b))?(a):(b))
#define SDMMC_GO_IDLE_DELAY_MS 20
static const char* TAG = "sdmmc_cmd";
@ -33,6 +34,8 @@ static esp_err_t sdmmc_send_app_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd);
static esp_err_t sdmmc_send_cmd_go_idle_state(sdmmc_card_t* card);
static esp_err_t sdmmc_send_cmd_send_if_cond(sdmmc_card_t* card, uint32_t ocr);
static esp_err_t sdmmc_send_cmd_send_op_cond(sdmmc_card_t* card, uint32_t ocr, uint32_t *ocrp);
static esp_err_t sdmmc_send_cmd_read_ocr(sdmmc_card_t *card, uint32_t *ocrp);
static esp_err_t sdmmc_send_cmd_send_cid(sdmmc_card_t *card, sdmmc_cid_t *out_cid);
static esp_err_t sdmmc_decode_cid(sdmmc_response_t resp, sdmmc_cid_t* out_cid);
static esp_err_t sddmc_send_cmd_all_send_cid(sdmmc_card_t* card, sdmmc_cid_t* out_cid);
static esp_err_t sdmmc_send_cmd_set_relative_addr(sdmmc_card_t* card, uint16_t* out_rca);
@ -45,21 +48,36 @@ static esp_err_t sdmmc_send_cmd_send_scr(sdmmc_card_t* card, sdmmc_scr_t *out_sc
static esp_err_t sdmmc_send_cmd_set_bus_width(sdmmc_card_t* card, int width);
static esp_err_t sdmmc_send_cmd_stop_transmission(sdmmc_card_t* card, uint32_t* status);
static esp_err_t sdmmc_send_cmd_send_status(sdmmc_card_t* card, uint32_t* out_status);
static esp_err_t sdmmc_send_cmd_crc_on_off(sdmmc_card_t* card, bool crc_enable);
static uint32_t get_host_ocr(float voltage);
static void response_ntoh(sdmmc_response_t response);
esp_err_t sdmmc_card_init(const sdmmc_host_t* config,
sdmmc_card_t* card)
static bool host_is_spi(const sdmmc_card_t* card)
{
return (card->host.flags & SDMMC_HOST_FLAG_SPI) != 0;
}
esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card)
{
ESP_LOGD(TAG, "%s", __func__);
memset(card, 0, sizeof(*card));
memcpy(&card->host, config, sizeof(*config));
const bool is_spi = host_is_spi(card);
/* GO_IDLE_STATE (CMD0) command resets the card */
esp_err_t err = sdmmc_send_cmd_go_idle_state(card);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: go_idle_state (1) returned 0x%x", __func__, err);
return err;
}
ets_delay_us(10000);
vTaskDelay(SDMMC_GO_IDLE_DELAY_MS / portTICK_PERIOD_MS);
sdmmc_send_cmd_go_idle_state(card);
vTaskDelay(SDMMC_GO_IDLE_DELAY_MS / portTICK_PERIOD_MS);
/* SEND_IF_COND (CMD8) command is used to identify SDHC/SDXC cards.
* SD v1 and non-SD cards will not respond to this command.
*/
uint32_t host_ocr = get_host_ocr(config->io_voltage);
err = sdmmc_send_cmd_send_if_cond(card, host_ocr);
if (err == ESP_OK) {
@ -71,23 +89,63 @@ esp_err_t sdmmc_card_init(const sdmmc_host_t* config,
ESP_LOGE(TAG, "%s: send_if_cond (1) returned 0x%x", __func__, err);
return err;
}
/* In SPI mode, READ_OCR (CMD58) command is used to figure out which voltage
* ranges the card can support. This step is skipped since 1.8V isn't
* supported on the ESP32.
*/
/* In SD mode, CRC checks of data transfers are mandatory and performed
* by the hardware. In SPI mode, CRC16 of data transfers is optional and
* needs to be enabled.
*/
if (is_spi) {
err = sdmmc_send_cmd_crc_on_off(card, true);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: sdmmc_send_cmd_crc_on_off returned 0x%x", __func__, err);
return err;
}
}
/* Send SEND_OP_COND (ACMD41) command to the card until it becomes ready. */
err = sdmmc_send_cmd_send_op_cond(card, host_ocr, &card->ocr);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: send_op_cond (1) returned 0x%x", __func__, err);
return err;
}
host_ocr &= card->ocr;
if (is_spi) {
err = sdmmc_send_cmd_read_ocr(card, &card->ocr);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: read_ocr returned 0x%x", __func__, err);
return err;
}
}
ESP_LOGD(TAG, "host_ocr=0x%x card_ocr=0x%x", host_ocr, card->ocr);
/* Clear all voltage bits in host's OCR which the card doesn't support.
* Don't touch CCS bit because in SPI mode cards don't report CCS in ACMD41
* response.
*/
host_ocr &= (card->ocr | (~SD_OCR_VOL_MASK));
ESP_LOGD(TAG, "sdmmc_card_init: host_ocr=%08x, card_ocr=%08x", host_ocr, card->ocr);
err = sddmc_send_cmd_all_send_cid(card, &card->cid);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: all_send_cid returned 0x%x", __func__, err);
return err;
}
err = sdmmc_send_cmd_set_relative_addr(card, &card->rca);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: set_relative_addr returned 0x%x", __func__, err);
return err;
if (!is_spi) {
err = sddmc_send_cmd_all_send_cid(card, &card->cid);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: all_send_cid returned 0x%x", __func__, err);
return err;
}
err = sdmmc_send_cmd_set_relative_addr(card, &card->rca);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: set_relative_addr returned 0x%x", __func__, err);
return err;
}
} else {
err = sdmmc_send_cmd_send_cid(card, &card->cid);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: send_cid returned 0x%x", __func__, err);
return err;
}
}
err = sdmmc_send_cmd_send_csd(card, &card->csd);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: send_csd returned 0x%x", __func__, err);
@ -100,10 +158,12 @@ esp_err_t sdmmc_card_init(const sdmmc_host_t* config,
__func__, card->csd.capacity, max_sdsc_capacity);
card->csd.capacity = max_sdsc_capacity;
}
err = sdmmc_send_cmd_select_card(card);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: select_card returned 0x%x", __func__, err);
return err;
if (!is_spi) {
err = sdmmc_send_cmd_select_card(card);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: select_card returned 0x%x", __func__, err);
return err;
}
}
if ((card->ocr & SD_OCR_SDHC_CAP) == 0) {
err = sdmmc_send_cmd_set_blocklen(card, &card->csd);
@ -138,7 +198,7 @@ esp_err_t sdmmc_card_init(const sdmmc_host_t* config,
}
}
uint32_t status = 0;
while (!(status & MMC_R1_READY_FOR_DATA)) {
while (!host_is_spi(card) && !(status & MMC_R1_READY_FOR_DATA)) {
// TODO: add some timeout here
uint32_t count = 0;
err = sdmmc_send_cmd_send_status(card, &status);
@ -173,7 +233,7 @@ esp_err_t sdmmc_card_init(const sdmmc_host_t* config,
return err;
}
if (memcmp(&card->scr, &scr_tmp, sizeof(scr_tmp)) != 0) {
ESP_LOGE(TAG, "data check fail!");
ESP_LOGE(TAG, "got corrupted data after increasing clock frequency");
return ESP_ERR_INVALID_RESPONSE;
}
return ESP_OK;
@ -223,7 +283,8 @@ static esp_err_t sdmmc_send_app_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd)
if (err != ESP_OK) {
return err;
}
if (!(MMC_R1(app_cmd.response) & MMC_R1_APP_CMD)) {
// Check APP_CMD status bit (only in SD mode)
if (!host_is_spi(card) && !(MMC_R1(app_cmd.response) & MMC_R1_APP_CMD)) {
ESP_LOGW(TAG, "card doesn't support APP_CMD");
return ESP_ERR_NOT_SUPPORTED;
}
@ -255,6 +316,7 @@ static esp_err_t sdmmc_send_cmd_send_if_cond(sdmmc_card_t* card, uint32_t ocr)
}
uint8_t response = cmd.response[0] & 0xff;
if (response != pattern) {
ESP_LOGD(TAG, "%s: received=0x%x expected=0x%x", __func__, response, pattern);
return ESP_ERR_INVALID_RESPONSE;
}
return ESP_OK;
@ -273,9 +335,17 @@ static esp_err_t sdmmc_send_cmd_send_op_cond(sdmmc_card_t* card, uint32_t ocr, u
if (err != ESP_OK) {
return err;
}
if ((MMC_R3(cmd.response) & MMC_OCR_MEM_READY) ||
ocr == 0) {
break;
// In SD protocol, card sets MEM_READY bit in OCR when it is ready.
// In SPI protocol, card clears IDLE_STATE bit in R1 response.
if (!host_is_spi(card)) {
if ((MMC_R3(cmd.response) & MMC_OCR_MEM_READY) ||
ocr == 0) {
break;
}
} else {
if ((SD_SPI_R1(cmd.response) & SD_SPI_R1_IDLE_STATE) == 0) {
break;
}
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
@ -288,7 +358,22 @@ static esp_err_t sdmmc_send_cmd_send_op_cond(sdmmc_card_t* card, uint32_t ocr, u
return ESP_OK;
}
static esp_err_t sdmmc_decode_cid(sdmmc_response_t resp, sdmmc_cid_t* out_cid)
static esp_err_t sdmmc_send_cmd_read_ocr(sdmmc_card_t *card, uint32_t *ocrp)
{
assert(ocrp);
sdmmc_command_t cmd = {
.opcode = SD_READ_OCR,
.flags = SCF_CMD_BCR | SCF_RSP_R2
};
esp_err_t err = sdmmc_send_cmd(card, &cmd);
if (err != ESP_OK) {
return err;
}
*ocrp = SD_SPI_R3(cmd.response);
return ESP_OK;
}
esp_err_t sdmmc_decode_cid(sdmmc_response_t resp, sdmmc_cid_t* out_cid)
{
out_cid->mfg_id = SD_CID_MID(resp);
out_cid->oem_id = SD_CID_OID(resp);
@ -313,6 +398,26 @@ static esp_err_t sddmc_send_cmd_all_send_cid(sdmmc_card_t* card, sdmmc_cid_t* ou
return sdmmc_decode_cid(cmd.response, out_cid);
}
static esp_err_t sdmmc_send_cmd_send_cid(sdmmc_card_t *card, sdmmc_cid_t *out_cid)
{
assert(out_cid);
assert(host_is_spi(card) && "SEND_CID should only be used in SPI mode");
sdmmc_response_t buf;
sdmmc_command_t cmd = {
.opcode = MMC_SEND_CID,
.flags = SCF_CMD_READ | SCF_CMD_ADTC,
.arg = 0,
.data = &buf[0],
.datalen = sizeof(buf)
};
esp_err_t err = sdmmc_send_cmd(card, &cmd);
if (err != ESP_OK) {
return err;
}
response_ntoh(buf);
return sdmmc_decode_cid(buf, out_cid);
}
static esp_err_t sdmmc_send_cmd_set_relative_addr(sdmmc_card_t* card, uint16_t* out_rca)
{
@ -374,16 +479,27 @@ static esp_err_t sdmmc_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_cs
static esp_err_t sdmmc_send_cmd_send_csd(sdmmc_card_t* card, sdmmc_csd_t* out_csd)
{
/* The trick with SEND_CSD is that in SPI mode, it acts as a data read
* command, while in SD mode it is an AC command with R2 response.
*/
sdmmc_response_t spi_buf;
const bool is_spi = host_is_spi(card);
sdmmc_command_t cmd = {
.opcode = MMC_SEND_CSD,
.arg = MMC_ARG_RCA(card->rca),
.flags = SCF_CMD_AC | SCF_RSP_R2
.arg = is_spi ? 0 : MMC_ARG_RCA(card->rca),
.flags = is_spi ? (SCF_CMD_READ | SCF_CMD_ADTC | SCF_RSP_R1) :
(SCF_CMD_AC | SCF_RSP_R2),
.data = is_spi ? &spi_buf[0] : 0,
.datalen = is_spi ? sizeof(spi_buf) : 0,
};
esp_err_t err = sdmmc_send_cmd(card, &cmd);
if (err != ESP_OK) {
return err;
}
return sdmmc_decode_csd(cmd.response, out_csd);
if (is_spi) {
response_ntoh(spi_buf);
}
return sdmmc_decode_csd(is_spi ? spi_buf : cmd.response, out_csd);
}
static esp_err_t sdmmc_send_cmd_select_card(sdmmc_card_t* card)
@ -426,8 +542,6 @@ static esp_err_t sdmmc_send_cmd_send_scr(sdmmc_card_t* card, sdmmc_scr_t *out_sc
};
esp_err_t err = sdmmc_send_app_cmd(card, &cmd);
if (err == ESP_OK) {
buf[0] = (buf[0]);
buf[1] = (buf[1]);
err = sdmmc_decode_scr(buf, out_scr);
}
free(buf);
@ -459,6 +573,17 @@ static esp_err_t sdmmc_send_cmd_stop_transmission(sdmmc_card_t* card, uint32_t*
return err;
}
static esp_err_t sdmmc_send_cmd_crc_on_off(sdmmc_card_t* card, bool crc_enable)
{
assert(host_is_spi(card) && "CRC_ON_OFF can only be used in SPI mode");
sdmmc_command_t cmd = {
.opcode = SD_CRC_ON_OFF,
.arg = crc_enable ? 1 : 0,
.flags = SCF_CMD_AC | SCF_RSP_R1
};
return sdmmc_send_cmd(card, &cmd);
}
static uint32_t get_host_ocr(float voltage)
{
// TODO: report exact voltage to the card
@ -467,6 +592,13 @@ static uint32_t get_host_ocr(float voltage)
return SD_OCR_VOL_MASK;
}
static void response_ntoh(sdmmc_response_t response)
{
for (int i = 0; i < 4; ++i) {
response[i] = __builtin_bswap32(response[i]);
}
}
static esp_err_t sdmmc_send_cmd_send_status(sdmmc_card_t* card, uint32_t* out_status)
{
sdmmc_command_t cmd = {
@ -514,7 +646,7 @@ esp_err_t sdmmc_write_sectors(sdmmc_card_t* card, const void* src,
}
uint32_t status = 0;
size_t count = 0;
while (!(status & MMC_R1_READY_FOR_DATA)) {
while (!host_is_spi(card) && !(status & MMC_R1_READY_FOR_DATA)) {
// TODO: add some timeout here
err = sdmmc_send_cmd_send_status(card, &status);
if (err != ESP_OK) {
@ -557,7 +689,7 @@ esp_err_t sdmmc_read_sectors(sdmmc_card_t* card, void* dst,
}
uint32_t status = 0;
size_t count = 0;
while (!(status & MMC_R1_READY_FOR_DATA)) {
while (!host_is_spi(card) && !(status & MMC_R1_READY_FOR_DATA)) {
// TODO: add some timeout here
err = sdmmc_send_cmd_send_status(card, &status);
if (err != ESP_OK) {