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 <[email protected]>
> ---
> 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 [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html