diff mbox

[U-Boot,v2] mmc: Split device init to decouple OCR-polling delay

Message ID 1354152073-26592-1-git-send-email-sjg@chromium.org
State Accepted, archived
Delegated to: Andy Fleming
Headers show

Commit Message

Simon Glass Nov. 29, 2012, 1:21 a.m. UTC
From: Che-Liang Chiou <clchiou@chromium.org>

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>
---
Changes in v2:
- Rebase to master

 drivers/mmc/mmc.c |  137 ++++++++++++++++++++++++++++++++++++++++------------
 include/mmc.h     |   30 ++++++++++++
 2 files changed, 135 insertions(+), 32 deletions(-)

Comments

Jaehoon Chung Nov. 29, 2012, 3:20 p.m. UTC | #1
Hi Simon,

Is it saved the 200ms? Could you tell me your environment?
I will check this patch..and share the result.

Best Regards,
Jaehoon Chung

2012/11/29 Simon Glass <sjg@chromium.org>:
> From: Che-Liang Chiou <clchiou@chromium.org>
>
> 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>
> ---
> Changes in v2:
> - Rebase to master
>
>  drivers/mmc/mmc.c |  137 ++++++++++++++++++++++++++++++++++++++++------------
>  include/mmc.h     |   30 ++++++++++++
>  2 files changed, 135 insertions(+), 32 deletions(-)
>
> diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
> index 72e8ce6..09695e8 100644
> --- a/drivers/mmc/mmc.c
> +++ b/drivers/mmc/mmc.c
> @@ -503,48 +503,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;
> @@ -1227,7 +1249,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;
>
> @@ -1267,17 +1289,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;
>  }
>
> @@ -1315,6 +1368,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);
> @@ -1325,5 +1397,6 @@ int mmc_initialize(bd_t *bis)
>
>         print_mmc_devices(',');
>
> +       do_preinit();
>         return 0;
>  }
> diff --git a/include/mmc.h b/include/mmc.h
> index a13e2bd..445d714 100644
> --- a/include/mmc.h
> +++ b/include/mmc.h
> @@ -62,6 +62,7 @@
>  #define UNUSABLE_ERR           -17 /* Unusable Card */
>  #define COMM_ERR               -18 /* Communications Error */
>  #define TIMEOUT                        -19
> +#define IN_PROGRESS            -20 /* operation is in progress */
>
>  #define MMC_CMD_GO_IDLE_STATE          0
>  #define MMC_CMD_SEND_OP_COND           1
> @@ -260,6 +261,10 @@ struct mmc {
>         int (*init)(struct mmc *mmc);
>         int (*getcd)(struct mmc *mmc);
>         uint b_max;
> +       char op_cond_pending;   /* 1 if we are waiting on an op_cond command */
> +       char init_in_progress;  /* 1 if we have done mmc_start_init() */
> +       char preinit;           /* start init as early as possible */
> +       uint op_cond_response;  /* the response byte from the last op_cond */
>  };
>
>  int mmc_register(struct mmc *mmc);
> @@ -276,6 +281,31 @@ int mmc_switch_part(int dev_num, unsigned int part_num);
>  int mmc_getcd(struct mmc *mmc);
>  void spl_mmc_load(void) __noreturn;
>
> +/**
> + * Start device initialization and return immediately; it does not block on
> + * polling OCR (operation condition register) status.  Then you should call
> + * mmc_init, which would block on polling OCR status and complete the device
> + * initializatin.
> + *
> + * @param mmc  Pointer to a MMC device struct
> + * @return 0 on success, IN_PROGRESS on waiting for OCR status, <0 on error.
> + */
> +int mmc_start_init(struct mmc *mmc);
> +
> +/**
> + * Set preinit flag of mmc device.
> + *
> + * This will cause the device to be pre-inited during mmc_initialize(),
> + * which may save boot time if the device is not accessed until later.
> + * Some eMMC devices take 200-300ms to init, but unfortunately they
> + * must be sent a series of commands to even get them to start preparing
> + * for operation.
> + *
> + * @param mmc          Pointer to a MMC device struct
> + * @param preinit      preinit flag value
> + */
> +void mmc_set_preinit(struct mmc *mmc, int preinit);
> +
>  #ifdef CONFIG_GENERIC_MMC
>  #define mmc_host_is_spi(mmc)   ((mmc)->host_caps & MMC_MODE_SPI)
>  struct mmc *mmc_spi_init(uint bus, uint cs, uint speed, uint mode);
> --
> 1.7.7.3
>
> _______________________________________________
> U-Boot mailing list
> U-Boot@lists.denx.de
> http://lists.denx.de/mailman/listinfo/u-boot
Jaehoon Chung Nov. 30, 2012, 8:25 a.m. UTC | #2
Hi,

This concept is very good.
But I have one question. I think need to call mmc_init() one more, right?
how did you save the boot time(200ms)?

On 11/29/2012 10:21 AM, Simon Glass wrote:
> From: Che-Liang Chiou <clchiou@chromium.org>
> 
> 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.
snip..
> +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);
It need not to return? if err is IN_PROGRESS, next condition is immediately run.
Then i think we didn't save the time before adjust this patch.
> +
> +	if (!err || err == IN_PROGRESS)
> +		err = mmc_complete_init(mmc);
> +	debug("%s: %d, time %lu\n", __func__, err, get_timer(start));
>  	return err;
>  }
>  
> @@ -1315,6 +1368,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);
> @@ -1325,5 +1397,6 @@ int mmc_initialize(bd_t *bis)
>  
>  	print_mmc_devices(',');
>  
> +	do_preinit();
>  	return 0;
>  }
> diff --git a/include/mmc.h b/include/mmc.h
> index a13e2bd..445d714 100644
> --- a/include/mmc.h
> +++ b/include/mmc.h
> @@ -62,6 +62,7 @@
>  #define UNUSABLE_ERR		-17 /* Unusable Card */
>  #define COMM_ERR		-18 /* Communications Error */
>  #define TIMEOUT			-19
> +#define IN_PROGRESS		-20 /* operation is in progress */
>  
>  #define MMC_CMD_GO_IDLE_STATE		0
>  #define MMC_CMD_SEND_OP_COND		1
> @@ -260,6 +261,10 @@ struct mmc {
>  	int (*init)(struct mmc *mmc);
>  	int (*getcd)(struct mmc *mmc);
>  	uint b_max;
> +	char op_cond_pending;	/* 1 if we are waiting on an op_cond command */
> +	char init_in_progress;	/* 1 if we have done mmc_start_init() */
> +	char preinit;		/* start init as early as possible */
> +	uint op_cond_response;	/* the response byte from the last op_cond */
>  };
>  
>  int mmc_register(struct mmc *mmc);
> @@ -276,6 +281,31 @@ int mmc_switch_part(int dev_num, unsigned int part_num);
>  int mmc_getcd(struct mmc *mmc);
>  void spl_mmc_load(void) __noreturn;
>  
> +/**
> + * Start device initialization and return immediately; it does not block on
> + * polling OCR (operation condition register) status.  Then you should call
> + * mmc_init, which would block on polling OCR status and complete the device
> + * initializatin.
> + *
> + * @param mmc	Pointer to a MMC device struct
> + * @return 0 on success, IN_PROGRESS on waiting for OCR status, <0 on error.
> + */
> +int mmc_start_init(struct mmc *mmc);
> +
> +/**
> + * Set preinit flag of mmc device.
> + *
> + * This will cause the device to be pre-inited during mmc_initialize(),
> + * which may save boot time if the device is not accessed until later.
> + * Some eMMC devices take 200-300ms to init, but unfortunately they
> + * must be sent a series of commands to even get them to start preparing
> + * for operation.
> + *
> + * @param mmc		Pointer to a MMC device struct
> + * @param preinit	preinit flag value
> + */
> +void mmc_set_preinit(struct mmc *mmc, int preinit);
> +
>  #ifdef CONFIG_GENERIC_MMC
>  #define mmc_host_is_spi(mmc)	((mmc)->host_caps & MMC_MODE_SPI)
>  struct mmc *mmc_spi_init(uint bus, uint cs, uint speed, uint mode);
>
Simon Glass Nov. 30, 2012, 11:05 p.m. UTC | #3
Hi,

