Merge commit "aa8b683a7d392271ed349c6ab9f36b8c313794b7" of git://git.kernel.org/pub...
authorkfx <kfx@rock-chips.com>
Thu, 4 Jul 2013 03:27:10 +0000 (11:27 +0800)
committerkfx <kfx@rock-chips.com>
Thu, 4 Jul 2013 03:27:10 +0000 (11:27 +0800)
mmc: core: add non-blocking mmc request function

    Previously there has only been one function mmc_wait_for_req()
    to start and wait for a request. This patch adds:

     * mmc_start_req() - starts a request wihtout waiting
         If there is on ongoing request wait for completion
         of that request and start the new one and return.
         Does not wait for the new command to complete.

    This patch also adds new function members in struct mmc_host_ops
    only called from core.c:

     * pre_req - asks the host driver to prepare for the next job
     * post_req - asks the host driver to clean up after a completed job

    The intention is to use pre_req() and post_req() to do cache maintenance
    while a request is active. pre_req() can be called while a request is
    active to minimize latency to start next job. post_req() can be used after
    the next job is started to clean up the request. This will minimize the
    host driver request end latency. post_req() is typically used before
    ending the block request and handing over the buffer to the block layer.

    Add a host-private member in mmc_data to be used by pre_req to mark the
    data. The host driver will then check this mark to see if the data is
    prepared or not.

drivers/mmc/core/core.c
include/linux/mmc/core.h
include/linux/mmc/host.h

index 193ed714012d6720cc3906622d282208b06ddbd0..bb7a80c359343b45d3cb604c0e09fcea922dc19e 100755 (executable)
@@ -199,9 +199,109 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
 
 static void mmc_wait_done(struct mmc_request *mrq)
 {
-       complete(mrq->done_data);
+       complete(&mrq->completion);
 }
 
+static void __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)
+{
+       init_completion(&mrq->completion);
+       mrq->done = mmc_wait_done;
+       mmc_start_request(host, mrq);
+}
+
+static void mmc_wait_for_req_done(struct mmc_host *host,
+                                 struct mmc_request *mrq)
+{
+       wait_for_completion(&mrq->completion);
+}
+
+/**
+ *     mmc_pre_req - Prepare for a new request
+ *     @host: MMC host to prepare command
+ *     @mrq: MMC request to prepare for
+ *     @is_first_req: true if there is no previous started request
+ *                     that may run in parellel to this call, otherwise false
+ *
+ *     mmc_pre_req() is called in prior to mmc_start_req() to let
+ *     host prepare for the new request. Preparation of a request may be
+ *     performed while another request is running on the host.
+ */
+static void mmc_pre_req(struct mmc_host *host, struct mmc_request *mrq,
+                bool is_first_req)
+{
+       if (host->ops->pre_req)
+               host->ops->pre_req(host, mrq, is_first_req);
+}
+
+/**
+ *     mmc_post_req - Post process a completed request
+ *     @host: MMC host to post process command
+ *     @mrq: MMC request to post process for
+ *     @err: Error, if non zero, clean up any resources made in pre_req
+ *
+ *     Let the host post process a completed request. Post processing of
+ *     a request may be performed while another reuqest is running.
+ */
+static void mmc_post_req(struct mmc_host *host, struct mmc_request *mrq,
+                        int err)
+{
+       if (host->ops->post_req)
+               host->ops->post_req(host, mrq, err);
+}
+
+/**
+ *     mmc_start_req - start a non-blocking request
+ *     @host: MMC host to start command
+ *     @areq: async request to start
+ *     @error: out parameter returns 0 for success, otherwise non zero
+ *
+ *     Start a new MMC custom command request for a host.
+ *     If there is on ongoing async request wait for completion
+ *     of that request and start the new one and return.
+ *     Does not wait for the new request to complete.
+ *
+ *      Returns the completed request, NULL in case of none completed.
+ *     Wait for the an ongoing request (previoulsy started) to complete and
+ *     return the completed request. If there is no ongoing request, NULL
+ *     is returned without waiting. NULL is not an error condition.
+ */
+struct mmc_async_req *mmc_start_req(struct mmc_host *host,
+                                   struct mmc_async_req *areq, int *error)
+{
+       int err = 0;
+       struct mmc_async_req *data = host->areq;
+
+       /* Prepare a new request */
+       if (areq)
+               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);
+               if (err) {
+                       mmc_post_req(host, host->areq->mrq, 0);
+                       if (areq)
+                               mmc_post_req(host, areq->mrq, -EINVAL);
+
+                       host->areq = NULL;
+                       goto out;
+               }
+       }
+
+       if (areq)
+               __mmc_start_req(host, areq->mrq);
+
+       if (host->areq)
+               mmc_post_req(host, host->areq->mrq, 0);
+
+       host->areq = areq;
+ out:
+       if (error)
+               *error = err;
+       return data;
+}
+EXPORT_SYMBOL(mmc_start_req);
+
 /**
  *     mmc_wait_for_req - start a request and wait for completion
  *     @host: MMC host to start command
@@ -211,16 +311,15 @@ static void mmc_wait_done(struct mmc_request *mrq)
  *     for the command to complete. Does not attempt to parse the
  *     response.
  */
