Re: [PATCH] mmc: sdhci: extend wait for busy signalling

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Prasanna

>
> Certain eMMC features like sanitize, BKOPS, cache, erase etc take
> long time (more than max time) to finish operation during which
> the eMMC pulls DAT0 low to indicate busyness.
> In such special rare cases, data timeout will be triggered as an
> error but actually operation is still in progress with DAT0 low!
>
> Hence trigger a workqueue to monitor the DAT0 busy signalling by
> the eMMC and extend wait for successful completion of operation.
>
> Signed-off-by: Prasanna NAVARATNA <prasanna.navaratna@xxxxxxxxxxxx>
> ---
>  drivers/mmc/host/sdhci.c  |   68 ++++++++++++++++++++++++++++++++++++++++++++-
>  include/linux/mmc/sdhci.h |    2 +
>  2 files changed, 69 insertions(+), 1 deletions(-)
>
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index b4d7f27..5d6b065 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -42,7 +42,8 @@
>  #define SDHCI_USE_LEDS_CLASS
>  #endif
>
> -#define MAX_TUNING_LOOP 40
> +#define MAX_TUNING_LOOP        40
> +#define MAX_BUSY_WAIT_LOOP     100
>
>  static unsigned int debug_quirks = 0;
>  static unsigned int debug_quirks2;
> @@ -2100,6 +2101,44 @@ static const struct mmc_host_ops sdhci_ops = {
>
>  /*****************************************************************************\
>   *                                                                           *
> + * Workqueues                                                               *
> + *                                                                           *
> +\*****************************************************************************/
> +
> +/* Internal work. Work to wait for the busy signalling to finish. Certain
> + * eMMC operations may take too long time to complete.
> + */
> +static void sdhci_work_wait_for_busy(struct work_struct *work)
> +{
> +       struct sdhci_host *host = container_of(work, struct sdhci_host,
> +                                             wait_for_busy_work);
> +       int wait_cnt = 0;
> +
> +       /*
> +        * According to Arasan, when DTOERR occured while CMD38/CMD6,
> +        * which is not treated as normal and as an error by the Host,
> +        * host driver should reset CMD & DATA Lines for SDHC internal state.
> +        */
> +       pr_debug("Resetting CMD & DATA Lines at once\n");
> +       sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
> +
> +       pr_info("Waiting for BUSY signalling to end\n");
> +       while (wait_cnt++ < MAX_BUSY_WAIT_LOOP &&
> +               (sdhci_readl(host, SDHCI_PRESENT_STATE) &
> +                       SDHCI_DATA_LVL_DAT0_MASK) == 0) {
> +               msleep(100);
> +        }
> +       pr_info("End of BUSY signalling\n");
> +
> +       if (wait_cnt >= MAX_BUSY_WAIT_LOOP)
> +               pr_err("%s: Operation takes too long to finish!\n",
> +                               mmc_hostname(host->mmc));
> +
> +       sdhci_finish_command(host);
> +}
> +
> +/*****************************************************************************\
> + *                                                                           *
>   * Tasklets                                                                  *
>   *                                                                           *
>  \*****************************************************************************/
> @@ -2328,6 +2367,26 @@ static void sdhci_data_irq(struct sdhci_host *host,
> u32 intmask)
>                                 sdhci_finish_command(host);
>                                 return;
>                         }
> +
> +                       /*
> +                        * Some eMMC takes a long time for the erase & certain
> +                        * operations to finish. Timeout might be triggered well
> +                        * before erase or SWITCH operation finishes. If this
> +                        * happens schedule a workqueue work item to monitor
> +                        * the DAT0 line to wait for operation to finish.
> +                        */
> +                       if (intmask & SDHCI_INT_DATA_TIMEOUT &&
> +                                       (host->cmd->opcode == MMC_ERASE ||
> +                                        host->cmd->opcode == MMC_SWITCH)) {
> +                               if ((sdhci_readl(host, SDHCI_PRESENT_STATE) &
> +                                               SDHCI_DATA_LVL_DAT0_MASK) == 0) {
> +                                       schedule_work(&host->wait_for_busy_work);
> +                                       return;
> +                               } else {
> +                                       sdhci_finish_command(host);
> +                                       return;
> +                               }
> +                       }
>                 }
>
>                 pr_err("%s: Got data interrupt 0x%08x even "
> @@ -2559,6 +2618,9 @@ int sdhci_suspend_host(struct sdhci_host *host)
>                 host->flags &= ~SDHCI_NEEDS_RETUNING;
>         }
>
> +       /* Flush and wait for busy operation to complete */
> +       flush_work(&host->wait_for_busy_work);
> +
>         ret = mmc_suspend_host(host->mmc);
>         if (ret) {
>                 if (host->flags & SDHCI_USING_RETUNING_TIMER) {
> @@ -3206,6 +3268,7 @@ int sdhci_add_host(struct sdhci_host *host)
>                 sdhci_tasklet_finish, (unsigned long)host);
>
>         setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host);
> +       INIT_WORK(&host->wait_for_busy_work, sdhci_work_wait_for_busy);
>
>         if (host->version >= SDHCI_SPEC_300) {
>                 init_waitqueue_head(&host->buf_ready_int);
> @@ -3308,6 +3371,9 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
>         sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK);
>         free_irq(host->irq, host);
>
> +       /* Flush and wait for busy operation to complete */
> +       flush_work(&host->wait_for_busy_work);
> +
>         del_timer_sync(&host->timer);
>
>         tasklet_kill(&host->card_tasklet);
> diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
> index 3e781b8..3c86e43 100644
> --- a/include/linux/mmc/sdhci.h
> +++ b/include/linux/mmc/sdhci.h
> @@ -144,6 +144,8 @@ struct sdhci_host {
>         bool runtime_suspended; /* Host is runtime suspended */
>         bool bus_on;            /* Bus power prevents runtime suspend */
>
> +       struct work_struct wait_for_busy_work; /* work to wait for busy to end */
> +
>         struct mmc_request *mrq;        /* Current request */
>         struct mmc_command *cmd;        /* Current command */
>         struct mmc_data *data;  /* Current data request */
> --
> 1.7.6
>
>

Failes to compile here... I am using a 3.11 kernel with some local patches.

-----------------------------
  CHK     include/generated/uapi/linux/version.h
  CHK     include/generated/utsrelease.h
make[1]: `include/generated/mach-types.h' is up to date.
  CALL    scripts/checksyscalls.sh
  CHK     include/generated/compile.h
  CHK     kernel/config_data.h
  CC      drivers/mmc/host/sdhci.o
drivers/mmc/host/sdhci.c: In function ‘sdhci_work_wait_for_busy’:
drivers/mmc/host/sdhci.c:2128:24: error: ‘SDHCI_DATA_LVL_DAT0_MASK’
undeclared (first use in this function)
                        SDHCI_DATA_LVL_DAT0_MASK) == 0) {
                        ^
drivers/mmc/host/sdhci.c:2128:24: note: each undeclared identifier is
reported only once for each function it appears in
drivers/mmc/host/sdhci.c: In function ‘sdhci_data_irq’:
drivers/mmc/host/sdhci.c:2382:48: error: ‘SDHCI_DATA_LVL_DAT0_MASK’
undeclared (first use in this function)

SDHCI_DATA_LVL_DAT0_MASK) == 0) {
                                                ^
make[3]: *** [drivers/mmc/host/sdhci.o] Error 1
make[2]: *** [drivers/mmc/host] Error 2
make[1]: *** [drivers/mmc] Error 2
make: *** [drivers] Error 2
make: *** Waiting for unfinished jobs....

greets
--
Christian Gmeiner, MSc
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux USB Devel]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux