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 */
|
/* Fields in various card registers */
|
||||||
#define SDHC_HCS (1 << 30)
|
#define SDHC_HCS (1 << 30)
|
||||||
#define SDHC_CCS (1 << 30)
|
#define SDHC_CCS (1 << 30)
|
||||||
|
#define SDHC_BUSY (1 << 31)
|
||||||
#define SDHC_VHS_MASK (0x0F << 8)
|
#define SDHC_VHS_MASK (0x0F << 8)
|
||||||
#define SDHC_VHS_3V3 (1 << 8)
|
#define SDHC_VHS_3V3 (1 << 8)
|
||||||
#define SDHC_CHECK 0xAA
|
#define SDHC_CHECK 0xAA
|
||||||
#define SDHC_CSD_SIZE 16
|
#define SDHC_CSD_SIZE 16
|
||||||
|
#define SDHC_CSD_V1 0
|
||||||
#define SDHC_CSD_V2 1
|
#define SDHC_CSD_V2 1
|
||||||
|
|
||||||
/* Data block tokens */
|
/* Data block tokens */
|
||||||
|
|
|
@ -29,6 +29,7 @@ struct sdhc_spi_data {
|
||||||
struct device *cs;
|
struct device *cs;
|
||||||
u32_t pin;
|
u32_t pin;
|
||||||
|
|
||||||
|
bool high_capacity;
|
||||||
u32_t sector_count;
|
u32_t sector_count;
|
||||||
u8_t status;
|
u8_t status;
|
||||||
#if LOG_LEVEL >= LOG_LEVEL_DBG
|
#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);
|
return sdhc_spi_cmd_r1_idle(data, SDHC_GO_IDLE_STATE, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Checks the supported host volatage and basic protocol */
|
/* Checks the supported host voltage and basic protocol of a SDHC card */
|
||||||
static int sdhc_spi_check_card(struct sdhc_spi_data *data)
|
static int sdhc_spi_check_interface(struct sdhc_spi_data *data)
|
||||||
{
|
{
|
||||||
u32_t cond;
|
u32_t cond;
|
||||||
int err;
|
int err;
|
||||||
|
@ -501,8 +502,11 @@ static int sdhc_spi_detect(struct sdhc_spi_data *data)
|
||||||
u32_t ocr;
|
u32_t ocr;
|
||||||
struct sdhc_retry retry;
|
struct sdhc_retry retry;
|
||||||
u8_t structure;
|
u8_t structure;
|
||||||
|
u8_t readbllen;
|
||||||
u32_t csize;
|
u32_t csize;
|
||||||
|
u8_t csizemult;
|
||||||
u8_t buf[SDHC_CSD_SIZE];
|
u8_t buf[SDHC_CSD_SIZE];
|
||||||
|
bool is_v2;
|
||||||
|
|
||||||
data->cfg.frequency = SDHC_SPI_INITIAL_SPEED;
|
data->cfg.frequency = SDHC_SPI_INITIAL_SPEED;
|
||||||
data->status = DISK_STATUS_UNINIT;
|
data->status = DISK_STATUS_UNINIT;
|
||||||
|
@ -513,10 +517,9 @@ static int sdhc_spi_detect(struct sdhc_spi_data *data)
|
||||||
do {
|
do {
|
||||||
err = sdhc_spi_go_idle(data);
|
err = sdhc_spi_go_idle(data);
|
||||||
if (err == 0) {
|
if (err == 0) {
|
||||||
err = sdhc_spi_check_card(data);
|
err = sdhc_spi_check_interface(data);
|
||||||
if (err == 0) {
|
is_v2 = (err == 0) ? true : false;
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sdhc_retry_ok(&retry)) {
|
if (!sdhc_retry_ok(&retry)) {
|
||||||
|
@ -534,7 +537,8 @@ static int sdhc_spi_detect(struct sdhc_spi_data *data)
|
||||||
do {
|
do {
|
||||||
sdhc_spi_cmd_r1_raw(data, SDHC_APP_CMD, 0);
|
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) {
|
if (err == 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -545,15 +549,36 @@ static int sdhc_spi_detect(struct sdhc_spi_data *data)
|
||||||
return -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read OCR and confirm this is a SDHC card */
|
ocr = 0;
|
||||||
err = sdhc_spi_cmd_r3(data, SDHC_READ_OCR, 0, &ocr);
|
if (is_v2) {
|
||||||
if (err != 0) {
|
do {
|
||||||
return err;
|
/* 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) {
|
if ((ocr & SDHC_CCS) != 0U) {
|
||||||
/* A 'SDSC' card */
|
data->high_capacity = true;
|
||||||
return -ENOTSUP;
|
} 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 */
|
/* Read the CSD */
|
||||||
|
@ -569,22 +594,43 @@ static int sdhc_spi_detect(struct sdhc_spi_data *data)
|
||||||
|
|
||||||
/* Bits 126..127 are the structure version */
|
/* Bits 126..127 are the structure version */
|
||||||
structure = (buf[0] >> 6);
|
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 */
|
/* Unsupported CSD format */
|
||||||
return -ENOTSUP;
|
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.",
|
LOG_INF("Found a ~%u MiB SDHC card.",
|
||||||
data->sector_count / (1024 * 1024 / SDMMC_DEFAULT_BLOCK_SIZE));
|
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)
|
u8_t *buf, u32_t sector, u32_t count)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
u32_t addr;
|
||||||
|
|
||||||
err = sdhc_map_disk_status(data->status);
|
err = sdhc_map_disk_status(data->status);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
return err;
|
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);
|
sdhc_spi_set_cs(data, 0);
|
||||||
|
|
||||||
/* Send the start read command */
|
/* 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) {
|
if (err != 0) {
|
||||||
goto error;
|
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)
|
const u8_t *buf, u32_t sector, u32_t count)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
u32_t addr;
|
||||||
|
|
||||||
err = sdhc_map_disk_status(data->status);
|
err = sdhc_map_disk_status(data->status);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
|
@ -665,7 +722,16 @@ static int sdhc_spi_write(struct sdhc_spi_data *data,
|
||||||
|
|
||||||
/* Write the blocks one-by-one */
|
/* Write the blocks one-by-one */
|
||||||
for (; count != 0U; count--) {
|
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) {
|
if (err < 0) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue