mmc: host: Add facility to support re-tuning
authorShawn Lin <shawn.lin@rock-chips.com>
Mon, 8 Jun 2015 09:20:01 +0000 (17:20 +0800)
committerShawn Lin <shawn.lin@rock-chips.com>
Tue, 23 Jun 2015 01:36:42 +0000 (09:36 +0800)
  This commit comes from community but not upstreram.
We cannot wait for picking up such a great movement without
upstream admit it. We just take a light way to merge it and
minimize the change.

Signed-off-by: Shawn Lin <shawn.lin@rock-chips.com>
Signed-off-by: xiaoyao <xiaoyao@rock-chips.com>
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
drivers/mmc/card/block.c
drivers/mmc/card/queue.h
drivers/mmc/core/core.c
drivers/mmc/core/core.h
drivers/mmc/core/host.c
drivers/mmc/core/mmc.c
drivers/mmc/core/mmc_ops.c
drivers/mmc/core/mmc_ops.h
drivers/mmc/host/rk_sdmmc.c
include/linux/mmc/host.h

index e4aa64d5d958c81a1c1f0a2cfa1f9ca135065125..b0f1ec8929340ae44d1b447712f90e67b1e5ff18 100755 (executable)
@@ -1127,6 +1127,7 @@ static int mmc_blk_err_check(struct mmc_card *card,
                                                    mmc_active);
        struct mmc_blk_request *brq = &mq_mrq->brq;
        struct request *req = mq_mrq->req;
+       int need_retune = card->host->need_retune;
        int ecc_err = 0, gen_err = 0;
 
        /*
@@ -1224,6 +1225,14 @@ static int mmc_blk_err_check(struct mmc_card *card,
        }
 
        if (brq->data.error) {
+               pr_err("need_retune:%d,brq->retune_retry_done:%d.\n",need_retune,brq->retune_retry_done);
+               if (need_retune && !brq->retune_retry_done) {
+                       pr_err("%s: retrying because a re-tune was needed\n",
+                              req->rq_disk->disk_name);
+                       brq->retune_retry_done = 1;
+                       return MMC_BLK_RETRY;
+               }
+
                pr_err("%s: error %d transferring data, sector %u, nr %u, cmd response %#x, card status %#x\n",
                       req->rq_disk->disk_name, brq->data.error,
                       (unsigned)blk_rq_pos(req),
@@ -1782,7 +1791,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
        struct mmc_blk_data *md = mq->data;
        struct mmc_card *card = md->queue.card;
        struct mmc_blk_request *brq = &mq->mqrq_cur->brq;
-       int ret = 1, disable_multi = 0, retry = 0, type;
+       int ret = 1, disable_multi = 0, retry = 0, retune_retry_done = 0, type;
        enum mmc_blk_status status;
        struct mmc_queue_req *mq_rq;
        struct request *req = rqc;
@@ -1866,6 +1875,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
                                break;
                        goto cmd_abort;
                case MMC_BLK_RETRY:
+                       retune_retry_done = brq->retune_retry_done;
                        if (retry++ < 5)
                                break;
                        /* Fall through */
@@ -1928,6 +1938,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
                                mmc_start_req(card->host,
                                                &mq_rq->mmc_active, NULL);
                        }
+               mq_rq->brq.retune_retry_done = retune_retry_done;
                }
        } while (ret);
 
index 99e6521e61696202c036dfb00fe6bdcd96c0f613..e7959fb0ebc6213e630b56118f88256f31a6a8ff 100644 (file)
@@ -12,6 +12,7 @@ struct mmc_blk_request {
        struct mmc_command      cmd;
        struct mmc_command      stop;
        struct mmc_data         data;
+       int     retune_retry_done;
 };
 
 enum mmc_packed_type {
index e487872795ad111a3c52df362118e4a12cb00b9b..21233c15898751e2087e806ce292219475e8c251 100755 (executable)
@@ -193,6 +193,21 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
 }
 EXPORT_SYMBOL(mmc_request_done);
 
