disk: sdhc: Add support for standard-capacity SD cards

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 <markus.fuchs@de.sauter-bc.com>
This commit is contained in:
Markus Fuchs 2019-08-11 23:47:05 +02:00 committed by Kumar Gala
commit e52cff81a9
2 changed files with 96 additions and 28 deletions

View file

@ -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 */

View file

@ -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
@ -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;
@ -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;
@ -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)) {
@ -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;
}
@ -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 */
@ -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));
@ -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;
}
@ -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) {
@ -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;
}