|
@@ -160,9 +160,7 @@ static int sdio_enable_wide(struct mmc_card *card)
|
|
|
if (ret)
|
|
|
return ret;
|
|
|
|
|
|
- mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
|
|
|
-
|
|
|
- return 0;
|
|
|
+ return 1;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -222,10 +220,34 @@ static int sdio_disable_wide(struct mmc_card *card)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+static int sdio_enable_4bit_bus(struct mmc_card *card)
|
|
|
+{
|
|
|
+ int err;
|
|
|
+
|
|
|
+ if (card->type == MMC_TYPE_SDIO)
|
|
|
+ return sdio_enable_wide(card);
|
|
|
+
|
|
|
+ if ((card->host->caps & MMC_CAP_4_BIT_DATA) &&
|
|
|
+ (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
|
|
|
+ err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+ } else
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ err = sdio_enable_wide(card);
|
|
|
+ if (err <= 0)
|
|
|
+ mmc_app_set_bus_width(card, MMC_BUS_WIDTH_1);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
/*
|
|
|
* Test if the card supports high-speed mode and, if so, switch to it.
|
|
|
*/
|
|
|
-static int sdio_enable_hs(struct mmc_card *card)
|
|
|
+static int mmc_sdio_switch_hs(struct mmc_card *card, int enable)
|
|
|
{
|
|
|
int ret;
|
|
|
u8 speed;
|
|
@@ -240,7 +262,10 @@ static int sdio_enable_hs(struct mmc_card *card)
|
|
|
if (ret)
|
|
|
return ret;
|
|
|
|
|
|
- speed |= SDIO_SPEED_EHS;
|
|
|
+ if (enable)
|
|
|
+ speed |= SDIO_SPEED_EHS;
|
|
|
+ else
|
|
|
+ speed &= ~SDIO_SPEED_EHS;
|
|
|
|
|
|
ret = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_SPEED, speed, NULL);
|
|
|
if (ret)
|
|
@@ -249,6 +274,24 @@ static int sdio_enable_hs(struct mmc_card *card)
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Enable SDIO/combo card's high-speed mode. Return 0/1 if [not]supported.
|
|
|
+ */
|
|
|
+static int sdio_enable_hs(struct mmc_card *card)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = mmc_sdio_switch_hs(card, true);
|
|
|
+ if (ret <= 0 || card->type == MMC_TYPE_SDIO)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ ret = mmc_sd_switch_hs(card);
|
|
|
+ if (ret <= 0)
|
|
|
+ mmc_sdio_switch_hs(card, false);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
static unsigned mmc_sdio_get_max_clock(struct mmc_card *card)
|
|
|
{
|
|
|
unsigned max_dtr;
|
|
@@ -265,6 +308,9 @@ static unsigned mmc_sdio_get_max_clock(struct mmc_card *card)
|
|
|
max_dtr = card->cis.max_dtr;
|
|
|
}
|
|
|
|
|
|
+ if (card->type == MMC_TYPE_SD_COMBO)
|
|
|
+ max_dtr = min(max_dtr, mmc_sd_get_max_clock(card));
|
|
|
+
|
|
|
return max_dtr;
|
|
|
}
|
|
|
|
|
@@ -310,7 +356,24 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
|
|
|
goto err;
|
|
|
}
|
|
|
|
|
|
- card->type = MMC_TYPE_SDIO;
|
|
|
+ err = mmc_sd_get_cid(host, host->ocr & ocr, card->raw_cid);
|
|
|
+
|
|
|
+ if (!err) {
|
|
|
+ card->type = MMC_TYPE_SD_COMBO;
|
|
|
+
|
|
|
+ if (oldcard && (oldcard->type != MMC_TYPE_SD_COMBO ||
|
|
|
+ memcmp(card->raw_cid, oldcard->raw_cid, sizeof(card->raw_cid)) != 0)) {
|
|
|
+ mmc_remove_card(card);
|
|
|
+ return -ENOENT;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ card->type = MMC_TYPE_SDIO;
|
|
|
+
|
|
|
+ if (oldcard && oldcard->type != MMC_TYPE_SDIO) {
|
|
|
+ mmc_remove_card(card);
|
|
|
+ return -ENOENT;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
/*
|
|
|
* Call the optional HC's init_card function to handle quirks.
|
|
@@ -329,6 +392,17 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
|
|
|
mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
|
|
|
}
|
|
|
|
|
|
+ /*
|
|
|
+ * Read CSD, before selecting the card
|
|
|
+ */
|
|
|
+ if (!oldcard && card->type == MMC_TYPE_SD_COMBO) {
|
|
|
+ err = mmc_sd_get_csd(host, card);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ mmc_decode_cid(card);
|
|
|
+ }
|
|
|
+
|
|
|
/*
|
|
|
* Select card, as all following commands rely on that.
|
|
|
*/
|
|
@@ -356,14 +430,33 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
|
|
|
int same = (card->cis.vendor == oldcard->cis.vendor &&
|
|
|
card->cis.device == oldcard->cis.device);
|
|
|
mmc_remove_card(card);
|
|
|
- if (!same) {
|
|
|
- err = -ENOENT;
|
|
|
- goto err;
|
|
|
- }
|
|
|
+ if (!same)
|
|
|
+ return -ENOENT;
|
|
|
+
|
|
|
card = oldcard;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+ if (card->type == MMC_TYPE_SD_COMBO) {
|
|
|
+ err = mmc_sd_setup_card(host, card, oldcard != NULL);
|
|
|
+ /* handle as SDIO-only card if memory init failed */
|
|
|
+ if (err) {
|
|
|
+ mmc_go_idle(host);
|
|
|
+ if (mmc_host_is_spi(host))
|
|
|
+ /* should not fail, as it worked previously */
|
|
|
+ mmc_spi_set_crc(host, use_spi_crc);
|
|
|
+ card->type = MMC_TYPE_SDIO;
|
|
|
+ } else
|
|
|
+ card->dev.type = &sd_type;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If needed, disconnect card detection pull-up resistor.
|
|
|
+ */
|
|
|
+ err = sdio_disable_cd(card);
|
|
|
+ if (err)
|
|
|
+ goto remove;
|
|
|
+
|
|
|
/*
|
|
|
* Switch to high-speed (if supported).
|
|
|
*/
|
|
@@ -381,8 +474,10 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
|
|
|
/*
|
|
|
* Switch to wider bus (if supported).
|
|
|
*/
|
|
|
- err = sdio_enable_wide(card);
|
|
|
- if (err)
|
|
|
+ err = sdio_enable_4bit_bus(card);
|
|
|
+ if (err > 0)
|
|
|
+ mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
|
|
|
+ else if (err)
|
|
|
goto remove;
|
|
|
|
|
|
if (!oldcard)
|
|
@@ -496,9 +591,14 @@ static int mmc_sdio_resume(struct mmc_host *host)
|
|
|
mmc_claim_host(host);
|
|
|
err = mmc_sdio_init_card(host, host->ocr, host->card,
|
|
|
(host->pm_flags & MMC_PM_KEEP_POWER));
|
|
|
- if (!err)
|
|
|
+ if (!err) {
|
|
|
/* We may have switched to 1-bit mode during suspend. */
|
|
|
- err = sdio_enable_wide(host->card);
|
|
|
+ err = sdio_enable_4bit_bus(host->card);
|
|
|
+ if (err > 0) {
|
|
|
+ mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
|
|
|
+ err = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
if (!err && host->sdio_irqs)
|
|
|
mmc_signal_sdio_irq(host);
|
|
|
mmc_release_host(host);
|
|
@@ -582,13 +682,6 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
|
|
|
funcs = (ocr & 0x70000000) >> 28;
|
|
|
card->sdio_funcs = 0;
|
|
|
|
|
|
- /*
|
|
|
- * If needed, disconnect card detection pull-up resistor.
|
|
|
- */
|
|
|
- err = sdio_disable_cd(card);
|
|
|
- if (err)
|
|
|
- goto remove;
|
|
|
-
|
|
|
/*
|
|
|
* Initialize (but don't add) all present functions.
|
|
|
*/
|