summaryrefslogtreecommitdiff
path: root/drivers/mmc/core/core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/core/core.c')
-rw-r--r--drivers/mmc/core/core.c181
1 files changed, 151 insertions, 30 deletions
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index e55cde6d436d..2553d903a82b 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -58,6 +58,9 @@
*/
#define MMC_BKOPS_MAX_TIMEOUT (4 * 60 * 1000) /* max time to wait in ms */
+/* The max erase timeout, used when host->max_busy_timeout isn't specified */
+#define MMC_ERASE_TIMEOUT_MS (60 * 1000) /* 60 s */
+
static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
/*
@@ -117,6 +120,24 @@ static inline void mmc_should_fail_request(struct mmc_host *host,
#endif /* CONFIG_FAIL_MMC_REQUEST */
+static inline void mmc_complete_cmd(struct mmc_request *mrq)
+{
+ if (mrq->cap_cmd_during_tfr && !completion_done(&mrq->cmd_completion))
+ complete_all(&mrq->cmd_completion);
+}
+
+void mmc_command_done(struct mmc_host *host, struct mmc_request *mrq)
+{
+ if (!mrq->cap_cmd_during_tfr)
+ return;
+
+ mmc_complete_cmd(mrq);
+
+ pr_debug("%s: cmd done, tfr ongoing (CMD%u)\n",
+ mmc_hostname(host), mrq->cmd->opcode);
+}
+EXPORT_SYMBOL(mmc_command_done);
+
/**
* mmc_request_done - finish processing an MMC request
* @host: MMC host which completed request
@@ -143,6 +164,11 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
cmd->retries = 0;
}
+ if (host->ongoing_mrq == mrq)
+ host->ongoing_mrq = NULL;
+
+ mmc_complete_cmd(mrq);
+
trace_mmc_request_done(host, mrq);
if (err && cmd->retries && !mmc_card_removed(host->card)) {
@@ -155,7 +181,8 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
} else {
mmc_should_fail_request(host, mrq);
- led_trigger_event(host->led, LED_OFF);
+ if (!host->ongoing_mrq)
+ led_trigger_event(host->led, LED_OFF);
if (mrq->sbc) {
pr_debug("%s: req done <CMD%u>: %d: %08x %08x %08x %08x\n",
@@ -220,6 +247,15 @@ static void __mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
}
}
+ if (mrq->cap_cmd_during_tfr) {
+ host->ongoing_mrq = mrq;
+ /*
+ * Retry path could come through here without having waiting on
+ * cmd_completion, so ensure it is reinitialised.
+ */
+ reinit_completion(&mrq->cmd_completion);
+ }
+
trace_mmc_request_start(host, mrq);
host->ops->request(host, mrq);
@@ -386,6 +422,18 @@ static void mmc_wait_done(struct mmc_request *mrq)
complete(&mrq->completion);
}
+static inline void mmc_wait_ongoing_tfr_cmd(struct mmc_host *host)
+{
+ struct mmc_request *ongoing_mrq = READ_ONCE(host->ongoing_mrq);
+
+ /*
+ * If there is an ongoing transfer, wait for the command line to become
+ * available.
+ */
+ if (ongoing_mrq && !completion_done(&ongoing_mrq->cmd_completion))
+ wait_for_completion(&ongoing_mrq->cmd_completion);
+}
+
/*
*__mmc_start_data_req() - starts data request
* @host: MMC host to start the request
@@ -393,17 +441,24 @@ static void mmc_wait_done(struct mmc_request *mrq)
*
* Sets the done callback to be called when request is completed by the card.
* Starts data mmc request execution
+ * If an ongoing transfer is already in progress, wait for the command line
+ * to become available before sending another command.
*/
static int __mmc_start_data_req(struct mmc_host *host, struct mmc_request *mrq)
{
int err;
+ mmc_wait_ongoing_tfr_cmd(host);
+
mrq->done = mmc_wait_data_done;
mrq->host = host;
+ init_completion(&mrq->cmd_completion);
+
err = mmc_start_request(host, mrq);
if (err) {
mrq->cmd->error = err;
+ mmc_complete_cmd(mrq);
mmc_wait_data_done(mrq);
}
@@ -414,12 +469,17 @@ static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)
{
int err;
+ mmc_wait_ongoing_tfr_cmd(host);
+
init_completion(&mrq->completion);
mrq->done = mmc_wait_done;
+ init_completion(&mrq->cmd_completion);
+
err = mmc_start_request(host, mrq);
if (err) {
mrq->cmd->error = err;
+ mmc_complete_cmd(mrq);
complete(&mrq->completion);
}
@@ -483,8 +543,7 @@ static int mmc_wait_for_data_req_done(struct mmc_host *host,
return err;
}
-static void mmc_wait_for_req_done(struct mmc_host *host,
- struct mmc_request *mrq)
+void mmc_wait_for_req_done(struct mmc_host *host, struct mmc_request *mrq)
{
struct mmc_command *cmd;
@@ -525,6 +584,28 @@ static void mmc_wait_for_req_done(struct mmc_host *host,
mmc_retune_release(host);
}
+EXPORT_SYMBOL(mmc_wait_for_req_done);
+
+/**
+ * mmc_is_req_done - Determine if a 'cap_cmd_during_tfr' request is done
+ * @host: MMC host
+ * @mrq: MMC request
+ *
+ * mmc_is_req_done() is used with requests that have
+ * mrq->cap_cmd_during_tfr = true. mmc_is_req_done() must be called after
+ * starting a request and before waiting for it to complete. That is,
+ * either in between calls to mmc_start_req(), or after mmc_wait_for_req()
+ * and before mmc_wait_for_req_done(). If it is called at other times the
+ * result is not meaningful.
+ */
+bool mmc_is_req_done(struct mmc_host *host, struct mmc_request *mrq)
+{
+ if (host->areq)
+ return host->context_info.is_done_rcv;
+ else
+ return completion_done(&mrq->completion);
+}
+EXPORT_SYMBOL(mmc_is_req_done);
/**
* mmc_pre_req - Prepare for a new request
@@ -645,13 +726,18 @@ EXPORT_SYMBOL(mmc_start_req);
* @mrq: MMC request to start
*
* Start a new MMC custom command request for a host, and wait
- * for the command to complete. Does not attempt to parse the
- * response.
+ * for the command to complete. In the case of 'cap_cmd_during_tfr'
+ * requests, the transfer is ongoing and the caller can issue further
+ * commands that do not use the data lines, and then wait by calling
+ * mmc_wait_for_req_done().
+ * Does not attempt to parse the response.
*/
void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
{
__mmc_start_req(host, mrq);
- mmc_wait_for_req_done(host, mrq);
+
+ if (!mrq->cap_cmd_during_tfr)
+ mmc_wait_for_req_done(host, mrq);
}
EXPORT_SYMBOL(mmc_wait_for_req);
@@ -2202,6 +2288,54 @@ out:
return err;
}
+static unsigned int mmc_align_erase_size(struct mmc_card *card,
+ unsigned int *from,
+ unsigned int *to,
+ unsigned int nr)
+{
+ unsigned int from_new = *from, nr_new = nr, rem;
+
+ /*
+ * When the 'card->erase_size' is power of 2, we can use round_up/down()
+ * to align the erase size efficiently.
+ */
+ if (is_power_of_2(card->erase_size)) {
+ unsigned int temp = from_new;
+
+ from_new = round_up(temp, card->erase_size);
+ rem = from_new - temp;
+
+ if (nr_new > rem)
+ nr_new -= rem;
+ else
+ return 0;
+
+ nr_new = round_down(nr_new, card->erase_size);
+ } else {
+ rem = from_new % card->erase_size;
+ if (rem) {
+ rem = card->erase_size - rem;
+ from_new += rem;
+ if (nr_new > rem)
+ nr_new -= rem;
+ else
+ return 0;
+ }
+
+ rem = nr_new % card->erase_size;
+ if (rem)
+ nr_new -= rem;
+ }
+
+ if (nr_new == 0)
+ return 0;
+
+ *to = from_new + nr_new;
+ *from = from_new;
+
+ return nr_new;
+}
+
/**
* mmc_erase - erase sectors.
* @card: card to erase
@@ -2240,26 +2374,12 @@ int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr,
return -EINVAL;
}
- if (arg == MMC_ERASE_ARG) {
- rem = from % card->erase_size;
- if (rem) {
- rem = card->erase_size - rem;
- from += rem;
- if (nr > rem)
- nr -= rem;
- else
- return 0;
- }
- rem = nr % card->erase_size;
- if (rem)
- nr -= rem;
- }
+ if (arg == MMC_ERASE_ARG)
+ nr = mmc_align_erase_size(card, &from, &to, nr);
if (nr == 0)
return 0;
- to = from + nr;
-
if (to <= from)
return -EINVAL;
@@ -2352,6 +2472,8 @@ static unsigned int mmc_do_calc_max_discard(struct mmc_card *card,
struct mmc_host *host = card->host;
unsigned int max_discard, x, y, qty = 0, max_qty, min_qty, timeout;
unsigned int last_timeout = 0;
+ unsigned int max_busy_timeout = host->max_busy_timeout ?
+ host->max_busy_timeout : MMC_ERASE_TIMEOUT_MS;
if (card->erase_shift) {
max_qty = UINT_MAX >> card->erase_shift;
@@ -2374,15 +2496,15 @@ static unsigned int mmc_do_calc_max_discard(struct mmc_card *card,
* matter what size of 'host->max_busy_timeout', but if the
* 'host->max_busy_timeout' is large enough for more discard sectors,
* then we can continue to increase the max discard sectors until we
- * get a balance value.
+ * get a balance value. In cases when the 'host->max_busy_timeout'
+ * isn't specified, use the default max erase timeout.
*/
do {
y = 0;
for (x = 1; x && x <= max_qty && max_qty - x >= qty; x <<= 1) {
timeout = mmc_erase_timeout(card, arg, qty + x);
- if (qty + x > min_qty &&
- timeout > host->max_busy_timeout)
+ if (qty + x > min_qty && timeout > max_busy_timeout)
break;
if (timeout < last_timeout)
@@ -2427,9 +2549,6 @@ unsigned int mmc_calc_max_discard(struct mmc_card *card)
struct mmc_host *host = card->host;
unsigned int max_discard, max_trim;
- if (!host->max_busy_timeout)
- return UINT_MAX;
-
/*
* Without erase_group_def set, MMC erase timeout depends on clock
* frequence which can change. In that case, the best choice is
@@ -2447,7 +2566,8 @@ unsigned int mmc_calc_max_discard(struct mmc_card *card)
max_discard = 0;
}
pr_debug("%s: calculated max. discard sectors %u for timeout %u ms\n",
- mmc_hostname(host), max_discard, host->max_busy_timeout);
+ mmc_hostname(host), max_discard, host->max_busy_timeout ?
+ host->max_busy_timeout : MMC_ERASE_TIMEOUT_MS);
return max_discard;
}
EXPORT_SYMBOL(mmc_calc_max_discard);
@@ -2456,7 +2576,8 @@ int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen)
{
struct mmc_command cmd = {0};
- if (mmc_card_blockaddr(card) || mmc_card_ddr52(card))
+ if (mmc_card_blockaddr(card) || mmc_card_ddr52(card) ||
+ mmc_card_hs400(card) || mmc_card_hs400es(card))
return 0;
cmd.opcode = MMC_SET_BLOCKLEN;