diff options
Diffstat (limited to 'drivers/mmc/core/mmc_ops.c')
| -rw-r--r-- | drivers/mmc/core/mmc_ops.c | 142 | 
1 files changed, 95 insertions, 47 deletions
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index ef183483d5b..f51b5ba3bbe 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -23,6 +23,40 @@  #define MMC_OPS_TIMEOUT_MS	(10 * 60 * 1000) /* 10 minute timeout */ +static inline int __mmc_send_status(struct mmc_card *card, u32 *status, +				    bool ignore_crc) +{ +	int err; +	struct mmc_command cmd = {0}; + +	BUG_ON(!card); +	BUG_ON(!card->host); + +	cmd.opcode = MMC_SEND_STATUS; +	if (!mmc_host_is_spi(card->host)) +		cmd.arg = card->rca << 16; +	cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC; +	if (ignore_crc) +		cmd.flags &= ~MMC_RSP_CRC; + +	err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); +	if (err) +		return err; + +	/* NOTE: callers are required to understand the difference +	 * between "native" and SPI format status words! +	 */ +	if (status) +		*status = cmd.resp[0]; + +	return 0; +} + +int mmc_send_status(struct mmc_card *card, u32 *status) +{ +	return __mmc_send_status(card, status, false); +} +  static int _mmc_select_card(struct mmc_host *host, struct mmc_card *card)  {  	int err; @@ -370,19 +404,31 @@ int mmc_spi_set_crc(struct mmc_host *host, int use_crc)   *	@timeout_ms: timeout (ms) for operation performed by register write,   *                   timeout of zero implies maximum possible timeout   *	@use_busy_signal: use the busy signal as response type + *	@send_status: send status cmd to poll for busy + *	@ignore_crc: ignore CRC errors when sending status cmd to poll for busy   *   *	Modifies the EXT_CSD register for selected card.   */  int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, -	       unsigned int timeout_ms, bool use_busy_signal) +		unsigned int timeout_ms, bool use_busy_signal, bool send_status, +		bool ignore_crc)  { +	struct mmc_host *host = card->host;  	int err;  	struct mmc_command cmd = {0};  	unsigned long timeout; -	u32 status; +	u32 status = 0; +	bool use_r1b_resp = use_busy_signal; -	BUG_ON(!card); -	BUG_ON(!card->host); +	/* +	 * If the cmd timeout and the max_busy_timeout of the host are both +	 * specified, let's validate them. A failure means we need to prevent +	 * the host from doing hw busy detection, which is done by converting +	 * to a R1 response instead of a R1B. +	 */ +	if (timeout_ms && host->max_busy_timeout && +		(timeout_ms > host->max_busy_timeout)) +		use_r1b_resp = false;  	cmd.opcode = MMC_SWITCH;  	cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | @@ -390,17 +436,21 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,  		  (value << 8) |  		  set;  	cmd.flags = MMC_CMD_AC; -	if (use_busy_signal) +	if (use_r1b_resp) {  		cmd.flags |= MMC_RSP_SPI_R1B | MMC_RSP_R1B; -	else +		/* +		 * A busy_timeout of zero means the host can decide to use +		 * whatever value it finds suitable. +		 */ +		cmd.busy_timeout = timeout_ms; +	} else {  		cmd.flags |= MMC_RSP_SPI_R1 | MMC_RSP_R1; +	} - -	cmd.cmd_timeout_ms = timeout_ms;  	if (index == EXT_CSD_SANITIZE_START)  		cmd.sanitize_busy = true; -	err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); +	err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);  	if (err)  		return err; @@ -408,32 +458,55 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,  	if (!use_busy_signal)  		return 0; -	/* Must check status to be sure of no errors */ -	timeout = jiffies + msecs_to_jiffies(MMC_OPS_TIMEOUT_MS); +	/* +	 * CRC errors shall only be ignored in cases were CMD13 is used to poll +	 * to detect busy completion. +	 */ +	if ((host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp) +		ignore_crc = false; + +	/* We have an unspecified cmd timeout, use the fallback value. */ +	if (!timeout_ms) +		timeout_ms = MMC_OPS_TIMEOUT_MS; + +	/* Must check status to be sure of no errors. */ +	timeout = jiffies + msecs_to_jiffies(timeout_ms);  	do { -		err = mmc_send_status(card, &status); -		if (err) -			return err; -		if (card->host->caps & MMC_CAP_WAIT_WHILE_BUSY) +		if (send_status) { +			err = __mmc_send_status(card, &status, ignore_crc); +			if (err) +				return err; +		} +		if ((host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp)  			break; -		if (mmc_host_is_spi(card->host)) +		if (mmc_host_is_spi(host))  			break; +		/* +		 * We are not allowed to issue a status command and the host +		 * does'nt support MMC_CAP_WAIT_WHILE_BUSY, then we can only +		 * rely on waiting for the stated timeout to be sufficient. +		 */ +		if (!send_status) { +			mmc_delay(timeout_ms); +			return 0; +		} +  		/* Timeout if the device never leaves the program state. */  		if (time_after(jiffies, timeout)) {  			pr_err("%s: Card stuck in programming state! %s\n", -				mmc_hostname(card->host), __func__); +				mmc_hostname(host), __func__);  			return -ETIMEDOUT;  		}  	} while (R1_CURRENT_STATE(status) == R1_STATE_PRG); -	if (mmc_host_is_spi(card->host)) { +	if (mmc_host_is_spi(host)) {  		if (status & R1_SPI_ILLEGAL_COMMAND)  			return -EBADMSG;  	} else {  		if (status & 0xFDFFA000) -			pr_warning("%s: unexpected status %#x after " -			       "switch", mmc_hostname(card->host), status); +			pr_warn("%s: unexpected status %#x after switch\n", +				mmc_hostname(host), status);  		if (status & R1_SWITCH_ERROR)  			return -EBADMSG;  	} @@ -445,36 +518,11 @@ EXPORT_SYMBOL_GPL(__mmc_switch);  int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,  		unsigned int timeout_ms)  { -	return __mmc_switch(card, set, index, value, timeout_ms, true); +	return __mmc_switch(card, set, index, value, timeout_ms, true, true, +				false);  }  EXPORT_SYMBOL_GPL(mmc_switch); -int mmc_send_status(struct mmc_card *card, u32 *status) -{ -	int err; -	struct mmc_command cmd = {0}; - -	BUG_ON(!card); -	BUG_ON(!card->host); - -	cmd.opcode = MMC_SEND_STATUS; -	if (!mmc_host_is_spi(card->host)) -		cmd.arg = card->rca << 16; -	cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC; - -	err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); -	if (err) -		return err; - -	/* NOTE: callers are required to understand the difference -	 * between "native" and SPI format status words! -	 */ -	if (status) -		*status = cmd.resp[0]; - -	return 0; -} -  static int  mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode,  		  u8 len)  | 
