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

Commit bec8726a authored by Girish K S's avatar Girish K S Committed by Chris Ball
Browse files

mmc: core: Add Power Off Notify Feature eMMC 4.5



This patch adds support for the power off notify feature, available in
eMMC 4.5 devices. If the host has support for this feature, then the
mmc core will notify the device by setting the POWER_OFF_NOTIFICATION
byte in the extended csd register with a value of 1 (POWER_ON).

For suspend mode short timeout is used, whereas for the normal poweroff
long timeout is used.

Signed-off-by: default avatarGirish K S <girish.shivananjappa@linaro.org>
Signed-off-by: default avatarJaehoon Chung <jh80.chung@samsung.com>
Signed-off-by: default avatarChris Ball <cjb@laptop.org>
parent 326adda5
Loading
Loading
Loading
Loading
+34 −0
Original line number Original line Diff line number Diff line
@@ -1212,11 +1212,43 @@ static void mmc_power_up(struct mmc_host *host)


void mmc_power_off(struct mmc_host *host)
void mmc_power_off(struct mmc_host *host)
{
{
	struct mmc_card *card;
	unsigned int notify_type;
	unsigned int timeout;
	int err;

	mmc_host_clk_hold(host);
	mmc_host_clk_hold(host);


	card = host->card;
	host->ios.clock = 0;
	host->ios.clock = 0;
	host->ios.vdd = 0;
	host->ios.vdd = 0;


	if (card && mmc_card_mmc(card) &&
	    (card->poweroff_notify_state == MMC_POWERED_ON)) {

		if (host->power_notify_type == MMC_HOST_PW_NOTIFY_SHORT) {
			notify_type = EXT_CSD_POWER_OFF_SHORT;
			timeout = card->ext_csd.generic_cmd6_time;
			card->poweroff_notify_state = MMC_POWEROFF_SHORT;
		} else {
			notify_type = EXT_CSD_POWER_OFF_LONG;
			timeout = card->ext_csd.power_off_longtime;
			card->poweroff_notify_state = MMC_POWEROFF_LONG;
		}

		err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
				 EXT_CSD_POWER_OFF_NOTIFICATION,
				 notify_type, timeout);

		if (err && err != -EBADMSG)
			pr_err("Device failed to respond within %d poweroff "
			       "time. Forcefully powering down the device\n",
			       timeout);

		/* Set the card state to no notification after the poweroff */
		card->poweroff_notify_state = MMC_NO_POWER_NOTIFICATION;
	}

	/*
	/*
	 * Reset ocr mask to be the highest possible voltage supported for
	 * Reset ocr mask to be the highest possible voltage supported for
	 * this mmc host. This value will be used at next power up.
	 * this mmc host. This value will be used at next power up.
@@ -2208,6 +2240,7 @@ int mmc_pm_notify(struct notifier_block *notify_block,


		spin_lock_irqsave(&host->lock, flags);
		spin_lock_irqsave(&host->lock, flags);
		host->rescan_disable = 1;
		host->rescan_disable = 1;
		host->power_notify_type = MMC_HOST_PW_NOTIFY_SHORT;
		spin_unlock_irqrestore(&host->lock, flags);
		spin_unlock_irqrestore(&host->lock, flags);
		cancel_delayed_work_sync(&host->detect);
		cancel_delayed_work_sync(&host->detect);


@@ -2231,6 +2264,7 @@ int mmc_pm_notify(struct notifier_block *notify_block,


		spin_lock_irqsave(&host->lock, flags);
		spin_lock_irqsave(&host->lock, flags);
		host->rescan_disable = 0;
		host->rescan_disable = 0;
		host->power_notify_type = MMC_HOST_PW_NOTIFY_LONG;
		spin_unlock_irqrestore(&host->lock, flags);
		spin_unlock_irqrestore(&host->lock, flags);
		mmc_detect_change(host, 0);
		mmc_detect_change(host, 0);


+21 −2
Original line number Original line Diff line number Diff line
@@ -458,10 +458,12 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
	else
	else
		card->erased_byte = 0x0;
		card->erased_byte = 0x0;


	if (card->ext_csd.rev >= 6)
	if (card->ext_csd.rev >= 6) {
		card->ext_csd.generic_cmd6_time = 10 *
		card->ext_csd.generic_cmd6_time = 10 *
			ext_csd[EXT_CSD_GENERIC_CMD6_TIME];
			ext_csd[EXT_CSD_GENERIC_CMD6_TIME];
	else
		card->ext_csd.power_off_longtime = 10 *
			ext_csd[EXT_CSD_POWER_OFF_LONG_TIME];
	} else
		card->ext_csd.generic_cmd6_time = 0;
		card->ext_csd.generic_cmd6_time = 0;


out:
out:
@@ -845,6 +847,23 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
			goto free_card;
			goto free_card;
	}
	}


	/*
	 * If the host supports the power_off_notify capability then
	 * set the notification byte in the ext_csd register of device
	 */
	if ((host->caps2 & MMC_CAP2_POWEROFF_NOTIFY) &&
	    (card->poweroff_notify_state == MMC_NO_POWER_NOTIFICATION)) {
		err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
				 EXT_CSD_POWER_OFF_NOTIFICATION,
				 EXT_CSD_POWER_ON,
				 card->ext_csd.generic_cmd6_time);
		if (err && err != -EBADMSG)
			goto free_card;
	}

	if (!err)
		card->poweroff_notify_state = MMC_POWERED_ON;

	/*
	/*
	 * Activate high speed (if supported)
	 * Activate high speed (if supported)
	 */
	 */