On Thu, Nov 29, 2012 at 7:20 AM, Jae hoon Chung <jh80.chung@gmail.com> wrote:
> Hi Simon,
>
> Is it saved the 200ms? Could you tell me your environment?
> I will check this patch..and share the result.

The environment is snow (Samsung ARM Chromebook). The time save only
comes from not waiting for the MMC init. So:

1. Kick off MMC init
2. Go off and do something else for 200ms
3. Come back and MMC init should complete immediately

Regards,
Simon

>
> Best Regards,
> Jaehoon Chung
>
> 2012/11/29 Simon Glass <sjg@chromium.org>:
>> From: Che-Liang Chiou <clchiou@chromium.org>
>>
>> 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>
>> ---
>> Changes in v2:
>> - Rebase to master
>>
>>  drivers/mmc/mmc.c |  137 ++++++++++++++++++++++++++++++++++++++++------------
>>  include/mmc.h     |   30 ++++++++++++
>>  2 files changed, 135 insertions(+), 32 deletions(-)
>>
>> diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
>> index 72e8ce6..09695e8 100644
>> --- a/drivers/mmc/mmc.c
>> +++ b/drivers/mmc/mmc.c
>> @@ -503,48 +503,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;
>> @@ -1227,7 +1249,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;
>>
>> @@ -1267,17 +1289,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;
>>  }
>>
>> @@ -1315,6 +1368,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);
>> @@ -1325,5 +1397,6 @@ int mmc_initialize(bd_t *bis)
>>
>>         print_mmc_devices(',');
>>
>> +       do_preinit();
>>         return 0;
>>  }
>> diff --git a/include/mmc.h b/include/mmc.h
>> index a13e2bd..445d714 100644
>> --- a/include/mmc.h
>> +++ b/include/mmc.h
>> @@ -62,6 +62,7 @@
>>  #define UNUSABLE_ERR           -17 /* Unusable Card */
>>  #define COMM_ERR               -18 /* Communications Error */
>>  #define TIMEOUT                        -19
>> +#define IN_PROGRESS            -20 /* operation is in progress */
>>
>>  #define MMC_CMD_GO_IDLE_STATE          0
>>  #define MMC_CMD_SEND_OP_COND           1
>> @@ -260,6 +261,10 @@ struct mmc {
>>         int (*init)(struct mmc *mmc);
>>         int (*getcd)(struct mmc *mmc);
>>         uint b_max;
>> +       char op_cond_pending;   /* 1 if we are waiting on an op_cond command */
>> +       char init_in_progress;  /* 1 if we have done mmc_start_init() */
>> +       char preinit;           /* start init as early as possible */
>> +       uint op_cond_response;  /* the response byte from the last op_cond */
>>  };
>>
>>  int mmc_register(struct mmc *mmc);
>> @@ -276,6 +281,31 @@ int mmc_switch_part(int dev_num, unsigned int part_num);
>>  int mmc_getcd(struct mmc *mmc);
>>  void spl_mmc_load(void) __noreturn;
>>
>> +/**
>> + * Start device initialization and return immediately; it does not block on
>> + * polling OCR (operation condition register) status.  Then you should call
>> + * mmc_init, which would block on polling OCR status and complete the device
>> + * initializatin.
>> + *
>> + * @param mmc  Pointer to a MMC device struct
>> + * @return 0 on success, IN_PROGRESS on waiting for OCR status, <0 on error.
>> + */
>> +int mmc_start_init(struct mmc *mmc);
>> +
>> +/**
>> + * Set preinit flag of mmc device.
>> + *
>> + * This will cause the device to be pre-inited during mmc_initialize(),
>> + * which may save boot time if the device is not accessed until later.
>> + * Some eMMC devices take 200-300ms to init, but unfortunately they
>> + * must be sent a series of commands to even get them to start preparing
>> + * for operation.
>> + *
>> + * @param mmc          Pointer to a MMC device struct
>> + * @param preinit      preinit flag value
>> + */
>> +void mmc_set_preinit(struct mmc *mmc, int preinit);
>> +
>>  #ifdef CONFIG_GENERIC_MMC
>>  #define mmc_host_is_spi(mmc)   ((mmc)->host_caps & MMC_MODE_SPI)
>>  struct mmc *mmc_spi_init(uint bus, uint cs, uint speed, uint mode);
>> --
>> 1.7.7.3
>>
>> _______________________________________________
>> U-Boot mailing list
>> U-Boot@lists.denx.de
>> http://lists.denx.de/mailman/listinfo/u-boot
Simon Glass Nov. 30, 2012, 11:13 p.m. UTC | #4
Hi Jaehoon,

