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:
parent
ff37217117
commit
e52cff81a9
2 changed files with 96 additions and 28 deletions
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue