sd: split sdmmc common functions into sd_ops.c
split reusable portions of SDMMC protocol code into sd_ops.c, so other SD protocols can use these functions directly without compiling in the SDMMC subsystem. Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
This commit is contained in:
parent
b7cd970493
commit
94c858ed86
|
@ -4,7 +4,7 @@ if (CONFIG_SD_STACK)
|
|||
zephyr_interface_library_named(SD)
|
||||
|
||||
zephyr_library()
|
||||
zephyr_library_sources(sd.c)
|
||||
zephyr_library_sources(sd.c sd_ops.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_SDMMC_STACK sdmmc.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_SDIO_STACK sdio.c)
|
||||
endif()
|
||||
|
|
397
subsys/sd/sd_ops.c
Normal file
397
subsys/sd/sd_ops.c
Normal file
|
@ -0,0 +1,397 @@
|
|||
/*
|
||||
* 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/sd_spec.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
|
||||
#include "sd_utils.h"
|
||||
|
||||
LOG_MODULE_DECLARE(sd, CONFIG_SD_LOG_LEVEL);
|
||||
|
||||
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;
|
||||
|
||||
csd->csd_structure = (uint8_t)((raw_csd[3U] &
|
||||
0xC0000000U) >> 30U);
|
||||
csd->read_time1 = (uint8_t)((raw_csd[3U] &
|
||||
0xFF0000U) >> 16U);
|
||||
csd->read_time2 = (uint8_t)((raw_csd[3U] &
|
||||
0xFF00U) >> 8U);
|
||||
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) {
|
||||
csd->flags |= SD_CSD_READ_BLK_PARTIAL_FLAG;
|
||||
}
|
||||
if (raw_csd[2U] & 0x4000U) {
|
||||
csd->flags |= SD_CSD_READ_BLK_PARTIAL_FLAG;
|
||||
}
|
||||
if (raw_csd[2U] & 0x2000U) {
|
||||
csd->flags |= SD_CSD_READ_BLK_MISALIGN_FLAG;
|
||||
}
|
||||
if (raw_csd[2U] & 0x1000U) {
|
||||
csd->flags |= SD_CSD_DSR_IMPLEMENTED_FLAG;
|
||||
}
|
||||
|
||||
switch (csd->csd_structure) {
|
||||
case 0:
|
||||
csd->device_size = (uint32_t)((raw_csd[2U] &
|
||||
0x3FFU) << 2U);
|
||||
csd->device_size |= (uint32_t)((raw_csd[1U] &
|
||||
0xC0000000U) >> 30U);
|
||||
csd->read_current_min = (uint8_t)((raw_csd[1U] &
|
||||
0x38000000U) >> 27U);
|
||||
csd->read_current_max = (uint8_t)((raw_csd[1U] &
|
||||
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. */
|
||||
tmp_blk_count = ((csd->device_size + 1U) <<
|
||||
(csd->dev_size_mul + 2U));
|
||||
tmp_blk_size = (1U << (csd->read_blk_len));
|
||||
if (tmp_blk_size != SDMMC_DEFAULT_BLOCK_SIZE) {
|
||||
tmp_blk_count = (tmp_blk_count * tmp_blk_size);
|
||||
tmp_blk_size = SDMMC_DEFAULT_BLOCK_SIZE;
|
||||
tmp_blk_count = (tmp_blk_count / tmp_blk_size);
|
||||
}
|
||||
if (blk_count) {
|
||||
*blk_count = tmp_blk_count;
|
||||
}
|
||||
if (blk_size) {
|
||||
*blk_size = tmp_blk_size;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
tmp_blk_size = SDMMC_DEFAULT_BLOCK_SIZE;
|
||||
|
||||
csd->device_size = (uint32_t)((raw_csd[2U] &
|
||||
0x3FU) << 16U);
|
||||
csd->device_size |= (uint32_t)((raw_csd[1U] &
|
||||
0xFFFF0000U) >> 16U);
|
||||
|
||||
tmp_blk_count = ((csd->device_size + 1U) * 1024U);
|
||||
if (blk_count) {
|
||||
*blk_count = tmp_blk_count;
|
||||
}
|
||||
if (blk_size) {
|
||||
*blk_size = tmp_blk_size;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if ((uint8_t)((raw_csd[1U] & 0x4000U) >> 14U)) {
|
||||
csd->flags |= SD_CSD_ERASE_BLK_EN_FLAG;
|
||||
}
|
||||
csd->erase_size = (uint8_t)((raw_csd[1U] &
|
||||
0x3F80U) >> 7U);
|
||||
csd->write_prtect_size = (uint8_t)(raw_csd[1U] &
|
||||
0x7FU);
|
||||
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)) {
|
||||
csd->flags |= SD_CSD_WRITE_BLK_PARTIAL_FLAG;
|
||||
}
|
||||
if ((uint8_t)((raw_csd[0U] & 0x8000U) >> 15U)) {
|
||||
csd->flags |= SD_CSD_FILE_FMT_GRP_FLAG;
|
||||
}
|
||||
if ((uint8_t)((raw_csd[0U] & 0x4000U) >> 14U)) {
|
||||
csd->flags |= SD_CSD_COPY_FLAG;
|
||||
}
|
||||
if ((uint8_t)((raw_csd[0U] & 0x2000U) >> 13U)) {
|
||||
csd->flags |= SD_CSD_PERMANENT_WRITE_PROTECT_FLAG;
|
||||
}
|
||||
if ((uint8_t)((raw_csd[0U] & 0x1000U) >> 12U)) {
|
||||
csd->flags |= SD_CSD_TMP_WRITE_PROTECT_FLAG;
|
||||
}
|
||||
csd->file_fmt = (uint8_t)((raw_csd[0U] & 0xC00U) >> 10U);
|
||||
}
|
||||
|
||||
static inline void sdmmc_decode_cid(struct sd_cid *cid,
|
||||
uint32_t *raw_cid)
|
||||
{
|
||||
cid->manufacturer = (uint8_t)((raw_cid[3U] & 0xFF000000U) >> 24U);
|
||||
cid->application = (uint16_t)((raw_cid[3U] & 0xFFFF00U) >> 8U);
|
||||
|
||||
cid->name[0U] = (uint8_t)((raw_cid[3U] & 0xFFU));
|
||||
cid->name[1U] = (uint8_t)((raw_cid[2U] & 0xFF000000U) >> 24U);
|
||||
cid->name[2U] = (uint8_t)((raw_cid[2U] & 0xFF0000U) >> 16U);
|
||||
cid->name[3U] = (uint8_t)((raw_cid[2U] & 0xFF00U) >> 8U);
|
||||
cid->name[4U] = (uint8_t)((raw_cid[2U] & 0xFFU));
|
||||
|
||||
cid->version = (uint8_t)((raw_cid[1U] & 0xFF000000U) >> 24U);
|
||||
|
||||
cid->ser_num = (uint32_t)((raw_cid[1U] & 0xFFFFFFU) << 8U);
|
||||
cid->ser_num |= (uint32_t)((raw_cid[0U] & 0xFF000000U) >> 24U);
|
||||
|
||||
cid->date = (uint16_t)((raw_cid[0U] & 0xFFF00U) >> 8U);
|
||||
}
|
||||
|
||||
/* Reads card id/csd register (in SPI mode) */
|
||||
static int sdmmc_spi_read_cxd(struct sd_card *card,
|
||||
uint32_t opcode, uint32_t *cxd)
|
||||
{
|
||||
struct sdhc_command cmd = {0};
|
||||
struct sdhc_data data = {0};
|
||||
int ret, i;
|
||||
/* Use internal card buffer for data transfer */
|
||||
uint32_t *cxd_be = (uint32_t *)card->card_buffer;
|
||||
|
||||
cmd.opcode = opcode;
|
||||
cmd.arg = 0;
|
||||
cmd.response_type = SD_SPI_RSP_TYPE_R1;
|
||||
cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT;
|
||||
|
||||
/* CID/CSD is 16 bytes */
|
||||
data.block_size = 16;
|
||||
data.blocks = 1U;
|
||||
data.data = cxd_be;
|
||||
data.timeout_ms = CONFIG_SD_CMD_TIMEOUT;
|
||||
|
||||
ret = sdhc_request(card->sdhc, &cmd, &data);
|
||||
if (ret) {
|
||||
LOG_DBG("CMD%d failed: %d", opcode, ret);
|
||||
}
|
||||
/* Swap endianness of CXD */
|
||||
for (i = 0; i < 4; i++) {
|
||||
cxd[3-i] = sys_be32_to_cpu(cxd_be[i]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Reads card id/csd register (native SD mode */
|
||||
static int sdmmc_read_cxd(struct sd_card *card,
|
||||
uint32_t opcode, uint32_t rca, uint32_t *cxd)
|
||||
{
|
||||
struct sdhc_command cmd = {0};
|
||||
int ret;
|
||||
|
||||
cmd.opcode = opcode;
|
||||
cmd.arg = (rca << 16);
|
||||
cmd.response_type = SD_RSP_TYPE_R2;
|
||||
cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT;
|
||||
|
||||
ret = sdhc_request(card->sdhc, &cmd, NULL);
|
||||
if (ret) {
|
||||
LOG_DBG("CMD%d failed: %d", opcode, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* CSD/CID is 16 bytes */
|
||||
memcpy(cxd, cmd.response, 16);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Read card specific data register */
|
||||
int sdmmc_read_csd(struct sd_card *card)
|
||||
{
|
||||
int ret;
|
||||
uint32_t csd[4];
|
||||
/* Keep CSD on stack for reduced RAM usage */
|
||||
struct sd_csd card_csd;
|
||||
|
||||
if (card->host_props.is_spi && IS_ENABLED(CONFIG_SDHC_SUPPORTS_SPI_MODE)) {
|
||||
ret = sdmmc_spi_read_cxd(card, SD_SEND_CSD, csd);
|
||||
} else if (IS_ENABLED(CONFIG_SDHC_SUPPORTS_NATIVE_MODE)) {
|
||||
ret = sdmmc_read_cxd(card, SD_SEND_CSD, card->relative_addr, csd);
|
||||
} else {
|
||||
/* The host controller must run in either native or SPI mode */
|
||||
return -ENOTSUP;
|
||||
}
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
sdmmc_decode_csd(&card_csd, csd,
|
||||
&card->block_count, &card->block_size);
|
||||
LOG_DBG("Card block count %d, block size %d",
|
||||
card->block_count, card->block_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Reads card identification register, and decodes it */
|
||||
int sdmmc_read_cid(struct sd_card *card)
|
||||
{
|
||||
uint32_t cid[4];
|
||||
int ret;
|
||||
/* Keep CID on stack for reduced RAM usage */
|
||||
struct sd_cid card_cid;
|
||||
|
||||
if (card->host_props.is_spi && IS_ENABLED(CONFIG_SDHC_SUPPORTS_SPI_MODE)) {
|
||||
ret = sdmmc_spi_read_cxd(card, SD_SEND_CID, cid);
|
||||
} else if (IS_ENABLED(CONFIG_SDHC_SUPPORTS_NATIVE_MODE)) {
|
||||
ret = sdmmc_read_cxd(card, SD_ALL_SEND_CID, 0, cid);
|
||||
} else {
|
||||
/* The host controller must run in either native or SPI mode */
|
||||
return -ENOTSUP;
|
||||
}
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Decode SD CID */
|
||||
sdmmc_decode_cid(&card_cid, cid);
|
||||
LOG_DBG("Card MID: 0x%x, OID: %c%c", card_cid.manufacturer,
|
||||
((char *)&card_cid.application)[0],
|
||||
((char *)&card_cid.application)[1]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Implements signal voltage switch procedure described in section 3.6.1 of
|
||||
* SD specification.
|
||||
*/
|
||||
int sdmmc_switch_voltage(struct sd_card *card)
|
||||
{
|
||||
int ret, sd_clock;
|
||||
struct sdhc_command cmd = {0};
|
||||
|
||||
/* Check to make sure card supports 1.8V */
|
||||
if (!(card->flags & SD_1800MV_FLAG)) {
|
||||
/* Do not attempt to switch voltages */
|
||||
LOG_WRN("SD card reports as SDHC/SDXC, but does not support 1.8V");
|
||||
return 0;
|
||||
}
|
||||
/* Send CMD11 to request a voltage switch */
|
||||
cmd.opcode = SD_VOL_SWITCH;
|
||||
cmd.arg = 0U;
|
||||
cmd.response_type = SD_RSP_TYPE_R1;
|
||||
cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT;
|
||||
ret = sdhc_request(card->sdhc, &cmd, NULL);
|
||||
if (ret) {
|
||||
LOG_DBG("CMD11 failed");
|
||||
return ret;
|
||||
}
|
||||
/* Check R1 response for error */
|
||||
ret = sd_check_response(&cmd);
|
||||
if (ret) {
|
||||
LOG_DBG("SD response to CMD11 indicates error");
|
||||
return ret;
|
||||
}
|
||||
/*
|
||||
* Card should drive CMD and DAT[3:0] signals low at the next clock
|
||||
* cycle. Some cards will only drive these
|
||||
* lines low briefly, so we should check as soon as possible
|
||||
*/
|
||||
if (!(sdhc_card_busy(card->sdhc))) {
|
||||
/* Delay 1ms to allow card to drive lines low */
|
||||
sd_delay(1);
|
||||
if (!sdhc_card_busy(card->sdhc)) {
|
||||
/* Card did not drive CMD and DAT lines low */
|
||||
LOG_DBG("Card did not drive DAT lines low");
|
||||
return -EAGAIN;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Per SD spec (section "Timing to Switch Signal Voltage"),
|
||||
* host must gate clock at least 5ms.
|
||||
*/
|
||||
sd_clock = card->bus_io.clock;
|
||||
card->bus_io.clock = 0;
|
||||
ret = sdhc_set_io(card->sdhc, &card->bus_io);
|
||||
if (ret) {
|
||||
LOG_DBG("Failed to gate SD clock");
|
||||
return ret;
|
||||
}
|
||||
/* Now that clock is gated, change signal voltage */
|
||||
card->bus_io.signal_voltage = SD_VOL_1_8_V;
|
||||
ret = sdhc_set_io(card->sdhc, &card->bus_io);
|
||||
if (ret) {
|
||||
LOG_DBG("Failed to switch SD host to 1.8V");
|
||||
return ret;
|
||||
}
|
||||
sd_delay(10); /* Gate for 10ms, even though spec requires 5 */
|
||||
/* Restart the clock */
|
||||
card->bus_io.clock = sd_clock;
|
||||
ret = sdhc_set_io(card->sdhc, &card->bus_io);
|
||||
if (ret) {
|
||||
LOG_ERR("Failed to restart SD clock");
|
||||
return ret;
|
||||
}
|
||||
/*
|
||||
* If SD does not drive at least one of
|
||||
* DAT[3:0] high within 1ms, switch failed
|
||||
*/
|
||||
sd_delay(1);
|
||||
if (sdhc_card_busy(card->sdhc)) {
|
||||
LOG_DBG("Card failed to switch voltages");
|
||||
return -EAGAIN;
|
||||
}
|
||||
card->card_voltage = SD_VOL_1_8_V;
|
||||
LOG_INF("Card switched to 1.8V signaling");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Requests card to publish a new relative card address, and move from
|
||||
* identification to data mode
|
||||
*/
|
||||
int sdmmc_request_rca(struct sd_card *card)
|
||||
{
|
||||
struct sdhc_command cmd = {0};
|
||||
int ret;
|
||||
|
||||
cmd.opcode = SD_SEND_RELATIVE_ADDR;
|
||||
cmd.arg = 0;
|
||||
cmd.response_type = SD_RSP_TYPE_R6;
|
||||
cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT;
|
||||
/* Issue CMD3 until card responds with nonzero RCA */
|
||||
do {
|
||||
ret = sdhc_request(card->sdhc, &cmd, NULL);
|
||||
if (ret) {
|
||||
LOG_DBG("CMD3 failed");
|
||||
return ret;
|
||||
}
|
||||
/* Card RCA is in upper 16 bits of response */
|
||||
card->relative_addr = ((cmd.response[0U] & 0xFFFF0000) >> 16U);
|
||||
} while (card->relative_addr == 0U);
|
||||
LOG_DBG("Card relative addr: %d", card->relative_addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Selects card, moving it into data transfer mode
|
||||
*/
|
||||
int sdmmc_select_card(struct sd_card *card)
|
||||
{
|
||||
struct sdhc_command cmd = {0};
|
||||
int ret;
|
||||
|
||||
cmd.opcode = SD_SELECT_CARD;
|
||||
cmd.arg = (card->relative_addr << 16U);
|
||||
cmd.response_type = SD_RSP_TYPE_R1;
|
||||
cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT;
|
||||
|
||||
ret = sdhc_request(card->sdhc, &cmd, NULL);
|
||||
if (ret) {
|
||||
LOG_DBG("CMD7 failed");
|
||||
return ret;
|
||||
}
|
||||
ret = sd_check_response(&cmd);
|
||||
if (ret) {
|
||||
LOG_DBG("CMD7 reports error");
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
48
subsys/sd/sd_ops.h
Normal file
48
subsys/sd/sd_ops.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright 2022 NXP
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
|
||||
#ifndef ZEPHYR_SUBSYS_SD_SD_OPS_H_
|
||||
#define ZEPHYR_SUBSYS_SD_SD_OPS_H_
|
||||
|
||||
/*
|
||||
* Switches voltage of SD card to 1.8V, as described by
|
||||
* "Signal volatage switch procedure" in section 3.6.1 of SD specification.
|
||||
*/
|
||||
int sdmmc_switch_voltage(struct sd_card *card);
|
||||
|
||||
/*
|
||||
* Reads card identification register, and decodes it
|
||||
*/
|
||||
int sdmmc_read_cid(struct sd_card *card);
|
||||
|
||||
/*
|
||||
* Read card specific data register
|
||||
*/
|
||||
int sdmmc_read_csd(struct sd_card *card);
|
||||
|
||||
/*
|
||||
* Requests card to publish a new relative card address, and move from
|
||||
* identification to data mode
|
||||
*/
|
||||
int sdmmc_request_rca(struct sd_card *card);
|
||||
|
||||
/*
|
||||
* Selects card, moving it into data transfer mode
|
||||
*/
|
||||
int sdmmc_select_card(struct sd_card *card);
|
||||
|
||||
/* Returns 1 if host supports UHS, zero otherwise */
|
||||
static inline int sdmmc_host_uhs(struct sdhc_host_props *props)
|
||||
{
|
||||
return (props->host_caps.sdr50_support |
|
||||
props->host_caps.uhs_2_support |
|
||||
props->host_caps.sdr104_support |
|
||||
props->host_caps.ddr50_support)
|
||||
& (props->host_caps.vol_180_support);
|
||||
}
|
||||
|
||||
#endif /* ZEPHYR_SUBSYS_SD_SD_OPS_H_ */
|
|
@ -28,6 +28,15 @@ enum sd_return_codes {
|
|||
SD_RESTART = 3,
|
||||
};
|
||||
|
||||
/* Checks SD status return codes */
|
||||
static inline int sd_check_response(struct sdhc_command *cmd)
|
||||
{
|
||||
if (cmd->response_type == SD_RSP_TYPE_R1) {
|
||||
return (cmd->response[0U] & SD_R1_ERR_FLAGS);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Delay function for SD subsystem */
|
||||
static inline void sd_delay(unsigned int millis)
|
||||
{
|
||||
|
|
|
@ -14,122 +14,10 @@
|
|||
#include <zephyr/drivers/disk.h>
|
||||
|
||||
#include "sd_utils.h"
|
||||
#include "sd_ops.h"
|
||||
|
||||
LOG_MODULE_DECLARE(sd, CONFIG_SD_LOG_LEVEL);
|
||||
|
||||
|
||||
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;
|
||||
|
||||
csd->csd_structure = (uint8_t)((raw_csd[3U] &
|
||||
0xC0000000U) >> 30U);
|
||||
csd->read_time1 = (uint8_t)((raw_csd[3U] &
|
||||
0xFF0000U) >> 16U);
|
||||
csd->read_time2 = (uint8_t)((raw_csd[3U] &
|
||||
0xFF00U) >> 8U);
|
||||
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) {
|
||||
csd->flags |= SD_CSD_READ_BLK_PARTIAL_FLAG;
|
||||
}
|
||||
if (raw_csd[2U] & 0x4000U) {
|
||||
csd->flags |= SD_CSD_READ_BLK_PARTIAL_FLAG;
|
||||
}
|
||||
if (raw_csd[2U] & 0x2000U) {
|
||||
csd->flags |= SD_CSD_READ_BLK_MISALIGN_FLAG;
|
||||
}
|
||||
if (raw_csd[2U] & 0x1000U) {
|
||||
csd->flags |= SD_CSD_DSR_IMPLEMENTED_FLAG;
|
||||
}
|
||||
|
||||
switch (csd->csd_structure) {
|
||||
case 0:
|
||||
csd->device_size = (uint32_t)((raw_csd[2U] &
|
||||
0x3FFU) << 2U);
|
||||
csd->device_size |= (uint32_t)((raw_csd[1U] &
|
||||
0xC0000000U) >> 30U);
|
||||
csd->read_current_min = (uint8_t)((raw_csd[1U] &
|
||||
0x38000000U) >> 27U);
|
||||
csd->read_current_max = (uint8_t)((raw_csd[1U] &
|
||||
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. */
|
||||
tmp_blk_count = ((csd->device_size + 1U) <<
|
||||
(csd->dev_size_mul + 2U));
|
||||
tmp_blk_size = (1U << (csd->read_blk_len));
|
||||
if (tmp_blk_size != SDMMC_DEFAULT_BLOCK_SIZE) {
|
||||
tmp_blk_count = (tmp_blk_count * tmp_blk_size);
|
||||
tmp_blk_size = SDMMC_DEFAULT_BLOCK_SIZE;
|
||||
tmp_blk_count = (tmp_blk_count / tmp_blk_size);
|
||||
}
|
||||
if (blk_count) {
|
||||
*blk_count = tmp_blk_count;
|
||||
}
|
||||
if (blk_size) {
|
||||
*blk_size = tmp_blk_size;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
tmp_blk_size = SDMMC_DEFAULT_BLOCK_SIZE;
|
||||
|
||||
csd->device_size = (uint32_t)((raw_csd[2U] &
|
||||
0x3FU) << 16U);
|
||||
csd->device_size |= (uint32_t)((raw_csd[1U] &
|
||||
0xFFFF0000U) >> 16U);
|
||||
|
||||
tmp_blk_count = ((csd->device_size + 1U) * 1024U);
|
||||
if (blk_count) {
|
||||
*blk_count = tmp_blk_count;
|
||||
}
|
||||
if (blk_size) {
|
||||
*blk_size = tmp_blk_size;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if ((uint8_t)((raw_csd[1U] & 0x4000U) >> 14U)) {
|
||||
csd->flags |= SD_CSD_ERASE_BLK_EN_FLAG;
|
||||
}
|
||||
csd->erase_size = (uint8_t)((raw_csd[1U] &
|
||||
0x3F80U) >> 7U);
|
||||
csd->write_prtect_size = (uint8_t)(raw_csd[1U] &
|
||||
0x7FU);
|
||||
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)) {
|
||||
csd->flags |= SD_CSD_WRITE_BLK_PARTIAL_FLAG;
|
||||
}
|
||||
if ((uint8_t)((raw_csd[0U] & 0x8000U) >> 15U)) {
|
||||
csd->flags |= SD_CSD_FILE_FMT_GRP_FLAG;
|
||||
}
|
||||
if ((uint8_t)((raw_csd[0U] & 0x4000U) >> 14U)) {
|
||||
csd->flags |= SD_CSD_COPY_FLAG;
|
||||
}
|
||||
if ((uint8_t)((raw_csd[0U] & 0x2000U) >> 13U)) {
|
||||
csd->flags |= SD_CSD_PERMANENT_WRITE_PROTECT_FLAG;
|
||||
}
|
||||
if ((uint8_t)((raw_csd[0U] & 0x1000U) >> 12U)) {
|
||||
csd->flags |= SD_CSD_TMP_WRITE_PROTECT_FLAG;
|
||||
}
|
||||
csd->file_fmt = (uint8_t)((raw_csd[0U] & 0xC00U) >> 10U);
|
||||
}
|
||||
|
||||
static inline void sdmmc_decode_scr(struct sd_scr *scr,
|
||||
uint32_t *raw_scr, uint32_t *version)
|
||||
{
|
||||
|
@ -172,35 +60,6 @@ static inline void sdmmc_decode_scr(struct sd_scr *scr,
|
|||
}
|
||||
}
|
||||
|
||||
static inline void sdmmc_decode_cid(struct sd_cid *cid,
|
||||
uint32_t *raw_cid)
|
||||
{
|
||||
cid->manufacturer = (uint8_t)((raw_cid[3U] & 0xFF000000U) >> 24U);
|
||||
cid->application = (uint16_t)((raw_cid[3U] & 0xFFFF00U) >> 8U);
|
||||
|
||||
cid->name[0U] = (uint8_t)((raw_cid[3U] & 0xFFU));
|
||||
cid->name[1U] = (uint8_t)((raw_cid[2U] & 0xFF000000U) >> 24U);
|
||||
cid->name[2U] = (uint8_t)((raw_cid[2U] & 0xFF0000U) >> 16U);
|
||||
cid->name[3U] = (uint8_t)((raw_cid[2U] & 0xFF00U) >> 8U);
|
||||
cid->name[4U] = (uint8_t)((raw_cid[2U] & 0xFFU));
|
||||
|
||||
cid->version = (uint8_t)((raw_cid[1U] & 0xFF000000U) >> 24U);
|
||||
|
||||
cid->ser_num = (uint32_t)((raw_cid[1U] & 0xFFFFFFU) << 8U);
|
||||
cid->ser_num |= (uint32_t)((raw_cid[0U] & 0xFF000000U) >> 24U);
|
||||
|
||||
cid->date = (uint16_t)((raw_cid[0U] & 0xFFF00U) >> 8U);
|
||||
}
|
||||
|
||||
/* Checks SD status return codes */
|
||||
static inline int sdmmc_check_response(struct sdhc_command *cmd)
|
||||
{
|
||||
if (cmd->response_type == SD_RSP_TYPE_R1) {
|
||||
return (cmd->response[0U] & SD_R1_ERR_FLAGS);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Helper to send SD app command */
|
||||
static int sdmmc_app_command(struct sd_card *card, int relative_card_address)
|
||||
{
|
||||
|
@ -216,7 +75,7 @@ static int sdmmc_app_command(struct sd_card *card, int relative_card_address)
|
|||
/* We want to retry transmission */
|
||||
return SD_RETRY;
|
||||
}
|
||||
ret = sdmmc_check_response(&cmd);
|
||||
ret = sd_check_response(&cmd);
|
||||
if (ret) {
|
||||
LOG_WRN("SD app command failed with R1 response of 0x%X",
|
||||
cmd.response[0]);
|
||||
|
@ -287,252 +146,6 @@ static int sdmmc_send_ocr(struct sd_card *card, int ocr)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Implements signal voltage switch procedure described in section 3.6.1 of
|
||||
* SD specification.
|
||||
*/
|
||||
static int sdmmc_switch_voltage(struct sd_card *card)
|
||||
{
|
||||
int ret, sd_clock;
|
||||
struct sdhc_command cmd = {0};
|
||||
|
||||
/* Check to make sure card supports 1.8V */
|
||||
if (!(card->flags & SD_1800MV_FLAG)) {
|
||||
/* Do not attempt to switch voltages */
|
||||
LOG_WRN("SD card reports as SDHC/SDXC, but does not support 1.8V");
|
||||
return 0;
|
||||
}
|
||||
/* Send CMD11 to request a voltage switch */
|
||||
cmd.opcode = SD_VOL_SWITCH;
|
||||
cmd.arg = 0U;
|
||||
cmd.response_type = SD_RSP_TYPE_R1;
|
||||
cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT;
|
||||
ret = sdhc_request(card->sdhc, &cmd, NULL);
|
||||
if (ret) {
|
||||
LOG_DBG("CMD11 failed");
|
||||
return ret;
|
||||
}
|
||||
/* Check R1 response for error */
|
||||
ret = sdmmc_check_response(&cmd);
|
||||
if (ret) {
|
||||
LOG_DBG("SD response to CMD11 indicates error");
|
||||
return ret;
|
||||
}
|
||||
/*
|
||||
* Card should drive CMD and DAT[3:0] signals low at the next clock
|
||||
* cycle. Some cards will only drive these
|
||||
* lines low briefly, so we should check as soon as possible
|
||||
*/
|
||||
if (!(sdhc_card_busy(card->sdhc))) {
|
||||
/* Delay 1ms to allow card to drive lines low */
|
||||
sd_delay(1);
|
||||
if (!sdhc_card_busy(card->sdhc)) {
|
||||
/* Card did not drive CMD and DAT lines low */
|
||||
LOG_DBG("Card did not drive DAT lines low");
|
||||
return -EAGAIN;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Per SD spec (section "Timing to Switch Signal Voltage"),
|
||||
* host must gate clock at least 5ms.
|
||||
*/
|
||||
sd_clock = card->bus_io.clock;
|
||||
card->bus_io.clock = 0;
|
||||
ret = sdhc_set_io(card->sdhc, &card->bus_io);
|
||||
if (ret) {
|
||||
LOG_DBG("Failed to gate SD clock");
|
||||
return ret;
|
||||
}
|
||||
/* Now that clock is gated, change signal voltage */
|
||||
card->bus_io.signal_voltage = SD_VOL_1_8_V;
|
||||
ret = sdhc_set_io(card->sdhc, &card->bus_io);
|
||||
if (ret) {
|
||||
LOG_DBG("Failed to switch SD host to 1.8V");
|
||||
return ret;
|
||||
}
|
||||
sd_delay(10); /* Gate for 10ms, even though spec requires 5 */
|
||||
/* Restart the clock */
|
||||
card->bus_io.clock = sd_clock;
|
||||
ret = sdhc_set_io(card->sdhc, &card->bus_io);
|
||||
if (ret) {
|
||||
LOG_ERR("Failed to restart SD clock");
|
||||
return ret;
|
||||
}
|
||||
/*
|
||||
* If SD does not drive at least one of
|
||||
* DAT[3:0] high within 1ms, switch failed
|
||||
*/
|
||||
sd_delay(1);
|
||||
if (sdhc_card_busy(card->sdhc)) {
|
||||
LOG_DBG("Card failed to switch voltages");
|
||||
return -EAGAIN;
|
||||
}
|
||||
card->card_voltage = SD_VOL_1_8_V;
|
||||
LOG_INF("Card switched to 1.8V signaling");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Reads card id/csd register (in SPI mode) */
|
||||
static int sdmmc_spi_read_cxd(struct sd_card *card,
|
||||
uint32_t opcode, uint32_t *cxd)
|
||||
{
|
||||
struct sdhc_command cmd = {0};
|
||||
struct sdhc_data data = {0};
|
||||
int ret, i;
|
||||
/* Use internal card buffer for data transfer */
|
||||
uint32_t *cxd_be = (uint32_t *)card->card_buffer;
|
||||
|
||||
cmd.opcode = opcode;
|
||||
cmd.arg = 0;
|
||||
cmd.response_type = SD_SPI_RSP_TYPE_R1;
|
||||
cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT;
|
||||
|
||||
/* CID/CSD is 16 bytes */
|
||||
data.block_size = 16;
|
||||
data.blocks = 1U;
|
||||
data.data = cxd_be;
|
||||
data.timeout_ms = CONFIG_SD_CMD_TIMEOUT;
|
||||
|
||||
ret = sdhc_request(card->sdhc, &cmd, &data);
|
||||
if (ret) {
|
||||
LOG_DBG("CMD%d failed: %d", opcode, ret);
|
||||
}
|
||||
/* Swap endianness of CXD */
|
||||
for (i = 0; i < 4; i++) {
|
||||
cxd[3-i] = sys_be32_to_cpu(cxd_be[i]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Reads card id/csd register (native SD mode */
|
||||
static int sdmmc_read_cxd(struct sd_card *card,
|
||||
uint32_t opcode, uint32_t rca, uint32_t *cxd)
|
||||
{
|
||||
struct sdhc_command cmd = {0};
|
||||
int ret;
|
||||
|
||||
cmd.opcode = opcode;
|
||||
cmd.arg = (rca << 16);
|
||||
cmd.response_type = SD_RSP_TYPE_R2;
|
||||
cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT;
|
||||
|
||||
ret = sdhc_request(card->sdhc, &cmd, NULL);
|
||||
if (ret) {
|
||||
LOG_DBG("CMD%d failed: %d", opcode, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* CSD/CID is 16 bytes */
|
||||
memcpy(cxd, cmd.response, 16);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Reads card identification register, and decodes it */
|
||||
static int sdmmc_read_cid(struct sd_card *card)
|
||||
{
|
||||
uint32_t cid[4];
|
||||
int ret;
|
||||
/* Keep CID on stack for reduced RAM usage */
|
||||
struct sd_cid card_cid;
|
||||
|
||||
if (card->host_props.is_spi && IS_ENABLED(CONFIG_SDHC_SUPPORTS_SPI_MODE)) {
|
||||
ret = sdmmc_spi_read_cxd(card, SD_SEND_CID, cid);
|
||||
} else if (IS_ENABLED(CONFIG_SDHC_SUPPORTS_NATIVE_MODE)) {
|
||||
ret = sdmmc_read_cxd(card, SD_ALL_SEND_CID, 0, cid);
|
||||
} else {
|
||||
/* The host controller must run in either native or SPI mode */
|
||||
return -ENOTSUP;
|
||||
}
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Decode SD CID */
|
||||
sdmmc_decode_cid(&card_cid, cid);
|
||||
LOG_DBG("Card MID: 0x%x, OID: %c%c", card_cid.manufacturer,
|
||||
((char *)&card_cid.application)[0],
|
||||
((char *)&card_cid.application)[1]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Requests card to publish a new relative card address, and move from
|
||||
* identification to data mode
|
||||
*/
|
||||
static int sdmmc_request_rca(struct sd_card *card)
|
||||
{
|
||||
struct sdhc_command cmd = {0};
|
||||
int ret;
|
||||
|
||||
cmd.opcode = SD_SEND_RELATIVE_ADDR;
|
||||
cmd.arg = 0;
|
||||
cmd.response_type = SD_RSP_TYPE_R6;
|
||||
cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT;
|
||||
/* Issue CMD3 until card responds with nonzero RCA */
|
||||
do {
|
||||
ret = sdhc_request(card->sdhc, &cmd, NULL);
|
||||
if (ret) {
|
||||
LOG_DBG("CMD3 failed");
|
||||
return ret;
|
||||
}
|
||||
/* Card RCA is in upper 16 bits of response */
|
||||
card->relative_addr = ((cmd.response[0U] & 0xFFFF0000) >> 16U);
|
||||
} while (card->relative_addr == 0U);
|
||||
LOG_DBG("Card relative addr: %d", card->relative_addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read card specific data register */
|
||||
static int sdmmc_read_csd(struct sd_card *card)
|
||||
{
|
||||
int ret;
|
||||
uint32_t csd[4];
|
||||
/* Keep CSD on stack for reduced RAM usage */
|
||||
struct sd_csd card_csd;
|
||||
|
||||
if (card->host_props.is_spi && IS_ENABLED(CONFIG_SDHC_SUPPORTS_SPI_MODE)) {
|
||||
ret = sdmmc_spi_read_cxd(card, SD_SEND_CSD, csd);
|
||||
} else if (IS_ENABLED(CONFIG_SDHC_SUPPORTS_NATIVE_MODE)) {
|
||||
ret = sdmmc_read_cxd(card, SD_SEND_CSD, card->relative_addr, csd);
|
||||
} else {
|
||||
/* The host controller must run in either native or SPI mode */
|
||||
return -ENOTSUP;
|
||||
}
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
sdmmc_decode_csd(&card_csd, csd,
|
||||
&card->block_count, &card->block_size);
|
||||
LOG_DBG("Card block count %d, block size %d",
|
||||
card->block_count, card->block_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdmmc_select_card(struct sd_card *card)
|
||||
{
|
||||
struct sdhc_command cmd = {0};
|
||||
int ret;
|
||||
|
||||
cmd.opcode = SD_SELECT_CARD;
|
||||
cmd.arg = (card->relative_addr << 16U);
|
||||
cmd.response_type = SD_RSP_TYPE_R1;
|
||||
cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT;
|
||||
|
||||
ret = sdhc_request(card->sdhc, &cmd, NULL);
|
||||
if (ret) {
|
||||
LOG_DBG("CMD7 failed");
|
||||
return ret;
|
||||
}
|
||||
ret = sdmmc_check_response(&cmd);
|
||||
if (ret) {
|
||||
LOG_DBG("CMD7 reports error");
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Reads SD configuration register */
|
||||
static int sdmmc_read_scr(struct sd_card *card)
|
||||
{
|
||||
|
@ -637,7 +250,7 @@ static int sdmmc_set_bus_width(struct sd_card *card, enum sdhc_bus_width width)
|
|||
LOG_DBG("Error on ACMD6: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
ret = sdmmc_check_response(&cmd);
|
||||
ret = sd_check_response(&cmd);
|
||||
if (ret) {
|
||||
LOG_DBG("ACMD6 reports error, response 0x%x", cmd.response[0U]);
|
||||
return ret;
|
||||
|
@ -718,16 +331,6 @@ static int sdmmc_read_switch(struct sd_card *card)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Returns 1 if host supports UHS, zero otherwise */
|
||||
static inline int sdmmc_host_uhs(struct sdhc_host_props *props)
|
||||
{
|
||||
return (props->host_caps.sdr50_support |
|
||||
props->host_caps.uhs_2_support |
|
||||
props->host_caps.sdr104_support |
|
||||
props->host_caps.ddr50_support)
|
||||
& (props->host_caps.vol_180_support);
|
||||
}
|
||||
|
||||
static inline void sdmmc_select_bus_speed(struct sd_card *card)
|
||||
{
|
||||
/*
|
||||
|
@ -1327,7 +930,7 @@ static int sdmmc_query_written(struct sd_card *card, uint32_t *num_written)
|
|||
LOG_DBG("ACMD22 failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
ret = sdmmc_check_response(&cmd);
|
||||
ret = sd_check_response(&cmd);
|
||||
if (ret) {
|
||||
LOG_DBG("ACMD22 reports error");
|
||||
return ret;
|
||||
|
|
Loading…
Reference in a new issue