On Fri, Nov 30, 2012 at 12:25 AM, Jaehoon Chung <jh80.chung@samsung.com> wrote:
> Hi,
>
> This concept is very good.
> But I have one question. I think need to call mmc_init() one more, right?
> how did you save the boot time(200ms)?
>
> On 11/29/2012 10:21 AM, Simon Glass wrote:
>> From: Che-Liang Chiou <clchiou@chromium.org>
>>
>> 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.
> snip..
>> +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);
> It need not to return? if err is IN_PROGRESS, next condition is immediately run.
> Then i think we didn't save the time before adjust this patch.

It's a little confusing, but the way it works is that mmc_preinit()
calls mmc_start_init() early in boot. Then when mmc_init() finally
gets called (later) it finishes off the init. We still need mmc_init()
to actually fully complete the init. If it were to return before
completing the init then we would be unable to use the MMC.

>> +
>> +     if (!err || err == IN_PROGRESS)
>> +             err = mmc_complete_init(mmc);
>> +     debug("%s: %d, time %lu\n", __func__, err, get_timer(start));
>>       return err;
>>  }

[snip]

Regards,
Simon
Simon Glass Dec. 15, 2012, 5:18 p.m. UTC | #5
Hi,

On Fri, Nov 30, 2012 at 3:13 PM, Simon Glass <sjg@chromium.org> wrote:
> Hi Jaehoon,
>
> On Fri, Nov 30, 2012 at 12:25 AM, Jaehoon Chung <jh80.chung@samsung.com> wrote:
>> Hi,
>>
>> This concept is very good.
>> But I have one question. I think need to call mmc_init() one more, right?
>> how did you save the boot time(200ms)?
>>
>> On 11/29/2012 10:21 AM, Simon Glass wrote:
>>> From: Che-Liang Chiou <clchiou@chromium.org>
>>>
>>> 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.
>> snip..
>>> +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);
>> It need not to return? if err is IN_PROGRESS, next condition is immediately run.
>> Then i think we didn't save the time before adjust this patch.
>
> It's a little confusing, but the way it works is that mmc_preinit()
> calls mmc_start_init() early in boot. Then when mmc_init() finally
> gets called (later) it finishes off the init. We still need mmc_init()
> to actually fully complete the init. If it were to return before
> completing the init then we would be unable to use the MMC.
>
>>> +
>>> +     if (!err || err == IN_PROGRESS)
>>> +             err = mmc_complete_init(mmc);
>>> +     debug("%s: %d, time %lu\n", __func__, err, get_timer(start));
>>>       return err;
>>>  }
>

Does this patch look good now? I am wondering if it will be including
in release, or in next?

Regards,
Simon

