Skip to content

Commit

Permalink
disk: sdhc: Add support for standard-capacity SD cards
Browse files Browse the repository at this point in the history
This patch adds support for SDSCv1 and SDSCv2 cards. It has been tested
with 2 GiB SDSC and 4 GiB to 32 GiB SDHC cards from SanDisk.

Signed-off-by: Markus Fuchs <[email protected]>
  • Loading branch information
mrfuchs authored and galak committed Sep 19, 2019
1 parent ff37217 commit e52cff8
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 28 deletions.
2 changes: 2 additions & 0 deletions subsys/disk/disk_access_sdhc.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,12 @@ enum sdhc_app_ext_cmd {
/* Fields in various card registers */
#define SDHC_HCS (1 << 30)
#define SDHC_CCS (1 << 30)
#define SDHC_BUSY (1 << 31)
#define SDHC_VHS_MASK (0x0F << 8)
#define SDHC_VHS_3V3 (1 << 8)
#define SDHC_CHECK 0xAA
#define SDHC_CSD_SIZE 16
#define SDHC_CSD_V1 0
#define SDHC_CSD_V2 1

/* Data block tokens */
Expand Down
122 changes: 94 additions & 28 deletions subsys/disk/disk_access_spi_sdhc.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ struct sdhc_spi_data {
struct device *cs;
u32_t pin;

bool high_capacity;
u32_t sector_count;
u8_t status;
#if LOG_LEVEL >= LOG_LEVEL_DBG
Expand Down Expand Up @@ -468,8 +469,8 @@ static int sdhc_spi_go_idle(struct sdhc_spi_data *data)
return sdhc_spi_cmd_r1_idle(data, SDHC_GO_IDLE_STATE, 0);
}

/* Checks the supported host volatage and basic protocol */
static int sdhc_spi_check_card(struct sdhc_spi_data *data)
/* Checks the supported host voltage and basic protocol of a SDHC card */
static int sdhc_spi_check_interface(struct sdhc_spi_data *data)
{
u32_t cond;
int err;
Expand Down Expand Up @@ -501,8 +502,11 @@ static int sdhc_spi_detect(struct sdhc_spi_data *data)
u32_t ocr;
struct sdhc_retry retry;
u8_t structure;
u8_t readbllen;
u32_t csize;
u8_t csizemult;
u8_t buf[SDHC_CSD_SIZE];
bool is_v2;

data->cfg.frequency = SDHC_SPI_INITIAL_SPEED;
data->status = DISK_STATUS_UNINIT;
Expand All @@ -513,10 +517,9 @@ static int sdhc_spi_detect(struct sdhc_spi_data *data)
do {
err = sdhc_spi_go_idle(data);
if (err == 0) {
err = sdhc_spi_check_card(data);
if (err == 0) {
break;
}
err = sdhc_spi_check_interface(data);
is_v2 = (err == 0) ? true : false;
break;
}

if (!sdhc_retry_ok(&retry)) {
Expand All @@ -534,7 +537,8 @@ static int sdhc_spi_detect(struct sdhc_spi_data *data)
do {
sdhc_spi_cmd_r1_raw(data, SDHC_APP_CMD, 0);

err = sdhc_spi_cmd_r1(data, SDHC_SEND_OP_COND, SDHC_HCS);
/* Set HCS only if card conforms to specification v2.00 (cf. 4.2.3) */
err = sdhc_spi_cmd_r1(data, SDHC_SEND_OP_COND, is_v2 ? SDHC_HCS : 0);
if (err == 0) {
break;
}
Expand All @@ -545,15 +549,36 @@ static int sdhc_spi_detect(struct sdhc_spi_data *data)
return -ETIMEDOUT;
}

/* Read OCR and confirm this is a SDHC card */
err = sdhc_spi_cmd_r3(data, SDHC_READ_OCR, 0, &ocr);
if (err != 0) {
return err;
ocr = 0;
if (is_v2) {
do {
/* Read OCR to check if this is a SDSC or SDHC card.
* CCS bit is valid after BUSY bit is set.
*/
err = sdhc_spi_cmd_r3(data, SDHC_READ_OCR, 0, &ocr);
if (err != 0) {
return err;
}
if ((ocr & SDHC_BUSY) != 0U) {
break;
}
} while (sdhc_retry_ok(&retry));

if (err != 0) {
/* Card never finished power-up */
return -ETIMEDOUT;
}
}

if ((ocr & SDHC_CCS) == 0U) {
/* A 'SDSC' card */
return -ENOTSUP;
if ((ocr & SDHC_CCS) != 0U) {
data->high_capacity = true;
} else {
/* A 'SDSC' card: Set block length to 512 bytes. */
data->high_capacity = false;
err = sdhc_cmd_r1(data, SDHC_SET_BLOCK_SIZE, SDMMC_DEFAULT_BLOCK_SIZE);
if (err != 0) {
return err;
}
}

/* Read the CSD */
Expand All @@ -569,22 +594,43 @@ static int sdhc_spi_detect(struct sdhc_spi_data *data)

/* Bits 126..127 are the structure version */
structure = (buf[0] >> 6);
if (structure != SDHC_CSD_V2) {
switch (structure) {
case SDHC_CSD_V1:
/* The maximum read data block length is given by bits 80..83 raised
* to the power of 2. Possible values are 9, 10 and 11 for 512, 1024
* and 2048 bytes, respectively. This driver does not make use of block
* lengths greater than 512 bytes, but forces 512 byte block transfers
* instead.
*/
readbllen = buf[5] & ((1 << 4) - 1);
if ((readbllen < 9) || (readbllen > 11)) {
/* Invalid maximum read data block length (cf. section 5.3.2) */
return -ENOTSUP;
}
/* The capacity of the card is given by bits 62..73 plus 1 multiplied
* by bits 47..49 plus 2 raised to the power of 2 in maximum read data
* blocks.
*/
csize = (sys_get_be32(&buf[6]) >> 14) & ((1 << 12) - 1);
csizemult = (u8_t) ((sys_get_be16(&buf[9]) >> 7) & ((1 << 3) - 1));
data->sector_count = ((csize + 1) << (csizemult + 2 + readbllen - 9));
break;
case SDHC_CSD_V2:
/* Bits 48..69 are the capacity of the card in 512 KiB units, minus 1.
*/
csize = sys_get_be32(&buf[6]) & ((1 << 22) - 1);
if (csize < 4112) {
/* Invalid capacity (cf. section 5.3.3) */
return -ENOTSUP;
}
data->sector_count = (csize + 1) *
(512 * 1024 / SDMMC_DEFAULT_BLOCK_SIZE);
break;
default:
/* Unsupported CSD format */
return -ENOTSUP;
}

/* Bits 48..69 are the capacity of the card in 512 KiB units, minus 1.
*/
csize = sys_get_be32(&buf[6]) & ((1 << 22) - 1);
if (csize < 4112) {
/* Invalid capacity according to section 5.3.3 */
return -ENOTSUP;
}

data->sector_count = (csize + 1) *
(512 * 1024 / SDMMC_DEFAULT_BLOCK_SIZE);

LOG_INF("Found a ~%u MiB SDHC card.",
data->sector_count / (1024 * 1024 / SDMMC_DEFAULT_BLOCK_SIZE));

Expand Down Expand Up @@ -615,16 +661,26 @@ static int sdhc_spi_read(struct sdhc_spi_data *data,
u8_t *buf, u32_t sector, u32_t count)
{
int err;
u32_t addr;

err = sdhc_map_disk_status(data->status);
if (err != 0) {
return err;
}

/* Translate sector number to data address.
* SDSC cards use byte addressing, SDHC cards use block addressing.
*/
if (data->high_capacity) {
addr = sector;
} else {
addr = sector * SDMMC_DEFAULT_BLOCK_SIZE;
}

sdhc_spi_set_cs(data, 0);

/* Send the start read command */
err = sdhc_spi_cmd_r1(data, SDHC_READ_MULTIPLE_BLOCK, sector);
err = sdhc_spi_cmd_r1(data, SDHC_READ_MULTIPLE_BLOCK, addr);
if (err != 0) {
goto error;
}
Expand Down Expand Up @@ -655,6 +711,7 @@ static int sdhc_spi_write(struct sdhc_spi_data *data,
const u8_t *buf, u32_t sector, u32_t count)
{
int err;
u32_t addr;

err = sdhc_map_disk_status(data->status);
if (err != 0) {
Expand All @@ -665,7 +722,16 @@ static int sdhc_spi_write(struct sdhc_spi_data *data,

/* Write the blocks one-by-one */
for (; count != 0U; count--) {
err = sdhc_spi_cmd_r1(data, SDHC_WRITE_BLOCK, sector);
/* Translate sector number to data address.
* SDSC cards use byte addressing, SDHC cards use block addressing.
*/
if (data->high_capacity) {
addr = sector;
} else {
addr = sector * SDMMC_DEFAULT_BLOCK_SIZE;
}

err = sdhc_spi_cmd_r1(data, SDHC_WRITE_BLOCK, addr);
if (err < 0) {
goto error;
}
Expand Down

0 comments on commit e52cff8

Please sign in to comment.