summaryrefslogtreecommitdiff
path: root/drivers/mmc
diff options
context:
space:
mode:
authorMohit Kataria <mkataria@nvidia.com>2012-06-05 15:05:31 +0530
committerSimone Willett <swillett@nvidia.com>2012-06-11 16:27:31 -0700
commit5702f8c67472c05feb28a3f45550bb7ab1ca865d (patch)
tree5f0e8d293cbd0df207e69136c28c856cf1fd8be6 /drivers/mmc
parenta79e68c19180953ba479b3730e2945974660b43d (diff)
mmc: proactively issue bkops_start and refresh
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>
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/core/core.c102
-rw-r--r--drivers/mmc/core/mmc.c17
-rw-r--r--drivers/mmc/core/mmc_ops.c63
-rw-r--r--drivers/mmc/core/mmc_ops.h1
4 files changed, 183 insertions, 0 deletions
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index c5338cd45aa7..35f3df8810e0 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -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
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 69fb2275845c..254713bce18c 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -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;
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index 28bed97ae1a5..c85c58aca3e2 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -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;
+}
diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h
index d8f157dee147..a453531b46b6 100644
--- a/drivers/mmc/core/mmc_ops.h
+++ b/drivers/mmc/core/mmc_ops.h
@@ -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