Commit 08ee80cc authored by Philip Rakity's avatar Philip Rakity Committed by Chris Ball

mmc: core: eMMC bus width may not work on all platforms

CMD19 -- The offical way to validate bus widths from the JEDEC spec
does not work on all platforms.  Some platforms that use PCI/PCIe
to connect their SD controllers are known to fail.

If the quirk MMC_BUS_WIDTH_TEST is not defined we try to figure out
the bus width by reading the ext_csd at different bus widths and
compare this against the ext_csd read in 1 bit mode.  If no ext_csd
is available we default to 1 bit operations.

Code has been tested on mmp2 against 8 bit eMMC and Transcend 2GB
card that is known to not work in 4 bit mode.  The physical pins
on the card are not present to support 4 bit operation.
Signed-off-by: default avatarPhilip Rakity <prakity@marvell.com>
Signed-off-by: default avatarChris Ball <cjb@laptop.org>
parent 4f3d3e9b
...@@ -174,14 +174,17 @@ static int mmc_decode_csd(struct mmc_card *card) ...@@ -174,14 +174,17 @@ static int mmc_decode_csd(struct mmc_card *card)
} }
/* /*
* Read and decode extended CSD. * Read extended CSD.
*/ */
static int mmc_read_ext_csd(struct mmc_card *card) static int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd)
{ {
int err; int err;
u8 *ext_csd; u8 *ext_csd;
BUG_ON(!card); BUG_ON(!card);
BUG_ON(!new_ext_csd);
*new_ext_csd = NULL;
if (card->csd.mmca_vsn < CSD_SPEC_VER_4) if (card->csd.mmca_vsn < CSD_SPEC_VER_4)
return 0; return 0;
...@@ -199,12 +202,15 @@ static int mmc_read_ext_csd(struct mmc_card *card) ...@@ -199,12 +202,15 @@ static int mmc_read_ext_csd(struct mmc_card *card)
err = mmc_send_ext_csd(card, ext_csd); err = mmc_send_ext_csd(card, ext_csd);
if (err) { if (err) {
kfree(ext_csd);
*new_ext_csd = NULL;
/* If the host or the card can't do the switch, /* If the host or the card can't do the switch,
* fail more gracefully. */ * fail more gracefully. */
if ((err != -EINVAL) if ((err != -EINVAL)
&& (err != -ENOSYS) && (err != -ENOSYS)
&& (err != -EFAULT)) && (err != -EFAULT))
goto out; return err;
/* /*
* High capacity cards should have this "magic" size * High capacity cards should have this "magic" size
...@@ -222,9 +228,23 @@ static int mmc_read_ext_csd(struct mmc_card *card) ...@@ -222,9 +228,23 @@ static int mmc_read_ext_csd(struct mmc_card *card)
mmc_hostname(card->host)); mmc_hostname(card->host));
err = 0; err = 0;
} }
} else
*new_ext_csd = ext_csd;
goto out; return err;
} }
/*
* Decode extended CSD.
*/
static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
{
int err = 0;
BUG_ON(!card);
if (!ext_csd)
return 0;
/* Version is coded in the CSD_STRUCTURE byte in the EXT_CSD register */ /* Version is coded in the CSD_STRUCTURE byte in the EXT_CSD register */
if (card->csd.structure == 3) { if (card->csd.structure == 3) {
...@@ -372,8 +392,69 @@ static int mmc_read_ext_csd(struct mmc_card *card) ...@@ -372,8 +392,69 @@ static int mmc_read_ext_csd(struct mmc_card *card)
card->erased_byte = 0x0; card->erased_byte = 0x0;
out: out:
return err;
}
static inline void mmc_free_ext_csd(u8 *ext_csd)
{
kfree(ext_csd); kfree(ext_csd);
}
static int mmc_compare_ext_csds(struct mmc_card *card, u8 *ext_csd,
unsigned bus_width)
{
u8 *bw_ext_csd;
int err;
err = mmc_get_ext_csd(card, &bw_ext_csd);
if (err)
return err;
if ((ext_csd == NULL || bw_ext_csd == NULL)) {
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 = (!(ext_csd[EXT_CSD_PARTITION_SUPPORT] ==
bw_ext_csd[EXT_CSD_PARTITION_SUPPORT]) &&
(ext_csd[EXT_CSD_ERASED_MEM_CONT] ==
bw_ext_csd[EXT_CSD_ERASED_MEM_CONT]) &&
(ext_csd[EXT_CSD_REV] ==
bw_ext_csd[EXT_CSD_REV]) &&
(ext_csd[EXT_CSD_STRUCTURE] ==
bw_ext_csd[EXT_CSD_STRUCTURE]) &&
(ext_csd[EXT_CSD_CARD_TYPE] ==
bw_ext_csd[EXT_CSD_CARD_TYPE]) &&
(ext_csd[EXT_CSD_S_A_TIMEOUT] ==
bw_ext_csd[EXT_CSD_S_A_TIMEOUT]) &&
(ext_csd[EXT_CSD_HC_WP_GRP_SIZE] ==
bw_ext_csd[EXT_CSD_HC_WP_GRP_SIZE]) &&
(ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT] ==
bw_ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT]) &&
(ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] ==
bw_ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]) &&
(ext_csd[EXT_CSD_SEC_TRIM_MULT] ==
bw_ext_csd[EXT_CSD_SEC_TRIM_MULT]) &&
(ext_csd[EXT_CSD_SEC_ERASE_MULT] ==
bw_ext_csd[EXT_CSD_SEC_ERASE_MULT]) &&
(ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT] ==
bw_ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT]) &&
(ext_csd[EXT_CSD_TRIM_MULT] ==
bw_ext_csd[EXT_CSD_TRIM_MULT]) &&
memcmp(&ext_csd[EXT_CSD_SEC_CNT],
&bw_ext_csd[EXT_CSD_SEC_CNT],
4) != 0);
if (err)
err = -EINVAL;
out:
mmc_free_ext_csd(bw_ext_csd);
return err; return err;
} }
...@@ -438,6 +519,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, ...@@ -438,6 +519,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
u32 cid[4]; u32 cid[4];
unsigned int max_dtr; unsigned int max_dtr;
u32 rocr; u32 rocr;
u8 *ext_csd = NULL;
BUG_ON(!host); BUG_ON(!host);
WARN_ON(!host->claimed); WARN_ON(!host->claimed);
...@@ -536,7 +618,11 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, ...@@ -536,7 +618,11 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
/* /*
* Fetch and process extended CSD. * Fetch and process extended CSD.
*/ */
err = mmc_read_ext_csd(card);
err = mmc_get_ext_csd(card, &ext_csd);
if (err)
goto free_card;
err = mmc_read_ext_csd(card, ext_csd);
if (err) if (err)
goto free_card; goto free_card;
...@@ -676,13 +762,17 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, ...@@ -676,13 +762,17 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
0); 0);
if (!err) { if (!err) {
mmc_set_bus_width(card->host, bus_width); mmc_set_bus_width(card->host, bus_width);
/* /*
* If controller can't handle bus width test, * If controller can't handle bus width test,
* use the highest bus width to maintain * compare ext_csd previously read in 1 bit mode
* compatibility with previous MMC behavior. * against ext_csd at new bus width
*/ */
if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST)) if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST))
break; err = mmc_compare_ext_csds(card,
ext_csd,
bus_width);
else
err = mmc_bus_test(card, bus_width); err = mmc_bus_test(card, bus_width);
if (!err) if (!err)
break; break;
...@@ -730,12 +820,14 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, ...@@ -730,12 +820,14 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
if (!oldcard) if (!oldcard)
host->card = card; host->card = card;
mmc_free_ext_csd(ext_csd);
return 0; return 0;
free_card: free_card:
if (!oldcard) if (!oldcard)
mmc_remove_card(card); mmc_remove_card(card);
err: err:
mmc_free_ext_csd(ext_csd);
return err; return err;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment