mmc: proactively issue bkops_start and refresh
Mohit Kataria [Tue, 5 Jun 2012 09:35:31 +0000 (14:35 +0530)]
Adding CMD56 implementation.
Doing the following for manfid 0x90 and FW revisions 0x73 and
0x7b (both are non-standard custom FW):
1. Adding change to issue BKOPS_START whenever 20 seconds have gone
by without any slow write operations.
2. Adding change to issue CMD56 to refresh (custom feature) 1 block
whenever 60 seconds have gone by without any slow write operations.

Corresponding changes are already there in embedded branches:
http://git-master/r/#change,93247
http://git-master/r/#change,97555

Bug 847037.
Bug 874256.
Bug 963737.

Change-Id: Ie36b52620a75320abfedc36d1408647b36eddb46
Signed-off-by: Vishal Singh <vissingh@nvidia.com>
Reviewed-on: http://git-master/r/102259
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Luis Dib <ldib@nvidia.com>
Reviewed-by: Varun Wadekar <vwadekar@nvidia.com>
GVS: Gerrit_Virtual_Submit
Reviewed-by: Sachin Nikam <snikam@nvidia.com>

drivers/mmc/core/core.c
drivers/mmc/core/mmc.c
drivers/mmc/core/mmc_ops.c
drivers/mmc/core/mmc_ops.h
include/linux/mmc/card.h
include/linux/mmc/core.h

