diff options
Diffstat (limited to 'drivers/mmc/core/core.c')
-rw-r--r-- | drivers/mmc/core/core.c | 101 |
1 files changed, 82 insertions, 19 deletions
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 950b97d7412a..f545a3e6eb80 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -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); |