+static void __mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
+{
+       int err;
+
+       /* Assumes host controller has been runtime resumed by mmc_claim_host */
+       err = mmc_retune(host);
+       if (err) {
+               mrq->cmd->error = err;
+               mmc_request_done(host, mrq);
+               return;
+       }
+
+       host->ops->request(host, mrq);
+}
+
 static void
 mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
 {
@@ -200,6 +215,7 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
        unsigned int i, sz;
        struct scatterlist *sg;
 #endif
+       mmc_retune_hold(host);
 
        if (mrq->sbc) {
                pr_debug("<%s: starting CMD%u arg %08x flags %08x>\n",
@@ -253,7 +269,7 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
        }
        mmc_host_clk_hold(host);
        led_trigger_event(host->led, LED_FULL);
-       host->ops->request(host, mrq);
+       __mmc_start_request(host, mrq);
 }
 
 /**
@@ -296,6 +312,8 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)
                timeout = MMC_BKOPS_MAX_TIMEOUT;
                use_busy_signal = true;
        } else {
+               /* Hold re-tuning for ongoing bkops */
+               mmc_retune_hold(card->host);
                timeout = 0;
                use_busy_signal = false;
        }
@@ -306,6 +324,9 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)
        if (err) {
                pr_warn("%s: Error %d starting bkops\n",
                        mmc_hostname(card->host), err);
+               /* bkops not ongoing, so release re-tuning */
+               if (!use_busy_signal)
+                       mmc_retune_release(card->host);
                goto out;
        }
 
@@ -413,22 +434,22 @@ static int mmc_wait_for_data_req_done(struct mmc_host *host,
                                                            host->areq);
                                break; /* return err */
                        } else {
+                               mmc_retune_recheck(host);
                                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);
+                               __mmc_start_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 */
-                       }
+                       if (!next_req)
+                               return MMC_BLK_NEW_REQUEST;
                }
        }
+       mmc_retune_release(host);
        return err;
 }
 
@@ -503,8 +524,9 @@ static void mmc_wait_for_req_done(struct mmc_host *host,
                         mmc_hostname(host), cmd->opcode, cmd->error);
                cmd->retries--;
                cmd->error = 0;
-               host->ops->request(host, mrq);
+               __mmc_start_request(host, mrq);
        }
+       mmc_retune_release(host);
 }
 
 /**
@@ -751,6 +773,7 @@ int mmc_stop_bkops(struct mmc_card *card)
         */
        if (!err || (err == -EINVAL)) {
                mmc_card_clr_doing_bkops(card);
+               mmc_retune_release(card->host);
                err = 0;
        }
 
@@ -1116,6 +1139,32 @@ void mmc_set_ungated(struct mmc_host *host)
 }
 #endif
 
+int mmc_execute_tuning(struct mmc_card *card)
+{
+       struct mmc_host *host = card->host;
+       u32 opcode;
+       int err;
+
+       if (!host->ops->execute_tuning)
+               return 0;
+
+       if (mmc_card_mmc(card))
+               opcode = MMC_SEND_TUNING_BLOCK_HS200;
+       else
+               opcode = MMC_SEND_TUNING_BLOCK;
+
+       mmc_host_clk_hold(host);
+       err = host->ops->execute_tuning(host, opcode);
+       mmc_host_clk_release(host);
+
+       if (err)
+               pr_err("%s: tuning execution failed\n", mmc_hostname(host));
+       else
+               mmc_retune_enable(host);
+
+       return err;
+}
+
 /*
  * Change the bus mode (open drain/push-pull) of a host.
  */
@@ -1576,6 +1625,7 @@ void mmc_power_up(struct mmc_host *host, u32 ocr)
                return;
 
        mmc_host_clk_hold(host);
+       mmc_retune_disable(host);
 
        host->ios.vdd = fls(ocr) - 1;
        if (mmc_host_is_spi(host))
@@ -1617,6 +1667,7 @@ void mmc_power_off(struct mmc_host *host)
                return;
 
        mmc_host_clk_hold(host);
+       mmc_retune_disable(host);
 
        host->ios.clock = 0;
        host->ios.vdd = 0;
@@ -1961,6 +2012,8 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
        unsigned int fr, nr;
        int err;
 
+       mmc_retune_hold(card->host);
+
        fr = from;
        nr = to - from + 1;
 
@@ -2067,7 +2120,7 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
        } while (!(cmd.resp[0] & R1_READY_FOR_DATA) ||
                 (R1_CURRENT_STATE(cmd.resp[0]) == R1_STATE_PRG));
 out:
-
+       mmc_retune_release(card->host);
        return err;
 }
 
index 443a584660f0132de2b07ee232e068575a63faf4..cab4a76f1400e93f2c1cc1e07350de2d613c96ab 100644 (file)
@@ -81,5 +81,6 @@ void mmc_add_card_debugfs(struct mmc_card *card);
 void mmc_remove_card_debugfs(struct mmc_card *card);
 
 void mmc_init_context_info(struct mmc_host *host);
