]> Pileus Git - ~andy/linux/blobdiff - drivers/mmc/core/core.c
rt2x00: fix random stalls
[~andy/linux] / drivers / mmc / core / core.c
index 950b97d7412a4f6f5f562401f132ed0d568a969a..f545a3e6eb80587fa3995bbf1413e68b205c9cdf 100644 (file)
@@ -48,7 +48,7 @@ static struct workqueue_struct *workqueue;
  * performance cost, and for other reasons may not always be desired.
  * So we allow it it to be disabled.
  */
-int use_spi_crc = 1;
+bool use_spi_crc = 1;
 module_param(use_spi_crc, bool, 0);
 
 /*
@@ -58,9 +58,9 @@ module_param(use_spi_crc, bool, 0);
  * overridden if necessary.
  */
 #ifdef CONFIG_MMC_UNSAFE_RESUME
-int mmc_assume_removable;
+bool mmc_assume_removable;
 #else
-int mmc_assume_removable = 1;
+bool mmc_assume_removable = 1;
 #endif
 EXPORT_SYMBOL(mmc_assume_removable);
 module_param_named(removable, mmc_assume_removable, bool, 0644);
@@ -140,7 +140,7 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
                        cmd->retries = 0;
        }
 
-       if (err && cmd->retries) {
+       if (err && cmd->retries && !mmc_card_removed(host->card)) {
                /*
                 * Request starter must handle retries - see
                 * mmc_wait_for_req_done().
@@ -247,6 +247,11 @@ static void __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)
 {
        init_completion(&mrq->completion);
        mrq->done = mmc_wait_done;
+       if (mmc_card_removed(host->card)) {
+               mrq->cmd->error = -ENOMEDIUM;
+               complete(&mrq->completion);
+               return;
+       }
        mmc_start_request(host, mrq);
 }
 
@@ -259,7 +264,8 @@ static void mmc_wait_for_req_done(struct mmc_host *host,
                wait_for_completion(&mrq->completion);
 
                cmd = mrq->cmd;
-               if (!cmd->error || !cmd->retries)
+               if (!cmd->error || !cmd->retries ||
+                   mmc_card_removed(host->card))
                        break;
 
                pr_debug("%s: req failed (CMD%u): %d, retrying...\n",
@@ -1456,7 +1462,7 @@ void mmc_detect_change(struct mmc_host *host, unsigned long delay)
        WARN_ON(host->removed);
        spin_unlock_irqrestore(&host->lock, flags);
 #endif
-
+       host->detect_change = 1;
        mmc_schedule_delayed_work(&host->detect, delay);
 }
 
@@ -2049,6 +2055,43 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
        return -EIO;
 }
 
+int _mmc_detect_card_removed(struct mmc_host *host)
+{
+       int ret;
+
+       if ((host->caps & MMC_CAP_NONREMOVABLE) || !host->bus_ops->alive)
+               return 0;
+
+       if (!host->card || mmc_card_removed(host->card))
+               return 1;
+
+       ret = host->bus_ops->alive(host);
+       if (ret) {
+               mmc_card_set_removed(host->card);
+               pr_debug("%s: card remove detected\n", mmc_hostname(host));
+       }
+
+       return ret;
+}
+
+int mmc_detect_card_removed(struct mmc_host *host)
+{
+       struct mmc_card *card = host->card;
+
+       WARN_ON(!host->claimed);
+       /*
+        * The card will be considered unchanged unless we have been asked to
+        * detect a change or host requires polling to provide card detection.
+        */
+       if (card && !host->detect_change && !(host->caps & MMC_CAP_NEEDS_POLL))
+               return mmc_card_removed(card);
+
+       host->detect_change = 0;
+
+       return _mmc_detect_card_removed(host);
+}
+EXPORT_SYMBOL(mmc_detect_card_removed);
+
 void mmc_rescan(struct work_struct *work)
 {
        static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
@@ -2069,6 +2112,8 @@ void mmc_rescan(struct work_struct *work)
            && !(host->caps & MMC_CAP_NONREMOVABLE))
                host->bus_ops->detect(host);
 
+       host->detect_change = 0;
+
        /*
         * Let mmc_bus_put() free the bus/bus_ops if we've found that
         * the card is no longer present.
@@ -2130,6 +2175,7 @@ void mmc_stop_host(struct mmc_host *host)
 
        mmc_bus_get(host);
        if (host->bus_ops && !host->bus_dead) {
+               /* Calling bus_ops->remove() with a claimed host can deadlock */
                if (host->bus_ops->remove)
                        host->bus_ops->remove(host);
 
@@ -2201,6 +2247,9 @@ int mmc_card_awake(struct mmc_host *host)
 {
        int err = -ENOSYS;
 
+       if (host->caps2 & MMC_CAP2_NO_SLEEP_CMD)
+               return 0;
+
        mmc_bus_get(host);
 
        if (host->bus_ops && !host->bus_dead && host->bus_ops->awake)
@@ -2216,6 +2265,9 @@ int mmc_card_sleep(struct mmc_host *host)
 {
        int err = -ENOSYS;
 
+       if (host->caps2 & MMC_CAP2_NO_SLEEP_CMD)
+               return 0;
+
        mmc_bus_get(host);
 
        if (host->bus_ops && !host->bus_dead && host->bus_ops->sleep)
@@ -2270,6 +2322,7 @@ EXPORT_SYMBOL(mmc_flush_cache);
 int mmc_cache_ctrl(struct mmc_host *host, u8 enable)
 {
        struct mmc_card *card = host->card;
+       unsigned int timeout;
        int err = 0;
 
        if (!(host->caps2 & MMC_CAP2_CACHE_CTRL) ||
@@ -2280,16 +2333,18 @@ int mmc_cache_ctrl(struct mmc_host *host, u8 enable)
                        (card->ext_csd.cache_size > 0)) {
                enable = !!enable;
 
-               if (card->ext_csd.cache_ctrl ^ enable)
+               if (card->ext_csd.cache_ctrl ^ enable) {
+                       timeout = enable ? card->ext_csd.generic_cmd6_time : 0;
                        err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
-                                       EXT_CSD_CACHE_CTRL, enable, 0);
-               if (err)
-                       pr_err("%s: cache %s error %d\n",
-                                       mmc_hostname(card->host),
-                                       enable ? "on" : "off",
-                                       err);
-               else
-                       card->ext_csd.cache_ctrl = enable;
+                                       EXT_CSD_CACHE_CTRL, enable, timeout);
+                       if (err)
+                               pr_err("%s: cache %s error %d\n",
+                                               mmc_hostname(card->host),
+                                               enable ? "on" : "off",
+                                               err);
+                       else
+                               card->ext_csd.cache_ctrl = enable;
+               }
        }
 
        return err;
@@ -2310,7 +2365,13 @@ int mmc_suspend_host(struct mmc_host *host)
                cancel_delayed_work(&host->disable);
        cancel_delayed_work(&host->detect);
        mmc_flush_scheduled_work();
-       err = mmc_cache_ctrl(host, 0);
+       if (mmc_try_claim_host(host)) {
+               err = mmc_cache_ctrl(host, 0);
+               mmc_do_release_host(host);
+       } else {
+               err = -EBUSY;
+       }
+
        if (err)
                goto out;
 
@@ -2338,7 +2399,9 @@ int mmc_suspend_host(struct mmc_host *host)
                        if (err == -ENOSYS || !host->bus_ops->resume) {
                                /*
                                 * We simply "remove" the card in this case.
-                                * It will be redetected on resume.
+                                * It will be redetected on resume.  (Calling
+                                * bus_ops->remove() with a claimed host can
+                                * deadlock.)
                                 */
                                if (host->bus_ops->remove)
                                        host->bus_ops->remove(host);
@@ -2431,11 +2494,11 @@ int mmc_pm_notify(struct notifier_block *notify_block,
                if (!host->bus_ops || host->bus_ops->suspend)
                        break;
 
-               mmc_claim_host(host);
-
+               /* Calling bus_ops->remove() with a claimed host can deadlock */
                if (host->bus_ops->remove)
                        host->bus_ops->remove(host);
 
+               mmc_claim_host(host);
                mmc_detach_bus(host);
                mmc_power_off(host);
                mmc_release_host(host);