mmc: Fix sd/sdio/mmc initialization frequency retries
authorAndy Ross <andy.ross@windriver.com>
Mon, 3 Jan 2011 18:36:56 +0000 (10:36 -0800)
committerChris Ball <cjb@laptop.org>
Sun, 9 Jan 2011 04:52:25 +0000 (23:52 -0500)
Rewrite and clean up mmc_rescan() to properly retry frequencies lower
than 400kHz.  Failures can happen both in sd_send_* calls and
mmc_attach_*.  Break out "mmc_rescan_try_freq" from the frequency
selection loop.  Symmetrize claim/release logic in mmc_attach_* API,
and move the sd_send_* calls there to make mmc_rescan easier to read.

Signed-off-by: Andy Ross <andy.ross@windriver.com>
Reviewed-and-Tested-by: Hein Tibosch <hein_tibosch@yahoo.es>
Reviewed-by: Chris Ball <cjb@laptop.org>
Signed-off-by: Chris Ball <cjb@laptop.org>
drivers/mmc/core/core.c
drivers/mmc/core/core.h
drivers/mmc/core/mmc.c
drivers/mmc/core/sd.c
drivers/mmc/core/sdio.c

index 97e0624eb9b60dd3a5dc9e3c87ee62ab723d0bc6..198f70bad9084a66dc5b2372ca3e81c61dff566e 100644 (file)
@@ -1485,25 +1485,41 @@ int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen)
 }
 EXPORT_SYMBOL(mmc_set_blocklen);
 
+static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
+{
+       host->f_init = freq;
+
+#ifdef CONFIG_MMC_DEBUG
+       pr_info("%s: %s: trying to init card at %u Hz\n",
+               mmc_hostname(host), __func__, host->f_init);
+#endif
+       mmc_power_up(host);
+       sdio_reset(host);
+       mmc_go_idle(host);
+
+       mmc_send_if_cond(host, host->ocr_avail);
+
+       /* Order's important: probe SDIO, then SD, then MMC */
+       if (!mmc_attach_sdio(host))
+               return 0;
+       if (!mmc_attach_sd(host))
+               return 0;
+       if (!mmc_attach_mmc(host))
+               return 0;
+
+       mmc_power_off(host);
+       return -EIO;
+}
+
 void mmc_rescan(struct work_struct *work)
 {
+       static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
        struct mmc_host *host =
                container_of(work, struct mmc_host, detect.work);
-       u32 ocr;
-       int err;
-       unsigned long flags;
        int i;
-       const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
-
-       spin_lock_irqsave(&host->lock, flags);
 
-       if (host->rescan_disable) {
-               spin_unlock_irqrestore(&host->lock, flags);
+       if (host->rescan_disable)
                return;
-       }
-
-       spin_unlock_irqrestore(&host->lock, flags);
-
 
        mmc_bus_get(host);
 
@@ -1526,8 +1542,6 @@ void mmc_rescan(struct work_struct *work)
                goto out;
        }
 
-       /* detect a newly inserted card */
-
        /*
         * Only we can add a new handler, so it's safe to
         * release the lock here.
@@ -1537,77 +1551,16 @@ void mmc_rescan(struct work_struct *work)
        if (host->ops->get_cd && host->ops->get_cd(host) == 0)
                goto out;
 
+       mmc_claim_host(host);
        for (i = 0; i < ARRAY_SIZE(freqs); i++) {
-               mmc_claim_host(host);
-
-               if (freqs[i] >= host->f_min)
-                       host->f_init = freqs[i];
-               else if (!i || freqs[i-1] > host->f_min)
-                       host->f_init = host->f_min;
-               else {
-                       mmc_release_host(host);
-                       goto out;
-               }
-#ifdef CONFIG_MMC_DEBUG
-               pr_info("%s: %s: trying to init card at %u Hz\n",
-                       mmc_hostname(host), __func__, host->f_init);
-#endif
-               mmc_power_up(host);
-               sdio_reset(host);
-               mmc_go_idle(host);
-
-               mmc_send_if_cond(host, host->ocr_avail);
-
-               /*
-                * First we search for SDIO...
-                */
-               err = mmc_send_io_op_cond(host, 0, &ocr);
-               if (!err) {
-                       if (mmc_attach_sdio(host, ocr)) {
-                               mmc_claim_host(host);
-                               /*
-                                * Try SDMEM (but not MMC) even if SDIO
-                                * is broken.
-                                */
-                               mmc_power_up(host);
-                               sdio_reset(host);
-                               mmc_go_idle(host);
-                               mmc_send_if_cond(host, host->ocr_avail);
-
-                               if (mmc_send_app_op_cond(host, 0, &ocr))
-                                       goto out_fail;
-
-                               if (mmc_attach_sd(host, ocr))
-                                       mmc_power_off(host);
-                       }
-                       goto out;
-               }
-
-               /*
-                * ...then normal SD...
-                */
-               err = mmc_send_app_op_cond(host, 0, &ocr);
-               if (!err) {
-                       if (mmc_attach_sd(host, ocr))
-                               mmc_power_off(host);
-                       goto out;
-               }
-
-               /*
-                * ...and finally MMC.
-                */
-               err = mmc_send_op_cond(host, 0, &ocr);
-               if (!err) {
-                       if (mmc_attach_mmc(host, ocr))
-                               mmc_power_off(host);
-                       goto out;
-               }
-
-out_fail:
-               mmc_release_host(host);
-               mmc_power_off(host);
+               if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min)))
+                       break;
+               if (freqs[i] < host->f_min)
+                       break;
        }
