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.c95
1 files changed, 89 insertions, 6 deletions
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index d083d2e57abd..f0ed0afe033d 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -120,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
@@ -146,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)) {
@@ -158,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",
@@ -223,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);
@@ -389,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
@@ -396,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);
}
@@ -417,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);
}
@@ -486,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;
@@ -528,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
@@ -648,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);