Commit d3049504 authored by Adrian Hunter's avatar Adrian Hunter Committed by Chris Ball

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: default avatarAdrian Hunter <adrian.hunter@intel.com>
Acked-by: default avatarSujit Reddy Thumma <sthumma@codeaurora.org>
Signed-off-by: default avatarChris Ball <cjb@laptop.org>
parent 482fce99
...@@ -140,7 +140,7 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) ...@@ -140,7 +140,7 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
cmd->retries = 0; cmd->retries = 0;
} }
if (err && cmd->retries) { if (err && cmd->retries && !mmc_card_removed(host->card)) {
/* /*
* Request starter must handle retries - see * Request starter must handle retries - see
* mmc_wait_for_req_done(). * mmc_wait_for_req_done().
...@@ -247,6 +247,11 @@ static void __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq) ...@@ -247,6 +247,11 @@ static void __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)
{ {
init_completion(&mrq->completion); init_completion(&mrq->completion);
mrq->done = mmc_wait_done; mrq->done = mmc_wait_done;
if (mmc_card_removed(host->card)) {
mrq->cmd->error = -ENOMEDIUM;
complete(&mrq->completion);
return;
}
mmc_start_request(host, mrq); mmc_start_request(host, mrq);
} }
...@@ -259,7 +264,8 @@ static void mmc_wait_for_req_done(struct mmc_host *host, ...@@ -259,7 +264,8 @@ static void mmc_wait_for_req_done(struct mmc_host *host,
wait_for_completion(&mrq->completion); wait_for_completion(&mrq->completion);
cmd = mrq->cmd; cmd = mrq->cmd;
if (!cmd->error || !cmd->retries) if (!cmd->error || !cmd->retries ||
mmc_card_removed(host->card))
break; break;
pr_debug("%s: req failed (CMD%u): %d, retrying...\n", 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) ...@@ -1456,7 +1462,7 @@ void mmc_detect_change(struct mmc_host *host, unsigned long delay)
WARN_ON(host->removed); WARN_ON(host->removed);
spin_unlock_irqrestore(&host->lock, flags); spin_unlock_irqrestore(&host->lock, flags);
#endif #endif
host->detect_change = 1;
mmc_schedule_delayed_work(&host->detect, delay); mmc_schedule_delayed_work(&host->detect, delay);
} }
...@@ -2049,6 +2055,43 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq) ...@@ -2049,6 +2055,43 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
return -EIO; 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) void mmc_rescan(struct work_struct *work)
{ {
static const unsigned freqs[] = { 400000, 300000, 200000, 100000 }; static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
...@@ -2069,6 +2112,8 @@ void mmc_rescan(struct work_struct *work) ...@@ -2069,6 +2112,8 @@ void mmc_rescan(struct work_struct *work)
&& !(host->caps & MMC_CAP_NONREMOVABLE)) && !(host->caps & MMC_CAP_NONREMOVABLE))
host->bus_ops->detect(host); host->bus_ops->detect(host);
host->detect_change = 0;
/* /*
* Let mmc_bus_put() free the bus/bus_ops if we've found that * Let mmc_bus_put() free the bus/bus_ops if we've found that
* the card is no longer present. * the card is no longer present.
......
...@@ -24,6 +24,7 @@ struct mmc_bus_ops { ...@@ -24,6 +24,7 @@ struct mmc_bus_ops {
int (*resume)(struct mmc_host *); int (*resume)(struct mmc_host *);
int (*power_save)(struct mmc_host *); int (*power_save)(struct mmc_host *);
int (*power_restore)(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); 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); ...@@ -59,6 +60,8 @@ void mmc_rescan(struct work_struct *work);
void mmc_start_host(struct mmc_host *host); void mmc_start_host(struct mmc_host *host);
void mmc_stop_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_mmc(struct mmc_host *host);
int mmc_attach_sd(struct mmc_host *host); int mmc_attach_sd(struct mmc_host *host);
int mmc_attach_sdio(struct mmc_host *host); int mmc_attach_sdio(struct mmc_host *host);
......
...@@ -1104,6 +1104,14 @@ static void mmc_remove(struct mmc_host *host) ...@@ -1104,6 +1104,14 @@ static void mmc_remove(struct mmc_host *host)
host->card = NULL; host->card = NULL;
} }
/*
* 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. * Card detection callback from host.
*/ */
...@@ -1119,7 +1127,7 @@ 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. * 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); mmc_release_host(host);
...@@ -1224,6 +1232,7 @@ static const struct mmc_bus_ops mmc_ops = { ...@@ -1224,6 +1232,7 @@ static const struct mmc_bus_ops mmc_ops = {
.suspend = NULL, .suspend = NULL,
.resume = NULL, .resume = NULL,
.power_restore = mmc_power_restore, .power_restore = mmc_power_restore,
.alive = mmc_alive,
}; };
static const struct mmc_bus_ops mmc_ops_unsafe = { static const struct mmc_bus_ops mmc_ops_unsafe = {
...@@ -1234,6 +1243,7 @@ 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, .suspend = mmc_suspend,
.resume = mmc_resume, .resume = mmc_resume,
.power_restore = mmc_power_restore, .power_restore = mmc_power_restore,
.alive = mmc_alive,
}; };
static void mmc_attach_bus_ops(struct mmc_host *host) static void mmc_attach_bus_ops(struct mmc_host *host)
......
...@@ -1018,6 +1018,14 @@ static void mmc_sd_remove(struct mmc_host *host) ...@@ -1018,6 +1018,14 @@ static void mmc_sd_remove(struct mmc_host *host)
host->card = NULL; host->card = NULL;
} }
/*
* 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. * Card detection callback from host.
*/ */
...@@ -1033,7 +1041,7 @@ 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. * 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); mmc_release_host(host);
...@@ -1102,6 +1110,7 @@ static const struct mmc_bus_ops mmc_sd_ops = { ...@@ -1102,6 +1110,7 @@ static const struct mmc_bus_ops mmc_sd_ops = {
.suspend = NULL, .suspend = NULL,
.resume = NULL, .resume = NULL,
.power_restore = mmc_sd_power_restore, .power_restore = mmc_sd_power_restore,
.alive = mmc_sd_alive,
}; };
static const struct mmc_bus_ops mmc_sd_ops_unsafe = { static const struct mmc_bus_ops mmc_sd_ops_unsafe = {
...@@ -1110,6 +1119,7 @@ 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, .suspend = mmc_sd_suspend,
.resume = mmc_sd_resume, .resume = mmc_sd_resume,
.power_restore = mmc_sd_power_restore, .power_restore = mmc_sd_power_restore,
.alive = mmc_sd_alive,
}; };
static void mmc_sd_attach_bus_ops(struct mmc_host *host) static void mmc_sd_attach_bus_ops(struct mmc_host *host)
......
...@@ -819,6 +819,14 @@ static void mmc_sdio_remove(struct mmc_host *host) ...@@ -819,6 +819,14 @@ static void mmc_sdio_remove(struct mmc_host *host)
host->card = NULL; host->card = NULL;
} }
/*
* 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. * Card detection callback from host.
*/ */
...@@ -841,7 +849,7 @@ 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. * Just check if our card has been removed.
*/ */
err = mmc_select_card(host->card); err = _mmc_detect_card_removed(host);
mmc_release_host(host); mmc_release_host(host);
...@@ -1019,6 +1027,7 @@ static const struct mmc_bus_ops mmc_sdio_ops = { ...@@ -1019,6 +1027,7 @@ static const struct mmc_bus_ops mmc_sdio_ops = {
.suspend = mmc_sdio_suspend, .suspend = mmc_sdio_suspend,
.resume = mmc_sdio_resume, .resume = mmc_sdio_resume,
.power_restore = mmc_sdio_power_restore, .power_restore = mmc_sdio_power_restore,
.alive = mmc_sdio_alive,
}; };
......
...@@ -209,6 +209,7 @@ struct mmc_card { ...@@ -209,6 +209,7 @@ struct mmc_card {
#define MMC_STATE_HIGHSPEED_DDR (1<<4) /* card is in high speed mode */ #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_STATE_ULTRAHIGHSPEED (1<<5) /* card is in ultra high speed mode */
#define MMC_CARD_SDXC (1<<6) /* card is SDXC */ #define MMC_CARD_SDXC (1<<6) /* card is SDXC */
#define MMC_CARD_REMOVED (1<<7) /* card has been removed */
unsigned int quirks; /* card quirks */ 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_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 */ #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) ...@@ -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_card_uhs(c) ((c)->state & MMC_STATE_ULTRAHIGHSPEED)
#define mmc_sd_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_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_present(c) ((c)->state |= MMC_STATE_PRESENT)
#define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY) #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) ...@@ -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_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED)
#define mmc_sd_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_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. * Quirk add/remove for MMC products.
......
...@@ -180,6 +180,8 @@ extern int mmc_try_claim_host(struct mmc_host *host); ...@@ -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_flush_cache(struct mmc_card *);
extern int mmc_detect_card_removed(struct mmc_host *host);
/** /**
* mmc_claim_host - exclusively claim a host * mmc_claim_host - exclusively claim a host
* @host: mmc host to claim * @host: mmc host to claim
......
...@@ -299,6 +299,7 @@ struct mmc_host { ...@@ -299,6 +299,7 @@ struct mmc_host {
int claim_cnt; /* "claim" nesting count */ int claim_cnt; /* "claim" nesting count */
struct delayed_work detect; struct delayed_work detect;
int detect_change; /* card detect flag */
const struct mmc_bus_ops *bus_ops; /* current bus driver */ const struct mmc_bus_ops *bus_ops; /* current bus driver */
unsigned int bus_refs; /* reference counter */ unsigned int bus_refs; /* reference counter */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment