summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mci/mci-core.c133
1 files changed, 107 insertions, 26 deletions
diff --git a/drivers/mci/mci-core.c b/drivers/mci/mci-core.c
index 2377fefd73..0bf4449d83 100644
--- a/drivers/mci/mci-core.c
+++ b/drivers/mci/mci-core.c
@@ -391,10 +391,10 @@ static int mci_switch(struct mci *mci, unsigned set, unsigned index,
*/
static int mmc_change_freq(struct mci *mci)
{
- char *ext_csd = sector_buf;
char cardtype;
int err;
+ mci->ext_csd = xmalloc(512);
mci->card_caps = 0;
/* Only version 4 supports high-speed */
@@ -403,13 +403,13 @@ static int mmc_change_freq(struct mci *mci)
mci->card_caps |= MMC_MODE_4BIT;
- err = mci_send_ext_csd(mci, ext_csd);
+ err = mci_send_ext_csd(mci, mci->ext_csd);
if (err) {
dev_dbg(mci->mci_dev, "Preparing for frequency setup failed: %d\n", err);
return err;
}
- cardtype = ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_MASK;
+ cardtype = mci->ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_MASK;
err = mci_switch(mci, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1);
@@ -419,7 +419,7 @@ static int mmc_change_freq(struct mci *mci)
}
/* Now check to see that it worked */
- err = mci_send_ext_csd(mci, ext_csd);
+ err = mci_send_ext_csd(mci, mci->ext_csd);
if (err) {
dev_dbg(mci->mci_dev, "Verifying frequency change failed: %d\n", err);
@@ -427,7 +427,7 @@ static int mmc_change_freq(struct mci *mci)
}
/* No high-speed support */
- if (!ext_csd[EXT_CSD_HS_TIMING])
+ if (!mci->ext_csd[EXT_CSD_HS_TIMING])
return 0;
/* High Speed is set, there are two types: 52MHz and 26MHz */
@@ -767,6 +767,67 @@ static void mci_extract_card_capacity_from_csd(struct mci *mci)
dev_dbg(mci->mci_dev, "Capacity: %u MiB\n", (unsigned)mci->capacity >> 20);
}
+static int mmc_compare_ext_csds(struct mci *mci, unsigned bus_width)
+{
+ u8 *bw_ext_csd;
+ int err;
+
+ if (bus_width == MMC_BUS_WIDTH_1)
+ return 0;
+
+ bw_ext_csd = xmalloc(512);
+ err = mci_send_ext_csd(mci, bw_ext_csd);
+ if (err) {
+ dev_info(mci->mci_dev, "mci_send_ext_csd failed with %d\n", err);
+ if (bus_width != MMC_BUS_WIDTH_1)
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (bus_width == MMC_BUS_WIDTH_1)
+ goto out;
+ /* only compare read only fields */
+ err = (mci->ext_csd[EXT_CSD_PARTITION_SUPPORT] ==
+ bw_ext_csd[EXT_CSD_PARTITION_SUPPORT]) &&
+ (mci->ext_csd[EXT_CSD_ERASED_MEM_CONT] ==
+ bw_ext_csd[EXT_CSD_ERASED_MEM_CONT]) &&
+ (mci->ext_csd[EXT_CSD_REV] ==
+ bw_ext_csd[EXT_CSD_REV]) &&
+ (mci->ext_csd[EXT_CSD_STRUCTURE] ==
+ bw_ext_csd[EXT_CSD_STRUCTURE]) &&
+ (mci->ext_csd[EXT_CSD_CARD_TYPE] ==
+ bw_ext_csd[EXT_CSD_CARD_TYPE]) &&
+ (mci->ext_csd[EXT_CSD_S_A_TIMEOUT] ==
+ bw_ext_csd[EXT_CSD_S_A_TIMEOUT]) &&
+ (mci->ext_csd[EXT_CSD_HC_WP_GRP_SIZE] ==
+ bw_ext_csd[EXT_CSD_HC_WP_GRP_SIZE]) &&
+ (mci->ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT] ==
+ bw_ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT]) &&
+ (mci->ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] ==
+ bw_ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]) &&
+ (mci->ext_csd[EXT_CSD_SEC_TRIM_MULT] ==
+ bw_ext_csd[EXT_CSD_SEC_TRIM_MULT]) &&
+ (mci->ext_csd[EXT_CSD_SEC_ERASE_MULT] ==
+ bw_ext_csd[EXT_CSD_SEC_ERASE_MULT]) &&
+ (mci->ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT] ==
+ bw_ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT]) &&
+ (mci->ext_csd[EXT_CSD_TRIM_MULT] ==
+ bw_ext_csd[EXT_CSD_TRIM_MULT]) &&
+ (mci->ext_csd[EXT_CSD_SEC_CNT + 0] ==
+ bw_ext_csd[EXT_CSD_SEC_CNT + 0]) &&
+ (mci->ext_csd[EXT_CSD_SEC_CNT + 1] ==
+ bw_ext_csd[EXT_CSD_SEC_CNT + 1]) &&
+ (mci->ext_csd[EXT_CSD_SEC_CNT + 2] ==
+ bw_ext_csd[EXT_CSD_SEC_CNT + 2]) &&
+ (mci->ext_csd[EXT_CSD_SEC_CNT + 3] ==
+ bw_ext_csd[EXT_CSD_SEC_CNT + 3]) ?
+ 0 : -EINVAL;
+
+out:
+ free(bw_ext_csd);
+ return err;
+}
+
static int mci_startup_sd(struct mci *mci)
{
struct mci_cmd cmd;
@@ -802,29 +863,18 @@ static int mci_startup_sd(struct mci *mci)
static int mci_startup_mmc(struct mci *mci)
{
+ struct mci_host *host = mci->host;
int err;
+ int idx = 0;
+ static unsigned ext_csd_bits[] = {
+ EXT_CSD_BUS_WIDTH_4,
+ EXT_CSD_BUS_WIDTH_8,
+ };
+ static unsigned bus_widths[] = {
+ MMC_BUS_WIDTH_4,
+ MMC_BUS_WIDTH_8,
+ };
- if (mci->card_caps & MMC_MODE_4BIT) {
- dev_dbg(mci->mci_dev, "Set MMC bus width to 4 bit\n");
- /* Set the card to use 4 bit*/
- err = mci_switch(mci, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_4);
- if (err) {
- dev_dbg(mci->mci_dev, "Changing MMC bus width failed: %d\n", err);
- return err;
- }
- mci_set_bus_width(mci, MMC_BUS_WIDTH_4);
- } else if (mci->card_caps & MMC_MODE_8BIT) {
- dev_dbg(mci->mci_dev, "Set MMC bus width to 8 bit\n");
- /* Set the card to use 8 bit*/
- err = mci_switch(mci, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_8);
- if (err) {
- dev_dbg(mci->mci_dev, "Changing MMC bus width failed: %d\n", err);
- return err;
- }
- mci_set_bus_width(mci, MMC_BUS_WIDTH_8);
- }
/* if possible, speed up the transfer */
if (mci->card_caps & MMC_MODE_HS) {
if (mci->card_caps & MMC_MODE_HS_52MHz)
@@ -834,6 +884,37 @@ static int mci_startup_mmc(struct mci *mci)
} else
mci_set_clock(mci, 20000000);
+ /*
+ * Unlike SD, MMC cards dont have a configuration register to notify
+ * supported bus width. So bus test command should be run to identify
+ * the supported bus width or compare the ext csd values of current
+ * bus width and ext csd values of 1 bit mode read earlier.
+ */
+ if (host->host_caps & MMC_MODE_8BIT)
+ idx = 1;
+
+ for (; idx >= 0; idx--) {
+
+ /*
+ * Host is capable of 8bit transfer, then switch
+ * the device to work in 8bit transfer mode. If the
+ * mmc switch command returns error then switch to
+ * 4bit transfer mode. On success set the corresponding
+ * bus width on the host.
+ */
+ err = mci_switch(mci, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_BUS_WIDTH,
+ ext_csd_bits[idx]);
+ if (err)
+ continue;
+
+ mci_set_bus_width(mci, bus_widths[idx]);
+
+ err = mmc_compare_ext_csds(mci, bus_widths[idx]);
+ if (!err)
+ break;
+ }
+
return 0;
}