Add the ability to partially initialize the MMC device by
using device sleep/awake sequence (CMD5).
Device will be sent to sleep state during mmc runtime/system suspend
and will be woken up during mmc runtime/system resume.
By using this sequence the device doesn't need full initialization
which gives 25% time reduction in system/runtime resume path.
1) Micron eMMC (ManfID 0x13)
Partial init Full Init
a) _mmc_resume: _mmc_resume :
Total time : 62ms Total time : 84ms
(Improvement % from full init = ~26%)
2) Kingston eMMC (ManfID 0x70)
Partial init Full Init
a) _mmc_resume: _mmc_resume :
Total time : 46ms Total time : 62ms
(Improvement % from full init = ~25%).
Co-developed-by: Veerabhadrarao Badiganti <[email protected]>
Signed-off-by: Veerabhadrarao Badiganti <[email protected]>
Signed-off-by: Sarthak Garg <[email protected]>
---
drivers/mmc/core/mmc.c | 162 ++++++++++++++++++++++++++++++++++++---
include/linux/mmc/card.h | 4 +
include/linux/mmc/host.h | 2 +
3 files changed, 159 insertions(+), 9 deletions(-)
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 89cd48fcec79..e84516717170 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -1951,7 +1951,27 @@ static int mmc_sleep_busy_cb(void *cb_data, bool *busy)
return 0;
}
-static int mmc_sleep(struct mmc_host *host)
+/*
+ * Returns true if card supports sleep/awake command and host can simply do
+ * sleep/awake instead of full card initialization as part of resume.
+ */
+static inline int mmc_can_sleepawake(struct mmc_host *host)
+{
+ return mmc_can_sleep(host->card) && (host->caps2 & MMC_CAP2_SLEEP_AWAKE);
+}
+
+/**
+ * mmc_sleepawake - function to sleep or awake the device
+ * @sleep: if true then sleep command is sent else awake
+ *
+ * This function first deselects the card and then sends the sleep command
+ * in case of sleep whereas in case of awake first awake command is send
+ * and then the card is selected.
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+
+static int mmc_sleepawake(struct mmc_host *host, bool sleep)
{
struct mmc_command cmd = {};
struct mmc_card *card = host->card;
@@ -1962,14 +1982,17 @@ static int mmc_sleep(struct mmc_host *host)
/* Re-tuning can't be done once the card is deselected */
mmc_retune_hold(host);
- err = mmc_deselect_cards(host);
- if (err)
- goto out_release;
+ if (sleep) {
+ err = mmc_deselect_cards(host);
+ if (err)
+ goto out_release;
+ }
cmd.opcode = MMC_SLEEP_AWAKE;
cmd.arg = card->rca << 16;
- cmd.arg |= 1 << 15;
use_r1b_resp = mmc_prepare_busy_cmd(host, &cmd, timeout_ms);
+ if (sleep)
+ cmd.arg |= BIT(15);
err = mmc_wait_for_cmd(host, &cmd, 0);
if (err)
@@ -1992,6 +2015,9 @@ static int mmc_sleep(struct mmc_host *host)
err = __mmc_poll_for_busy(host, 0, timeout_ms, &mmc_sleep_busy_cb, host);
out_release:
+ if (!sleep)
+ err = mmc_select_card(card);
+
mmc_retune_release(host);
return err;
}
@@ -2089,6 +2115,73 @@ static int _mmc_flush_cache(struct mmc_host *host)
pr_err("%s: cache flush error %d\n",
mmc_hostname(host), err);
}
+ return err;
+}
+
+/*
+ * Save read/write fields of ext_csd register that the sw changes
+ * as part of suspend.
+ */
+static int mmc_save_card_ext_csd(struct mmc_host *host)
+{
+ int err;
+ u8 *ext_csd;
+ struct mmc_card *card = host->card;
+
+ err = mmc_get_ext_csd(card, &ext_csd);
+ if (err || !ext_csd) {
+ pr_err("%s: %s: mmc_get_ext_csd failed (%d)\n",
+ mmc_hostname(host), __func__, err);
+ return err;
+ }
+
+ card->ext_csd.raw_ext_csd_cmdq = ext_csd[EXT_CSD_CMDQ_MODE_EN];
+ card->ext_csd.raw_ext_csd_cache_ctrl = ext_csd[EXT_CSD_CACHE_CTRL];
+ card->ext_csd.raw_ext_csd_bus_width = ext_csd[EXT_CSD_BUS_WIDTH];
+ card->ext_csd.raw_ext_csd_hs_timing = ext_csd[EXT_CSD_HS_TIMING];
+
+ kfree(ext_csd);
+
+ return 0;
+}
+
+/*
+ * Get the ext_csd register from the card post resume and compare with
+ * read/write fields of ext_csd register that the sw changes.
+ */
+static int mmc_test_awake_ext_csd(struct mmc_host *host)
+{
+ struct mmc_card *card = host->card;
+ u8 *ext_csd;
+ int err;
+
+ err = mmc_get_ext_csd(card, &ext_csd);
+ if (err) {
+ pr_err("%s: %s: mmc_get_ext_csd failed (%d)\n",
+ mmc_hostname(host), __func__, err);
+ return err;
+ }
+
+ pr_debug("%s: %s: type(cached:current) cmdq(%d:%d) cache_ctrl(%d:%d) bus_width (%d:%d) timing(%d:%d)\n",
+ mmc_hostname(host), __func__,
+ card->ext_csd.raw_ext_csd_cmdq,
+ ext_csd[EXT_CSD_CMDQ_MODE_EN],
+ card->ext_csd.raw_ext_csd_cache_ctrl,
+ ext_csd[EXT_CSD_CACHE_CTRL],
+ card->ext_csd.raw_ext_csd_bus_width,
+ ext_csd[EXT_CSD_BUS_WIDTH],
+ card->ext_csd.raw_ext_csd_hs_timing,
+ ext_csd[EXT_CSD_HS_TIMING]);
+ err = !((card->ext_csd.raw_ext_csd_cmdq ==
+ ext_csd[EXT_CSD_CMDQ_MODE_EN]) &&
+ (card->ext_csd.raw_ext_csd_cache_ctrl ==
+ ext_csd[EXT_CSD_CACHE_CTRL]) &&
+ (card->ext_csd.raw_ext_csd_bus_width ==
+ ext_csd[EXT_CSD_BUS_WIDTH]) &&
+ (card->ext_csd.raw_ext_csd_hs_timing ==
+ ext_csd[EXT_CSD_HS_TIMING]));
+
+ kfree(ext_csd);
return err;
}
@@ -2112,9 +2205,15 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
((host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) || !is_suspend ||
(host->caps2 & MMC_CAP2_FULL_PWR_CYCLE_IN_SUSPEND)))
err = mmc_poweroff_notify(host->card, notify_type);
- else if (mmc_can_sleep(host->card))
- err = mmc_sleep(host);
- else if (!mmc_host_is_spi(host))
+ else if (mmc_can_sleep(host->card)) {
+ if (mmc_can_sleepawake(host)) {
+ memcpy(&host->cached_ios, &host->ios, sizeof(host->cached_ios));
+ err = mmc_save_card_ext_csd(host);
+ if (err)
+ goto out;
+ }
+ err = mmc_sleepawake(host, true);
+ } else if (!mmc_host_is_spi(host))
err = mmc_deselect_cards(host);
if (!err) {
@@ -2126,6 +2225,39 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
return err;
}
+static int mmc_partial_init(struct mmc_host *host)
+{
+ int err = 0;
+ struct mmc_card *card = host->card;
+
+ mmc_set_bus_width(host, host->cached_ios.bus_width);
+ mmc_set_timing(host, host->cached_ios.timing);
+ if (host->cached_ios.enhanced_strobe) {
+ host->ios.enhanced_strobe = true;
+ if (host->ops->hs400_enhanced_strobe)
+ host->ops->hs400_enhanced_strobe(host, &host->ios);
+ }
+ mmc_set_clock(host, host->cached_ios.clock);
+ mmc_set_bus_mode(host, host->cached_ios.bus_mode);
+
+ if (!mmc_card_hs400es(card) &&
+ (mmc_card_hs200(card) || mmc_card_hs400(card))) {
+ err = mmc_execute_tuning(card);
+ if (err) {
+ pr_err("%s: %s: Tuning failed (%d)\n",
+ mmc_hostname(host), __func__, err);
+ goto out;
+ }
+ }
+
+ err = mmc_test_awake_ext_csd(host);
+ if (err)
+ pr_debug("%s: %s: fail on ext_csd read (%d)\n",
+ mmc_hostname(host), __func__, err);
+out:
+ return err;
+}
+
/*
* Suspend callback
*/
@@ -2156,7 +2288,19 @@ static int _mmc_resume(struct mmc_host *host)
goto out;
mmc_power_up(host, host->card->ocr);
- err = mmc_init_card(host, host->card->ocr, host->card);
+
+ if (mmc_can_sleepawake(host)) {
+ err = mmc_sleepawake(host, false);
+ if (!err)
+ err = mmc_partial_init(host);
+ else
+ pr_err("%s: %s: awake failed (%d), fallback to full init\n",
+ mmc_hostname(host), __func__, err);
+ }
+
+ if (!mmc_can_sleepawake(host) || err)
+ err = mmc_init_card(host, host->card->ocr, host->card);
+
mmc_card_clr_suspended(host->card);
out:
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index daa2f40d9ce6..fbc832ec6d57 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -86,6 +86,8 @@ struct mmc_ext_csd {
unsigned int data_tag_unit_size; /* DATA TAG UNIT size */
unsigned int boot_ro_lock; /* ro lock support */
bool boot_ro_lockable;
+ u8 raw_ext_csd_cmdq; /* 15 */
+ u8 raw_ext_csd_cache_ctrl; /* 33 */
bool ffu_capable; /* Firmware upgrade support */
bool cmdq_en; /* Command Queue enabled */
bool cmdq_support; /* Command Queue supported */
@@ -96,7 +98,9 @@ struct mmc_ext_csd {
u8 raw_partition_support; /* 160 */
u8 raw_rpmb_size_mult; /* 168 */
u8 raw_erased_mem_count; /* 181 */
+ u8 raw_ext_csd_bus_width; /* 183 */
u8 strobe_support; /* 184 */
+ u8 raw_ext_csd_hs_timing; /* 185 */
u8 raw_ext_csd_structure; /* 194 */
u8 raw_card_type; /* 196 */
u8 raw_driver_strength; /* 197 */
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 62a6847a3b6f..45ded42ad8f1 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -427,6 +427,7 @@ struct mmc_host {
#define MMC_CAP2_CRYPTO 0
#endif
#define MMC_CAP2_ALT_GPT_TEGRA (1 << 28) /* Host with eMMC that has GPT entry at a non-standard location */
+#define MMC_CAP2_SLEEP_AWAKE (1 << 29) /* Use Sleep/Awake (CMD5) */
int fixed_drv_type; /* fixed driver type for non-removable media */
@@ -445,6 +446,7 @@ struct mmc_host {
spinlock_t lock; /* lock for claim and bus ops */
struct mmc_ios ios; /* current io bus settings */
+ struct mmc_ios cached_ios;
/* group bitfields together to minimize padding */
unsigned int use_spi_crc:1;
--
2.17.1
Hi Sarthak,
kernel test robot noticed the following build warnings:
[auto build test WARNING on linus/master]
[also build test WARNING on ulf-hansson-mmc-mirror/next v6.6-rc3 next-20230929]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Sarthak-Garg/mmc-core-Add-partial-initialization-support/20230929-183238
base: linus/master
patch link: https://lore.kernel.org/r/20230929102831.9702-2-quic_sartgarg%40quicinc.com
patch subject: [PATCH V2 1/2] mmc: core: Add partial initialization support
config: m68k-allyesconfig (https://download.01.org/0day-ci/archive/20230929/[email protected]/config)
compiler: m68k-linux-gcc (GCC) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20230929/[email protected]/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/
All warnings (new ones prefixed by >>):
>> drivers/mmc/core/mmc.c:1975: warning: Function parameter or member 'host' not described in 'mmc_sleepawake'
vim +1975 drivers/mmc/core/mmc.c
8786b9922e6b87 Sarthak Garg 2023-09-29 1962
8786b9922e6b87 Sarthak Garg 2023-09-29 1963 /**
8786b9922e6b87 Sarthak Garg 2023-09-29 1964 * mmc_sleepawake - function to sleep or awake the device
8786b9922e6b87 Sarthak Garg 2023-09-29 1965 * @sleep: if true then sleep command is sent else awake
8786b9922e6b87 Sarthak Garg 2023-09-29 1966 *
8786b9922e6b87 Sarthak Garg 2023-09-29 1967 * This function first deselects the card and then sends the sleep command
8786b9922e6b87 Sarthak Garg 2023-09-29 1968 * in case of sleep whereas in case of awake first awake command is send
8786b9922e6b87 Sarthak Garg 2023-09-29 1969 * and then the card is selected.
8786b9922e6b87 Sarthak Garg 2023-09-29 1970 *
8786b9922e6b87 Sarthak Garg 2023-09-29 1971 * Returns 0 on success, non-zero value on failure
8786b9922e6b87 Sarthak Garg 2023-09-29 1972 */
8786b9922e6b87 Sarthak Garg 2023-09-29 1973
8786b9922e6b87 Sarthak Garg 2023-09-29 1974 static int mmc_sleepawake(struct mmc_host *host, bool sleep)
07a682160866e3 Ulf Hansson 2013-04-19 @1975 {
c7836d1593b87c Masahiro Yamada 2016-12-19 1976 struct mmc_command cmd = {};
07a682160866e3 Ulf Hansson 2013-04-19 1977 struct mmc_card *card = host->card;
cb962e04b04fb6 Ulf Hansson 2014-01-14 1978 unsigned int timeout_ms = DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000);
e62f1e0b2384e2 Ulf Hansson 2021-05-04 1979 bool use_r1b_resp;
07a682160866e3 Ulf Hansson 2013-04-19 1980 int err;
07a682160866e3 Ulf Hansson 2013-04-19 1981
436f8daa6f5a29 Adrian Hunter 2015-05-07 1982 /* Re-tuning can't be done once the card is deselected */
436f8daa6f5a29 Adrian Hunter 2015-05-07 1983 mmc_retune_hold(host);
436f8daa6f5a29 Adrian Hunter 2015-05-07 1984
8786b9922e6b87 Sarthak Garg 2023-09-29 1985 if (sleep) {
07a682160866e3 Ulf Hansson 2013-04-19 1986 err = mmc_deselect_cards(host);
07a682160866e3 Ulf Hansson 2013-04-19 1987 if (err)
436f8daa6f5a29 Adrian Hunter 2015-05-07 1988 goto out_release;
8786b9922e6b87 Sarthak Garg 2023-09-29 1989 }
07a682160866e3 Ulf Hansson 2013-04-19 1990
07a682160866e3 Ulf Hansson 2013-04-19 1991 cmd.opcode = MMC_SLEEP_AWAKE;
07a682160866e3 Ulf Hansson 2013-04-19 1992 cmd.arg = card->rca << 16;
e62f1e0b2384e2 Ulf Hansson 2021-05-04 1993 use_r1b_resp = mmc_prepare_busy_cmd(host, &cmd, timeout_ms);
8786b9922e6b87 Sarthak Garg 2023-09-29 1994 if (sleep)
8786b9922e6b87 Sarthak Garg 2023-09-29 1995 cmd.arg |= BIT(15);
cb962e04b04fb6 Ulf Hansson 2014-01-14 1996
07a682160866e3 Ulf Hansson 2013-04-19 1997 err = mmc_wait_for_cmd(host, &cmd, 0);
07a682160866e3 Ulf Hansson 2013-04-19 1998 if (err)
436f8daa6f5a29 Adrian Hunter 2015-05-07 1999 goto out_release;
07a682160866e3 Ulf Hansson 2013-04-19 2000
07a682160866e3 Ulf Hansson 2013-04-19 2001 /*
6fa79651cc808f Ulf Hansson 2021-05-04 2002 * If the host does not wait while the card signals busy, then we can
6fa79651cc808f Ulf Hansson 2021-05-04 2003 * try to poll, but only if the host supports HW polling, as the
6fa79651cc808f Ulf Hansson 2021-05-04 2004 * SEND_STATUS cmd is not allowed. If we can't poll, then we simply need
6fa79651cc808f Ulf Hansson 2021-05-04 2005 * to wait the sleep/awake timeout.
07a682160866e3 Ulf Hansson 2013-04-19 2006 */
6fa79651cc808f Ulf Hansson 2021-05-04 2007 if (host->caps & MMC_CAP_WAIT_WHILE_BUSY && use_r1b_resp)
6fa79651cc808f Ulf Hansson 2021-05-04 2008 goto out_release;
6fa79651cc808f Ulf Hansson 2021-05-04 2009
6fa79651cc808f Ulf Hansson 2021-05-04 2010 if (!host->ops->card_busy) {
cb962e04b04fb6 Ulf Hansson 2014-01-14 2011 mmc_delay(timeout_ms);
6fa79651cc808f Ulf Hansson 2021-05-04 2012 goto out_release;
6fa79651cc808f Ulf Hansson 2021-05-04 2013 }
6fa79651cc808f Ulf Hansson 2021-05-04 2014
1760fdb6fe9f79 Ulf Hansson 2022-03-04 2015 err = __mmc_poll_for_busy(host, 0, timeout_ms, &mmc_sleep_busy_cb, host);
07a682160866e3 Ulf Hansson 2013-04-19 2016
436f8daa6f5a29 Adrian Hunter 2015-05-07 2017 out_release:
8786b9922e6b87 Sarthak Garg 2023-09-29 2018 if (!sleep)
8786b9922e6b87 Sarthak Garg 2023-09-29 2019 err = mmc_select_card(card);
8786b9922e6b87 Sarthak Garg 2023-09-29 2020
436f8daa6f5a29 Adrian Hunter 2015-05-07 2021 mmc_retune_release(host);
07a682160866e3 Ulf Hansson 2013-04-19 2022 return err;
07a682160866e3 Ulf Hansson 2013-04-19 2023 }
07a682160866e3 Ulf Hansson 2013-04-19 2024
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki