Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 52ef7c19 authored by Maya Erez's avatar Maya Erez
Browse files

mmc: core: add partial initialization support



This change adds the ability to partially initialize
the MMC card by using card Sleep/Awake sequence (CMD5).
Card will be sent to Sleep state during runtime/system suspend
and will be woken up during runtime/system resume.
By using this sequence the card doesn't need full initialization
which gives time reduction in system/runtime resume path.

Change-Id: Id8dadf03ef4226f7c4675fadbacac7bb15c0289e
Signed-off-by: default avatarTalel Shenhar <tatias@codeaurora.org>
Signed-off-by: default avatarMaya Erez <merez@codeaurora.org>
parent ee630a97
Loading
Loading
Loading
Loading
+162 −13
Original line number Diff line number Diff line
@@ -1405,7 +1405,7 @@ static int mmc_select_cmdq(struct mmc_card *card)
	}

	mmc_host_clk_release(card->host);
	pr_info("%s: CMDQ enabled on card\n", mmc_hostname(host));
	pr_info_once("%s: CMDQ enabled on card\n", mmc_hostname(host));
out:
	return ret;
}
@@ -2049,24 +2049,28 @@ err:
	return err;
}

static int mmc_can_sleep(struct mmc_card *card)
static int mmc_can_sleepawake(struct mmc_host *host)
{
	return (card && card->ext_csd.rev >= 3);
	return host && (host->caps2 & MMC_CAP2_SLEEP_AWAKE) && host->card &&
		(host->card->ext_csd.rev >= 3);
}

static int mmc_sleep(struct mmc_host *host)
static int mmc_sleepawake(struct mmc_host *host, bool sleep)
{
	struct mmc_command cmd = {0};
	struct mmc_card *card = host->card;
	unsigned int timeout_ms = DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000);
	int err;

	if (sleep) {
		err = mmc_deselect_cards(host);
		if (err)
			return err;
	}

	cmd.opcode = MMC_SLEEP_AWAKE;
	cmd.arg = card->rca << 16;
	if (sleep)
		cmd.arg |= 1 << 15;

	/*
@@ -2095,6 +2099,9 @@ static int mmc_sleep(struct mmc_host *host)
	if (!cmd.busy_timeout || !(host->caps & MMC_CAP_WAIT_WHILE_BUSY))
		mmc_delay(timeout_ms);

	if (!sleep)
		err = mmc_select_card(card);

	return err;
}

@@ -2203,6 +2210,69 @@ static void mmc_detect(struct mmc_host *host)
	}
}

static int mmc_cache_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;
	}

	/* only cache read/write fields that the sw changes */
	card->ext_csd.raw_ext_csd_cmdq = ext_csd[EXT_CSD_CMDQ];
	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];

	mmc_free_ext_csd(ext_csd);

	return 0;
}

static int mmc_test_awake_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) {
		pr_err("%s: %s: mmc_get_ext_csd failed (%d)\n",
			mmc_hostname(host), __func__, err);
		return err;
	}

	/* only compare read/write fields that the sw changes */
	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],
		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]) &&
		(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]));

	mmc_free_ext_csd(ext_csd);

	return err;
}