+int mmc_execute_tuning(struct mmc_card *card);
 #endif
 
index 2385b6f33c3e520c8157df41bbca1fa5afda95d7..5cca83629049e0966f4e81b27077ffdcd6ce51d6 100755 (executable)
@@ -300,6 +300,53 @@ static inline void mmc_host_clk_sysfs_init(struct mmc_host *host)
 
 #endif
 
+void mmc_retune_enable(struct mmc_host *host)
+{
+       host->can_retune = 1;
+}
+
+void mmc_retune_disable(struct mmc_host *host)
+{
+       host->can_retune = 0;
+       host->need_retune = 0;
+}
+
+void mmc_retune_hold(struct mmc_host *host)
+{
+       if (!host->hold_retune)
+               host->retune_now = 1;
+       host->hold_retune += 1;
+}
+EXPORT_SYMBOL(mmc_retune_hold);
+
+void mmc_retune_release(struct mmc_host *host)
+{
+       if (host->hold_retune)
+               host->hold_retune -= 1;
+       else
+               WARN_ON(1);
+}
+EXPORT_SYMBOL(mmc_retune_release);
+
+int mmc_retune(struct mmc_host *host)
+{
+       int err;
+
+       if (!host->need_retune || host->doing_retune || !host->card)
+               return 0;
+
+       host->need_retune = 0;
+
+       host->doing_retune = 1;
+
+       err = mmc_execute_tuning(host->card);
+
+       host->doing_retune = 0;
+
+       return err;
+}
+EXPORT_SYMBOL(mmc_retune);
+
 /**
  *     mmc_of_parse() - parse host's device-tree node
  *     @host: host whose node should be parsed.
index e67293b2f6706b2b422155d9bc870ad149d28b14..d4517603db46104e346e4ededf676606d4a2ed78 100755 (executable)
@@ -1112,6 +1112,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
                 * 3. set the clock to > 52Mhz <=200MHz and
                 * 4. execute tuning for HS200
                 */
+               /*
                if ((host->caps2 & MMC_CAP2_HS200) &&
                    card->host->ops->execute_tuning) {
                        mmc_host_clk_hold(card->host);
@@ -1124,6 +1125,9 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
                                   mmc_hostname(card->host));
                        goto err;
                }
+               */
+               if (host->caps2 & MMC_CAP2_HS200)
+                       mmc_execute_tuning(card);
 
                ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ?
                                EXT_CSD_BUS_WIDTH_8 : EXT_CSD_BUS_WIDTH_4;
index e5b5eeb548d17eb08e1f374a83024dc5e4a1170b..07fb48dc67ae495db2a43dc69f33170bc8b65d37 100644 (file)
@@ -395,6 +395,21 @@ int mmc_spi_set_crc(struct mmc_host *host, int use_crc)
        return err;
 }
 
+int mmc_switch_status_error(struct mmc_host *host, u32 status)
+{
+       if (mmc_host_is_spi(host)) {
+               if (status & R1_SPI_ILLEGAL_COMMAND)
+                       return -EBADMSG;
+       } else {
+               if (status & 0xFDFFA000)
+                       pr_warn("%s: unexpected status %#x after switch\n",
+                               mmc_hostname(host), status);
+               if (status & R1_SWITCH_ERROR)
+                       return -EBADMSG;
+       }
+       return 0;
+}
+
 /**
  *     __mmc_switch - modify EXT_CSD register
  *     @card: the MMC card associated with the data transfer
@@ -411,6 +426,7 @@ int mmc_spi_set_crc(struct mmc_host *host, int use_crc)
 int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
                unsigned int timeout_ms, bool use_busy_signal, bool send_status)
 {
+       struct mmc_host *host = card->host;
        int err;
        struct mmc_command cmd = {0};
        unsigned long timeout;
@@ -420,6 +436,8 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
        BUG_ON(!card);
        BUG_ON(!card->host);
 
+       mmc_retune_hold(host);
+
        cmd.opcode = MMC_SWITCH;
        cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
                  (index << 16) |
@@ -438,11 +456,11 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
 
        err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
        if (err)
-               return err;
+               goto out;
 
        /* No need to check card status in case of unblocking command */
        if (!use_busy_signal)
