You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1207 lines
35 KiB

/* vim: set ts=4 sw=4 fenc=cp936 et fdm=marker: */
/*
* mmc driver
*
* ChangeLog:
* 2022/03/29: surenyi926 add
*/
#include <stddef.h>
#include <string.h>
#include "mmcDevice.h"
#include "mmcPlatform.h"
#include "mmc.h" /* MMC_BLKSIZE */
/* {{{: mmc devices */
static struct mmc_device *__mmc_dev_list = NULL, **__mmc_dev_tail = &__mmc_dev_list;
/* }}} */
/* {{{: driver api */
int mmcAddDevice(struct mmc_device *dev)
{
struct mmc_device *mmc;
if ((dev == NULL) || (mmcLock() != 0)) {
return -1;
}
/* redundancy ? */
mmc = __mmc_dev_list;
while (mmc) {
if (strcmp(mmc_device_name(mmc), mmc_device_name(dev)) == 0) {
mmcPrintf("%s is in the mmc subsystem\r\n", mmc_device_name(dev));
mmcUnlock();
return 1;
}
mmc = mmc->next;
}
dev->next = NULL;
*__mmc_dev_tail = dev;
__mmc_dev_tail = &dev->next;
mmcUnlock();
return 0;
}
void mmcSetDSR(struct mmc_device *dev, uint16_t dsr)
{
dev->dsr = dsr;
}
int mmcSetCapacity(struct mmc_device *mmc, int part_num)
{
if ((mmc->quirks & MMC_QUIRK_CAPACITY) == MMC_QUIRK_CAPACITY) {
return 0;
}
switch (part_num) {
case 0:
mmc->capacity = mmc->capacity_user;
break;
case 1:
case 2:
mmc->capacity = mmc->capacity_boot;
break;
case 3:
mmc->capacity = mmc->capacity_rpmb;
break;
case 4:
case 5:
case 6:
case 7:
mmc->capacity = mmc->capacity_gp[part_num - 4];
break;
default:
return -1;
}
return 0;
}
int mmcSendStatus(struct mmc_device *mmc, int timeout)
{
struct mmc_cmd cmd;
int err, retries = 5;
/* CMD13: SEND_STATUS */
cmd.cmdidx = 13;
cmd.resp_type = MMC_RSP_R1;
cmd.cmdarg = mmc->rca << 16;
while (1) {
err = mmc_send_command(mmc, &cmd, NULL);
if (!err) {
if ((cmd.response[0] & MMC_STATUS_RDY_FOR_DATA) && ((cmd.response[0] & MMC_STATUS_CURR_STATE) != MMC_STATE_PRG))
break;
if (cmd.response[0] & MMC_STATUS_MASK) {
mmcPrintf("Status Error: 0x%08x\r\n", cmd.response[0]);
return -70;
}
} else if (--retries < 0)
return err;
if (timeout-- <= 0)
break;
mmcDelayUs(1000);
}
if (timeout <= 0) {
mmcPrintf("Timeout waiting card ready\r\n");
return -110;
}
return 0;
}
static const uint8_t tuning_blk_pattern_4bit[] = {
0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc,
0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef,
0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb,
0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef,
0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c,
0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee,
0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff,
0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde,
};
static const uint8_t tuning_blk_pattern_8bit[] = {
0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00,
0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc,
0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff,
0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff,
0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd,
0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb,
0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff,
0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff,
0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00,
0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc,
0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff,
0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee,
0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd,
0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff,
0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff,
0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee,
};
int mmcSendTuning(struct mmc_device *mmc, unsigned int opcode)
{
struct mmc_cmd cmd;
struct mmc_data data;
const uint8_t *tuning_block_pattern;
uint8_t data_buf[128];
int size, err;
if (mmc->bus_width == 8) {
tuning_block_pattern = tuning_blk_pattern_8bit;
size = sizeof(tuning_blk_pattern_8bit);
} else if (mmc->bus_width == 4) {
tuning_block_pattern = tuning_blk_pattern_4bit;
size = sizeof(tuning_blk_pattern_4bit);
} else {
return -22;
}
/* CMD21: SEND_TUNNING_BLOCK */
cmd.cmdidx = opcode;
cmd.cmdarg = 0;
cmd.resp_type = MMC_RSP_R1;
data.dest = (void *)data_buf;
data.blocks = 1;
data.blocksize = size;
data.flags = MMC_DATA_READ;
err = mmc_send_command(mmc, &cmd, &data);
if (err)
return err;
if (memcmp(data_buf, tuning_block_pattern, size)) {
return -5;
}
return 0;
}
void mmcForEachDevice(int (*func)(struct mmc_device *, void *arg), void *arg)
{
struct mmc_device *cur, *next;
if (!func) {
return;
}
if (mmcLock() != 0) {
return;
}
cur = __mmc_dev_list;
while (cur) {
next = cur->next;
if (func(cur, arg)) {
break;
}
cur = next;
}
mmcUnlock();
}
void mmcPrintInfo(struct mmc_device *mmc)
{
int i;
double cap;
mmcPrintf(" Device: %s\r\n", mmc->name);
mmcPrintf(" Manufacturer ID: %x\r\n", mmc->cid[0] >> 24);
mmcPrintf(" OEM: %x\r\n", (mmc->cid[0] >> 8) & 0xffff);
mmcPrintf(" Name: %c%c%c%c%c[%02x-%02x-%02x-%02x-%02x]\r\n",
mmc->cid[0] & 0xff, (mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff,
(mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff,
mmc->cid[0] & 0xff, (mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff,
(mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff);
mmcPrintf(" Rd Block Len: %d\r\n", mmc->read_bl_len);
mmcPrintf(" MMC version: %d.%d",
EXTRACT_SDMMC_MAJOR_VERSION(mmc->version),
EXTRACT_SDMMC_MINOR_VERSION(mmc->version));
if (EXTRACT_SDMMC_CHANGE_VERSION(mmc->version) != 0)
mmcPrintf(".%d", EXTRACT_SDMMC_CHANGE_VERSION(mmc->version));
mmcPrintf("\r\n");
cap = 1.0 * mmc->capacity / 1024.0 / 1024.0 / 1024.0;
mmcPrintf(" High Capacity: %s\r\n", ((mmc->ocr & OCR_HCS) == OCR_HCS) ? "Yes" : "No");
mmcPrintf(" Capacity: %.2lfGB\r\n", cap);
mmcPrintf(" Bus Width: %d-bit%s\r\n", mmc->bus_width, mmc->ddr_mode ? " DDR" : "");
mmcPrintf(" Erase Group Size: %llu\r\n", (((uint64_t)mmc->erase_grp_size) << 9));
if (mmc->version >= MMC_VERSION_4_41) {
int has_enh = (mmc->part_support & ENHNCD_SUPPORT) != 0;
int usr_enh = has_enh && (mmc->part_attr & EXT_CSD_ENH_USR);
mmcPrintf(" User Capacity: %llu %s ", mmc->capacity_user, usr_enh ? " ENH" : "");
if (mmc->wr_rel_set & EXT_CSD_WR_DATA_REL_USR)
mmcPrintf(" WRREL\r\n");
else
mmcPrintf("\r\n");
if (usr_enh) {
mmcPrintf("User Enhanced Start: %llu\r\n", mmc->enh_user_start);
mmcPrintf(" User Enhanced Size: %llu\r\n", mmc->enh_user_size);
}
mmcPrintf("Boot Capacity: %llu %s\r\n", mmc->capacity_boot, has_enh ? " ENH" : "");
mmcPrintf("RPMB Capacity: %llu %s\r\n", mmc->capacity_rpmb, has_enh ? " ENH" : "");
for (i = 0; i < ARRAY_SIZE(mmc->capacity_gp); i++) {
int is_enh = has_enh && (mmc->part_attr & EXT_CSD_ENH_GP(i));
if (mmc->capacity_gp[i]) {
mmcPrintf("GP%i Capacity: %llu%s", i+1, mmc->capacity_gp[i], is_enh ? " ENH" : "");
if (mmc->wr_rel_set & EXT_CSD_WR_DATA_REL_GP(i))
mmcPrintf(" WRREL\r\n");
else
mmcPrintf("\r\n");
}
}
}
mmcPrintf("\r\n");
mmcPrintf("CID: %08x-%08x-%08x-%08x\r\n", mmc->cid[0], mmc->cid[1], mmc->cid[2], mmc->cid[3]);
}
static inline int goto_idle(struct mmc_device *mmc)
{
struct mmc_cmd cmd;
/* CMD0: put mmc device to idle mode*/
cmd.cmdidx = 0; /* CMD0 */
cmd.cmdarg = 0;
cmd.resp_type = MMC_RSP_NONE;
return mmc_send_command(mmc, &cmd, NULL);
}
int mmcGoIdle(struct mmc_device *mmc)
{
return goto_idle(mmc);
}
/* }}} */
/* {{{: interal functions */
static int send_op_cond(struct mmc_device *mmc)
{
struct mmc_cmd cmd;
int err;
/* CMD1: set operations conditions */
cmd.cmdidx = 1;
cmd.resp_type = MMC_RSP_R3;
cmd.cmdarg = mmc->voltages & OCR_VOLTAGE_MASK;
if (mmc->hcs_mode) {
cmd.cmdarg |= OCR_HCS;
}
err = mmc_send_command(mmc, &cmd, NULL);
if (err)
return err;
mmc->ocr = cmd.response[0];
return 0;
}
static int mmc_send_ext_csd(struct mmc_device *mmc, uint8_t *ext_csd)
{
struct mmc_cmd cmd;
struct mmc_data data;
/* CMD8: Get the Card Status Register */
cmd.cmdidx = 8;
cmd.resp_type = MMC_RSP_R1;
cmd.cmdarg = 0;
data.dest = (char *)ext_csd;
data.blocks = 1;
data.blocksize = MMC_BLKSIZE;
data.flags = MMC_DATA_READ;
return mmc_send_command(mmc, &cmd, &data);
}
static int __mmc_switch(struct mmc_device *mmc, uint8_t index, uint8_t value, int send_status)
{
struct mmc_cmd cmd;
int timeout = 1000;
int retries = 3;
int ret;
/* CMD6: switch */
cmd.cmdidx = 6;
cmd.resp_type = MMC_RSP_R1b;
cmd.cmdarg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | (index << 16) | (value << 8);
/* mmcPrintf("switch (6): %x\r\n", cmd.cmdarg); */
while (retries > 0) {
ret = mmc_send_command(mmc, &cmd, NULL);
if (ret) {
retries--;
continue;
}
if (!send_status) {
mmcDelayUs(50000);
return 0;
}
/* Waiting for the ready status */
return mmcSendStatus(mmc, timeout);
}
/* mmcPrintf("%s[%d]: %d\r\n", __func__, __LINE__, ret); */
return ret;
}
static int mmc_switch(struct mmc_device *mmc, uint8_t index, uint8_t value)
{
return __mmc_switch(mmc, index, value, 1);
}
static int mmc_startup_v4(struct mmc_device *mmc)
{
int err, i;
uint64_t capacity;
int has_parts = 0;
int part_completed;
uint8_t *ext_csd;
static const uint32_t mmc_versions[] = {
MMC_VERSION_4,
MMC_VERSION_4_1,
MMC_VERSION_4_2,
MMC_VERSION_4_3,
MMC_VERSION_4_4,
MMC_VERSION_4_41,
MMC_VERSION_4_5,
MMC_VERSION_5_0,
MMC_VERSION_5_1
};
if (mmc->version < MMC_VERSION_4)
return 0;
/* check ext_csd version and capacity */
err = mmc_send_ext_csd(mmc, mmc->ext_csd);
if (err)
goto error;
ext_csd = mmc->ext_csd;
#if 0
for (i = 0; i < sizeof mmc->ext_csd; ++i) {
mmcPrintf("%02x%s", mmc->ext_csd[i], (i + 1) % 16 == 0 ? "\r\n" : " ");
}
mmcPrintf("\r\n");
#endif
if (ext_csd[EXT_CSD_REV] >= ARRAY_SIZE(mmc_versions)) {
/* mmcPrintf("EXT_CSD_REV: %d\r\n", ext_csd[EXT_CSD_REV]); */
return -22;
}
mmc->version = mmc_versions[ext_csd[EXT_CSD_REV]];
if (mmc->version >= MMC_VERSION_4_2) {
/*
* According to the JEDEC Standard, the value of
* ext_csd's capacity is valid if the value is more
* than 2GB
*/
capacity = ext_csd[EXT_CSD_SEC_CNT] << 0
| ext_csd[EXT_CSD_SEC_CNT + 1] << 8
| ext_csd[EXT_CSD_SEC_CNT + 2] << 16
| ext_csd[EXT_CSD_SEC_CNT + 3] << 24;
capacity *= MMC_BLKSIZE;
if ((capacity >> 20) > 2 * 1024)
mmc->capacity_user = capacity;
}
/* The partition data may be non-zero but it is only
* effective if PARTITION_SETTING_COMPLETED is set in
* EXT_CSD, so ignore any data if this bit is not set,
* except for enabling the high-capacity group size
* definition (see below).
*/
part_completed = !!(ext_csd[EXT_CSD_PARTITION_SETTING] &
EXT_CSD_PARTITION_SETTING_COMPLETED);
/* store the partition info of emmc */
mmc->part_support = ext_csd[EXT_CSD_PARTITIONING_SUPPORT];
if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) ||
ext_csd[EXT_CSD_BOOT_MULT])
mmc->part_config = ext_csd[EXT_CSD_PART_CONF];
if (part_completed &&
(ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & ENHNCD_SUPPORT))
mmc->part_attr = ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE];
mmc->capacity_boot = ext_csd[EXT_CSD_BOOT_MULT] << 17;
mmc->capacity_rpmb = ext_csd[EXT_CSD_RPMB_MULT] << 17;
for (i = 0; i < 4; i++) {
int idx = EXT_CSD_GP_SIZE_MULT + i * 3;
uint32_t mult = (ext_csd[idx + 2] << 16) + (ext_csd[idx + 1] << 8) + ext_csd[idx];
if (mult)
has_parts = 1;
if (!part_completed)
continue;
mmc->capacity_gp[i] = mult;
mmc->capacity_gp[i] *= ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
mmc->capacity_gp[i] *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
mmc->capacity_gp[i] <<= 19;
}
if (part_completed) {
mmc->enh_user_size =
(ext_csd[EXT_CSD_ENH_SIZE_MULT + 2] << 16) +
(ext_csd[EXT_CSD_ENH_SIZE_MULT + 1] << 8) +
ext_csd[EXT_CSD_ENH_SIZE_MULT];
mmc->enh_user_size *= ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
mmc->enh_user_size *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
mmc->enh_user_size <<= 19;
mmc->enh_user_start =
(ext_csd[EXT_CSD_ENH_START_ADDR + 3] << 24) +
(ext_csd[EXT_CSD_ENH_START_ADDR + 2] << 16) +
(ext_csd[EXT_CSD_ENH_START_ADDR + 1] << 8) +
ext_csd[EXT_CSD_ENH_START_ADDR];
if ((mmc->ocr & OCR_HCS) == OCR_HCS)
mmc->enh_user_start <<= 9;
}
/*
* Host needs to enable ERASE_GRP_DEF bit if device is
* partitioned. This bit will be lost every time after a reset
* or power off. This will affect erase size.
*/
if (part_completed)
has_parts = 1;
if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) &&
(ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & PART_ENH_ATTRIB))
has_parts = 1;
if (has_parts) {
err = mmc_switch(mmc, EXT_CSD_ERASE_GROUP_DEF, 1);
if (err)
goto error;
ext_csd[EXT_CSD_ERASE_GROUP_DEF] = 1;
}
if (ext_csd[EXT_CSD_ERASE_GROUP_DEF] & 0x01) {
/* Read out group size from ext_csd */
mmc->erase_grp_size = ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * 1024;
/*
* if high capacity and partition setting completed
* SEC_COUNT is valid even if it is smaller than 2 GiB
* JEDEC Standard JESD84-B45, 6.2.4
*/
if (((mmc->ocr & OCR_HCS) == OCR_HCS) && part_completed) {
capacity = (ext_csd[EXT_CSD_SEC_CNT]) | (ext_csd[EXT_CSD_SEC_CNT + 1] << 8)
| (ext_csd[EXT_CSD_SEC_CNT + 2] << 16) | (ext_csd[EXT_CSD_SEC_CNT + 3] << 24);
capacity *= MMC_BLKSIZE;
mmc->capacity_user = capacity;
}
} else {
/* Calculate the group size from the csd value. */
int erase_gsz, erase_gmul;
erase_gsz = (mmc->csd[2] & 0x00007c00) >> 10;
erase_gmul = (mmc->csd[2] & 0x000003e0) >> 5;
mmc->erase_grp_size = (erase_gsz + 1) * (erase_gmul + 1);
}
mmc->wr_rel_set = ext_csd[EXT_CSD_WR_REL_SET];
return 0;
error:
return err;
}
const char *mmc_mode_name(enum bus_mode mode)
{
static const char *const names[] = {
[MMC_LEGACY] = "MMC legacy",
[SD_LEGACY] = "SD Legacy",
[MMC_HS] = "MMC High Speed (26MHz)",
[SD_HS] = "SD High Speed (50MHz)",
[UHS_SDR12] = "UHS SDR12 (25MHz)",
[UHS_SDR25] = "UHS SDR25 (50MHz)",
[UHS_SDR50] = "UHS SDR50 (100MHz)",
[UHS_SDR104] = "UHS SDR104 (208MHz)",
[UHS_DDR50] = "UHS DDR50 (50MHz)",
[MMC_HS_52] = "MMC High Speed (52MHz)",
[MMC_DDR_52] = "MMC DDR52 (52MHz)",
[MMC_HS_200] = "HS200 (200MHz)",
[MMC_HS_400] = "HS400 (200MHz)",
};
if (mode >= MMC_MODES_END)
return "Unknown mode";
else
return names[mode];
}
static uint32_t mmc_mode2freq(struct mmc_device *mmc, enum bus_mode mode)
{
static const int freqs[] = {
[MMC_LEGACY] = 25000000,
[SD_LEGACY] = 25000000,
[MMC_HS] = 26000000,
[SD_HS] = 50000000,
[MMC_HS_52] = 52000000,
[MMC_DDR_52] = 52000000,
[UHS_SDR12] = 25000000,
[UHS_SDR25] = 50000000,
[UHS_SDR50] = 100000000,
[UHS_DDR50] = 50000000,
[UHS_SDR104] = 208000000,
[MMC_HS_200] = 200000000,
[MMC_HS_400] = 200000000,
};
if (mode == MMC_LEGACY)
return mmc->legacy_speed;
if (mode >= MMC_MODES_END)
return 0;
else
return freqs[mode];
}
static inline int mmc_is_mode_ddr(enum bus_mode mode)
{
if (mode == MMC_DDR_52)
return 1;
else if (mode == UHS_DDR50)
return 1;
else if (mode == MMC_HS_400)
return 1;
else
return 0;
}
static int mmc_select_mode(struct mmc_device *mmc, enum bus_mode mode)
{
mmc->selected_mode = mode;
mmc->tran_speed = mmc_mode2freq(mmc, mode);
mmc->ddr_mode = mmc_is_mode_ddr(mode);
mmcPrintf("%s: selecting mode %s\r\n", mmc_device_name(mmc), mmc_mode_name(mode)); /* , mmc->tran_speed / 1000000); */
return 0;
}
static int mmc_set_card_speed(struct mmc_device *mmc, enum bus_mode mode, int hsdowngrade)
{
int err;
int speed_bits;
uint8_t test_csd[MMC_BLKSIZE];
switch (mode) {
case MMC_HS:
case MMC_HS_52:
case MMC_DDR_52:
speed_bits = EXT_CSD_TIMING_HS;
break;
case MMC_HS_200:
speed_bits = EXT_CSD_TIMING_HS200;
break;
case MMC_HS_400:
speed_bits = EXT_CSD_TIMING_HS400;
break;
case MMC_LEGACY:
speed_bits = EXT_CSD_TIMING_LEGACY;
break;
default:
return -22;
}
err = __mmc_switch(mmc, EXT_CSD_HS_TIMING, speed_bits, !hsdowngrade);
if (err)
return err;
/*
* In case the eMMC is in HS200/HS400 mode and we are downgrading
* to HS mode, the card clock are still running much faster than
* the supported HS mode clock, so we can not reliably read out
* Extended CSD. Reconfigure the controller to run at HS mode.
*/
if (hsdowngrade) {
mmc_select_mode(mmc, MMC_HS);
mmc_set_clock(mmc, mmc_mode2freq(mmc, MMC_HS), 1);
}
if ((mode == MMC_HS) || (mode == MMC_HS_52)) {
/* Now check to see that it worked */
err = mmc_send_ext_csd(mmc, test_csd);
if (err)
return err;
/* No high-speed support */
if (!test_csd[EXT_CSD_HS_TIMING])
return -524;
}
return 0;
}
static int mmc_select_hs400(struct mmc_device *mmc)
{
int err;
/* Set timing to HS200 for tuning */
err = mmc_set_card_speed(mmc, MMC_HS_200, 0);
if (err)
return err;
/* configure the bus mode (host) */
mmc_select_mode(mmc, MMC_HS_200);
mmc_set_clock(mmc, mmc->tran_speed, 1);
/* execute tuning if needed */
err = mmc_execute_tuning(mmc, 21); /* CMD21 */
if (err) {
mmcPrintf("tuning failed\r\n");
return err;
}
/* Set back to HS */
mmc_set_card_speed(mmc, MMC_HS, 1);
err = mmc_switch(mmc, EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_8 | EXT_CSD_DDR_FLAG);
if (err)
return err;
err = mmc_set_card_speed(mmc, MMC_HS_400, 0);
if (err)
return err;
mmc_select_mode(mmc, MMC_HS_400);
err = mmc_set_clock(mmc, mmc->tran_speed, 1);
if (err)
return err;
return 0;
}
static inline int bus_width(uint32_t cap)
{
if (cap == MMC_MODE_8BIT)
return 8;
if (cap == MMC_MODE_4BIT)
return 4;
if (cap == MMC_MODE_1BIT)
return 1;
mmcPrintf("invalid bus witdh capability 0x%x\r\n", cap);
return 0;
}
/*
* read the compare the part of ext csd that is constant.
* This can be used to check that the transfer is working
* as expected.
*/
static int mmc_read_and_compare_ext_csd(struct mmc_device *mmc)
{
int err;
const uint8_t *ext_csd = mmc->ext_csd;
uint8_t test_csd[MMC_BLKSIZE];
if (mmc->version < MMC_VERSION_4)
return 0;
err = mmc_send_ext_csd(mmc, test_csd);
if (err)
return err;
/* Only compare read only fields */
if (ext_csd[EXT_CSD_PARTITIONING_SUPPORT] == test_csd[EXT_CSD_PARTITIONING_SUPPORT] &&
ext_csd[EXT_CSD_HC_WP_GRP_SIZE] == test_csd[EXT_CSD_HC_WP_GRP_SIZE] &&
ext_csd[EXT_CSD_REV] == test_csd[EXT_CSD_REV] &&
ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] == test_csd[EXT_CSD_HC_ERASE_GRP_SIZE] &&
memcmp(&ext_csd[EXT_CSD_SEC_CNT], &test_csd[EXT_CSD_SEC_CNT], 4) == 0)
return 0;
return -74;
}
struct mode_width_tuning {
enum bus_mode mode;
uint32_t widths;
uint32_t tuning;
};
static const struct mode_width_tuning mmc_modes_by_pref[] = {
{
.mode = MMC_HS_400,
.widths = MMC_MODE_8BIT,
.tuning = 21, /* CMD21 */
},
{
.mode = MMC_HS_200,
.widths = MMC_MODE_8BIT | MMC_MODE_4BIT,
.tuning = 21, /* CMD21 */
},
{
.mode = MMC_DDR_52,
.widths = MMC_MODE_8BIT | MMC_MODE_4BIT,
},
{
.mode = MMC_HS_52,
.widths = MMC_MODE_8BIT | MMC_MODE_4BIT | MMC_MODE_1BIT,
},
{
.mode = MMC_HS,
.widths = MMC_MODE_8BIT | MMC_MODE_4BIT | MMC_MODE_1BIT,
},
{
.mode = MMC_LEGACY,
.widths = MMC_MODE_8BIT | MMC_MODE_4BIT | MMC_MODE_1BIT,
}
};
#define for_each_mmc_mode_by_pref(caps, mwt) \
for (mwt = mmc_modes_by_pref;\
mwt < mmc_modes_by_pref + ARRAY_SIZE(mmc_modes_by_pref);\
mwt++) \
if (caps & MMC_CAP(mwt->mode))
static const struct ext_csd_bus_width {
uint32_t cap;
int is_ddr;
uint32_t ext_csd_bits;
} ext_csd_bus_width[] = {
{MMC_MODE_8BIT, 1, EXT_CSD_DDR_BUS_WIDTH_8},
{MMC_MODE_4BIT, 1, EXT_CSD_DDR_BUS_WIDTH_4},
{MMC_MODE_8BIT, 0, EXT_CSD_BUS_WIDTH_8},
{MMC_MODE_4BIT, 0, EXT_CSD_BUS_WIDTH_4},
{MMC_MODE_1BIT, 0, EXT_CSD_BUS_WIDTH_1},
};
#define for_each_supported_width(caps, ddr, ecbv) \
for (ecbv = ext_csd_bus_width;\
ecbv < ext_csd_bus_width + ARRAY_SIZE(ext_csd_bus_width);\
ecbv++) \
if ((ddr == ecbv->is_ddr) && (caps & ecbv->cap))
static int mmc_select_mode_and_width(struct mmc_device *mmc, uint32_t card_caps)
{
int err;
const struct mode_width_tuning *mwt;
const struct ext_csd_bus_width *ecbw;
/* Restrict card's capabilities by what the host can do */
card_caps &= mmc->host_caps;
/* mmcPrintf("card_caps: %x\r\n", card_caps); */
/* Only version 4 of MMC supports wider bus widths */
if (mmc->version < MMC_VERSION_4)
return 0;
/*
* In case the eMMC is in HS200/HS400 mode, downgrade to HS mode
* before doing anything else, since a transition from either of
* the HS200/HS400 mode directly to legacy mode is not supported.
*/
if (mmc->selected_mode == MMC_HS_200 || mmc->selected_mode == MMC_HS_400)
mmc_set_card_speed(mmc, MMC_HS, 1);
else
mmc_set_clock(mmc, mmc->legacy_speed, 1);
for_each_mmc_mode_by_pref(card_caps, mwt) {
for_each_supported_width(card_caps & mwt->widths,
mmc_is_mode_ddr(mwt->mode), ecbw) {
/* configure the bus width (card + host) */
err = mmc_switch(mmc, EXT_CSD_BUS_WIDTH, ecbw->ext_csd_bits & ~EXT_CSD_DDR_FLAG);
/* mmcPrintf("%s[%d]: %d\r\n", __func__, __LINE__, err); */
if (err)
goto error;
mmc_set_bus_width(mmc, bus_width(ecbw->cap));
if (mwt->mode == MMC_HS_400) {
err = mmc_select_hs400(mmc);
if (err) {
mmcPrintf("Select HS400 failed %d\r\n", err);
goto error;
}
} else {
/* configure the bus speed (card) */
err = mmc_set_card_speed(mmc, mwt->mode, 0);
if (err)
goto error;
/*
* configure the bus width AND the ddr mode
* (card). The host side will be taken care
* of in the next step
*/
if (ecbw->ext_csd_bits & EXT_CSD_DDR_FLAG) {
err = mmc_switch(mmc, EXT_CSD_BUS_WIDTH, ecbw->ext_csd_bits);
if (err)
goto error;
}
/* configure the bus mode (host) */
mmc_select_mode(mmc, mwt->mode);
mmc_set_clock(mmc, mmc->tran_speed, 1);
/* execute tuning if needed */
if (mwt->tuning) {
err = mmc_execute_tuning(mmc, mwt->tuning);
if (err) {
mmcPrintf("%s: retraining tuning parameters.\r\n", mmc_device_name(mmc));
goto error;
}
/* tunning success, skip ext_csd checking. */
if (mmc->quirks & MMC_QUIRK_TUNNING) {
return 0;
}
}
}
/* do a transfer to check the configuration */
err = mmc_read_and_compare_ext_csd(mmc);
if (!err)
return 0;
error:
/* if an error occured, revert to a safer bus mode */
mmc_switch(mmc, EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_1);
mmc_select_mode(mmc, MMC_LEGACY);
mmc_set_bus_width(mmc, 1);
mmc_set_clock(mmc, mmc->tran_speed, 1);
}
}
/* mmcPrintf("%s: fallback to legacy mode\r\n", __func__); */
return -524;
}
static int mmc_get_capabilities(struct mmc_device *mmc)
{
uint8_t *ext_csd = mmc->ext_csd;
char cardtype;
mmc->card_caps |= MMC_MODE_1BIT | MMC_CAP(MMC_LEGACY);
/* Only version 4 supports high-speed */
if (mmc->version < MMC_VERSION_4)
return 0;
mmc->card_caps |= MMC_MODE_4BIT | MMC_MODE_8BIT;
cardtype = ext_csd[EXT_CSD_CARD_TYPE];
mmc->cardtype = cardtype;
if (cardtype & (EXT_CSD_CARD_TYPE_HS200_1_2V | EXT_CSD_CARD_TYPE_HS200_1_8V)) {
mmc->card_caps |= MMC_MODE_HS200;
}
if (cardtype & (EXT_CSD_CARD_TYPE_HS400_1_2V | EXT_CSD_CARD_TYPE_HS400_1_8V)) {
mmc->card_caps |= MMC_MODE_HS400;
}
if (cardtype & EXT_CSD_CARD_TYPE_52) {
if (cardtype & EXT_CSD_CARD_TYPE_DDR_52)
mmc->card_caps |= MMC_MODE_DDR_52MHz;
mmc->card_caps |= MMC_MODE_HS_52MHz;
}
if (cardtype & EXT_CSD_CARD_TYPE_26)
mmc->card_caps |= MMC_MODE_HS;
return 0;
}
/* frequency bases */
/* divided by 10 to be nice to platforms without floating point */
static const int fbase[] = {
10000,
100000,
1000000,
10000000,
};
/* Multiplier values for TRAN_SPEED. Multiplied by 10 to be nice
* to platforms without floating point.
*/
static const uint8_t multipliers[] = {
0, /* reserved */
10,
12,
13,
15,
20,
25,
30,
35,
40,
45,
50,
55,
60,
70,
80,
};
static int startup(struct mmc_device *mmc)
{
struct mmc_cmd cmd;
int i, err, version;
uint64_t cmult, csize;
uint32_t mult, freq;
/* send CMD2, put card to Indentify mode */
/* Put the Card in Identify Mode */
cmd.cmdidx = 2;
cmd.resp_type = MMC_RSP_R2;
cmd.cmdarg = 0;
err = mmc_send_command(mmc, &cmd, NULL);
if (err && (mmc->quirks & MMC_QUIRK_RETRY_SEND_CID)) {
int retries = 4;
/*
* It has been seen that SEND_CID may fail on the first
* attempt, let's try a few more time
*/
do {
mmcDelayUs(50000);
err = mmc_send_command(mmc, &cmd, NULL);
if (!err)
break;
} while (retries--);
}
if (err)
return -1;
memcpy(mmc->cid, cmd.response, MMC_CID_LEN); /* sizeof (cmd.response) == 16 */
/* CMD3: set the Relative Address. */
cmd.cmdidx = 3;
cmd.cmdarg = mmc->rca << 16;
cmd.resp_type = MMC_RSP_R1;
err = mmc_send_command(mmc, &cmd, NULL);
if (err)
return -2;
/* CMD9: Get the Card-Specific Data (*/
cmd.cmdidx = 9;
cmd.resp_type = MMC_RSP_R2;
cmd.cmdarg = mmc->rca << 16;
err = mmc_send_command(mmc, &cmd, NULL);
if (err)
return err;
for (i = 0; i < 4; ++i) {
mmc->csd[i] = cmd.response[i];
}
if (mmc->version == MMC_VERSION_UNKNOWN) {
version = (cmd.response[0] >> 26) & 0xf;
/* mmcPrintf("%s: mmc version %d\r\n", mmc_device_name(mmc), version); */
switch (version) {
case 0:
mmc->version = MMC_VERSION_1_2;
break;
case 1:
mmc->version = MMC_VERSION_1_4;
break;
case 2:
mmc->version = MMC_VERSION_2_2;
break;
case 3:
mmc->version = MMC_VERSION_3;
break;
case 4:
mmc->version = MMC_VERSION_4;
break;
case 6:
mmc->version = MMC_VERSION_5_1;
break;
default:
mmc->version = MMC_VERSION_1_2;
break;
}
}
/* divide frequency by 10, since the mults are 10x bigger */
freq = fbase[(cmd.response[0] & 0x7)];
mult = multipliers[((cmd.response[0] >> 3) & 0xf)];
mmc->legacy_speed = freq * mult;
mmc_select_mode(mmc, MMC_LEGACY);
mmc->dsr_imp = ((cmd.response[1] >> 12) & 0x1);
mmc->read_bl_len = 1 << ((cmd.response[1] >> 16) & 0xf);
mmc->write_bl_len = 1 << ((cmd.response[3] >> 22) & 0xf);
if ((mmc->ocr & OCR_HCS) == OCR_HCS) { /* high capacity */
csize = (mmc->csd[1] & 0x3f) << 16 | (mmc->csd[2] & 0xffff0000) >> 16;
cmult = 8;
} else {
csize = (mmc->csd[1] & 0x3ff) << 2 | (mmc->csd[2] & 0xc0000000) >> 30;
cmult = (mmc->csd[2] & 0x00038000) >> 15;
}
mmc->capacity_user = (csize + 1) << (cmult + 2);
mmc->capacity_user *= mmc->read_bl_len;
mmc->capacity_boot = 0;
mmc->capacity_rpmb = 0;
for (i = 0; i < 4; i++)
mmc->capacity_gp[i] = 0;
if (mmc->read_bl_len > MMC_BLKSIZE)
mmc->read_bl_len = MMC_BLKSIZE;
if (mmc->write_bl_len > MMC_BLKSIZE)
mmc->write_bl_len = MMC_BLKSIZE;
if (mmc->dsr_imp && (0xffffffff != mmc->dsr)) { /* device output driver */
cmd.cmdidx = 4; /* CMD4: set dsr */
cmd.cmdarg = ((mmc->dsr & 0xffff) << 16) | 0xffff;
cmd.resp_type = MMC_RSP_NONE;
if (mmc_send_command(mmc, &cmd, NULL))
mmcPrintf("%s: SET_DSR failed\r\n", mmc_device_name(mmc));
else {
mmcPrintf("%s: dsr_imp enable, dsr(0x%04x)\r\n", mmc_device_name(mmc), mmc->dsr);
}
}
/* CMD7: Select the card, and put it into Transfer Mode */
cmd.cmdidx = 7;
cmd.resp_type = MMC_RSP_R1;
cmd.cmdarg = mmc->rca << 16;
err = mmc_send_command(mmc, &cmd, NULL);
if (err)
return -3;
mmc->erase_grp_size = 1;
mmc->part_config = MMCPART_NOAVAILABLE;
mmc_startup_v4(mmc);
mmcSetCapacity(mmc, 0);
mmc_get_capabilities(mmc);
err = mmc_select_mode_and_width(mmc, mmc->card_caps);
if (err) {
return -4;
}
/* switch to trans state */
mmc_start_xfer(mmc);
return 0;
}
static int do_init(struct mmc_device *mmc)
{
int count = 100;
if (goto_idle(mmc)) {
return -1;
}
mmcDelayUs(10000);
if (mmc->quirks & MMC_QUIRK_RETRY_IDLE) {
goto_idle(mmc);
mmcDelayUs(10000);
}
do {
if (send_op_cond(mmc)) {
goto skip;
}
if (mmc->ocr & OCR_NOT_BUSY) {
break;
}
skip:
mmcDelayUs(50000);
} while (count-- > 0);
if (!(mmc->ocr & OCR_NOT_BUSY)) { /* timeout */
return -3;
}
mmc->rca = 1;
if (startup(mmc))
return -4;
mmcPrintf("%s: capacity 0x%llx\r\n", mmc_device_name(mmc), mmc->capacity);
return 0;
}
/* }}} */
/* {{{: user level api */
int mmcBlocksXfer(size_t off , size_t blocks, void *buffer, int (*rwio)(struct mmc_device *, size_t, size_t, void *))
{
struct mmc_device *mmc;
int err;
size_t blk_save, nblks, lba = 0, lba_end;
uint8_t *bp = buffer;
if (mmcLock() != 0) {
return -1;
}
blk_save = blocks;
for (mmc = __mmc_dev_list; (mmc != NULL) && (blocks > 0); mmc = mmc->next) {
lba_end = lba + mmc->capacity / MMC_BLKSIZE;
if ((off >= lba) && (off < lba_end)) {
nblks = lba_end - off;
if (nblks > blocks)
nblks = blocks;
if ((err = rwio(mmc, off - lba, nblks, bp)) != 0) {
mmcPrintf("%s: read off %d blks %d failed\r\n", mmc_device_name(mmc), off, nblks);
mmcUnlock();
return err;
}
off += nblks;
blocks -= nblks;
bp += nblks * MMC_BLKSIZE;
}
lba = lba_end;
}
mmcUnlock();
return (blk_save - blocks);
}
int mmcDeviceStart(struct mmc_device *mmc)
{
int nret;
mmc->dsr_imp = 0;
mmc->dsr = 0xffffffff;
nret = mmc_hwinit(mmc);
if (nret > 0) { /* already initialized */
return 0;
}
if (nret < 0) {
mmc->capacity = 0;
mmcPrintf("mmc(%s): hwinit failed: %d\r\n", mmc_device_name(mmc), nret);
return -1;
}
goto_idle(mmc);
mmcDelayUs(100000);
nret = do_init(mmc);
if (nret) {
mmcPrintf("mmc(%s): init failed %d\r\n", mmc_device_name(mmc), nret);
mmc->capacity = 0;
return -2;
}
return 0;
}
void mmcDeviceRemoveAll()
{
struct mmc_device *mmc;
uint32_t caps_filtered;
if (mmcLock() != 0) {
return;
}
for (mmc = __mmc_dev_list; mmc != NULL; mmc = mmc->next) {
caps_filtered = mmc->card_caps & ~(MMC_CAP(MMC_HS_200) | MMC_CAP(MMC_HS_400));
mmc_select_mode_and_width(mmc, caps_filtered);
if (mmc->driver && mmc->driver->free)
mmc->driver->free(mmc);
}
__mmc_dev_list = NULL;
__mmc_dev_tail = &__mmc_dev_list;
mmcUnlock();
}
/* }}} */