> [snip]
>
> Regards,
> Simon
Jaehoon Chung Dec. 17, 2012, 2:12 a.m. UTC | #6
On 12/16/2012 02:18 AM, Simon Glass wrote:
> Hi,
> 
> On Fri, Nov 30, 2012 at 3:13 PM, Simon Glass <sjg@chromium.org> wrote:
>> Hi Jaehoon,
>>
>> On Fri, Nov 30, 2012 at 12:25 AM, Jaehoon Chung <jh80.chung@samsung.com> wrote:
>>> Hi,
>>>
>>> This concept is very good.
>>> But I have one question. I think need to call mmc_init() one more, right?
>>> how did you save the boot time(200ms)?
>>>
>>> On 11/29/2012 10:21 AM, Simon Glass wrote:
>>>> From: Che-Liang Chiou <clchiou@chromium.org>
>>>>
>>>> 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.
>>> snip..
>>>> +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);
>>> It need not to return? if err is IN_PROGRESS, next condition is immediately run.
>>> Then i think we didn't save the time before adjust this patch.
>>
>> It's a little confusing, but the way it works is that mmc_preinit()
>> calls mmc_start_init() early in boot. Then when mmc_init() finally
>> gets called (later) it finishes off the init. We still need mmc_init()
>> to actually fully complete the init. If it were to return before
>> completing the init then we would be unable to use the MMC.
>>
>>>> +
>>>> +     if (!err || err == IN_PROGRESS)
>>>> +             err = mmc_complete_init(mmc);
>>>> +     debug("%s: %d, time %lu\n", __func__, err, get_timer(start));
>>>>       return err;
>>>>  }
>>
> 
> Does this patch look good now? I am wondering if it will be including
> in release, or in next?
Well, concept is very good. But i didn't see the any benefit yet.
I will test more...and share the result.

Best Regards,
Jaehoon Chung
> 
> Regards,
> Simon
> 
>> [snip]
>>
>> Regards,
>> Simon
>
Simon Glass Dec. 17, 2012, 3:44 a.m. UTC | #7
Hi,

On Sun, Dec 16, 2012 at 6:12 PM, Jaehoon Chung <jh80.chung@samsung.com> wrote:
> On 12/16/2012 02:18 AM, Simon Glass wrote:
>> Hi,
>>
>> On Fri, Nov 30, 2012 at 3:13 PM, Simon Glass <sjg@chromium.org> wrote:
>>> Hi Jaehoon,
>>>
>>> On Fri, Nov 30, 2012 at 12:25 AM, Jaehoon Chung <jh80.chung@samsung.com> wrote:
>>>> Hi,
>>>>
>>>> This concept is very good.
>>>> But I have one question. I think need to call mmc_init() one more, right?
>>>> how did you save the boot time(200ms)?
>>>>
>>>> On 11/29/2012 10:21 AM, Simon Glass wrote:
>>>>> From: Che-Liang Chiou <clchiou@chromium.org>
>>>>>
>>>>> 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.
>>>> snip..
>>>>> +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);
>>>> It need not to return? if err is IN_PROGRESS, next condition is immediately run.
>>>> Then i think we didn't save the time before adjust this patch.
>>>
>>> It's a little confusing, but the way it works is that mmc_preinit()
>>> calls mmc_start_init() early in boot. Then when mmc_init() finally
>>> gets called (later) it finishes off the init. We still need mmc_init()
>>> to actually fully complete the init. If it were to return before
>>> completing the init then we would be unable to use the MMC.
>>>
>>>>> +
>>>>> +     if (!err || err == IN_PROGRESS)
>>>>> +             err = mmc_complete_init(mmc);
>>>>> +     debug("%s: %d, time %lu\n", __func__, err, get_timer(start));
>>>>>       return err;
>>>>>  }
>>>
>>
>> Does this patch look good now? I am wondering if it will be including
>> in release, or in next?
> Well, concept is very good. But i didn't see the any benefit yet.
> I will test more...and share the result.

