diff options
author | Adrian Hunter <adrian.hunter@intel.com> | 2011-11-28 16:22:00 +0200 |
---|---|---|
committer | Chris Ball <cjb@laptop.org> | 2012-01-11 23:58:43 -0500 |
commit | d30495048892980e5d453328d1cc9343b3f7e917 (patch) | |
tree | eff2ac501e38bad357c837eb6bf2835f7e83eba8 | |
parent | 482fce997e143a8d5429406fe066d31aa76ef70a (diff) |
mmc: allow upper layers to know immediately if card has been removed
Add a function mmc_detect_card_removed() which upper layers can use to
determine immediately if a card has been removed. This function should
be called after an I/O request fails so that all queued I/O requests
can be errored out immediately instead of waiting for the card device
to be removed.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Acked-by: Sujit Reddy Thumma <sthumma@codeaurora.org>
Signed-off-by: Chris Ball <cjb@laptop.org>
-rw-r--r-- | drivers/mmc/core/core.c | 51 | ||||
-rw-r--r-- | drivers/mmc/core/core.h | 3 | ||||
-rw-r--r-- | drivers/mmc/core/mmc.c | 12 | ||||
-rw-r--r-- | drivers/mmc/core/sd.c | 12 | ||||
-rw-r--r-- | drivers/mmc/core/sdio.c | 11 | ||||
-rw-r--r-- | include/linux/mmc/card.h | 3 | ||||
-rw-r--r-- | include/linux/mmc/core.h | 2 | ||||
-rw-r--r-- | include/linux/mmc/host.h | 1 |
8 files changed, 89 insertions, 6 deletions
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 950b97d7412..a2aa860956e 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -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. diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 14664f1fb16..34009241213 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -24,6 +24,7 @@ struct mmc_bus_ops { int (*resume)(struct mmc_host *); int (*power_save)(struct mmc_host *); int (*power_restore)(struct mmc_host *); + int (*alive)(struct mmc_host *); }; void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops); @@ -59,6 +60,8 @@ 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_detect_card_removed(struct mmc_host *host); + int mmc_attach_mmc(struct mmc_host *host); int mmc_attach_sd(struct mmc_host *host); int mmc_attach_sdio(struct mmc_host *host); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index d240427c124..fc1059bb6a0 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1105,6 +1105,14 @@ static void mmc_remove(struct mmc_host *host) } /* + * Card detection - card is alive. + */ +static int mmc_alive(struct mmc_host *host) +{ + return mmc_send_status(host->card, NULL); +} + +/* * Card detection callback from host. */ static void mmc_detect(struct mmc_host *host) @@ -1119,7 +1127,7 @@ static void mmc_detect(struct mmc_host *host) /* * Just check if our card has been removed. */ - err = mmc_send_status(host->card, NULL); + err = _mmc_detect_card_removed(host); mmc_release_host(host); @@ -1224,6 +1232,7 @@ static const struct mmc_bus_ops mmc_ops = { .suspend = NULL, .resume = NULL, .power_restore = mmc_power_restore, + .alive = mmc_alive, }; static const struct mmc_bus_ops mmc_ops_unsafe = { @@ -1234,6 +1243,7 @@ static const struct mmc_bus_ops mmc_ops_unsafe = { .suspend = mmc_suspend, .resume = mmc_resume, .power_restore = mmc_power_restore, + .alive = mmc_alive, }; static void mmc_attach_bus_ops(struct mmc_host *host) diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 85b858f6d5d..6f27d35081b 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -1019,6 +1019,14 @@ static void mmc_sd_remove(struct mmc_host *host) } /* + * Card detection - card is alive. + */ +static int mmc_sd_alive(struct mmc_host *host) +{ + return mmc_send_status(host->card, NULL); +} + +/* * Card detection callback from host. */ static void mmc_sd_detect(struct mmc_host *host) @@ -1033,7 +1041,7 @@ static void mmc_sd_detect(struct mmc_host *host) /* * Just check if our card has been removed. */ - err = mmc_send_status(host->card, NULL); + err = _mmc_detect_card_removed(host); mmc_release_host(host); @@ -1102,6 +1110,7 @@ static const struct mmc_bus_ops mmc_sd_ops = { .suspend = NULL, .resume = NULL, .power_restore = mmc_sd_power_restore, + .alive = mmc_sd_alive, }; static const struct mmc_bus_ops mmc_sd_ops_unsafe = { @@ -1110,6 +1119,7 @@ static const struct mmc_bus_ops mmc_sd_ops_unsafe = { .suspend = mmc_sd_suspend, .resume = mmc_sd_resume, .power_restore = mmc_sd_power_restore, + .alive = mmc_sd_alive, }; static void mmc_sd_attach_bus_ops(struct mmc_host *host) diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 8c04f7f46de..b77f770ce5d 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -820,6 +820,14 @@ static void mmc_sdio_remove(struct mmc_host *host) } /* + * Card detection - card is alive. + */ +static int mmc_sdio_alive(struct mmc_host *host) +{ + return mmc_select_card(host->card); +} + +/* * Card detection callback from host. */ static void mmc_sdio_detect(struct mmc_host *host) @@ -841,7 +849,7 @@ static void mmc_sdio_detect(struct mmc_host *host) /* * Just check if our card has been removed. */ - err = mmc_select_card(host->card); + err = _mmc_detect_card_removed(host); mmc_release_host(host); @@ -1019,6 +1027,7 @@ static const struct mmc_bus_ops mmc_sdio_ops = { .suspend = mmc_sdio_suspend, .resume = mmc_sdio_resume, .power_restore = mmc_sdio_power_restore, + .alive = mmc_sdio_alive, }; diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 534974c3ef0..6402d9224d6 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -209,6 +209,7 @@ struct mmc_card { #define MMC_STATE_HIGHSPEED_DDR (1<<4) /* card is in high speed mode */ #define MMC_STATE_ULTRAHIGHSPEED (1<<5) /* card is in ultra high speed mode */ #define MMC_CARD_SDXC (1<<6) /* card is SDXC */ +#define MMC_CARD_REMOVED (1<<7) /* card has been removed */ unsigned int quirks; /* card quirks */ #define MMC_QUIRK_LENIENT_FN0 (1<<0) /* allow SDIO FN0 writes outside of the VS CCCR range */ #define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1) /* use func->cur_blksize */ @@ -370,6 +371,7 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data) #define mmc_card_uhs(c) ((c)->state & MMC_STATE_ULTRAHIGHSPEED) #define mmc_sd_card_uhs(c) ((c)->state & MMC_STATE_ULTRAHIGHSPEED) #define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC) +#define mmc_card_removed(c) ((c) && ((c)->state & MMC_CARD_REMOVED)) #define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT) #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY) @@ -379,6 +381,7 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data) #define mmc_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED) #define mmc_sd_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED) #define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC) +#define mmc_card_set_removed(c) ((c)->state |= MMC_CARD_REMOVED) /* * Quirk add/remove for MMC products. diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index 174a844a5dd..87a976cc565 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -180,6 +180,8 @@ extern int mmc_try_claim_host(struct mmc_host *host); extern int mmc_flush_cache(struct mmc_card *); +extern int mmc_detect_card_removed(struct mmc_host *host); + /** * mmc_claim_host - exclusively claim a host * @host: mmc host to claim diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 706f72279a1..9a03d033574 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -299,6 +299,7 @@ struct mmc_host { int claim_cnt; /* "claim" nesting count */ struct delayed_work detect; + int detect_change; /* card detect flag */ const struct mmc_bus_ops *bus_ops; /* current bus driver */ unsigned int bus_refs; /* reference counter */ |