diff options
| author | Che-Liang Chiou <clchiou@chromium.org> | 2012-11-28 15:21:13 +0000 | 
|---|---|---|
| committer | Andy Fleming <afleming@freescale.com> | 2013-05-06 16:12:38 -0500 | 
| commit | e95504497ecac46907204b0ee3460b708a2981ac (patch) | |
| tree | c59e230a6096b5883c83d3399717a3408f601355 /drivers/mmc/mmc.c | |
| parent | 5ed6f447af60aabd2669d913f673793c1ce48f47 (diff) | |
| download | olio-uboot-2014.01-e95504497ecac46907204b0ee3460b708a2981ac.tar.xz olio-uboot-2014.01-e95504497ecac46907204b0ee3460b708a2981ac.zip | |
mmc: Split device init to decouple OCR-polling delay
Most of time that MMC driver spends on initializing a device is polling
OCR (operation conditions register).  To decouple this polling loop,
device init is split into two parts: The first part fires the OCR query
command, and the second part polls the result.  So the caller is now no
longer bound to the OCR-polling delay; he may fire the query, go
somewhere and then come back later for the result.
To use this, call mmc_set_preinit() on any device which needs this.
This can save significant amounts of time on boot (e.g. 200ms) by
hiding the MMC init time behind other init.
Signed-off-by: Che-Liang Chiou <clchiou@chromium.org>
Signed-off-by: Simon Glass <sjg@chromium.org>
Acked-by: Jaehoon Chung <jh80.chung@samsung.com>
Signed-off-by: Andy Fleming <afleming@freescale.com>
Diffstat (limited to 'drivers/mmc/mmc.c')
| -rw-r--r-- | drivers/mmc/mmc.c | 137 | 
1 files changed, 105 insertions, 32 deletions
| diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 2590f1bcc..0a2f5358e 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -524,48 +524,70 @@ static int sd_send_op_cond(struct mmc *mmc)  	return 0;  } -static int mmc_send_op_cond(struct mmc *mmc) +/* We pass in the cmd since otherwise the init seems to fail */ +static int mmc_send_op_cond_iter(struct mmc *mmc, struct mmc_cmd *cmd, +		int use_arg)  { -	int timeout = 10000; -	struct mmc_cmd cmd;  	int err; +	cmd->cmdidx = MMC_CMD_SEND_OP_COND; +	cmd->resp_type = MMC_RSP_R3; +	cmd->cmdarg = 0; +	if (use_arg && !mmc_host_is_spi(mmc)) { +		cmd->cmdarg = +			(mmc->voltages & +			(mmc->op_cond_response & OCR_VOLTAGE_MASK)) | +			(mmc->op_cond_response & OCR_ACCESS_MODE); + +		if (mmc->host_caps & MMC_MODE_HC) +			cmd->cmdarg |= OCR_HCS; +	} +	err = mmc_send_cmd(mmc, cmd, NULL); +	if (err) +		return err; +	mmc->op_cond_response = cmd->response[0]; +	return 0; +} + +int mmc_send_op_cond(struct mmc *mmc) +{ +	struct mmc_cmd cmd; +	int err, i; +  	/* Some cards seem to need this */  	mmc_go_idle(mmc);   	/* Asking to the card its capabilities */ - 	cmd.cmdidx = MMC_CMD_SEND_OP_COND; - 	cmd.resp_type = MMC_RSP_R3; - 	cmd.cmdarg = 0; - - 	err = mmc_send_cmd(mmc, &cmd, NULL); +	mmc->op_cond_pending = 1; +	for (i = 0; i < 2; i++) { +		err = mmc_send_op_cond_iter(mmc, &cmd, i != 0); +		if (err) +			return err; - 	if (err) - 		return err; +		/* exit if not busy (flag seems to be inverted) */ +		if (mmc->op_cond_response & OCR_BUSY) +			return 0; +	} +	return IN_PROGRESS; +} - 	udelay(1000); +int mmc_complete_op_cond(struct mmc *mmc) +{ +	struct mmc_cmd cmd; +	int timeout = 1000; +	uint start; +	int err; +	mmc->op_cond_pending = 0; +	start = get_timer(0);  	do { -		cmd.cmdidx = MMC_CMD_SEND_OP_COND; -		cmd.resp_type = MMC_RSP_R3; -		cmd.cmdarg = (mmc_host_is_spi(mmc) ? 0 : -				(mmc->voltages & -				(cmd.response[0] & OCR_VOLTAGE_MASK)) | -				(cmd.response[0] & OCR_ACCESS_MODE)); - -		if (mmc->host_caps & MMC_MODE_HC) -			cmd.cmdarg |= OCR_HCS; - -		err = mmc_send_cmd(mmc, &cmd, NULL); - +		err = mmc_send_op_cond_iter(mmc, &cmd, 1);  		if (err)  			return err; - -		udelay(1000); -	} while (!(cmd.response[0] & OCR_BUSY) && timeout--); - -	if (timeout <= 0) -		return UNUSABLE_ERR; +		if (get_timer(start) > timeout) +			return UNUSABLE_ERR; +		udelay(100); +	} while (!(mmc->op_cond_response & OCR_BUSY));  	if (mmc_host_is_spi(mmc)) { /* read OCR for spi */  		cmd.cmdidx = MMC_CMD_SPI_READ_OCR; @@ -1274,7 +1296,7 @@ block_dev_desc_t *mmc_get_dev(int dev)  }  #endif -int mmc_init(struct mmc *mmc) +int mmc_start_init(struct mmc *mmc)  {  	int err; @@ -1314,17 +1336,48 @@ int mmc_init(struct mmc *mmc)  	if (err == TIMEOUT) {  		err = mmc_send_op_cond(mmc); -		if (err) { +		if (err && err != IN_PROGRESS) {  			printf("Card did not respond to voltage select!\n");  			return UNUSABLE_ERR;  		}  	} -	err = mmc_startup(mmc); +	if (err == IN_PROGRESS) +		mmc->init_in_progress = 1; + +	return err; +} + +static int mmc_complete_init(struct mmc *mmc) +{ +	int err = 0; + +	if (mmc->op_cond_pending) +		err = mmc_complete_op_cond(mmc); + +	if (!err) +		err = mmc_startup(mmc);  	if (err)  		mmc->has_init = 0;  	else  		mmc->has_init = 1; +	mmc->init_in_progress = 0; +	return err; +} + +int mmc_init(struct mmc *mmc) +{ +	int err = IN_PROGRESS; +	unsigned start = get_timer(0); + +	if (mmc->has_init) +		return 0; +	if (!mmc->init_in_progress) +		err = mmc_start_init(mmc); + +	if (!err || err == IN_PROGRESS) +		err = mmc_complete_init(mmc); +	debug("%s: %d, time %lu\n", __func__, err, get_timer(start));  	return err;  } @@ -1362,6 +1415,25 @@ int get_mmc_num(void)  	return cur_dev_num;  } +void mmc_set_preinit(struct mmc *mmc, int preinit) +{ +	mmc->preinit = preinit; +} + +static void do_preinit(void) +{ +	struct mmc *m; +	struct list_head *entry; + +	list_for_each(entry, &mmc_devices) { +		m = list_entry(entry, struct mmc, link); + +		if (m->preinit) +			mmc_start_init(m); +	} +} + +  int mmc_initialize(bd_t *bis)  {  	INIT_LIST_HEAD (&mmc_devices); @@ -1372,5 +1444,6 @@ int mmc_initialize(bd_t *bis)  	print_mmc_devices(','); +	do_preinit();  	return 0;  } |