diff options
Diffstat (limited to 'drivers/mmc/core/mmc.c')
| -rw-r--r-- | drivers/mmc/core/mmc.c | 156 | 
1 files changed, 136 insertions, 20 deletions
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index f36c76f8b232..e726903170a8 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -21,6 +21,7 @@  #include <linux/mmc/mmc.h>  #include "core.h" +#include "host.h"  #include "bus.h"  #include "mmc_ops.h"  #include "sd_ops.h" @@ -266,8 +267,10 @@ static void mmc_manage_enhanced_area(struct mmc_card *card, u8 *ext_csd)  			 * calculate the enhanced data area offset, in bytes  			 */  			card->ext_csd.enhanced_area_offset = -				(ext_csd[139] << 24) + (ext_csd[138] << 16) + -				(ext_csd[137] << 8) + ext_csd[136]; +				(((unsigned long long)ext_csd[139]) << 24) + +				(((unsigned long long)ext_csd[138]) << 16) + +				(((unsigned long long)ext_csd[137]) << 8) + +				(((unsigned long long)ext_csd[136]));  			if (mmc_card_blockaddr(card))  				card->ext_csd.enhanced_area_offset <<= 9;  			/* @@ -434,6 +437,7 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)  	card->ext_csd.raw_trim_mult =  		ext_csd[EXT_CSD_TRIM_MULT];  	card->ext_csd.raw_partition_support = ext_csd[EXT_CSD_PARTITION_SUPPORT]; +	card->ext_csd.raw_driver_strength = ext_csd[EXT_CSD_DRIVER_STRENGTH];  	if (card->ext_csd.rev >= 4) {  		if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED] &  		    EXT_CSD_PART_SETTING_COMPLETED) @@ -1040,6 +1044,7 @@ static int mmc_select_hs400(struct mmc_card *card)  {  	struct mmc_host *host = card->host;  	int err = 0; +	u8 val;  	/*  	 * HS400 mode requires 8-bit bus width @@ -1055,8 +1060,10 @@ static int mmc_select_hs400(struct mmc_card *card)  	mmc_set_timing(card->host, MMC_TIMING_MMC_HS);  	mmc_set_bus_speed(card); +	val = EXT_CSD_TIMING_HS | +	      card->drive_strength << EXT_CSD_DRV_STR_SHIFT;  	err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, -			   EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS, +			   EXT_CSD_HS_TIMING, val,  			   card->ext_csd.generic_cmd6_time,  			   true, true, true);  	if (err) { @@ -1075,8 +1082,10 @@ static int mmc_select_hs400(struct mmc_card *card)  		return err;  	} +	val = EXT_CSD_TIMING_HS400 | +	      card->drive_strength << EXT_CSD_DRV_STR_SHIFT;  	err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, -			   EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS400, +			   EXT_CSD_HS_TIMING, val,  			   card->ext_csd.generic_cmd6_time,  			   true, true, true);  	if (err) { @@ -1091,6 +1100,115 @@ static int mmc_select_hs400(struct mmc_card *card)  	return 0;  } +int mmc_hs200_to_hs400(struct mmc_card *card) +{ +	return mmc_select_hs400(card); +} + +/* Caller must hold re-tuning */ +static int mmc_switch_status(struct mmc_card *card) +{ +	u32 status; +	int err; + +	err = mmc_send_status(card, &status); +	if (err) +		return err; + +	return mmc_switch_status_error(card->host, status); +} + +int mmc_hs400_to_hs200(struct mmc_card *card) +{ +	struct mmc_host *host = card->host; +	bool send_status = true; +	unsigned int max_dtr; +	int err; +	u8 val; + +	if (host->caps & MMC_CAP_WAIT_WHILE_BUSY) +		send_status = false; + +	/* Reduce frequency to HS */ +	max_dtr = card->ext_csd.hs_max_dtr; +	mmc_set_clock(host, max_dtr); + +	/* Switch HS400 to HS DDR */ +	val = EXT_CSD_TIMING_HS | +	      card->drive_strength << EXT_CSD_DRV_STR_SHIFT; +	err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, +			   val, card->ext_csd.generic_cmd6_time, +			   true, send_status, true); +	if (err) +		goto out_err; + +	mmc_set_timing(host, MMC_TIMING_MMC_DDR52); + +	if (!send_status) { +		err = mmc_switch_status(card); +		if (err) +			goto out_err; +	} + +	/* Switch HS DDR to HS */ +	err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, +			   EXT_CSD_BUS_WIDTH_8, card->ext_csd.generic_cmd6_time, +			   true, send_status, true); +	if (err) +		goto out_err; + +	mmc_set_timing(host, MMC_TIMING_MMC_HS); + +	if (!send_status) { +		err = mmc_switch_status(card); +		if (err) +			goto out_err; +	} + +	/* Switch HS to HS200 */ +	val = EXT_CSD_TIMING_HS200 | +	      card->drive_strength << EXT_CSD_DRV_STR_SHIFT; +	err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, +			   val, card->ext_csd.generic_cmd6_time, true, +			   send_status, true); +	if (err) +		goto out_err; + +	mmc_set_timing(host, MMC_TIMING_MMC_HS200); + +	if (!send_status) { +		err = mmc_switch_status(card); +		if (err) +			goto out_err; +	} + +	mmc_set_bus_speed(card); + +	return 0; + +out_err: +	pr_err("%s: %s failed, error %d\n", mmc_hostname(card->host), +	       __func__, err); +	return err; +} + +static void mmc_select_driver_type(struct mmc_card *card) +{ +	int card_drv_type, drive_strength, drv_type; + +	card_drv_type = card->ext_csd.raw_driver_strength | +			mmc_driver_type_mask(0); + +	drive_strength = mmc_select_drive_strength(card, +						   card->ext_csd.hs200_max_dtr, +						   card_drv_type, &drv_type); + +	card->drive_strength = drive_strength; + +	if (drv_type) +		mmc_set_driver_type(card->host, drv_type); +} +  /*   * For device supporting HS200 mode, the following sequence   * should be done before executing the tuning process. @@ -1102,6 +1220,7 @@ static int mmc_select_hs200(struct mmc_card *card)  {  	struct mmc_host *host = card->host;  	int err = -EINVAL; +	u8 val;  	if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200_1_2V)  		err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120); @@ -1113,14 +1232,18 @@ static int mmc_select_hs200(struct mmc_card *card)  	if (err)  		goto err; +	mmc_select_driver_type(card); +  	/*  	 * Set the bus width(4 or 8) with host's support and  	 * switch to HS200 mode if bus width is set successfully.  	 */  	err = mmc_select_bus_width(card);  	if (!IS_ERR_VALUE(err)) { +		val = EXT_CSD_TIMING_HS200 | +		      card->drive_strength << EXT_CSD_DRV_STR_SHIFT;  		err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, -				   EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS200, +				   EXT_CSD_HS_TIMING, val,  				   card->ext_csd.generic_cmd6_time,  				   true, true, true);  		if (!err) @@ -1511,9 +1634,12 @@ static int mmc_sleep(struct mmc_host *host)  	unsigned int timeout_ms = DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000);  	int err; +	/* Re-tuning can't be done once the card is deselected */ +	mmc_retune_hold(host); +  	err = mmc_deselect_cards(host);  	if (err) -		return err; +		goto out_release;  	cmd.opcode = MMC_SLEEP_AWAKE;  	cmd.arg = card->rca << 16; @@ -1534,7 +1660,7 @@ static int mmc_sleep(struct mmc_host *host)  	err = mmc_wait_for_cmd(host, &cmd, 0);  	if (err) -		return err; +		goto out_release;  	/*  	 * If the host does not wait while the card signals busy, then we will @@ -1545,6 +1671,8 @@ static int mmc_sleep(struct mmc_host *host)  	if (!cmd.busy_timeout || !(host->caps & MMC_CAP_WAIT_WHILE_BUSY))  		mmc_delay(timeout_ms); +out_release: +	mmc_retune_release(host);  	return err;  } @@ -1782,17 +1910,6 @@ static int mmc_runtime_resume(struct mmc_host *host)  	return 0;  } -static int mmc_power_restore(struct mmc_host *host) -{ -	int ret; - -	mmc_claim_host(host); -	ret = mmc_init_card(host, host->card->ocr, host->card); -	mmc_release_host(host); - -	return ret; -} -  int mmc_can_reset(struct mmc_card *card)  {  	u8 rst_n_function; @@ -1830,7 +1947,7 @@ static int mmc_reset(struct mmc_host *host)  	mmc_set_initial_state(host);  	mmc_host_clk_release(host); -	return mmc_power_restore(host); +	return mmc_init_card(host, card->ocr, card);  }  static const struct mmc_bus_ops mmc_ops = { @@ -1840,7 +1957,6 @@ static const struct mmc_bus_ops mmc_ops = {  	.resume = mmc_resume,  	.runtime_suspend = mmc_runtime_suspend,  	.runtime_resume = mmc_runtime_resume, -	.power_restore = mmc_power_restore,  	.alive = mmc_alive,  	.shutdown = mmc_shutdown,  	.reset = mmc_reset,  |