Merge branch 'stable/bug.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git...
[pandora-kernel.git] / drivers / mmc / core / core.c
index 68091dd..f091b43 100644 (file)
@@ -198,9 +198,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
@@ -212,16 +312,9 @@ static void mmc_wait_done(struct mmc_request *mrq)
  */
 void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
 {
-       DECLARE_COMPLETION_ONSTACK(complete);
-
-       mrq->done_data = &complete;
-       mrq->done = mmc_wait_done;
-
-       mmc_start_request(host, mrq);
-
-       wait_for_completion(&complete);
+       __mmc_start_req(host, mrq);
+       mmc_wait_for_req_done(host, mrq);
 }
-
 EXPORT_SYMBOL(mmc_wait_for_req);
 
 /**
@@ -1245,7 +1338,7 @@ static unsigned int mmc_mmc_erase_timeout(struct mmc_card *card,
                 */
                timeout_clks <<= 1;
                timeout_us += (timeout_clks * 1000) /
-                             (card->host->ios.clock / 1000);
+                             (mmc_host_clk_rate(card->host) / 1000);
 
                erase_timeout = timeout_us / 1000;
 
@@ -1516,6 +1609,82 @@ int mmc_erase_group_aligned(struct mmc_card *card, unsigned int from,
 }
 EXPORT_SYMBOL(mmc_erase_group_aligned);
 
+static unsigned int mmc_do_calc_max_discard(struct mmc_card *card,
+                                           unsigned int arg)
+{
+       struct mmc_host *host = card->host;
+       unsigned int max_discard, x, y, qty = 0, max_qty, timeout;
+       unsigned int last_timeout = 0;
+
+       if (card->erase_shift)
+               max_qty = UINT_MAX >> card->erase_shift;
+       else if (mmc_card_sd(card))
+               max_qty = UINT_MAX;
+       else
+               max_qty = UINT_MAX / card->erase_size;
+
+       /* Find the largest qty with an OK timeout */
+       do {
+               y = 0;
+               for (x = 1; x && x <= max_qty && max_qty - x >= qty; x <<= 1) {
+                       timeout = mmc_erase_timeout(card, arg, qty + x);
+                       if (timeout > host->max_discard_to)
+                               break;
+                       if (timeout < last_timeout)
+                               break;
+                       last_timeout = timeout;
+                       y = x;
+               }
+               qty += y;
+       } while (y);
+
+       if (!qty)
+               return 0;
+
+       if (qty == 1)
+               return 1;
+
+       /* Convert qty to sectors */
+       if (card->erase_shift)
+               max_discard = --qty << card->erase_shift;
+       else if (mmc_card_sd(card))
+               max_discard = qty;
+       else
+               max_discard = --qty * card->erase_size;
+
+       return max_discard;
+}
+
+unsigned int mmc_calc_max_discard(struct mmc_card *card)
+{
+       struct mmc_host *host = card->host;
+       unsigned int max_discard, max_trim;
+
+       if (!host->max_discard_to)
+               return UINT_MAX;
+
+       /*
+        * Without erase_group_def set, MMC erase timeout depends on clock
+        * frequence which can change.  In that case, the best choice is
+        * just the preferred erase size.
+        */
+       if (mmc_card_mmc(card) && !(card->ext_csd.erase_group_def & 1))
+               return card->pref_erase;
+
+       max_discard = mmc_do_calc_max_discard(card, MMC_ERASE_ARG);
+       if (mmc_can_trim(card)) {
+               max_trim = mmc_do_calc_max_discard(card, MMC_TRIM_ARG);
+               if (max_trim < max_discard)
+                       max_discard = max_trim;
+       } else if (max_discard < card->erase_size) {
+               max_discard = 0;
+       }
+       pr_debug("%s: calculated max. discard sectors %u for timeout %u ms\n",
+                mmc_hostname(host), max_discard, host->max_discard_to);
+       return max_discard;
+}
+EXPORT_SYMBOL(mmc_calc_max_discard);
+
 int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen)
 {
        struct mmc_command cmd = {0};
@@ -1663,6 +1832,10 @@ int mmc_power_save_host(struct mmc_host *host)
 {
        int ret = 0;
 
+#ifdef CONFIG_MMC_DEBUG
+       pr_info("%s: %s: powering down\n", mmc_hostname(host), __func__);
+#endif
+
        mmc_bus_get(host);
 
        if (!host->bus_ops || host->bus_dead || !host->bus_ops->power_restore) {
@@ -1685,6 +1858,10 @@ int mmc_power_restore_host(struct mmc_host *host)
 {
        int ret;
 
+#ifdef CONFIG_MMC_DEBUG
+       pr_info("%s: %s: powering up\n", mmc_hostname(host), __func__);
+#endif
+
        mmc_bus_get(host);
 
        if (!host->bus_ops || host->bus_dead || !host->bus_ops->power_restore) {