-               return 0;
+               goto out;
 
        /*
         * Must check status to be sure of no errors
@@ -458,7 +476,7 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
                if (send_status) {
                        err = __mmc_send_status(card, &status, ignore_crc);
                        if (err)
-                               return err;
+                               goto out;
                }
                if (card->host->caps & MMC_CAP_WAIT_WHILE_BUSY)
                        break;
@@ -472,29 +490,22 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
                 */
                if (!send_status) {
                        mmc_delay(timeout_ms);
-                       return 0;
+                       goto out;
                }
 
                /* Timeout if the device never leaves the program state. */
                if (time_after(jiffies, timeout)) {
                        pr_err("%s: Card stuck in programming state! %s\n",
                                mmc_hostname(card->host), __func__);
-                       return -ETIMEDOUT;
+                       err = -ETIMEDOUT;
+                       goto out;
                }
        } while (R1_CURRENT_STATE(status) == R1_STATE_PRG);
 
-       if (mmc_host_is_spi(card->host)) {
-               if (status & R1_SPI_ILLEGAL_COMMAND)
-                       return -EBADMSG;
-       } else {
-               if (status & 0xFDFFA000)
-                       pr_warning("%s: unexpected status %#x after "
-                              "switch", mmc_hostname(card->host), status);
-               if (status & R1_SWITCH_ERROR)
-                       return -EBADMSG;
-       }
-
-       return 0;
+       err = mmc_switch_status_error(host, status);
+out:
+       mmc_retune_release(host);
+       return err;
 }
 EXPORT_SYMBOL_GPL(__mmc_switch);
 
index 80ae9f4e0293a045ca8e4ebb89023e3c64e062da..40f66480866cb71d3621ccabef59bc0fddc2e1da 100644 (file)
@@ -26,6 +26,6 @@ int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp);
 int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
 int mmc_bus_test(struct mmc_card *card, u8 bus_width);
 int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status);
-
+int mmc_switch_status_error(struct mmc_host *host, u32 status);
 #endif
 
index 4fdcf930232a8851b38129ce908f1fafd807e66d..e26b0d4b10e1e9cdfadeb264138509c2197ce5c1 100755 (executable)
@@ -2914,6 +2914,9 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
                        mci_writel(host, RINTSTS, DW_MCI_DATA_ERROR_FLAGS);
                        host->data_status = pending;
                        smp_wmb();
+                       printk(KERN_ERR "[%s] Data transmission error !!!!  MINTSTS: [0x%08x]\n",
+                              mmc_hostname(host->mmc), pending);
+                       mmc_retune_needed(host->mmc);
                        set_bit(EVENT_DATA_ERROR, &host->pending_events);
 
                        MMC_DBG_INFO_FUNC(host->mmc,
index 4303be83cc63baac6d512b4729c1c95bcac49b0b..e401512a20a477e7dde932a5df6c51176780fd7d 100755 (executable)
@@ -334,6 +334,13 @@ struct mmc_host {
        int                     rescan_disable; /* disable card detection */
        int                     rescan_entered; /* used with nonremovable devices */
 
+
+       unsigned int            can_retune:1;   /* re-tuning can be used */
+       unsigned int            doing_retune:1; /* re-tuning in progress */
+       unsigned int            retune_now:1;   /* do re-tuning at next req */
+       int                     need_retune;    /* re-tuning is needed */
+       int                     hold_retune;    /* hold off re-tuning */
+
        struct mmc_card         *card;          /* device attached to this host */
 
        wait_queue_head_t       wq;
@@ -533,4 +540,36 @@ static inline unsigned int mmc_host_clk_rate(struct mmc_host *host)
        return host->ios.clock;
 }
 #endif
+
+void mmc_retune_enable(struct mmc_host *host);
+void mmc_retune_disable(struct mmc_host *host);
+void mmc_retune_hold(struct mmc_host *host);
+void mmc_retune_release(struct mmc_host *host);
+int mmc_retune(struct mmc_host *host);
+
+static inline void mmc_retune_needed(struct mmc_host *host)
+{
+       if ((host->can_retune) && (host->doing_retune == 0)) {
+               host->need_retune = 1;
+               pr_err("[%s] Data transmission error, need to "
+                      "try retuning %d.\n", mmc_hostname(host),
+                      host->need_retune);
+       } else {
+               pr_err("[%s] host was already tuning, Don't"
+                      " need to retry tune again ignore %d.\n",
+                      mmc_hostname(host), host->need_retune);
+       }
+}
+
+static inline void mmc_retune_not_needed(struct mmc_host *host)
+{
+       host->need_retune = 0;
+}
+
+static inline void mmc_retune_recheck(struct mmc_host *host)
+{
+       if (host->hold_retune <= 1)
+               host->retune_now = 1;
+}
+
 #endif /* LINUX_MMC_HOST_H */