diff options
Diffstat (limited to 'drivers/mmc/core/sd.c')
| -rw-r--r-- | drivers/mmc/core/sd.c | 206 | 
1 files changed, 94 insertions, 112 deletions
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 5e8823dc3ef..0c44510bf71 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -11,8 +11,10 @@   */  #include <linux/err.h> +#include <linux/sizes.h>  #include <linux/slab.h>  #include <linux/stat.h> +#include <linux/pm_runtime.h>  #include <linux/mmc/host.h>  #include <linux/mmc/card.h> @@ -44,6 +46,13 @@ static const unsigned int tacc_mant[] = {  	35,	40,	45,	50,	55,	60,	70,	80,  }; +static const unsigned int sd_au_size[] = { +	0,		SZ_16K / 512,		SZ_32K / 512,	SZ_64K / 512, +	SZ_128K / 512,	SZ_256K / 512,		SZ_512K / 512,	SZ_1M / 512, +	SZ_2M / 512,	SZ_4M / 512,		SZ_8M / 512,	(SZ_8M + SZ_4M) / 512, +	SZ_16M / 512,	(SZ_16M + SZ_8M) / 512,	SZ_32M / 512,	SZ_64M / 512, +}; +  #define UNSTUFF_BITS(resp,start,size)					\  	({								\  		const int __size = size;				\ @@ -215,7 +224,7 @@ static int mmc_decode_scr(struct mmc_card *card)  static int mmc_read_ssr(struct mmc_card *card)  {  	unsigned int au, es, et, eo; -	int err, i, max_au; +	int err, i;  	u32 *ssr;  	if (!(card->csd.cmdclass & CCC_APP_SPEC)) { @@ -239,26 +248,25 @@ static int mmc_read_ssr(struct mmc_card *card)  	for (i = 0; i < 16; i++)  		ssr[i] = be32_to_cpu(ssr[i]); -	/* SD3.0 increases max AU size to 64MB (0xF) from 4MB (0x9) */ -	max_au = card->scr.sda_spec3 ? 0xF : 0x9; -  	/*  	 * UNSTUFF_BITS only works with four u32s so we have to offset the  	 * bitfield positions accordingly.  	 */  	au = UNSTUFF_BITS(ssr, 428 - 384, 4); -	if (au > 0 && au <= max_au) { -		card->ssr.au = 1 << (au + 4); -		es = UNSTUFF_BITS(ssr, 408 - 384, 16); -		et = UNSTUFF_BITS(ssr, 402 - 384, 6); -		eo = UNSTUFF_BITS(ssr, 400 - 384, 2); -		if (es && et) { -			card->ssr.erase_timeout = (et * 1000) / es; -			card->ssr.erase_offset = eo * 1000; +	if (au) { +		if (au <= 9 || card->scr.sda_spec3) { +			card->ssr.au = sd_au_size[au]; +			es = UNSTUFF_BITS(ssr, 408 - 384, 16); +			et = UNSTUFF_BITS(ssr, 402 - 384, 6); +			if (es && et) { +				eo = UNSTUFF_BITS(ssr, 400 - 384, 2); +				card->ssr.erase_timeout = (et * 1000) / es; +				card->ssr.erase_offset = eo * 1000; +			} +		} else { +			pr_warning("%s: SD Status: Invalid Allocation Unit size.\n", +				   mmc_hostname(card->host));  		} -	} else { -		pr_warning("%s: SD Status: Invalid Allocation Unit " -			"size.\n", mmc_hostname(card->host));  	}  out:  	kfree(ssr); @@ -699,18 +707,10 @@ static struct attribute *sd_std_attrs[] = {  	&dev_attr_serial.attr,  	NULL,  }; - -static struct attribute_group sd_std_attr_group = { -	.attrs = sd_std_attrs, -}; - -static const struct attribute_group *sd_attr_groups[] = { -	&sd_std_attr_group, -	NULL, -}; +ATTRIBUTE_GROUPS(sd_std);  struct device_type sd_type = { -	.groups = sd_attr_groups, +	.groups = sd_std_groups,  };  /* @@ -721,6 +721,7 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr)  	int err;  	u32 max_current;  	int retries = 10; +	u32 pocr = ocr;  try_again:  	if (!retries) { @@ -773,7 +774,8 @@ try_again:  	 */  	if (!mmc_host_is_spi(host) && rocr &&  	   ((*rocr & 0x41000000) == 0x41000000)) { -		err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180); +		err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180, +					pocr);  		if (err == -EAGAIN) {  			retries--;  			goto try_again; @@ -885,7 +887,7 @@ unsigned mmc_sd_get_max_clock(struct mmc_card *card)  {  	unsigned max_dtr = (unsigned int)-1; -	if (mmc_card_highspeed(card)) { +	if (mmc_card_hs(card)) {  		if (max_dtr > card->sw_caps.hs_max_dtr)  			max_dtr = card->sw_caps.hs_max_dtr;  	} else if (max_dtr > card->csd.max_dtr) { @@ -895,12 +897,6 @@ unsigned mmc_sd_get_max_clock(struct mmc_card *card)  	return max_dtr;  } -void mmc_sd_go_highspeed(struct mmc_card *card) -{ -	mmc_card_set_highspeed(card); -	mmc_set_timing(card->host, MMC_TIMING_SD_HS); -} -  /*   * Handle the detection and initialisation of a card.   * @@ -935,6 +931,7 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,  		if (IS_ERR(card))  			return PTR_ERR(card); +		card->ocr = ocr;  		card->type = MMC_TYPE_SD;  		memcpy(card->raw_cid, cid, sizeof(card->raw_cid));  	} @@ -974,16 +971,13 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,  		err = mmc_sd_init_uhs_card(card);  		if (err)  			goto free_card; - -		/* Card is an ultra-high-speed card */ -		mmc_card_set_uhs(card);  	} else {  		/*  		 * Attempt to change to high-speed (if supported)  		 */  		err = mmc_sd_switch_hs(card);  		if (err > 0) -			mmc_sd_go_highspeed(card); +			mmc_set_timing(card->host, MMC_TIMING_SD_HS);  		else if (err)  			goto free_card; @@ -1064,10 +1058,7 @@ static void mmc_sd_detect(struct mmc_host *host)  	}  } -/* - * Suspend callback from host. - */ -static int mmc_sd_suspend(struct mmc_host *host) +static int _mmc_sd_suspend(struct mmc_host *host)  {  	int err = 0; @@ -1075,34 +1066,77 @@ static int mmc_sd_suspend(struct mmc_host *host)  	BUG_ON(!host->card);  	mmc_claim_host(host); + +	if (mmc_card_suspended(host->card)) +		goto out; +  	if (!mmc_host_is_spi(host))  		err = mmc_deselect_cards(host); -	host->card->state &= ~MMC_STATE_HIGHSPEED; -	if (!err) + +	if (!err) {  		mmc_power_off(host); +		mmc_card_set_suspended(host->card); +	} + +out:  	mmc_release_host(host); +	return err; +} + +/* + * Callback for suspend + */ +static int mmc_sd_suspend(struct mmc_host *host) +{ +	int err; + +	err = _mmc_sd_suspend(host); +	if (!err) { +		pm_runtime_disable(&host->card->dev); +		pm_runtime_set_suspended(&host->card->dev); +	}  	return err;  }  /* - * Resume callback from host. - *   * This function tries to determine if the same card is still present   * and, if so, restore all state to it.   */ -static int mmc_sd_resume(struct mmc_host *host) +static int _mmc_sd_resume(struct mmc_host *host)  { -	int err; +	int err = 0;  	BUG_ON(!host);  	BUG_ON(!host->card);  	mmc_claim_host(host); -	mmc_power_up(host); -	mmc_select_voltage(host, host->ocr); -	err = mmc_sd_init_card(host, host->ocr, host->card); + +	if (!mmc_card_suspended(host->card)) +		goto out; + +	mmc_power_up(host, host->card->ocr); +	err = mmc_sd_init_card(host, host->card->ocr, host->card); +	mmc_card_clr_suspended(host->card); + +out:  	mmc_release_host(host); +	return err; +} + +/* + * Callback for resume + */ +static int mmc_sd_resume(struct mmc_host *host) +{ +	int err = 0; + +	if (!(host->caps & MMC_CAP_RUNTIME_RESUME)) { +		err = _mmc_sd_resume(host); +		pm_runtime_set_active(&host->card->dev); +		pm_runtime_mark_last_busy(&host->card->dev); +	} +	pm_runtime_enable(&host->card->dev);  	return err;  } @@ -1117,18 +1151,11 @@ static int mmc_sd_runtime_suspend(struct mmc_host *host)  	if (!(host->caps & MMC_CAP_AGGRESSIVE_PM))  		return 0; -	mmc_claim_host(host); - -	err = mmc_sd_suspend(host); -	if (err) { +	err = _mmc_sd_suspend(host); +	if (err)  		pr_err("%s: error %d doing aggessive suspend\n",  			mmc_hostname(host), err); -		goto out; -	} -	mmc_power_off(host); -out: -	mmc_release_host(host);  	return err;  } @@ -1139,18 +1166,14 @@ static int mmc_sd_runtime_resume(struct mmc_host *host)  {  	int err; -	if (!(host->caps & MMC_CAP_AGGRESSIVE_PM)) +	if (!(host->caps & (MMC_CAP_AGGRESSIVE_PM | MMC_CAP_RUNTIME_RESUME)))  		return 0; -	mmc_claim_host(host); - -	mmc_power_up(host); -	err = mmc_sd_resume(host); +	err = _mmc_sd_resume(host);  	if (err)  		pr_err("%s: error %d doing aggessive resume\n",  			mmc_hostname(host), err); -	mmc_release_host(host);  	return 0;  } @@ -1158,9 +1181,8 @@ static int mmc_sd_power_restore(struct mmc_host *host)  {  	int ret; -	host->card->state &= ~MMC_STATE_HIGHSPEED;  	mmc_claim_host(host); -	ret = mmc_sd_init_card(host, host->ocr, host->card); +	ret = mmc_sd_init_card(host, host->card->ocr, host->card);  	mmc_release_host(host);  	return ret; @@ -1169,16 +1191,6 @@ static int mmc_sd_power_restore(struct mmc_host *host)  static const struct mmc_bus_ops mmc_sd_ops = {  	.remove = mmc_sd_remove,  	.detect = mmc_sd_detect, -	.suspend = NULL, -	.resume = NULL, -	.power_restore = mmc_sd_power_restore, -	.alive = mmc_sd_alive, -	.shutdown = mmc_sd_suspend, -}; - -static const struct mmc_bus_ops mmc_sd_ops_unsafe = { -	.remove = mmc_sd_remove, -	.detect = mmc_sd_detect,  	.runtime_suspend = mmc_sd_runtime_suspend,  	.runtime_resume = mmc_sd_runtime_resume,  	.suspend = mmc_sd_suspend, @@ -1188,24 +1200,13 @@ static const struct mmc_bus_ops mmc_sd_ops_unsafe = {  	.shutdown = mmc_sd_suspend,  }; -static void mmc_sd_attach_bus_ops(struct mmc_host *host) -{ -	const struct mmc_bus_ops *bus_ops; - -	if (!mmc_card_is_removable(host)) -		bus_ops = &mmc_sd_ops_unsafe; -	else -		bus_ops = &mmc_sd_ops; -	mmc_attach_bus(host, bus_ops); -} -  /*   * Starting point for SD card init.   */  int mmc_attach_sd(struct mmc_host *host)  {  	int err; -	u32 ocr; +	u32 ocr, rocr;  	BUG_ON(!host);  	WARN_ON(!host->claimed); @@ -1214,7 +1215,7 @@ int mmc_attach_sd(struct mmc_host *host)  	if (err)  		return err; -	mmc_sd_attach_bus_ops(host); +	mmc_attach_bus(host, &mmc_sd_ops);  	if (host->ocr_avail_sd)  		host->ocr_avail = host->ocr_avail_sd; @@ -1229,31 +1230,12 @@ int mmc_attach_sd(struct mmc_host *host)  			goto err;  	} -	/* -	 * Sanity check the voltages that the card claims to -	 * support. -	 */ -	if (ocr & 0x7F) { -		pr_warning("%s: card claims to support voltages " -		       "below the defined range. These will be ignored.\n", -		       mmc_hostname(host)); -		ocr &= ~0x7F; -	} - -	if ((ocr & MMC_VDD_165_195) && -	    !(host->ocr_avail_sd & MMC_VDD_165_195)) { -		pr_warning("%s: SD card claims to support the " -		       "incompletely defined 'low voltage range'. This " -		       "will be ignored.\n", mmc_hostname(host)); -		ocr &= ~MMC_VDD_165_195; -	} - -	host->ocr = mmc_select_voltage(host, ocr); +	rocr = mmc_select_voltage(host, ocr);  	/*  	 * Can we support the voltage(s) of the card(s)?  	 */ -	if (!host->ocr) { +	if (!rocr) {  		err = -EINVAL;  		goto err;  	} @@ -1261,7 +1243,7 @@ int mmc_attach_sd(struct mmc_host *host)  	/*  	 * Detect and init the card.  	 */ -	err = mmc_sd_init_card(host, host->ocr, NULL); +	err = mmc_sd_init_card(host, rocr, NULL);  	if (err)  		goto err;  | 