OK thanks. Assuming that make sure that the pre-init is definitely
done, I suggest you then make U-Boot go and do something else for
200ms, then do the full mmc init after that. It should complete
immediately.

So there is no time saving in mmc init, but you can do something else
while waiting for the MMC init to complete (it takes 200ms or so for
me).

Another thing to note with eMMC is that we probe for SD first. This
takes a little bit of time. We could perhaps use device tree to
provide information about the type of attached memory, and thus avoid
that probe.

Regards,
Simon

>
> Best Regards,
> Jaehoon Chung
>>
>> Regards,
>> Simon
>>
>>> [snip]
>>>
>>> Regards,
>>> Simon
>>
>
Simon Glass Feb. 8, 2013, 4:48 p.m. UTC | #8
Hi,

On Sun, Dec 16, 2012 at 7:44 PM, Simon Glass <sjg@chromium.org> wrote:
> Hi,
>
> On Sun, Dec 16, 2012 at 6:12 PM, Jaehoon Chung <jh80.chung@samsung.com> wrote:
>> On 12/16/2012 02:18 AM, Simon Glass wrote:
>>> Hi,
>>>
>>> On Fri, Nov 30, 2012 at 3:13 PM, Simon Glass <sjg@chromium.org> wrote:
>>>> Hi Jaehoon,
>>>>
>>>> On Fri, Nov 30, 2012 at 12:25 AM, Jaehoon Chung <jh80.chung@samsung.com> wrote:
>>>>> Hi,
>>>>>
>>>>> This concept is very good.
>>>>> But I have one question. I think need to call mmc_init() one more, right?
>>>>> how did you save the boot time(200ms)?
>>>>>
>>>>> On 11/29/2012 10:21 AM, Simon Glass wrote:
>>>>>> From: Che-Liang Chiou <clchiou@chromium.org>
>>>>>>
>>>>>> 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.
>>>>> snip..
>>>>>> +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);
>>>>> It need not to return? if err is IN_PROGRESS, next condition is immediately run.
>>>>> Then i think we didn't save the time before adjust this patch.
>>>>
>>>> It's a little confusing, but the way it works is that mmc_preinit()
>>>> calls mmc_start_init() early in boot. Then when mmc_init() finally
>>>> gets called (later) it finishes off the init. We still need mmc_init()
>>>> to actually fully complete the init. If it were to return before
>>>> completing the init then we would be unable to use the MMC.
>>>>
>>>>>> +
>>>>>> +     if (!err || err == IN_PROGRESS)
>>>>>> +             err = mmc_complete_init(mmc);
>>>>>> +     debug("%s: %d, time %lu\n", __func__, err, get_timer(start));
>>>>>>       return err;
>>>>>>  }
>>>>
>>>
>>> Does this patch look good now? I am wondering if it will be including
>>> in release, or in next?
>> Well, concept is very good. But i didn't see the any benefit yet.
>> I will test more...and share the result.
>
> OK thanks. Assuming that make sure that the pre-init is definitely
> done, I suggest you then make U-Boot go and do something else for
> 200ms, then do the full mmc init after that. It should complete
> immediately.
>
> So there is no time saving in mmc init, but you can do something else
> while waiting for the MMC init to complete (it takes 200ms or so for
> me).
>
> Another thing to note with eMMC is that we probe for SD first. This
> takes a little bit of time. We could perhaps use device tree to
> provide information about the type of attached memory, and thus avoid
> that probe.

Does this patch look suitable for merging at this point?

Regards,
Simon
Jaehoon Chung Feb. 13, 2013, 3:14 a.m. UTC | #9
Hi Simon,

It looks good to me.

Acked-by: Jaehoon Chung <jh80.chung@samsung.com>

