From patchwork Fri Dec 26 09:13:43 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: addy ke X-Patchwork-Id: 5542651 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 16A54BEEA8 for ; Fri, 26 Dec 2014 09:16:40 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 0FDA9201EC for ; Fri, 26 Dec 2014 09:16:39 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id EE3C920173 for ; Fri, 26 Dec 2014 09:16:37 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751404AbaLZJQU (ORCPT ); Fri, 26 Dec 2014 04:16:20 -0500 Received: from regular1.263xmail.com ([211.150.99.134]:40459 "EHLO regular1.263xmail.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751165AbaLZJQS (ORCPT ); Fri, 26 Dec 2014 04:16:18 -0500 Received: from addy.ke?rock-chips.com (unknown [192.168.167.12]) by regular1.263xmail.com (Postfix) with SMTP id 3739F5E7F; Fri, 26 Dec 2014 17:16:05 +0800 (CST) X-263anti-spam: KSV:0; X-MAIL-GRAY: 0 X-MAIL-DELIVERY: 1 X-KSVirus-check: 0 X-ABS-CHECKED: 4 X-ADDR-CHECKED: 0 Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.263.net (Postfix) with ESMTP id 7E93E40D; Fri, 26 Dec 2014 17:15:59 +0800 (CST) X-RL-SENDER: addy.ke@rock-chips.com X-FST-TO: robh+dt@kernel.org X-SENDER-IP: 58.22.7.114 X-LOGIN-NAME: addy.ke@rock-chips.com X-UNIQUE-TAG: <28872ce8a4d5b6b5720a5f24a754f919> X-ATTACHMENT-NUM: 0 X-SENDER: kfx@rock-chips.com X-DNS-TYPE: 0 Received: from localhost.localdomain (unknown [58.22.7.114]) by smtp.263.net (Postfix) whith ESMTP id 21427GX0INJ; Fri, 26 Dec 2014 17:16:03 +0800 (CST) From: Addy Ke To: robh+dt@kernel.org, pawel.moll@arm.com, mark.rutland@arm.com, ijc+devicetree@hellion.org.uk, galak@codeaurora.org, rdunlap@infradead.org, tgih.jun@samsung.com, jh80.chung@samsung.com, chris@printf.net, ulf.hansson@linaro.org, dinguyen@altera.com, heiko@sntech.de, olof@lixom.net, dianders@chromium.org, sonnyrao@chromium.org, amstan@chromium.org Cc: devicetree@vger.kernel.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mmc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-rockchip@lists.infradead.org, zhenfu.fang@rock-chips.com, cf@rock-chips.com, lintao@rock-chips.com, chenfen@rock-chips.com, zyf@rock-chips.com, xjq@rock-chips.com, huangtao@rock-chips.com, zyw@rock-chips.com, yzq@rock-chips.com, hj@rock-chips.com, kever.yang@rock-chips.com, zhangqing@rock-chips.com, hl@rock-chips.com, Addy Ke Subject: [PATCH v4] mmc: dw_mmc: add quirk for broken data transfer over scheme Date: Fri, 26 Dec 2014 17:13:43 +0800 Message-Id: <1419585223-8214-1-git-send-email-addy.ke@rock-chips.com> X-Mailer: git-send-email 1.8.3.2 In-Reply-To: <1417576563-5033-1-git-send-email-addy.ke@rock-chips.com> References: <1417576563-5033-1-git-send-email-addy.ke@rock-chips.com> 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.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable 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 This patch add a new quirk to add a s/w timer to notify the driver to terminate current transfer and report a data timeout to the core, if DTO interrupt does NOT come within the given time. dw_mmc call mmc_request_done func to finish transfer depends on DTO interrupt. If DTO interrupt does not come in sending data state, the current transfer will be blocked. We got the reply from synopsys: There are two counters but both use the same value of [31:8] bits. Data timeout counter doesn't wait for stop clock and you should get DRTO even when the clock is not stopped. Host Starvation timeout counter is triggered with stop clock condition. This means that host should get DRTO and DTO interrupt. But this case really exists, when driver reads tuning data from card on RK3288-pink2 board. I measured waveforms by oscilloscope and found that card clock was always on and data lines were always holded high level in sending data state. There are two possibility that data over interrupt doesn't come in reading data state on RK3X SoCs: - get command done interrupt, but doesn't get any data-related interrupt. - get data error interrupt, but doesn't get data over interrupt. We don't know why we have this problem, but we need it to fix this problem now. And I will post a follow up change when we find the root cause. Signed-off-by: Addy Ke --- Changes in v2: - fix some typo. - remove extra timeout value (250ms). - remove dw_mci_dto_start_monitor func. - use "broken-dto" for new quirk and change Subject for it. Changes in v3: - Remove dts for broken-dto, just add this quirk in dw_mci_rockchip_init Changes in v4: - fix bug that may cause 32 bit overflow by (drto_clks * 1000). - doesn't call mod_timer in writing data state, becase TMOUT register only for reading data. drivers/mmc/host/dw_mmc-rockchip.c | 3 ++ drivers/mmc/host/dw_mmc.c | 64 ++++++++++++++++++++++++++++++++++++-- include/linux/mmc/dw_mmc.h | 5 +++ 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index 5650ac4..ba92ebd 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -73,6 +73,9 @@ static int dw_mci_rockchip_init(struct dw_mci *host) /* It is slot 8 on Rockchip SoCs */ host->sdio_id0 = 8; + /* It needs this quirk on all Rockchip SoCs */ + host->pdata->quirks |= DW_MCI_QUIRK_BROKEN_DTO; + return 0; } diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 6e4d864..385dc0f 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1477,6 +1477,8 @@ static void dw_mci_tasklet_func(unsigned long priv) enum dw_mci_state state; enum dw_mci_state prev_state; unsigned int err; + unsigned int drto_clks; + unsigned int drto_ms; spin_lock(&host->lock); @@ -1542,8 +1544,22 @@ static void dw_mci_tasklet_func(unsigned long priv) } if (!test_and_clear_bit(EVENT_XFER_COMPLETE, - &host->pending_events)) + &host->pending_events)) { + drto_clks = mci_readl(host, TMOUT) >> 8; + drto_ms = DIV_ROUND_UP(drto_clks, + host->bus_hz / 1000); + + /* + * If all data-related interrupts don't come + * within the given time in reading data state. + * */ + if ((host->quirks & DW_MCI_QUIRK_BROKEN_DTO) && + (host->dir_status == DW_MCI_RECV_STATUS)) { + mod_timer(&host->dto_timer, jiffies + + msecs_to_jiffies(drto_ms)); + } break; + } set_bit(EVENT_XFER_COMPLETE, &host->completed_events); @@ -1573,8 +1589,22 @@ static void dw_mci_tasklet_func(unsigned long priv) case STATE_DATA_BUSY: if (!test_and_clear_bit(EVENT_DATA_COMPLETE, - &host->pending_events)) + &host->pending_events)) { + drto_clks = mci_readl(host, TMOUT) >> 8; + drto_ms = DIV_ROUND_UP(drto_clks, + host->bus_hz / 1000); + /* + * If data error interrupt comes but data over + * interrupt doesn't come within the given time. + * in reading data state. + * */ + if ((host->quirks & DW_MCI_QUIRK_BROKEN_DTO) && + (host->dir_status == DW_MCI_RECV_STATUS)) { + mod_timer(&host->dto_timer, jiffies + + msecs_to_jiffies(drto_ms)); + } break; + } host->data = NULL; set_bit(EVENT_DATA_COMPLETE, &host->completed_events); @@ -2135,6 +2165,10 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) } if (pending & SDMMC_INT_DATA_OVER) { + if ((host->quirks & DW_MCI_QUIRK_BROKEN_DTO) && + (host->dir_status == DW_MCI_RECV_STATUS)) + del_timer(&host->dto_timer); + mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER); if (!host->data_status) host->data_status = pending; @@ -2522,6 +2556,28 @@ ciu_out: return ret; } +static void dw_mci_dto_timer(unsigned long arg) +{ + struct dw_mci *host = (struct dw_mci *)arg; + + switch (host->state) { + case STATE_SENDING_DATA: + case STATE_DATA_BUSY: + /* + * If DTO interrupt does NOT come in sending data state, + * we should notify the driver to terminate current transfer + * and report a data timeout to the core. + */ + host->data_status = SDMMC_INT_DRTO; + set_bit(EVENT_DATA_ERROR, &host->pending_events); + set_bit(EVENT_DATA_COMPLETE, &host->pending_events); + tasklet_schedule(&host->tasklet); + break; + default: + break; + } +} + #ifdef CONFIG_OF static struct dw_mci_of_quirks { char *quirk; @@ -2670,6 +2726,10 @@ int dw_mci_probe(struct dw_mci *host) host->quirks = host->pdata->quirks; + if (host->quirks & DW_MCI_QUIRK_BROKEN_DTO) + setup_timer(&host->dto_timer, + dw_mci_dto_timer, (unsigned long)host); + spin_lock_init(&host->lock); spin_lock_init(&host->irq_lock); INIT_LIST_HEAD(&host->queue); diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h index 471fb31..2ece0dd 100644 --- a/include/linux/mmc/dw_mmc.h +++ b/include/linux/mmc/dw_mmc.h @@ -98,6 +98,7 @@ struct mmc_data; * @irq_flags: The flags to be passed to request_irq. * @irq: The irq value to be passed to request_irq. * @sdio_id0: Number of slot0 in the SDIO interrupt registers. + * @dto_timer: Timer for broken data transfer over scheme. * * Locking * ======= @@ -202,6 +203,8 @@ struct dw_mci { int irq; int sdio_id0; + + struct timer_list dto_timer; }; /* DMA ops for Internal/External DMAC interface */ @@ -226,6 +229,8 @@ struct dw_mci_dma_ops { #define DW_MCI_QUIRK_BROKEN_CARD_DETECTION BIT(3) /* No write protect */ #define DW_MCI_QUIRK_NO_WRITE_PROTECT BIT(4) +/* Timer for broken data transfer over scheme */ +#define DW_MCI_QUIRK_BROKEN_DTO BIT(5) /* Slot level quirks */ /* This slot has no write protect */