-out:
+       mmc_release_host(host);
+
+ out:
        if (host->caps & MMC_CAP_NEEDS_POLL)
                mmc_schedule_delayed_work(&host->detect, HZ);
 }
index 026c975b99a93f0b671879070fd5f4bf74650b24..ca1fdde29df6c9a90f7c1a931588048f5e3425e6 100644 (file)
@@ -57,9 +57,9 @@ void mmc_rescan(struct work_struct *work);
 void mmc_start_host(struct mmc_host *host);
 void mmc_stop_host(struct mmc_host *host);
 
-int mmc_attach_mmc(struct mmc_host *host, u32 ocr);
-int mmc_attach_sd(struct mmc_host *host, u32 ocr);
-int mmc_attach_sdio(struct mmc_host *host, u32 ocr);
+int mmc_attach_mmc(struct mmc_host *host);
+int mmc_attach_sd(struct mmc_host *host);
+int mmc_attach_sdio(struct mmc_host *host);
 
 /* Module parameters */
 extern int use_spi_crc;
index c86dd7384d7dd3355898d6347a6506fa1a7dc12b..16006ef153fe081c2f0f0a09cd4443ce7f3c49a1 100644 (file)
@@ -755,13 +755,18 @@ static void mmc_attach_bus_ops(struct mmc_host *host)
 /*
  * Starting point for MMC card init.
  */
-int mmc_attach_mmc(struct mmc_host *host, u32 ocr)
+int mmc_attach_mmc(struct mmc_host *host)
 {
        int err;
+       u32 ocr;
 
        BUG_ON(!host);
        WARN_ON(!host->claimed);
 
+       err = mmc_send_op_cond(host, 0, &ocr);
+       if (err)
+               return err;
+
        mmc_attach_bus_ops(host);
        if (host->ocr_avail_mmc)
                host->ocr_avail = host->ocr_avail_mmc;
@@ -804,20 +809,20 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr)
                goto err;
 
        mmc_release_host(host);
-
        err = mmc_add_card(host->card);
+       mmc_claim_host(host);
        if (err)
                goto remove_card;
 
        return 0;
 
 remove_card:
+       mmc_release_host(host);
        mmc_remove_card(host->card);
-       host->card = NULL;
        mmc_claim_host(host);
+       host->card = NULL;
 err:
        mmc_detach_bus(host);
-       mmc_release_host(host);
 
        printk(KERN_ERR "%s: error %d whilst initialising MMC card\n",
                mmc_hostname(host), err);
index de062ebd8b263f848386eac1226014dcb38ed3ee..d18c32bca99bae78fe3b0f04d22c2f124ce86cb5 100644 (file)
@@ -764,13 +764,18 @@ static void mmc_sd_attach_bus_ops(struct mmc_host *host)
 /*
  * Starting point for SD card init.
  */
-int mmc_attach_sd(struct mmc_host *host, u32 ocr)
+int mmc_attach_sd(struct mmc_host *host)
 {
        int err;
+       u32 ocr;
 
        BUG_ON(!host);
        WARN_ON(!host->claimed);
 
+       err = mmc_send_app_op_cond(host, 0, &ocr);
+       if (err)
+               return err;
+
        mmc_sd_attach_bus_ops(host);
        if (host->ocr_avail_sd)
                host->ocr_avail = host->ocr_avail_sd;
@@ -823,20 +828,20 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr)
                goto err;
 
        mmc_release_host(host);
-
        err = mmc_add_card(host->card);
+       mmc_claim_host(host);
        if (err)
                goto remove_card;
 
        return 0;
 
 remove_card:
+       mmc_release_host(host);
        mmc_remove_card(host->card);
        host->card = NULL;
        mmc_claim_host(host);
 err:
        mmc_detach_bus(host);
-       mmc_release_host(host);
 
        printk(KERN_ERR "%s: error %d whilst initialising SD card\n",
                mmc_hostname(host), err);
index 82f4b900898710adbbb29297b6f167946a01b5a6..5c4a54d9b6a402ed92dad1ad5b9d1252d908d368 100644 (file)
@@ -702,15 +702,19 @@ static const struct mmc_bus_ops mmc_sdio_ops = {
 /*
  * Starting point for SDIO card init.
  */
-int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
+int mmc_attach_sdio(struct mmc_host *host)
 {
-       int err;
-       int i, funcs;
+       int err, i, funcs;
+       u32 ocr;
        struct mmc_card *card;
 
        BUG_ON(!host);
        WARN_ON(!host->claimed);
 
+       err = mmc_send_io_op_cond(host, 0, &ocr);
+       if (err)
+               return err;
+
        mmc_attach_bus(host, &mmc_sdio_ops);
        if (host->ocr_avail_sdio)
                host->ocr_avail = host->ocr_avail_sdio;
@@ -783,12 +787,12 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
                        pm_runtime_enable(&card->sdio_func[i]->dev);
        }
 
-       mmc_release_host(host);
-
        /*
         * First add the card to the driver model...
         */
+       mmc_release_host(host);
        err = mmc_add_card(host->card);
+       mmc_claim_host(host);
        if (err)
                goto remove_added;
 
@@ -806,15 +810,17 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
 
 remove_added:
        /* Remove without lock if the device has been added. */
+       mmc_release_host(host);
        mmc_sdio_remove(host);
        mmc_claim_host(host);
 remove:
        /* And with lock if it hasn't been added. */
+       mmc_release_host(host);
        if (host->card)
                mmc_sdio_remove(host);
+       mmc_claim_host(host);
 err:
        mmc_detach_bus(host);
-       mmc_release_host(host);
 
        printk(KERN_ERR "%s: error %d whilst initialising SDIO card\n",
                mmc_hostname(host), err);