From patchwork Thu Sep 5 09:23:20 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Prasanna NAVARATNA X-Patchwork-Id: 2853985 Return-Path: X-Original-To: patchwork-linux-mmc@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 19CE0C0AB5 for ; Thu, 5 Sep 2013 09:23:46 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id CC2BE2039C for ; Thu, 5 Sep 2013 09:23:44 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 68DDD2021A for ; Thu, 5 Sep 2013 09:23:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S935567Ab3IEJXm (ORCPT ); Thu, 5 Sep 2013 05:23:42 -0400 Received: from plane.gmane.org ([80.91.229.3]:37623 "EHLO plane.gmane.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S935566Ab3IEJXl (ORCPT ); Thu, 5 Sep 2013 05:23:41 -0400 Received: from list by plane.gmane.org with local (Exim 4.69) (envelope-from ) id 1VHVmu-00038r-BT for linux-mmc@vger.kernel.org; Thu, 05 Sep 2013 11:23:40 +0200 Received: from 202.122.18.226 ([202.122.18.226]) by main.gmane.org with esmtp (Gmexim 0.1 (Debian)) id 1AlnuQ-0007hv-00 for ; Thu, 05 Sep 2013 11:23:40 +0200 Received: from prasanna.navaratna by 202.122.18.226 with local (Gmexim 0.1 (Debian)) id 1AlnuQ-0007hv-00 for ; Thu, 05 Sep 2013 11:23:40 +0200 X-Injected-Via-Gmane: http://gmane.org/ To: linux-mmc@vger.kernel.org From: Prasanna NAVARATNA Subject: [PATCH] mmc: sdhci: extend wait for busy signalling Date: Thu, 5 Sep 2013 09:23:20 +0000 (UTC) Lines: 151 Message-ID: Mime-Version: 1.0 X-Complaints-To: usenet@ger.gmane.org X-Gmane-NNTP-Posting-Host: sea.gmane.org User-Agent: Loom/3.14 (http://gmane.org/) X-Loom-IP: 202.122.18.226 (Mozilla/5.0 (Windows NT 6.1; rv:21.0) Gecko/20100101 Firefox/21.0) Sender: linux-mmc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-mmc@vger.kernel.org X-Spam-Status: No, score=-6.6 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, FREEMAIL_FROM, FSL_HELO_BARE_IP_2, RCVD_IN_DNSWL_HI, RCVD_NUMERIC_HELO, RP_MATCHES_RCVD,UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From 20efc7e04ebef28c03912824c7edb1bd8f14a20a Mon Sep 17 00:00:00 2001 From: Prasanna NAVARATNA Date: Tue, 3 Sep 2013 17:31:46 +0530 Subject: [PATCH] mmc: sdhci: extend wait for busy signalling 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 --- 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 */