-void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
+static void sdmmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
 {
 #if defined(CONFIG_SDMMC_RK29) && !defined(CONFIG_SDMMC_RK29_OLD)
        unsigned long datasize, waittime = 0xFFFF;
        u32 multi, unit;
 #endif
 
-       DECLARE_COMPLETION_ONSTACK(complete);
 
-       mrq->done_data = &complete;
+       init_completion(&mrq->completion);
        mrq->done = mmc_wait_done;
 
        mmc_start_request(host, mrq);
@@ -229,7 +328,7 @@ void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
     if( strncmp( mmc_hostname(host) ,"mmc0" , strlen("mmc0")) ) 
     {
         multi = (mrq->cmd->retries>0)?mrq->cmd->retries:1;
-        waittime = wait_for_completion_timeout(&complete,HZ*7*multi); //sdio; for cmd dead. Modifyed by xbw at 2011-06-02
+        waittime = wait_for_completion_timeout(&mrq->completion ,HZ*7*multi); //sdio; for cmd dead. Modifyed by xbw at 2011-06-02
     }
     else
     {   
@@ -242,7 +341,7 @@ void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
             multi += (datasize%unit)?1:0;
             multi = (multi>0) ? multi : 1;
             multi += (mrq->cmd->retries>0)?1:0;
-            waittime = wait_for_completion_timeout(&complete,HZ*7*multi); //It should be longer than bottom driver's time,due to the sum of two cmd time.
+            waittime = wait_for_completion_timeout(&mrq->completion,HZ*7*multi); //It should be longer than bottom driver's time,due to the sum of two cmd time.
                                                                           //modifyed by xbw at 2011-10-08
                                                                           //
                                                                           //example:
@@ -252,7 +351,7 @@ void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
         else
         {
             multi = (mrq->cmd->retries>0)?mrq->cmd->retries:1;
-            waittime = wait_for_completion_timeout(&complete,HZ*7*multi);
+            waittime = wait_for_completion_timeout(&mrq->completion,HZ*7*multi);
         }
     }
     
@@ -268,10 +367,23 @@ void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
         }
     }
 #else
-       wait_for_completion(&complete);
+       wait_for_completion(&mrq->completion);
 #endif
 }
 
+static void emmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
+{
+       __mmc_start_req(host, mrq);
+       mmc_wait_for_req_done(host, mrq);
+}
+
+void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
+{
+       if(HOST_IS_EMMC(host))
+               emmc_wait_for_req(host, mrq);
+       else
+               sdmmc_wait_for_req(host, mrq);
+}
 EXPORT_SYMBOL(mmc_wait_for_req);
 
 /**
index b6718e549a510780e0f8c9a3be17e64753d6c2f5..1b5a496780451eca899489d39cd623304752a2f2 100644 (file)
@@ -117,6 +117,7 @@ struct mmc_data {
 
        unsigned int            sg_len;         /* size of scatter list */
        struct scatterlist      *sg;            /* I/O scatter list */
+       s32                     host_cookie;    /* host private data */
 };
 
 struct mmc_request {
@@ -125,13 +126,16 @@ struct mmc_request {
        struct mmc_data         *data;
        struct mmc_command      *stop;
 
-       void                    *done_data;     /* completion data */
+       struct completion       completion;
        void                    (*done)(struct mmc_request *);/* completion function */
 };
 
 struct mmc_host;
 struct mmc_card;
+struct mmc_async_req;
 
+extern struct mmc_async_req *mmc_start_req(struct mmc_host *,
+                                          struct mmc_async_req *, int *);
 extern void mmc_wait_for_req(struct mmc_host *, struct mmc_request *);
 extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int);
 extern int mmc_app_cmd(struct mmc_host *, struct mmc_card *);
index 3b7d4c5001e91c3358fb79c55bcc6682b857f432..0cdc7d09e6f138ed4aa5e73fe2c57c37a15c62a7 100755 (executable)
@@ -107,6 +107,15 @@ struct mmc_host_ops {
         */
        int (*enable)(struct mmc_host *host);
        int (*disable)(struct mmc_host *host, int lazy);
+       /*
+        * It is optional for the host to implement pre_req and post_req in
+        * order to support double buffering of requests (prepare one
+        * request while another request is active).
+        */
+       void    (*post_req)(struct mmc_host *host, struct mmc_request *req,
+                           int err);
+       void    (*pre_req)(struct mmc_host *host, struct mmc_request *req,
+                          bool is_first_req);
        void    (*request)(struct mmc_host *host, struct mmc_request *req);
        /*
         * Avoid calling these three functions too often or in a "fast path",
@@ -145,6 +154,18 @@ struct mmc_host_ops {
 struct mmc_card;
 struct device;
 
+struct mmc_async_req {
+       /* active mmc request */
+       struct mmc_request      *mrq;
+       /*
+        * Check error status of completed mmc request.
+        * Returns 0 if success otherwise non zero.
+        */
+       int (*err_check) (struct mmc_card *, struct mmc_async_req *);
+};
+
+#define HOST_IS_EMMC(host)     (host->unused)
+
 struct mmc_host {
        struct device           *parent;
        struct device           class_dev;
@@ -302,6 +323,7 @@ struct mmc_host {
                int                             num_funcs;
        } embedded_sdio_data;
 #endif
+       struct mmc_async_req    *areq;          /* active async req */
 
        unsigned long           private[0] ____cacheline_aligned;
 };