From 5702f8c67472c05feb28a3f45550bb7ab1ca865d Mon Sep 17 00:00:00 2001 From: Mohit Kataria Date: Tue, 5 Jun 2012 15:05:31 +0530 Subject: 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 Reviewed-on: http://git-master/r/102259 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Luis Dib Reviewed-by: Varun Wadekar GVS: Gerrit_Virtual_Submit Reviewed-by: Sachin Nikam --- drivers/mmc/core/core.c | 102 +++++++++++++++++++++++++++++++++++++++++++++ drivers/mmc/core/mmc.c | 17 ++++++++ drivers/mmc/core/mmc_ops.c | 63 ++++++++++++++++++++++++++++ drivers/mmc/core/mmc_ops.h | 1 + 4 files changed, 183 insertions(+) (limited to 'drivers/mmc') 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 -- cgit v1.2.3