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

Jae hoon Chung jh80.chung at gmail.com
Thu Nov 29 16:20:01 CET 2012


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 at chromium.org>:
> From: Che-Liang Chiou <clchiou at 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 at chromium.org>
> Signed-off-by: Simon Glass <sjg at 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 at lists.denx.de
> http://lists.denx.de/mailman/listinfo/u-boot


More information about the U-Boot mailing list