From patchwork Thu Sep 8 07:29:10 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Seungwon Jeon X-Patchwork-Id: 1129052 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter2.kernel.org (8.14.4/8.14.4) with ESMTP id p887lFFC019834 for ; Thu, 8 Sep 2011 07:47:15 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932255Ab1IHHrO (ORCPT ); Thu, 8 Sep 2011 03:47:14 -0400 Received: from ganesha.gnumonks.org ([213.95.27.120]:47621 "EHLO ganesha.gnumonks.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932192Ab1IHHrN (ORCPT ); Thu, 8 Sep 2011 03:47:13 -0400 Received: from uucp by ganesha.gnumonks.org with local-bsmtp (Exim 4.72) (envelope-from ) id 1R1ZKJ-0000i6-Dq; Thu, 08 Sep 2011 09:47:11 +0200 Received: from [12.23.118.173] (helo=sunrise.dsn.sec.samsung.com) by jackpot.kr.gnumonks.org with esmtp (Exim 4.69) (envelope-from ) id 1R1YFy-00054p-2l; Thu, 08 Sep 2011 15:38:38 +0900 From: Seungwon Jeon To: linux-mmc@vger.kernel.org Cc: cjb@laptop.org, linux-samsung-soc@vger.kernel.org, kgene.kim@samsung.com, dh.han@samsung.com, Seungwon Jeon Subject: [PATCH] mmc: core: Add cache control for eMMC4.5 device Date: Thu, 8 Sep 2011 16:29:10 +0900 Message-Id: <1315466950-28973-1-git-send-email-tgih.jun@samsung.com> X-Mailer: git-send-email 1.7.1 Sender: linux-mmc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-mmc@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.6 (demeter2.kernel.org [140.211.167.43]); Thu, 08 Sep 2011 07:47:15 +0000 (UTC) This patch adds cache feature of eMMC4.5 Spec. If device supports cache capability, host can utilize some specific operations. Signed-off-by: Seungwon Jeon --- This patch is base on [PATCH v3] mmc: core: Add default timeout value for CMD6 drivers/mmc/card/block.c | 14 ++++++---- drivers/mmc/core/core.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/mmc/core/mmc.c | 23 +++++++++++++++++ include/linux/mmc/card.h | 2 + include/linux/mmc/core.h | 2 + include/linux/mmc/host.h | 3 ++ include/linux/mmc/mmc.h | 3 ++ 7 files changed, 103 insertions(+), 6 deletions(-) -- 1.7.0.4 -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index e702c61..84fa4bb 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -779,16 +779,18 @@ out: static int mmc_blk_issue_flush(struct mmc_queue *mq, struct request *req) { struct mmc_blk_data *md = mq->data; + struct mmc_card *card = md->queue.card; + int ret = 0; + + ret = mmc_flush_cache(card); + if (ret) + ret = -EIO; - /* - * No-op, only service this because we need REQ_FUA for reliable - * writes. - */ spin_lock_irq(&md->lock); - __blk_end_request_all(req, 0); + __blk_end_request_all(req, ret); spin_unlock_irq(&md->lock); - return 1; + return ret ? 0 : 1; } /* diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 557856b..14c2431 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1987,6 +1987,64 @@ int mmc_card_can_sleep(struct mmc_host *host) } EXPORT_SYMBOL(mmc_card_can_sleep); +/* + * Flush the cache to the non-volatile storage. + */ +int mmc_flush_cache(struct mmc_card *card) +{ + struct mmc_host *host = card->host; + int err = 0; + + if (!(host->caps & MMC_CAP_CACHE_CTRL)) + return err; + + if (mmc_card_mmc(card) && + (card->ext_csd.cache_size > 0) && + (card->ext_csd.cache_ctrl & 1)) { + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_FLUSH_CACHE, 1, 0); + if (err) + pr_err("%s: cache flush error %d\n", + mmc_hostname(card->host), err); + } + + return err; +} +EXPORT_SYMBOL(mmc_flush_cache); + +/* + * Turn the cache ON/OFF. + * Turning the cache OFF shall trigger flushing of the data + * to the non-volatile storage. + */ +int mmc_cache_ctrl(struct mmc_host *host, u8 enable) +{ + struct mmc_card *card = host->card; + int err = 0; + + if (!(host->caps & MMC_CAP_CACHE_CTRL)) + return err; + + if (card && mmc_card_mmc(card) && + (card->ext_csd.cache_size > 0)) { + enable = !!enable; + + if (card->ext_csd.cache_ctrl ^ enable) + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_CACHE_CTRL, enable, 0); + if (err) + pr_err("%s: cache %s error %d\n", + mmc_hostname(card->host), + enable ? "on" : "off", + err); + else + card->ext_csd.cache_ctrl = enable; + } + + return err; +} +EXPORT_SYMBOL(mmc_cache_ctrl); + #ifdef CONFIG_PM /** @@ -2001,6 +2059,9 @@ int mmc_suspend_host(struct mmc_host *host) cancel_delayed_work(&host->disable); cancel_delayed_work(&host->detect); mmc_flush_scheduled_work(); + err = mmc_cache_ctrl(host, 0); + if (err) + goto out; mmc_bus_get(host); if (host->bus_ops && !host->bus_dead) { @@ -2025,6 +2086,7 @@ int mmc_suspend_host(struct mmc_host *host) if (!err && !mmc_card_keep_power(host)) mmc_power_off(host); +out: return err; } diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index ea58a0c..035112b 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -419,6 +419,12 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) */ card->ext_csd.generic_cmd6_time = 0; + card->ext_csd.cache_size = + ext_csd[EXT_CSD_CACHE_SIZE + 0] << 0 | + ext_csd[EXT_CSD_CACHE_SIZE + 1] << 8 | + ext_csd[EXT_CSD_CACHE_SIZE + 2] << 16 | + ext_csd[EXT_CSD_CACHE_SIZE + 3] << 24; + out: return err; } @@ -851,6 +857,23 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } } + /* + * If cache size is higher than 0, this indicates + * the the existence of cache and it can be turned on. + */ + if ((host->caps & MMC_CAP_CACHE_CTRL) && + card->ext_csd.cache_size > 0) { + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_CACHE_CTRL, 1, 0); + if (err && err != -EBADMSG) + goto free_card; + + /* + * Only if no error, cache is turned on successfully. + */ + card->ext_csd.cache_ctrl = err ? 0 : 1; + } + if (!oldcard) host->card = card; diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index b713abf..519660b 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -50,6 +50,7 @@ struct mmc_ext_csd { u8 rel_sectors; u8 rel_param; u8 part_config; + u8 cache_ctrl; unsigned int part_time; /* Units: ms */ unsigned int sa_timeout; /* Units: 100ns */ unsigned int generic_cmd6_time; /* Units: 10ms */ @@ -65,6 +66,7 @@ struct mmc_ext_csd { unsigned long long enhanced_area_offset; /* Units: Byte */ unsigned int enhanced_area_size; /* Units: KB */ unsigned int boot_size; /* in bytes */ + unsigned int cache_size; /* Units: KB */ u8 raw_partition_support; /* 160 */ u8 raw_erased_mem_count; /* 181 */ u8 raw_ext_csd_structure; /* 194 */ diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index b8b1b7a..45b4acf 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -171,6 +171,8 @@ extern void mmc_release_host(struct mmc_host *host); extern void mmc_do_release_host(struct mmc_host *host); extern int mmc_try_claim_host(struct mmc_host *host); +extern int mmc_flush_cache(struct mmc_card *); + /** * mmc_claim_host - exclusively claim a host * @host: mmc host to claim diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 4c4bddf..6d0006d 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -230,6 +230,7 @@ struct mmc_host { #define MMC_CAP_MAX_CURRENT_600 (1 << 28) /* Host max current limit is 600mA */ #define MMC_CAP_MAX_CURRENT_800 (1 << 29) /* Host max current limit is 800mA */ #define MMC_CAP_CMD23 (1 << 30) /* CMD23 supported. */ +#define MMC_CAP_CACHE_CTRL (1 << 31) /* Allow cache control. */ mmc_pm_flag_t pm_caps; /* supported pm features */ @@ -335,6 +336,8 @@ extern int mmc_power_restore_host(struct mmc_host *host); extern void mmc_detect_change(struct mmc_host *, unsigned long delay); extern void mmc_request_done(struct mmc_host *, struct mmc_request *); +extern int mmc_cache_ctrl(struct mmc_host *, u8); + static inline void mmc_signal_sdio_irq(struct mmc_host *host) { host->ops->enable_sdio_irq(host, 0); diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index e869f00..b7fa9f7 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -270,6 +270,8 @@ struct _mmc_csd { * EXT_CSD fields */ +#define EXT_CSD_FLUSH_CACHE 32 /* W */ +#define EXT_CSD_CACHE_CTRL 33 /* R/W */ #define EXT_CSD_PARTITION_ATTRIBUTE 156 /* R/W */ #define EXT_CSD_PARTITION_SUPPORT 160 /* RO */ #define EXT_CSD_WR_REL_PARAM 166 /* RO */ @@ -294,6 +296,7 @@ struct _mmc_csd { #define EXT_CSD_SEC_FEATURE_SUPPORT 231 /* RO */ #define EXT_CSD_TRIM_MULT 232 /* RO */ #define EXT_CSD_GENERIC_CMD6_TIME 248 /* RO */ +#define EXT_CSD_CACHE_SIZE 249 /* RO, 4 bytes */ /* * EXT_CSD field definitions