On 02/09/2013 01:48 AM, Simon Glass wrote:
> Hi,
> 
> On Sun, Dec 16, 2012 at 7:44 PM, Simon Glass <sjg@chromium.org> wrote:
>> Hi,
>>
>> On Sun, Dec 16, 2012 at 6:12 PM, Jaehoon Chung <jh80.chung@samsung.com> wrote:
>>> On 12/16/2012 02:18 AM, Simon Glass wrote:
>>>> Hi,
>>>>
>>>> On Fri, Nov 30, 2012 at 3:13 PM, Simon Glass <sjg@chromium.org> wrote:
>>>>> Hi Jaehoon,
>>>>>
>>>>> On Fri, Nov 30, 2012 at 12:25 AM, Jaehoon Chung <jh80.chung@samsung.com> wrote:
>>>>>> Hi,
>>>>>>
>>>>>> This concept is very good.
>>>>>> But I have one question. I think need to call mmc_init() one more, right?
>>>>>> how did you save the boot time(200ms)?
>>>>>>
>>>>>> On 11/29/2012 10:21 AM, Simon Glass wrote:
>>>>>>> From: Che-Liang Chiou <clchiou@chromium.org>
>>>>>>>
>>>>>>> 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.
>>>>>> snip..
>>>>>>> +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);
>>>>>> It need not to return? if err is IN_PROGRESS, next condition is immediately run.
>>>>>> Then i think we didn't save the time before adjust this patch.
>>>>>
>>>>> It's a little confusing, but the way it works is that mmc_preinit()
>>>>> calls mmc_start_init() early in boot. Then when mmc_init() finally
>>>>> gets called (later) it finishes off the init. We still need mmc_init()
>>>>> to actually fully complete the init. If it were to return before
>>>>> completing the init then we would be unable to use the MMC.
>>>>>
>>>>>>> +
>>>>>>> +     if (!err || err == IN_PROGRESS)
>>>>>>> +             err = mmc_complete_init(mmc);
>>>>>>> +     debug("%s: %d, time %lu\n", __func__, err, get_timer(start));
>>>>>>>       return err;
>>>>>>>  }
>>>>>
>>>>
>>>> Does this patch look good now? I am wondering if it will be including
>>>> in release, or in next?
>>> Well, concept is very good. But i didn't see the any benefit yet.
>>> I will test more...and share the result.
>>
>> OK thanks. Assuming that make sure that the pre-init is definitely
>> done, I suggest you then make U-Boot go and do something else for
>> 200ms, then do the full mmc init after that. It should complete
>> immediately.
>>
>> So there is no time saving in mmc init, but you can do something else
>> while waiting for the MMC init to complete (it takes 200ms or so for
>> me).
>>
>> Another thing to note with eMMC is that we probe for SD first. This
>> takes a little bit of time. We could perhaps use device tree to
>> provide information about the type of attached memory, and thus avoid
>> that probe.
> 
> Does this patch look suitable for merging at this point?
> 
> Regards,
> Simon
>
Simon Glass March 16, 2013, 8:35 p.m. UTC | #10
Hi Andy,

On Tue, Feb 12, 2013 at 7:14 PM, Jaehoon Chung <jh80.chung@samsung.com> wrote:
> Hi Simon,
>
> It looks good to me.
>
> Acked-by: Jaehoon Chung <jh80.chung@samsung.com>

Do you think this will be picked up this time around?

Regards,
Simon
Simon Glass April 3, 2013, 6:49 p.m. UTC | #11
Hi Andy,

On Sat, Mar 16, 2013 at 1:35 PM, Simon Glass <sjg@chromium.org> wrote:

> Hi Andy,
>
> On Tue, Feb 12, 2013 at 7:14 PM, Jaehoon Chung <jh80.chung@samsung.com>
> wrote:
> > Hi Simon,
> >
> > It looks good to me.
> >
> > Acked-by: Jaehoon Chung <jh80.chung@samsung.com>
>
> Do you think this will be picked up this time around?
>

ping?

Any thoughts on this one please?

Regards,
Simon
Jaehoon Chung April 29, 2013, 5:02 a.m. UTC | #12
Hi all

