|
@@ -319,11 +319,44 @@ out:
|
|
|
}
|
|
|
EXPORT_SYMBOL(mmc_start_bkops);
|
|
|
|
|
|
+/*
|
|
|
+ * mmc_wait_data_done() - done callback for data request
|
|
|
+ * @mrq: done data request
|
|
|
+ *
|
|
|
+ * Wakes up mmc context, passed as a callback to host controller driver
|
|
|
+ */
|
|
|
+static void mmc_wait_data_done(struct mmc_request *mrq)
|
|
|
+{
|
|
|
+ mrq->host->context_info.is_done_rcv = true;
|
|
|
+ wake_up_interruptible(&mrq->host->context_info.wait);
|
|
|
+}
|
|
|
+
|
|
|
static void mmc_wait_done(struct mmc_request *mrq)
|
|
|
{
|
|
|
complete(&mrq->completion);
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ *__mmc_start_data_req() - starts data request
|
|
|
+ * @host: MMC host to start the request
|
|
|
+ * @mrq: data request to start
|
|
|
+ *
|
|
|
+ * Sets the done callback to be called when request is completed by the card.
|
|
|
+ * Starts data mmc request execution
|
|
|
+ */
|
|
|
+static int __mmc_start_data_req(struct mmc_host *host, struct mmc_request *mrq)
|
|
|
+{
|
|
|
+ mrq->done = mmc_wait_data_done;
|
|
|
+ mrq->host = host;
|
|
|
+ if (mmc_card_removed(host->card)) {
|
|
|
+ mrq->cmd->error = -ENOMEDIUM;
|
|
|
+ return -ENOMEDIUM;
|
|
|
+ }
|
|
|
+ mmc_start_request(host, mrq);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)
|
|
|
{
|
|
|
init_completion(&mrq->completion);
|
|
@@ -337,6 +370,62 @@ static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * mmc_wait_for_data_req_done() - wait for request completed
|
|
|
+ * @host: MMC host to prepare the command.
|
|
|
+ * @mrq: MMC request to wait for
|
|
|
+ *
|
|
|
+ * Blocks MMC context till host controller will ack end of data request
|
|
|
+ * execution or new request notification arrives from the block layer.
|
|
|
+ * Handles command retries.
|
|
|
+ *
|
|
|
+ * Returns enum mmc_blk_status after checking errors.
|
|
|
+ */
|
|
|
+static int mmc_wait_for_data_req_done(struct mmc_host *host,
|
|
|
+ struct mmc_request *mrq,
|
|
|
+ struct mmc_async_req *next_req)
|
|
|
+{
|
|
|
+ struct mmc_command *cmd;
|
|
|
+ struct mmc_context_info *context_info = &host->context_info;
|
|
|
+ int err;
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ while (1) {
|
|
|
+ wait_event_interruptible(context_info->wait,
|
|
|
+ (context_info->is_done_rcv ||
|
|
|
+ context_info->is_new_req));
|
|
|
+ spin_lock_irqsave(&context_info->lock, flags);
|
|
|
+ context_info->is_waiting_last_req = false;
|
|
|
+ spin_unlock_irqrestore(&context_info->lock, flags);
|
|
|
+ if (context_info->is_done_rcv) {
|
|
|
+ context_info->is_done_rcv = false;
|
|
|
+ context_info->is_new_req = false;
|
|
|
+ cmd = mrq->cmd;
|
|
|
+ if (!cmd->error || !cmd->retries ||
|
|
|
+ mmc_card_removed(host->card)) {
|
|
|
+ err = host->areq->err_check(host->card,
|
|
|
+ host->areq);
|
|
|
+ break; /* return err */
|
|
|
+ } else {
|
|
|
+ pr_info("%s: req failed (CMD%u): %d, retrying...\n",
|
|
|
+ mmc_hostname(host),
|
|
|
+ cmd->opcode, cmd->error);
|
|
|
+ cmd->retries--;
|
|
|
+ cmd->error = 0;
|
|
|
+ host->ops->request(host, mrq);
|
|
|
+ continue; /* wait for done/new event again */
|
|
|
+ }
|
|
|
+ } else if (context_info->is_new_req) {
|
|
|
+ context_info->is_new_req = false;
|
|
|
+ if (!next_req) {
|
|
|
+ err = MMC_BLK_NEW_REQUEST;
|
|
|
+ break; /* return err */
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
static void mmc_wait_for_req_done(struct mmc_host *host,
|
|
|
struct mmc_request *mrq)
|
|
|
{
|
|
@@ -426,8 +515,17 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,
|
|
|
mmc_pre_req(host, areq->mrq, !host->areq);
|
|
|
|
|
|
if (host->areq) {
|
|
|
- mmc_wait_for_req_done(host, host->areq->mrq);
|
|
|
- err = host->areq->err_check(host->card, host->areq);
|
|
|
+ err = mmc_wait_for_data_req_done(host, host->areq->mrq,
|
|
|
+ areq);
|
|
|
+ if (err == MMC_BLK_NEW_REQUEST) {
|
|
|
+ if (error)
|
|
|
+ *error = err;
|
|
|
+ /*
|
|
|
+ * The previous request was not completed,
|
|
|
+ * nothing to return
|
|
|
+ */
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
/*
|
|
|
* Check BKOPS urgency for each R1 response
|
|
|
*/
|
|
@@ -439,7 +537,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,
|
|
|
}
|
|
|
|
|
|
if (!err && areq)
|
|
|
- start_err = __mmc_start_req(host, areq->mrq);
|
|
|
+ start_err = __mmc_start_data_req(host, areq->mrq);
|
|
|
|
|
|
if (host->areq)
|
|
|
mmc_post_req(host, host->areq->mrq, 0);
|
|
@@ -2581,6 +2679,23 @@ int mmc_pm_notify(struct notifier_block *notify_block,
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
+/**
|
|
|
+ * mmc_init_context_info() - init synchronization context
|
|
|
+ * @host: mmc host
|
|
|
+ *
|
|
|
+ * Init struct context_info needed to implement asynchronous
|
|
|
+ * request mechanism, used by mmc core, host driver and mmc requests
|
|
|
+ * supplier.
|
|
|
+ */
|
|
|
+void mmc_init_context_info(struct mmc_host *host)
|
|
|
+{
|
|
|
+ spin_lock_init(&host->context_info.lock);
|
|
|
+ host->context_info.is_new_req = false;
|
|
|
+ host->context_info.is_done_rcv = false;
|
|
|
+ host->context_info.is_waiting_last_req = false;
|
|
|
+ init_waitqueue_head(&host->context_info.wait);
|
|
|
+}
|
|
|
+
|
|
|
static int __init mmc_init(void)
|
|
|
{
|
|
|
int ret;
|