static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
{
	int err = 0;
@@ -2254,10 +2324,13 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
	if (err)
		goto out;

	if (mmc_can_sleep(host->card))
		err = mmc_sleep(host);
	else if (!mmc_host_is_spi(host))
	if (mmc_can_sleepawake(host)) {
		memcpy(&host->cached_ios, &host->ios, sizeof(host->cached_ios));
		mmc_cache_card_ext_csd(host);
		err = mmc_sleepawake(host, true);
	} else if (!mmc_host_is_spi(host)) {
		err = mmc_deselect_cards(host);
	}

	if (!err) {
		mmc_power_off(host);
@@ -2272,6 +2345,71 @@ out:
	return err;
}

static int mmc_partial_init(struct mmc_host *host)
{
	int err = 0;
	struct mmc_card *card = host->card;

	pr_debug("%s: %s: starting partial init\n",
		mmc_hostname(host), __func__);

	mmc_set_bus_width(host, host->cached_ios.bus_width);
	mmc_set_timing(host, host->cached_ios.timing);
	mmc_set_clock(host, host->cached_ios.clock);
	mmc_set_bus_mode(host, host->cached_ios.bus_mode);

	mmc_host_clk_hold(host);

	if (mmc_card_hs200(card) || mmc_card_hs400(card)) {
		if (card->ext_csd.strobe_support && host->ops->enhanced_strobe)
			err = host->ops->enhanced_strobe(host);
		else
			err = host->ops->execute_tuning(host,
				MMC_SEND_TUNING_BLOCK_HS200);
		if (err)
			pr_warn("%s: %s: tuning execution failed (%d)\n",
				mmc_hostname(host), __func__, err);
	}

	/*
	 * The ext_csd is read to make sure the card did not went through
	 * Power-failure during sleep period.
	 * A subset of the W/E_P, W/C_P register will be tested. In case
	 * these registers values are different from the values that were
	 * cached during suspend, we will conclude that a Power-failure occurred
	 * and will do full initialization sequence.
	 * In addition, full init sequence also transfer ext_csd before moving
	 * to CMDQ mode which has a side affect of configuring SDHCI registers
	 * which needed to be done before moving to CMDQ mode. The same
	 * registers need to be configured for partial init.
	 */
	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);
		goto out;
	}
	pr_debug("%s: %s: reading and comparing ext_csd successful\n",
		mmc_hostname(host), __func__);

	if (card->ext_csd.cmdq_support && (card->host->caps2 &
					   MMC_CAP2_CMD_QUEUE)) {
		err = mmc_select_cmdq(card);
		if (err) {
			pr_warn("%s: %s: enabling CMDQ mode failed (%d)\n",
					mmc_hostname(card->host),
					__func__, err);
		}
	}
out:
	mmc_host_clk_release(host);

	pr_debug("%s: %s: done partial init (%d)\n",
		mmc_hostname(host), __func__, err);

	return err;
}

/*
 * Suspend callback
 */
@@ -2297,7 +2435,7 @@ static int mmc_suspend(struct mmc_host *host)
 */
static int _mmc_resume(struct mmc_host *host)
{
	int err = 0;
	int err = -ENOSYS;
	int retries;

	BUG_ON(!host);
@@ -2313,7 +2451,18 @@ static int _mmc_resume(struct mmc_host *host)
	mmc_power_up(host, host->card->ocr);
	retries = 3;
	while (retries) {
		if (mmc_can_sleepawake(host)) {
			err = mmc_sleepawake(host, false);
			if (!err)
				err = mmc_partial_init(host);
			if (err)
				pr_err("%s: %s: awake failed (%d), fallback to full init\n",
					mmc_hostname(host), __func__, err);
		}

		if (err)
			err = mmc_init_card(host, host->card->ocr, host->card);

		if (err) {
			pr_err("%s: MMC card re-init failed rc = %d (retries = %d)\n",
			       mmc_hostname(host), err, retries);
+4 −0
Original line number Diff line number Diff line
@@ -93,12 +93,16 @@ 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 */
	u8			raw_exception_status;	/* 54 */
	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 */
#define MMC_STROBE_SUPPORT	(1 << 0)
	u8			raw_ext_csd_hs_timing;	/* 185 */
	u8			raw_ext_csd_structure;	/* 194 */
	u8			raw_card_type;		/* 196 */
	u8			raw_drive_strength;	/* 197 */
+3 −0
Original line number Diff line number Diff line
@@ -433,6 +433,8 @@ struct mmc_host {
#define MMC_CAP2_NONHOTPLUG	(1 << 25)	/*Don't support hotplug*/
#define MMC_CAP2_CMD_QUEUE	(1 << 26)	/* support eMMC command queue */
#define MMC_CAP2_SANITIZE       (1 << 27)               /* Support Sanitize */
#define MMC_CAP2_SLEEP_AWAKE	(1 << 28)	/* Use Sleep/Awake (CMD5) */

	mmc_pm_flag_t		pm_caps;	/* supported pm features */

#ifdef CONFIG_MMC_CLKGATE
@@ -460,6 +462,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;