Just replied to remind this patch.

Best Regards,
Jaehoon Chung

On 04/04/2013 03:49 AM, Simon Glass wrote:
> Hi Andy,
> 
> On Sat, Mar 16, 2013 at 1:35 PM, Simon Glass <sjg@chromium.org> wrote:
> 
>> Hi Andy,
>>
>> On Tue, Feb 12, 2013 at 7:14 PM, Jaehoon Chung <jh80.chung@samsung.com>
>> wrote:
>>> Hi Simon,
>>>
>>> It looks good to me.
>>>
>>> Acked-by: Jaehoon Chung <jh80.chung@samsung.com>
>>
>> Do you think this will be picked up this time around?
>>
> 
> ping?
> 
> Any thoughts on this one please?
> 
> Regards,
> Simon
> 
> 
> 
> _______________________________________________
> U-Boot mailing list
> U-Boot@lists.denx.de
> http://lists.denx.de/mailman/listinfo/u-boot
>
diff mbox

Patch

diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
index 72e8ce6..09695e8 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -503,48 +503,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;
@@ -1227,7 +1249,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;
 
@@ -1267,17 +1289,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;
 }
 
@@ -1315,6 +1368,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);
@@ -1325,5 +1397,6 @@  int mmc_initialize(bd_t *bis)
 
 	print_mmc_devices(',');
 
+	do_preinit();
 	return 0;
 }
diff --git a/include/mmc.h b/include/mmc.h
index a13e2bd..445d714 100644
--- a/include/mmc.h
+++ b/include/mmc.h
@@ -62,6 +62,7 @@ 
 #define UNUSABLE_ERR		-17 /* Unusable Card */
 #define COMM_ERR		-18 /* Communications Error */
 #define TIMEOUT			-19
+#define IN_PROGRESS		-20 /* operation is in progress */
 
 #define MMC_CMD_GO_IDLE_STATE		0
 #define MMC_CMD_SEND_OP_COND		1
@@ -260,6 +261,10 @@  struct mmc {
 	int (*init)(struct mmc *mmc);
 	int (*getcd)(struct mmc *mmc);
 	uint b_max;
+	char op_cond_pending;	/* 1 if we are waiting on an op_cond command */
+	char init_in_progress;	/* 1 if we have done mmc_start_init() */
+	char preinit;		/* start init as early as possible */
+	uint op_cond_response;	/* the response byte from the last op_cond */
 };
 
 int mmc_register(struct mmc *mmc);
@@ -276,6 +281,31 @@  int mmc_switch_part(int dev_num, unsigned int part_num);
 int mmc_getcd(struct mmc *mmc);
 void spl_mmc_load(void) __noreturn;
 
+/**
+ * Start device initialization and return immediately; it does not block on
+ * polling OCR (operation condition register) status.  Then you should call
+ * mmc_init, which would block on polling OCR status and complete the device
+ * initializatin.
+ *
+ * @param mmc	Pointer to a MMC device struct
+ * @return 0 on success, IN_PROGRESS on waiting for OCR status, <0 on error.
+ */
+int mmc_start_init(struct mmc *mmc);
+
+/**
+ * Set preinit flag of mmc device.
+ *
+ * This will cause the device to be pre-inited during mmc_initialize(),
+ * which may save boot time if the device is not accessed until later.
+ * Some eMMC devices take 200-300ms to init, but unfortunately they
+ * must be sent a series of commands to even get them to start preparing
+ * for operation.
+ *
+ * @param mmc		Pointer to a MMC device struct
+ * @param preinit	preinit flag value
+ */
+void mmc_set_preinit(struct mmc *mmc, int preinit);
+
 #ifdef CONFIG_GENERIC_MMC
 #define mmc_host_is_spi(mmc)	((mmc)->host_caps & MMC_MODE_SPI)
 struct mmc *mmc_spi_init(uint bus, uint cs, uint speed, uint mode);