diff options
Diffstat (limited to 'drivers/mmc/core/core.c')
| -rw-r--r-- | drivers/mmc/core/core.c | 98 | 
1 files changed, 64 insertions, 34 deletions
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 5278ffb20e7..950b97d7412 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -529,6 +529,18 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card)  			data->timeout_clks = 0;  		}  	} + +	/* +	 * Some cards require longer data read timeout than indicated in CSD. +	 * Address this by setting the read timeout to a "reasonably high" +	 * value. For the cards tested, 300ms has proven enough. If necessary, +	 * this value can be increased if other problematic cards require this. +	 */ +	if (mmc_card_long_read_time(card) && data->flags & MMC_DATA_READ) { +		data->timeout_ns = 300000000; +		data->timeout_clks = 0; +	} +  	/*  	 * Some cards need very high timeouts if driven in SPI mode.  	 * The worst observed timeout was 900ms after writing a @@ -1213,6 +1225,46 @@ void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type)  	mmc_host_clk_release(host);  } +static void mmc_poweroff_notify(struct mmc_host *host) +{ +	struct mmc_card *card; +	unsigned int timeout; +	unsigned int notify_type = EXT_CSD_NO_POWER_NOTIFICATION; +	int err = 0; + +	card = host->card; + +	/* +	 * Send power notify command only if card +	 * is mmc and notify state is powered ON +	 */ +	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; +	} +} +  /*   * Apply power to the MMC stack.  This is a two-stage process.   * First, we enable power to the card without the clock running. @@ -1269,42 +1321,12 @@ static void mmc_power_up(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); -	card = host->card;  	host->ios.clock = 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; -	} +	mmc_poweroff_notify(host);  	/*  	 * Reset ocr mask to be the highest possible voltage supported for @@ -2196,7 +2218,7 @@ int mmc_card_sleep(struct mmc_host *host)  	mmc_bus_get(host); -	if (host->bus_ops && !host->bus_dead && host->bus_ops->awake) +	if (host->bus_ops && !host->bus_dead && host->bus_ops->sleep)  		err = host->bus_ops->sleep(host);  	mmc_bus_put(host); @@ -2302,8 +2324,17 @@ int mmc_suspend_host(struct mmc_host *host)  		 * pre-claim the host.  		 */  		if (mmc_try_claim_host(host)) { -			if (host->bus_ops->suspend) +			if (host->bus_ops->suspend) { +				/* +				 * For eMMC 4.5 device send notify command +				 * before sleep, because in sleep state eMMC 4.5 +				 * devices respond to only RESET and AWAKE cmd +				 */ +				mmc_poweroff_notify(host);  				err = host->bus_ops->suspend(host); +			} +			mmc_do_release_host(host); +  			if (err == -ENOSYS || !host->bus_ops->resume) {  				/*  				 * We simply "remove" the card in this case. @@ -2318,7 +2349,6 @@ int mmc_suspend_host(struct mmc_host *host)  				host->pm_flags = 0;  				err = 0;  			} -			mmc_do_release_host(host);  		} else {  			err = -EBUSY;  		}  |