]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/mmc/core/mmc.c
mmc: support unsafe resume of cards
[linux-2.6-omap-h63xx.git] / drivers / mmc / core / mmc.c
index c528017d6128b613f7acbcff5480c9eeac1f032c..619581e4916359762d41db0a6111da924c4f1fdb 100644 (file)
@@ -152,10 +152,9 @@ static void mmc_decode_csd(struct mmc_card *card)
 }
 
 /*
- * Read and decode extended CSD. Switch to high-speed and wide bus
- * if supported.
+ * Read and decode extended CSD.
  */
-static int mmc_process_ext_csd(struct mmc_card *card)
+static int mmc_read_ext_csd(struct mmc_card *card)
 {
        int err;
        u8 *ext_csd;
@@ -223,43 +222,157 @@ static int mmc_process_ext_csd(struct mmc_card *card)
                goto out;
        }
 
-       if (card->host->caps & MMC_CAP_MMC_HIGHSPEED) {
-               /* Activate highspeed support. */
+out:
+       kfree(ext_csd);
+
+       return err;
+}
+
+/*
+ * Handle the detection and initialisation of a card.
+ *
+ * In the case of a resume, "curcard" will contain the card
+ * we're trying to reinitialise.
+ */
+static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
+       struct mmc_card *oldcard)
+{
+       struct mmc_card *card;
+       int err;
+       u32 cid[4];
+       unsigned int max_dtr;
+
+       BUG_ON(!host);
+       BUG_ON(!host->claimed);
+
+       /*
+        * Since we're changing the OCR value, we seem to
+        * need to tell some cards to go back to the idle
+        * state.  We wait 1ms to give cards time to
+        * respond.
+        */
+       mmc_go_idle(host);
+
+       /* The extra bit indicates that we support high capacity */
+       err = mmc_send_op_cond(host, ocr | (1 << 30), NULL);
+       if (err != MMC_ERR_NONE)
+               goto err;
+
+       /*
+        * Fetch CID from card.
+        */
+       err = mmc_all_send_cid(host, cid);
+       if (err != MMC_ERR_NONE)
+               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));
+       }
+
+       /*
+        * Set card RCA.
+        */
+       err = mmc_set_relative_addr(card);
+       if (err != MMC_ERR_NONE)
+               goto free_card;
+
+       mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
+
+       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);
+       }
+
+       /*
+        * Select card, as all following commands rely on that.
+        */
+       err = mmc_select_card(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)
+        */
+       if ((card->ext_csd.hs_max_dtr != 0) &&
+               (host->caps & MMC_CAP_MMC_HIGHSPEED)) {
                err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
                        EXT_CSD_HS_TIMING, 1);
-               if (err != MMC_ERR_NONE) {
-                       printk(KERN_WARNING "%s: failed to switch "
-                               "card to mmc v4 high-speed mode.\n",
-                              mmc_hostname(card->host));
-                       err = MMC_ERR_NONE;
-                       goto out;
-               }
+               if (err != MMC_ERR_NONE)
+                       goto free_card;
 
                mmc_card_set_highspeed(card);
 
                mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
        }
 
-       /* Check for host support for wide-bus modes. */
-       if (card->host->caps & MMC_CAP_4_BIT_DATA) {
-               /* Activate 4-bit support. */
+       /*
+        * Compute bus speed.
+        */
+       max_dtr = (unsigned int)-1;
+
+       if (mmc_card_highspeed(card)) {
+               if (max_dtr > card->ext_csd.hs_max_dtr)
+                       max_dtr = card->ext_csd.hs_max_dtr;
+       } else if (max_dtr > card->csd.max_dtr) {
+               max_dtr = card->csd.max_dtr;
+       }
+
+       mmc_set_clock(host, max_dtr);
+
+       /*
+        * Activate wide bus (if supported).
+        */
+       if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) &&
+               (host->caps & MMC_CAP_4_BIT_DATA)) {
                err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
                        EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_4);
-               if (err != MMC_ERR_NONE) {
-                       printk(KERN_WARNING "%s: failed to switch "
-                               "card to mmc v4 4-bit bus mode.\n",
-                              mmc_hostname(card->host));
-                       err = MMC_ERR_NONE;
-                       goto out;
-               }
+               if (err != MMC_ERR_NONE)
+                       goto free_card;
 
                mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
        }
 
-out:
-       kfree(ext_csd);
+       if (!oldcard)
+               host->card = card;
 
-       return err;
+       return MMC_ERR_NONE;
+
+free_card:
+       if (!oldcard)
+               mmc_remove_card(card);
+err:
+
+       return MMC_ERR_FAILED;
 }
 
 /*
@@ -303,9 +416,60 @@ static void mmc_detect(struct mmc_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);
+}
+
+/*
+ * 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,
 };
 
 /*
@@ -313,16 +477,24 @@ static const struct mmc_bus_ops mmc_ops = {
  */
 int mmc_attach_mmc(struct mmc_host *host, u32 ocr)
 {
-       struct mmc_card *card;
        int err;
-       u32 cid[4];
-       unsigned int max_dtr;
 
        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);
 
        /*
@@ -332,85 +504,15 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr)
                goto err;
 
        /*
-        * Since we're changing the OCR value, we seem to
-        * need to tell some cards to go back to the idle
-        * state.  We wait 1ms to give cards time to
-        * respond.
-        */
-       mmc_go_idle(host);
-
-       /* The extra bit indicates that we support high capacity */
-       mmc_send_op_cond(host, host->ocr | (1 << 30), NULL);
-
-       /*
-        * Fetch CID from card.
+        * Detect and init the card.
         */
-       err = mmc_all_send_cid(host, cid);
+       err = mmc_sd_init_card(host, host->ocr, NULL);
        if (err != MMC_ERR_NONE)
                goto err;
 
-       /*
-        * 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));
-
-       /*
-        * Set card RCA.
-        */
-       err = mmc_set_relative_addr(card);
-       if (err != MMC_ERR_NONE)
-               goto free_card;
-
-       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;
-
-       mmc_decode_csd(card);
-       mmc_decode_cid(card);
-
-       /*
-        * Fetch and process extened CSD.
-        * This will switch into high-speed and wide bus modes,
-        * as available.
-        */
-       err = mmc_select_card(card);
-       if (err != MMC_ERR_NONE)
-               goto free_card;
-
-       err = mmc_process_ext_csd(card);
-       if (err != MMC_ERR_NONE)
-               goto free_card;
-
-       /*
-        * Compute bus speed.
-        */
-       max_dtr = (unsigned int)-1;
-
-       if (mmc_card_highspeed(card)) {
-               if (max_dtr > card->ext_csd.hs_max_dtr)
-                       max_dtr = card->ext_csd.hs_max_dtr;
-       } else if (max_dtr > card->csd.max_dtr) {
-               max_dtr = card->csd.max_dtr;
-       }
-
-       mmc_set_clock(host, max_dtr);
-
-       host->card = card;
-
        mmc_release_host(host);
 
-       err = mmc_register_card(card);
+       err = mmc_register_card(host->card);
        if (err)
                goto reclaim_host;
 
@@ -418,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);