mmc: Add MMC host software queue support
[linux-2.6-microblaze.git] / drivers / mmc / core / block.c
index 663d879..55d52fc 100644 (file)
@@ -168,6 +168,11 @@ MODULE_PARM_DESC(perdev_minors, "Minors numbers to allocate per device");
 
 static inline int mmc_blk_part_switch(struct mmc_card *card,
                                      unsigned int part_type);
+static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
+                              struct mmc_card *card,
+                              int disable_multi,
+                              struct mmc_queue *mq);
+static void mmc_blk_hsq_req_done(struct mmc_request *mrq);
 
 static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk)
 {
@@ -1532,9 +1537,30 @@ static int mmc_blk_cqe_issue_flush(struct mmc_queue *mq, struct request *req)
        return mmc_blk_cqe_start_req(mq->card->host, mrq);
 }
 
+static int mmc_blk_hsq_issue_rw_rq(struct mmc_queue *mq, struct request *req)
+{
+       struct mmc_queue_req *mqrq = req_to_mmc_queue_req(req);
+       struct mmc_host *host = mq->card->host;
+       int err;
+
+       mmc_blk_rw_rq_prep(mqrq, mq->card, 0, mq);
+       mqrq->brq.mrq.done = mmc_blk_hsq_req_done;
+       mmc_pre_req(host, &mqrq->brq.mrq);
+
+       err = mmc_cqe_start_req(host, &mqrq->brq.mrq);
+       if (err)
+               mmc_post_req(host, &mqrq->brq.mrq, err);
+
+       return err;
+}
+
 static int mmc_blk_cqe_issue_rw_rq(struct mmc_queue *mq, struct request *req)
 {
        struct mmc_queue_req *mqrq = req_to_mmc_queue_req(req);
+       struct mmc_host *host = mq->card->host;
+
+       if (host->hsq_enabled)
+               return mmc_blk_hsq_issue_rw_rq(mq, req);
 
        mmc_blk_data_prep(mq, mqrq, 0, NULL, NULL);
 
@@ -1920,6 +1946,41 @@ static void mmc_blk_urgent_bkops(struct mmc_queue *mq,
                mmc_run_bkops(mq->card);
 }
 
+static void mmc_blk_hsq_req_done(struct mmc_request *mrq)
+{
+       struct mmc_queue_req *mqrq =
+               container_of(mrq, struct mmc_queue_req, brq.mrq);
+       struct request *req = mmc_queue_req_to_req(mqrq);
+       struct request_queue *q = req->q;
+       struct mmc_queue *mq = q->queuedata;
+       struct mmc_host *host = mq->card->host;
+       unsigned long flags;
+
+       if (mmc_blk_rq_error(&mqrq->brq) ||
+           mmc_blk_urgent_bkops_needed(mq, mqrq)) {
+               spin_lock_irqsave(&mq->lock, flags);
+               mq->recovery_needed = true;
+               mq->recovery_req = req;
+               spin_unlock_irqrestore(&mq->lock, flags);
+
+               host->cqe_ops->cqe_recovery_start(host);
+
+               schedule_work(&mq->recovery_work);
+               return;
+       }
+
+       mmc_blk_rw_reset_success(mq, req);
+
+       /*
+        * Block layer timeouts race with completions which means the normal
+        * completion path cannot be used during recovery.
+        */
+       if (mq->in_recovery)
+               mmc_blk_cqe_complete_rq(mq, req);
+       else
+               blk_mq_complete_request(req);
+}
+
 void mmc_blk_mq_complete(struct request *req)
 {
        struct mmc_queue *mq = req->q->queuedata;