aboutsummaryrefslogtreecommitdiff
path: root/drivers/mmc/core/sd.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/core/sd.c')
-rw-r--r--drivers/mmc/core/sd.c318
1 files changed, 208 insertions, 110 deletions
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index ae54e8eb7fe..6597e778f70 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -263,55 +263,13 @@ out:
}
/*
- * Host is being removed. Free up the current card.
- */
-static void mmc_sd_remove(struct mmc_host *host)
-{
- BUG_ON(!host);
- BUG_ON(!host->card);
-
- mmc_remove_card(host->card);
- host->card = NULL;
-}
-
-/*
- * Card detection callback from host.
- */
-static void mmc_sd_detect(struct mmc_host *host)
-{
- int err;
-
- BUG_ON(!host);
- BUG_ON(!host->card);
-
- mmc_claim_host(host);
-
- /*
- * Just check if our card has been removed.
- */
- err = mmc_send_status(host->card, NULL);
-
- mmc_release_host(host);
-
- if (err != MMC_ERR_NONE) {
- mmc_remove_card(host->card);
- host->card = NULL;
-
- mmc_claim_host(host);
- mmc_detach_bus(host);
- mmc_release_host(host);
- }
-}
-
-static const struct mmc_bus_ops mmc_sd_ops = {
- .remove = mmc_sd_remove,
- .detect = mmc_sd_detect,
-};
-
-/*
- * Starting point for SD card init.
+ * Handle the detection and initialisation of a card.
+ *
+ * In the case of a resume, "curcard" will contain the card
+ * we're trying to reinitialise.
*/
-int mmc_attach_sd(struct mmc_host *host, u32 ocr)
+static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
+ struct mmc_card *oldcard)
{
struct mmc_card *card;
int err;
@@ -321,34 +279,6 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr)
BUG_ON(!host);
BUG_ON(!host->claimed);
- mmc_attach_bus(host, &mmc_sd_ops);
-
- /*
- * Sanity check the voltages that the card claims to
- * support.
- */
- if (ocr & 0x7F) {
- printk(KERN_WARNING "%s: card claims to support voltages "
- "below the defined range. These will be ignored.\n",
- mmc_hostname(host));
- ocr &= ~0x7F;
- }
-
- if (ocr & MMC_VDD_165_195) {
- printk(KERN_WARNING "%s: SD card claims to support the "
- "incompletely defined 'low voltage range'. This "
- "will be ignored.\n", mmc_hostname(host));
- ocr &= ~MMC_VDD_165_195;
- }
-
- host->ocr = mmc_select_voltage(host, ocr);
-
- /*
- * Can we support the voltage(s) of the card(s)?
- */
- if (!host->ocr)
- goto err;
-
/*
* Since we're changing the OCR value, we seem to
* need to tell some cards to go back to the idle
@@ -363,11 +293,13 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr)
* of the ocr to indicate that we can handle
* block-addressed SDHC cards.
*/
- err = mmc_send_if_cond(host, host->ocr);
+ err = mmc_send_if_cond(host, ocr);
if (err == MMC_ERR_NONE)
- ocr = host->ocr | (1 << 30);
+ ocr |= 1 << 30;
- mmc_send_app_op_cond(host, ocr, NULL);
+ err = mmc_send_app_op_cond(host, ocr, NULL);
+ if (err != MMC_ERR_NONE)
+ goto err;
/*
* Fetch CID from card.
@@ -376,15 +308,22 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr)
if (err != MMC_ERR_NONE)
goto err;
- /*
- * Allocate card structure.
- */
- card = mmc_alloc_card(host);
- if (IS_ERR(card))
- goto err;
+ if (oldcard) {
+ if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0)
+ goto err;
- card->type = MMC_TYPE_SD;
- memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
+ card = oldcard;
+ } else {
+ /*
+ * Allocate card structure.
+ */
+ card = mmc_alloc_card(host);
+ if (IS_ERR(card))
+ goto err;
+
+ card->type = MMC_TYPE_SD;
+ memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
+ }
/*
* Set card RCA.
@@ -395,35 +334,42 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr)
mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
- /*
- * Fetch CSD from card.
- */
- err = mmc_send_csd(card, card->raw_csd);
- if (err != MMC_ERR_NONE)
- goto free_card;
+ if (!oldcard) {
+ /*
+ * Fetch CSD from card.
+ */
+ err = mmc_send_csd(card, card->raw_csd);
+ if (err != MMC_ERR_NONE)
+ goto free_card;
- mmc_decode_csd(card);
- mmc_decode_cid(card);
+ mmc_decode_csd(card);
+ mmc_decode_cid(card);
+ }
/*
- * Fetch SCR from card.
+ * Select card, as all following commands rely on that.
*/
err = mmc_select_card(card);
if (err != MMC_ERR_NONE)
goto free_card;
- err = mmc_app_send_scr(card, card->raw_scr);
- if (err != MMC_ERR_NONE)
- goto free_card;
+ if (!oldcard) {
+ /*
+ * Fetch SCR from card.
+ */
+ err = mmc_app_send_scr(card, card->raw_scr);
+ if (err != MMC_ERR_NONE)
+ goto free_card;
- mmc_decode_scr(card);
+ mmc_decode_scr(card);
- /*
- * Fetch switch information from card.
- */
- err = mmc_read_switch(card);
- if (err != MMC_ERR_NONE)
- goto free_card;
+ /*
+ * Fetch switch information from card.
+ */
+ err = mmc_read_switch(card);
+ if (err != MMC_ERR_NONE)
+ goto free_card;
+ }
/*
* Attempt to change to high-speed (if supported)
@@ -458,11 +404,164 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr)
mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
}
- host->card = card;
+ if (!oldcard)
+ host->card = card;
+
+ return MMC_ERR_NONE;
+
+free_card:
+ if (!oldcard)
+ mmc_remove_card(card);
+err:
+
+ return MMC_ERR_FAILED;
+}
+
+/*
+ * Host is being removed. Free up the current card.
+ */
+static void mmc_sd_remove(struct mmc_host *host)
+{
+ BUG_ON(!host);
+ BUG_ON(!host->card);
+
+ mmc_remove_card(host->card);
+ host->card = NULL;
+}
+
+/*
+ * Card detection callback from host.
+ */
+static void mmc_sd_detect(struct mmc_host *host)
+{
+ int err;
+
+ BUG_ON(!host);
+ BUG_ON(!host->card);
+
+ mmc_claim_host(host);
+
+ /*
+ * Just check if our card has been removed.
+ */
+ err = mmc_send_status(host->card, NULL);
mmc_release_host(host);
- err = mmc_register_card(card);
+ if (err != MMC_ERR_NONE) {
+ mmc_remove_card(host->card);
+ host->card = NULL;
+
+ mmc_claim_host(host);
+ mmc_detach_bus(host);
+ mmc_release_host(host);
+ }
+}
+
+#ifdef CONFIG_MMC_UNSAFE_RESUME
+
+/*
+ * Suspend callback from host.
+ */
+static void mmc_sd_suspend(struct mmc_host *host)
+{
+ BUG_ON(!host);
+ BUG_ON(!host->card);
+
+ mmc_claim_host(host);
+ mmc_deselect_cards(host);
+ host->card->state &= ~MMC_STATE_HIGHSPEED;
+ mmc_release_host(host);
+}
+
+/*
+ * Resume callback from host.
+ *
+ * This function tries to determine if the same card is still present
+ * and, if so, restore all state to it.
+ */
+static void mmc_sd_resume(struct mmc_host *host)
+{
+ int err;
+
+ BUG_ON(!host);
+ BUG_ON(!host->card);
+
+ mmc_claim_host(host);
+
+ err = mmc_sd_init_card(host, host->ocr, host->card);
+ if (err != MMC_ERR_NONE) {
+ mmc_remove_card(host->card);
+ host->card = NULL;
+
+ mmc_detach_bus(host);
+ }
+
+ mmc_release_host(host);
+}
+
+#else
+
+#define mmc_sd_suspend NULL
+#define mmc_sd_resume NULL
+
+#endif
+
+static const struct mmc_bus_ops mmc_sd_ops = {
+ .remove = mmc_sd_remove,
+ .detect = mmc_sd_detect,
+ .suspend = mmc_sd_suspend,
+ .resume = mmc_sd_resume,
+};
+
+/*
+ * Starting point for SD card init.
+ */
+int mmc_attach_sd(struct mmc_host *host, u32 ocr)
+{
+ int err;
+
+ BUG_ON(!host);
+ BUG_ON(!host->claimed);
+
+ mmc_attach_bus(host, &mmc_sd_ops);
+
+ /*
+ * Sanity check the voltages that the card claims to
+ * support.
+ */
+ if (ocr & 0x7F) {
+ printk(KERN_WARNING "%s: card claims to support voltages "
+ "below the defined range. These will be ignored.\n",
+ mmc_hostname(host));
+ ocr &= ~0x7F;
+ }
+
+ if (ocr & MMC_VDD_165_195) {
+ printk(KERN_WARNING "%s: SD card claims to support the "
+ "incompletely defined 'low voltage range'. This "
+ "will be ignored.\n", mmc_hostname(host));
+ ocr &= ~MMC_VDD_165_195;
+ }
+
+ host->ocr = mmc_select_voltage(host, ocr);
+
+ /*
+ * Can we support the voltage(s) of the card(s)?
+ */
+ if (!host->ocr)
+ goto err;
+
+ /*
+ * Detect and init the card.
+ */
+ err = mmc_sd_init_card(host, host->ocr, NULL);
+ if (err != MMC_ERR_NONE)
+ goto err;
+
+ mmc_release_host(host);
+
+ err = mmc_register_card(host->card);
if (err)
goto reclaim_host;
@@ -470,8 +569,7 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr)
reclaim_host:
mmc_claim_host(host);
-free_card:
- mmc_remove_card(card);
+ mmc_remove_card(host->card);
host->card = NULL;
err:
mmc_detach_bus(host);