aboutsummaryrefslogtreecommitdiff
path: root/drivers/mmc/core
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/core')
-rw-r--r--drivers/mmc/core/Kconfig17
-rw-r--r--drivers/mmc/core/core.c29
-rw-r--r--drivers/mmc/core/core.h2
-rw-r--r--drivers/mmc/core/mmc.c289
-rw-r--r--drivers/mmc/core/sd.c318
5 files changed, 442 insertions, 213 deletions
diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig
new file mode 100644
index 00000000000..94222b9a15e
--- /dev/null
+++ b/drivers/mmc/core/Kconfig
@@ -0,0 +1,17 @@
+#
+# MMC core configuration
+#
+
+config MMC_UNSAFE_RESUME
+ bool "Allow unsafe resume (DANGEROUS)"
+ depends on MMC != n
+ help
+ If you say Y here, the MMC layer will assume that all cards
+ stayed in their respective slots during the suspend. The
+ normal behaviour is to remove them at suspend and
+ redetecting them at resume. Breaking this assumption will
+ in most cases result in data corruption.
+
+ This option is usually just for embedded systems which use
+ a MMC/SD card for rootfs. Most people should say N here.
+
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 75333a2461d..72c7cf4a9f9 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -677,14 +677,19 @@ int mmc_suspend_host(struct mmc_host *host, pm_message_t state)
mmc_bus_get(host);
if (host->bus_ops && !host->bus_dead) {
- if (host->bus_ops->remove)
- host->bus_ops->remove(host);
- mmc_detach_bus(host);
+ if (host->bus_ops->suspend)
+ host->bus_ops->suspend(host);
+ if (!host->bus_ops->resume) {
+ if (host->bus_ops->remove)
+ host->bus_ops->remove(host);
+
+ mmc_claim_host(host);
+ mmc_detach_bus(host);
+ mmc_release_host(host);
+ }
}
mmc_bus_put(host);
- BUG_ON(host->card);
-
mmc_power_off(host);
return 0;
@@ -698,7 +703,19 @@ EXPORT_SYMBOL(mmc_suspend_host);
*/
int mmc_resume_host(struct mmc_host *host)
{
- mmc_rescan(&host->detect.work);
+ mmc_bus_get(host);
+ if (host->bus_ops && !host->bus_dead) {
+ mmc_power_up(host);
+ BUG_ON(!host->bus_ops->resume);
+ host->bus_ops->resume(host);
+ }
+ mmc_bus_put(host);
+
+ /*
+ * We add a slight delay here so that resume can progress
+ * in parallel.
+ */
+ mmc_detect_change(host, 1);
return 0;
}
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index fad8edc3809..177264d090a 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -18,6 +18,8 @@
struct mmc_bus_ops {
void (*remove)(struct mmc_host *);
void (*detect)(struct mmc_host *);
+ void (*suspend)(struct mmc_host *);
+ void (*resume)(struct mmc_host *);
};
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 47449e87013..619581e4916 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -229,55 +229,13 @@ out:
}
/*
- * Host is being removed. Free up the current card.
- */
-static void mmc_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_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_ops = {
- .remove = mmc_remove,
- .detect = mmc_detect,
-};
-
-/*
- * Starting point for MMC 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_mmc(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;
@@ -287,27 +245,6 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr)
BUG_ON(!host);
BUG_ON(!host->claimed);
- mmc_attach_bus(host, &mmc_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;
- }
-
- host->ocr = mmc_select_voltage(host, ocr);
-
- /*
- * Can we support the voltage of the card?
- */
- 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
@@ -317,7 +254,9 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr)
mmc_go_idle(host);
/* The extra bit indicates that we support high capacity */
- mmc_send_op_cond(host, host->ocr | (1 << 30), NULL);
+ err = mmc_send_op_cond(host, ocr | (1 << 30), NULL);
+ if (err != MMC_ERR_NONE)
+ goto err;
/*
* Fetch CID from card.
@@ -326,16 +265,23 @@ int mmc_attach_mmc(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 = oldcard;
+ } else {
+ /*
+ * Allocate card structure.
+ */
+ card = mmc_alloc_card(host);
+ if (IS_ERR(card))
+ goto err;
- card->type = MMC_TYPE_MMC;
- card->rca = 1;
- memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
+ card->type = MMC_TYPE_MMC;
+ card->rca = 1;
+ memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
+ }
/*
* Set card RCA.
@@ -346,15 +292,17 @@ int mmc_attach_mmc(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);
+ }
/*
* Select card, as all following commands rely on that.
@@ -363,12 +311,14 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr)
if (err != MMC_ERR_NONE)
goto free_card;
- /*
- * Fetch and process extened CSD.
- */
- err = mmc_read_ext_csd(card);
- if (err != MMC_ERR_NONE)
- goto free_card;
+ if (!oldcard) {
+ /*
+ * Fetch and process extened CSD.
+ */
+ err = mmc_read_ext_csd(card);
+ if (err != MMC_ERR_NONE)
+ goto free_card;
+ }
/*
* Activate high speed (if supported)
@@ -412,11 +362,157 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr)
mmc_set_bus_width(card->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_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_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);
+ }
+}
+
+#ifdef CONFIG_MMC_UNSAFE_RESUME
+
+/*
+ * Suspend callback from host.
+ */
+static void mmc_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);
+}
- err = mmc_register_card(card);
+/*
+ * 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_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_suspend NULL
+#define mmc_resume NULL
+
+#endif
+
+static const struct mmc_bus_ops mmc_ops = {
+ .remove = mmc_remove,
+ .detect = mmc_detect,
+ .suspend = mmc_suspend,
+ .resume = mmc_resume,
+};
+
+/*
+ * Starting point for MMC card init.
+ */
+int mmc_attach_mmc(struct mmc_host *host, u32 ocr)
+{
+ int err;
+
+ BUG_ON(!host);
+ BUG_ON(!host->claimed);
+
+ mmc_attach_bus(host, &mmc_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;
+ }
+
+ host->ocr = mmc_select_voltage(host, ocr);
+
+ /*
+ * Can we support the voltage of the card?
+ */
+ 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;
@@ -424,8 +520,7 @@ int mmc_attach_mmc(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);
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);