summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/mmc/card/block.c32
-rw-r--r--drivers/mmc/card/queue.c29
-rw-r--r--drivers/mmc/card/queue.h2
3 files changed, 63 insertions, 0 deletions
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index daccedcb7f58..4a0dba010cfd 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -2735,6 +2735,34 @@ reset:
clear_bit(CMDQ_STATE_HALT, &host->cmdq_ctx.curr_state);
}
+static void mmc_blk_cmdq_shutdown(struct mmc_queue *mq)
+{
+ int err;
+ struct mmc_card *card = mq->card;
+ struct mmc_host *host = card->host;
+
+ err = mmc_cmdq_halt(host, true);
+ if (err) {
+ pr_err("%s: halt: failed: %d\n", __func__, err);
+ return;
+ }
+
+ mmc_claim_host(card->host);
+ /* disable CQ mode in card */
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_CMDQ, 0,
+ card->ext_csd.generic_cmd6_time);
+ if (err) {
+ pr_err("%s: failed to switch card to legacy mode: %d\n",
+ __func__, err);
+ goto out;
+ } else {
+ host->card->cmdq_init = false;
+ }
+out:
+ mmc_release_host(card->host);
+}
+
static enum blk_eh_timer_return mmc_blk_cmdq_req_timed_out(struct request *req)
{
struct mmc_queue *mq = req->q->queuedata;
@@ -2880,6 +2908,9 @@ out:
test_and_clear_bit(0, &ctx_info->req_starved))
blk_run_queue(mq->queue);
mmc_release_host(host);
+
+ if (blk_queue_stopped(mq->queue) && !ctx_info->active_reqs)
+ complete(&mq->cmdq_shutdown_complete);
return;
}
@@ -3340,6 +3371,7 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
md->queue.cmdq_issue_fn = mmc_blk_cmdq_issue_rq;
md->queue.cmdq_error_fn = mmc_blk_cmdq_err;
md->queue.cmdq_req_timed_out = mmc_blk_cmdq_req_timed_out;
+ md->queue.cmdq_shutdown = mmc_blk_cmdq_shutdown;
}
if (mmc_card_mmc(card) && !card->cmdq_init &&
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index be417461f44b..d47b18c20e71 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -628,6 +628,7 @@ int mmc_cmdq_init(struct mmc_queue *mq, struct mmc_card *card)
blk_queue_softirq_done(mq->queue, mmc_cmdq_softirq_done);
INIT_WORK(&mq->cmdq_err_work, mmc_cmdq_error_work);
+ init_completion(&mq->cmdq_shutdown_complete);
blk_queue_rq_timed_out(mq->queue, mmc_cmdq_rq_timed_out);
blk_queue_rq_timeout(mq->queue, 120 * HZ);
@@ -659,6 +660,12 @@ void mmc_cmdq_clean(struct mmc_queue *mq, struct mmc_card *card)
mq->mqrq_cmdq = NULL;
}
+static void mmc_wait_for_pending_reqs(struct mmc_queue *mq)
+{
+ wait_for_completion(&mq->cmdq_shutdown_complete);
+ mq->cmdq_shutdown(mq);
+}
+
/**
* mmc_queue_suspend - suspend a MMC request queue
* @mq: MMC queue to suspend
@@ -673,6 +680,27 @@ int mmc_queue_suspend(struct mmc_queue *mq, int wait)
struct request_queue *q = mq->queue;
unsigned long flags;
int rc = 0;
+ struct mmc_card *card = mq->card;
+
+ if (card->cmdq_init) {
+ struct mmc_host *host = card->host;
+ unsigned long flags;
+
+ spin_lock_irqsave(q->queue_lock, flags);
+ blk_stop_queue(q);
+ spin_unlock_irqrestore(q->queue_lock, flags);
+
+ if (host->cmdq_ctx.active_reqs) {
+ if (!wait)
+ rc = -EBUSY;
+ else
+ mmc_wait_for_pending_reqs(mq);
+ } else {
+ mq->cmdq_shutdown(mq);
+ }
+
+ goto out;
+ }
if (!(test_and_set_bit(MMC_QUEUE_SUSPENDED, &mq->flags))) {
spin_lock_irqsave(q->queue_lock, flags);
@@ -695,6 +723,7 @@ int mmc_queue_suspend(struct mmc_queue *mq, int wait)
rc = 0;
}
}
+out:
return rc;
}
diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
index 388f099a06f5..ab89102a4baa 100644
--- a/drivers/mmc/card/queue.h
+++ b/drivers/mmc/card/queue.h
@@ -70,9 +70,11 @@ struct mmc_queue {
int num_wr_reqs_to_start_packing;
bool no_pack_for_random;
struct work_struct cmdq_err_work;
+ struct completion cmdq_shutdown_complete;
int (*err_check_fn) (struct mmc_card *, struct mmc_async_req *);
void (*packed_test_fn) (struct request_queue *, struct mmc_queue_req *);
+ void (*cmdq_shutdown)(struct mmc_queue *);
};
extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *,