This is an option for use by developers; most people should
          say N here.  This enables MMC core and driver debugging.
 
+source "drivers/mmc/core/Kconfig"
+
 source "drivers/mmc/card/Kconfig"
 
 source "drivers/mmc/host/Kconfig"
 
--- /dev/null
+#
+# 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.
+
 
 
        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;
  */
 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;
 }
 
 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);
 
 }
 
 /*
- * 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;
        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
        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.
        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.
 
        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.
        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)
                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;
 
 
 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);
 
 }
 
 /*
- * 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;
        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
         * 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.
        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.
 
        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)
                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;
 
 
 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);