+9 −0
Original line number Original line Diff line number Diff line
@@ -2739,6 +2739,15 @@ int sdhci_add_host(struct sdhci_host *host)
	if (caps[1] & SDHCI_DRIVER_TYPE_D)
	if (caps[1] & SDHCI_DRIVER_TYPE_D)
		mmc->caps |= MMC_CAP_DRIVER_TYPE_D;
		mmc->caps |= MMC_CAP_DRIVER_TYPE_D;


	/*
	 * If Power Off Notify capability is enabled by the host,
	 * set notify to short power off notify timeout value.
	 */
	if (mmc->caps2 & MMC_CAP2_POWEROFF_NOTIFY)
		mmc->power_notify_type = MMC_HOST_PW_NOTIFY_SHORT;
	else
		mmc->power_notify_type = MMC_HOST_PW_NOTIFY_NONE;

	/* Initial value for re-tuning timer count */
	/* Initial value for re-tuning timer count */
	host->tuning_count = (caps[1] & SDHCI_RETUNING_TIMER_COUNT_MASK) >>
	host->tuning_count = (caps[1] & SDHCI_RETUNING_TIMER_COUNT_MASK) >>
			      SDHCI_RETUNING_TIMER_COUNT_SHIFT;
			      SDHCI_RETUNING_TIMER_COUNT_SHIFT;
+6 −0
Original line number Original line Diff line number Diff line
@@ -55,6 +55,7 @@ struct mmc_ext_csd {
	unsigned int		part_time;		/* Units: ms */
	unsigned int		part_time;		/* Units: ms */
	unsigned int		sa_timeout;		/* Units: 100ns */
	unsigned int		sa_timeout;		/* Units: 100ns */
	unsigned int		generic_cmd6_time;	/* Units: 10ms */
	unsigned int		generic_cmd6_time;	/* Units: 10ms */
	unsigned int            power_off_longtime;     /* Units: ms */
	unsigned int		hs_max_dtr;
	unsigned int		hs_max_dtr;
	unsigned int		sectors;
	unsigned int		sectors;
	unsigned int		card_type;
	unsigned int		card_type;
@@ -209,6 +210,11 @@ struct mmc_card {
#define MMC_QUIRK_BLK_NO_CMD23	(1<<7)		/* Avoid CMD23 for regular multiblock */
#define MMC_QUIRK_BLK_NO_CMD23	(1<<7)		/* Avoid CMD23 for regular multiblock */
#define MMC_QUIRK_BROKEN_BYTE_MODE_512 (1<<8)	/* Avoid sending 512 bytes in */
#define MMC_QUIRK_BROKEN_BYTE_MODE_512 (1<<8)	/* Avoid sending 512 bytes in */
						/* byte mode */
						/* byte mode */
	unsigned int    poweroff_notify_state;	/* eMMC4.5 notify feature */
#define MMC_NO_POWER_NOTIFICATION	0
#define MMC_POWERED_ON			1
#define MMC_POWEROFF_SHORT		2
#define MMC_POWEROFF_LONG		3


	unsigned int		erase_size;	/* erase size in sectors */
	unsigned int		erase_size;	/* erase size in sectors */
 	unsigned int		erase_shift;	/* if erase unit is power 2 */
 	unsigned int		erase_shift;	/* if erase unit is power 2 */
+5 −0
Original line number Original line Diff line number Diff line
@@ -239,8 +239,13 @@ struct mmc_host {
	unsigned int		caps2;		/* More host capabilities */
	unsigned int		caps2;		/* More host capabilities */


#define MMC_CAP2_BOOTPART_NOACC	(1 << 0)	/* Boot partition no access */
#define MMC_CAP2_BOOTPART_NOACC	(1 << 0)	/* Boot partition no access */
#define MMC_CAP2_POWEROFF_NOTIFY (1 << 2)	/* Notify poweroff supported */


	mmc_pm_flag_t		pm_caps;	/* supported pm features */
	mmc_pm_flag_t		pm_caps;	/* supported pm features */
	unsigned int        power_notify_type;
#define MMC_HOST_PW_NOTIFY_NONE		0
#define MMC_HOST_PW_NOTIFY_SHORT	1
#define MMC_HOST_PW_NOTIFY_LONG		2


#ifdef CONFIG_MMC_CLKGATE
#ifdef CONFIG_MMC_CLKGATE
	int			clk_requests;	/* internal reference counter */
	int			clk_requests;	/* internal reference counter */
Loading