index c5338cd..35f3df8 100644 (file)
@@ -272,13 +272,44 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,
 {
        int err = 0;
        struct mmc_async_req *data = host->areq;
+       struct mmc_card *card = host->card;
+       struct timeval before_time, after_time;
 
        /* Prepare a new request */
        if (areq)
                mmc_pre_req(host, areq->mrq, !host->areq);
 
        if (host->areq) {
+               if (card->ext_csd.refresh &&
+                       (host->areq->mrq->data->flags & MMC_DATA_WRITE))
+                               do_gettimeofday(&before_time);
                mmc_wait_for_req_done(host, host->areq->mrq);
+               if (card->ext_csd.refresh &&
+                       (host->areq->mrq->data->flags & MMC_DATA_WRITE)) {
+                       do_gettimeofday(&after_time);
+                       switch (after_time.tv_sec - before_time.tv_sec) {
+                               case 0:
+                                       if (after_time.tv_usec -
+                                               before_time.tv_usec >=
+                                                       MMC_SLOW_WRITE_TIME) {
+                                               card->ext_csd.last_tv_sec =
+                                                       after_time.tv_sec;
+                                               card->ext_csd.last_bkops_tv_sec =
+                                                       after_time.tv_sec;
+                                       }
+                                       break;
+                               case 1:
+                                       if (after_time.tv_usec -
+                                               before_time.tv_usec <
+                                                       MMC_SLOW_WRITE_TIME - 1000000)
+                                               break;
+                               default:
+                                       card->ext_csd.last_tv_sec =
+                                               after_time.tv_sec;
+                                       card->ext_csd.last_bkops_tv_sec =
+                                               after_time.tv_sec;
+                       }
+               }
                err = host->areq->err_check(host->card, host->areq);
                if (err) {
                        mmc_post_req(host, host->areq->mrq, 0);
@@ -331,6 +362,7 @@ int mmc_bkops_start(struct mmc_card *card, bool is_synchronous)
 {
        int err;
        unsigned long flags;
+       struct timeval before_time, after_time;
 
        BUG_ON(!card);
 
@@ -338,10 +370,29 @@ int mmc_bkops_start(struct mmc_card *card, bool is_synchronous)
                return 1;
 
        mmc_claim_host(card->host);
+       if (card->ext_csd.refresh)
+               do_gettimeofday(&before_time);
        err = mmc_send_bk_ops_cmd(card, is_synchronous);
        if (err)
                pr_err("%s: abort bk ops (%d error)\n",
                        mmc_hostname(card->host), err);
+       if (card->ext_csd.refresh) {
+               do_gettimeofday(&after_time);
+               switch (after_time.tv_sec - before_time.tv_sec) {
+                       case 0:
+                               if (after_time.tv_usec - before_time.tv_usec >=
+                                       MMC_SLOW_WRITE_TIME)
+                                       card->ext_csd.last_tv_sec = after_time.tv_sec;
+                               break;
+                       case 1:
+                               if (after_time.tv_usec - before_time.tv_usec <
+                                       MMC_SLOW_WRITE_TIME - 1000000)
+                                       break;
+                       default:
+                               card->ext_csd.last_tv_sec = after_time.tv_sec;
+               }
+               card->ext_csd.last_bkops_tv_sec = after_time.tv_sec;
+       }
 
        /*
         * Incase of asynchronous backops, set card state
@@ -360,6 +411,57 @@ int mmc_bkops_start(struct mmc_card *card, bool is_synchronous)
 }
 EXPORT_SYMBOL(mmc_bkops_start);
 
+static void mmc_bkops_work(struct work_struct *work)
+{
+       struct mmc_card *card = container_of(work, struct mmc_card, bkops);
+       mmc_bkops_start(card, true);
+}
+
+static void mmc_refresh_work(struct work_struct *work)
+{
+       struct mmc_card *card = container_of(work, struct mmc_card, refresh);
+       char buf[512];
+       mmc_gen_cmd(card, buf, 0x44, 0x1, 0x0, 0x1);
+}
+
+void mmc_refresh(unsigned long data)
+{
+       struct mmc_card *card = (struct mmc_card *) data;
+       struct timeval cur_time;
+       __kernel_time_t timeout, timeout1, timeout2;
+
+       if ((!card) || (!card->ext_csd.refresh))
+               return;
+
+       INIT_WORK(&card->bkops, (work_func_t) mmc_bkops_work);
+       INIT_WORK(&card->refresh, (work_func_t) mmc_refresh_work);
+
+       do_gettimeofday(&cur_time);
+       timeout1 = MMC_REFRESH_INTERVAL - (cur_time.tv_sec -
+               card->ext_csd.last_tv_sec);
+       if ((cur_time.tv_sec < card->ext_csd.last_tv_sec) ||
+               (timeout1 <= 0)) {
+               queue_work(workqueue, &card->refresh);
+               card->ext_csd.last_tv_sec = cur_time.tv_sec;
+               card->ext_csd.last_bkops_tv_sec = cur_time.tv_sec;
+               timeout1 = MMC_REFRESH_INTERVAL;
+       }
+
+       timeout2 = MMC_BKOPS_INTERVAL - (cur_time.tv_sec -
+               card->ext_csd.last_bkops_tv_sec);
+       if ((cur_time.tv_sec < card->ext_csd.last_bkops_tv_sec) ||
+               (timeout2 <= 0)) {
+               mmc_card_set_need_bkops(card);
+               queue_work(workqueue, &card->bkops);
+               timeout2 = MMC_BKOPS_INTERVAL;
+       }
+
+       timeout = timeout1 < timeout2 ? timeout1 : timeout2;
+       card->timer.expires = jiffies + timeout*HZ;
+       add_timer(&card->timer);
+}
+EXPORT_SYMBOL(mmc_refresh);
+
 /**
  *     mmc_interrupt_hpi - Issue for High priority Interrupt
  *     @card: the MMC card associated with the HPI transfer
index 69fb227..254713b 100644 (file)
@@ -96,6 +96,7 @@ static int mmc_decode_cid(struct mmc_card *card)
                card->cid.prod_name[3]  = UNSTUFF_BITS(resp, 72, 8);
                card->cid.prod_name[4]  = UNSTUFF_BITS(resp, 64, 8);
                card->cid.prod_name[5]  = UNSTUFF_BITS(resp, 56, 8);
+               card->cid.prod_rev      = UNSTUFF_BITS(resp, 48, 8);
                card->cid.serial        = UNSTUFF_BITS(resp, 16, 32);
                card->cid.month         = UNSTUFF_BITS(resp, 12, 4);
                card->cid.year          = UNSTUFF_BITS(resp, 8, 4) + 1997;
@@ -425,6 +426,11 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
                /* Check whether the eMMC card supports background ops */
                if (ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1)
                        card->ext_csd.bk_ops = 1;
+
+               /* Check whether the eMMC card needs proactive refresh */
+               if ((card->cid.manfid == 0x90) && ((card->cid.prod_rev == 0x73)
+                       || (card->cid.prod_rev == 0x7b)))
+                       card->ext_csd.refresh = 1;
        }
 
        if (ext_csd[EXT_CSD_ERASED_MEM_CONT])
@@ -672,6 +678,17 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
                if (err)
                        goto free_card;
 
+               if (card->ext_csd.refresh) {
+                       init_timer(&card->timer);
+                       card->timer.data = (unsigned long) card;
+                       card->timer.function = mmc_refresh;
+                       card->timer.expires = MMC_BKOPS_INTERVAL <
+                               MMC_REFRESH_INTERVAL ? MMC_BKOPS_INTERVAL :
+                               MMC_REFRESH_INTERVAL;
+                       card->timer.expires *= HZ;
+                       card->timer.expires += jiffies;
+                       add_timer(&card->timer);
+               }
                /* If doing byte addressing, check if required to do sector
                 * addressing.  Handle the case of <2GB cards needing sector
                 * addressing.  See section 8.1 JEDEC Standard JED84-A441;
index 28bed97..c85c58a 100644 (file)
@@ -617,3 +617,66 @@ int mmc_send_bk_ops_cmd(struct mmc_card *card, bool is_synchronous)
 
        return 0;
 }
+
+int mmc_gen_cmd(struct mmc_card *card, void *buf, u8 index, u8 arg1, u8 arg2, u8 mode)
+{
+       struct mmc_request mrq;
+       struct mmc_command cmd;
+       struct mmc_data data;
+       struct mmc_command stop;
+       struct scatterlist sg;
+       void *data_buf;
+
+       mmc_set_blocklen(card, 512);
+
+       data_buf = kmalloc(512, GFP_KERNEL);
+       if (data_buf == NULL)
+               return -ENOMEM;
+
+       memset(&mrq, 0, sizeof(struct mmc_request));
+       memset(&cmd, 0, sizeof(struct mmc_command));
+       memset(&data, 0, sizeof(struct mmc_data));
+       memset(&stop, 0, sizeof(struct mmc_command));
+
+       mrq.cmd = &cmd;
+       mrq.data = &data;
+       mrq.stop = &stop;
+
+       cmd.opcode = MMC_GEN_CMD;
+       cmd.arg = (arg2 << 16) |
+                 (arg1 << 8) |
+                 (index << 1) |
+                 mode;
+
+       cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
+
+       data.blksz = 512;
+       data.blocks = 1;
+       data.flags = MMC_DATA_READ;
+       data.sg = &sg;
+       data.sg_len = 1;
+
+       stop.opcode = MMC_STOP_TRANSMISSION;
+       stop.arg = 0;
+       stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
+
+       sg_init_one(&sg, data_buf, 512);
+
+       mmc_set_data_timeout(&data, card);
+
+       mmc_claim_host(card->host);
+       mmc_wait_for_req(card->host, &mrq);
+       mmc_release_host(card->host);
+
+       memcpy(buf, data_buf, 512);
+       kfree(data_buf);
+
+       if (cmd.error)
+               return cmd.error;
+       if (data.error)
+               return data.error;
+       if (stop.error)
+               return stop.error;
+
+       return 0;
+}
index d8f157d..a453531 100644 (file)
@@ -29,6 +29,7 @@ int mmc_card_sleepawake(struct mmc_host *host, int sleep);
 int mmc_bus_test(struct mmc_card *card, u8 bus_width);
 int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status);
 int mmc_send_bk_ops_cmd(struct mmc_card *card, bool is_synchronous);
+int mmc_gen_cmd(struct mmc_card *card, void *buf, u8 index, u8 arg1, u8 arg2, u8 mode);
 
 #endif
 
index 9178aa4..8f17619 100644 (file)
@@ -22,6 +22,7 @@ struct mmc_cid {
        unsigned char           hwrev;
        unsigned char           fwrev;
        unsigned char           month;
+       unsigned short          prod_rev;
 };
 
 struct mmc_csd {
@@ -83,6 +84,9 @@ struct mmc_ext_csd {
        u8                      out_of_int_time;        /* out of int time */
        bool                    bk_ops;                 /* BK ops support bit */
        bool                    bk_ops_en;              /* BK ops enable bit */
+       bool                    refresh;                /* refresh of blocks supported */
+       __kernel_time_t         last_tv_sec;            /* last time a block was refreshed */
+       __kernel_time_t         last_bkops_tv_sec;      /* last time bkops was done */
 };
 
 struct sd_scr {
@@ -224,6 +228,10 @@ struct mmc_card {
        unsigned int            sd_bus_speed;   /* Bus Speed Mode set for the card */
 
        struct dentry           *debugfs_root;
+
+       struct timer_list       timer;
+       struct work_struct      bkops;
+       struct work_struct      refresh;
 };
 
 /*
index c3e55fa..0c4472e 100644 (file)
 #include <linux/interrupt.h>
 #include <linux/device.h>
 
+#define MMC_SLOW_WRITE_TIME    500000  /* time (us) */
+#define MMC_REFRESH_INTERVAL   60      /* time (s) */
+#define MMC_BKOPS_INTERVAL     20      /* time (s) */
+
 struct request;
 struct mmc_data;
 struct mmc_request;
@@ -138,6 +142,7 @@ extern struct mmc_async_req *mmc_start_req(struct mmc_host *,
                                           struct mmc_async_req *, int *);
 extern int mmc_interrupt_hpi(struct mmc_card *);
 extern int mmc_bkops_start(struct mmc_card *card, bool is_synchronous);
+extern void mmc_refresh(unsigned long data);
 
 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);