mmc: support unsafe resume of cards
[GitHub/exynos8895/android_kernel_samsung_universal8895.git] / drivers / mmc / core / sd.c
index ae54e8eb7feacb1bc2720995100b4510e1945374..6597e778f70e28687eab42cfa81a0f00e94ddafc 100644 (file)
@@ -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);