From patchwork Mon Oct 24 14:21:25 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Linus Walleij X-Patchwork-Id: 9392177 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 5E7F460762 for ; Mon, 24 Oct 2016 14:21:46 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 4E09D29098 for ; Mon, 24 Oct 2016 14:21:46 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 42ADC2909F; Mon, 24 Oct 2016 14:21:46 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.8 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI,T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 2081B29098 for ; Mon, 24 Oct 2016 14:21:42 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S940854AbcJXOVl (ORCPT ); Mon, 24 Oct 2016 10:21:41 -0400 Received: from mail-lf0-f43.google.com ([209.85.215.43]:35038 "EHLO mail-lf0-f43.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S938845AbcJXOVk (ORCPT ); Mon, 24 Oct 2016 10:21:40 -0400 Received: by mail-lf0-f43.google.com with SMTP id f134so33708594lfg.2 for ; Mon, 24 Oct 2016 07:21:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=kCDWvU8HeO681VwDbIguD2Rpn0lS1JBfD/okBqa/aic=; b=gG/zxXTYQJaU3OZd4H8x0KTNtVMMzTo5Xf2WNG9XjSDy1ZEeH8QVy0Of9/Du7WeHAh a0cmv4V3jiLHBBjFQ0YgZyU6v/tOZ1tMeNxao9tjTkkPlTFlMvJD9Fx2cpBrUnjhu5e6 Y5Rrpu/1JuJZA8q5nlCumbMw38OHiW/YgbM5k= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=kCDWvU8HeO681VwDbIguD2Rpn0lS1JBfD/okBqa/aic=; b=cEBn433ut0Xkx6AA8uTYgXX5JaPhlMD9U8mRkTRkMRYWDbsZUKATyJV0ynWx3SuxBy RmISzoDmCzHUuMETZiOqPINSloMZb0EFK6G0MWC914rVp755OGHo6pb8CM2zUWxx80nj DXeOZIUNReAPOVfJchcvHkZeMbbCpmj/SRWdCYbsrBjVNhNs96srlARwOoGxwxi5iUHl mgwFch4FM1FJpx2w9XqMVTAu9yHPzicZun6TOBY/oFkXtBhJZkBf87f2673DorBmMjag fyVGmSXdYJfsgO1uuJ5n1GFoh677dMaZ9o7G+9/PBPZdmVn/9nBHgw7PP79Gdx4dIpB+ n8HQ== X-Gm-Message-State: ABUngvfiOyTj/ek4mt+h/e/acMr8FBCoc4mq/hsB5+t4kiZvCck6qeUHQeC+js6lDGkOBSFt X-Received: by 10.25.190.69 with SMTP id o66mr7622426lff.109.1477318898695; Mon, 24 Oct 2016 07:21:38 -0700 (PDT) Received: from linuslaptop.ideon.se ([85.235.10.227]) by smtp.gmail.com with ESMTPSA id u10sm3148841lff.37.2016.10.24.07.21.37 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 24 Oct 2016 07:21:38 -0700 (PDT) From: Linus Walleij To: linux-mmc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Srinivas Kandagatla Cc: Russell King , Ulf Hansson , Linus Walleij Subject: [PATCH 2/3] mmc: mmci: refactor ST Micro busy detection Date: Mon, 24 Oct 2016 16:21:25 +0200 Message-Id: <1477318886-24851-2-git-send-email-linus.walleij@linaro.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1477318886-24851-1-git-send-email-linus.walleij@linaro.org> References: <1477318886-24851-1-git-send-email-linus.walleij@linaro.org> Sender: linux-mmc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-mmc@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The ST Micro-specific busy detection was made after the assumption that only this variant supports busy detection. So when doing busy detection, the host immediately tries to use some ST-specific register bits. Since the qualcomm variant also supports some busy detection schemes, encapsulate the variant flags better in the variant struct and prepare to add more variants by just providing some bitmasks to the logic. Put the entire busy detection logic within an if()-clause in the mmci_cmd_irq() function so the code is only executed when busy detection is enabled, and so that it is kept in (almost) one place, and add comments describing what is going on so the code can be understood. Tested on the Ux500 by introducing some prints in the busy detection path and noticing how the IRQ is enabled, used and disabled successfully. Cc: Srinivas Kandagatla Signed-off-by: Linus Walleij --- drivers/mmc/host/mmci.c | 113 +++++++++++++++++++++++++++++++++++------------- drivers/mmc/host/mmci.h | 2 +- 2 files changed, 85 insertions(+), 30 deletions(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 79b135752d3d..06e19d8359e0 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -71,7 +71,12 @@ static unsigned int fmax = 515633; * @f_max: maximum clk frequency supported by the controller. * @signal_direction: input/out direction of bus signals can be indicated * @pwrreg_clkgate: MMCIPOWER register must be used to gate the clock - * @busy_detect: true if busy detection on dat0 is supported + * @busy_detect: true if the variant supports busy detection on DAT0. + * @busy_dpsm_flag: bitmask enabling busy detection in the DPSM + * @busy_detect_flag: bitmask identifying the bit in the MMCISTATUS register + * indicating that the card is busy + * @busy_detect_mask: bitmask identifying the bit in the MMCIMASK0 to mask for + * getting busy end detection interrupts * @pwrreg_nopower: bits in MMCIPOWER don't controls ext. power supply * @explicit_mclk_control: enable explicit mclk control in driver. * @qcom_fifo: enables qcom specific fifo pio read logic. @@ -98,6 +103,9 @@ struct variant_data { bool signal_direction; bool pwrreg_clkgate; bool busy_detect; + u32 busy_dpsm_flag; + u32 busy_detect_flag; + u32 busy_detect_mask; bool pwrreg_nopower; bool explicit_mclk_control; bool qcom_fifo; @@ -178,6 +186,9 @@ static struct variant_data variant_ux500 = { .signal_direction = true, .pwrreg_clkgate = true, .busy_detect = true, + .busy_dpsm_flag = MCI_ST_DPSM_BUSYMODE, + .busy_detect_flag = MCI_ST_CARDBUSY, + .busy_detect_mask = MCI_ST_BUSYENDMASK, .pwrreg_nopower = true, }; @@ -199,6 +210,9 @@ static struct variant_data variant_ux500v2 = { .signal_direction = true, .pwrreg_clkgate = true, .busy_detect = true, + .busy_dpsm_flag = MCI_ST_DPSM_BUSYMODE, + .busy_detect_flag = MCI_ST_CARDBUSY, + .busy_detect_mask = MCI_ST_BUSYENDMASK, .pwrreg_nopower = true, }; @@ -220,6 +234,7 @@ static struct variant_data variant_qcom = { .qcom_dml = true, }; +/* Busy detection for the ST Micro variant */ static int mmci_card_busy(struct mmc_host *mmc) { struct mmci_host *host = mmc_priv(mmc); @@ -227,7 +242,7 @@ static int mmci_card_busy(struct mmc_host *mmc) int busy = 0; spin_lock_irqsave(&host->lock, flags); - if (readl(host->base + MMCISTATUS) & MCI_ST_CARDBUSY) + if (readl(host->base + MMCISTATUS) & host->variant->busy_detect_flag) busy = 1; spin_unlock_irqrestore(&host->lock, flags); @@ -294,8 +309,8 @@ static void mmci_write_pwrreg(struct mmci_host *host, u32 pwr) */ static void mmci_write_datactrlreg(struct mmci_host *host, u32 datactrl) { - /* Keep ST Micro busy mode if enabled */ - datactrl |= host->datactrl_reg & MCI_ST_DPSM_BUSYMODE; + /* Keep busy mode in DPSM if enabled */ + datactrl |= host->datactrl_reg & host->variant->busy_dpsm_flag; if (host->datactrl_reg != datactrl) { host->datactrl_reg = datactrl; @@ -973,37 +988,66 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd, unsigned int status) { void __iomem *base = host->base; - bool sbc, busy_resp; + bool sbc; if (!cmd) return; sbc = (cmd == host->mrq->sbc); - busy_resp = host->variant->busy_detect && (cmd->flags & MMC_RSP_BUSY); - if (!((status|host->busy_status) & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT| - MCI_CMDSENT|MCI_CMDRESPEND))) + /* + * We need to be one of these interrupts to be considered worth + * handling. Note that we tag on any latent IRQs postponed + * due to waiting for busy status. + */ + if (!((status|host->busy_status) & + (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|MCI_CMDSENT|MCI_CMDRESPEND))) return; - /* Check if we need to wait for busy completion. */ - if (host->busy_status && (status & MCI_ST_CARDBUSY)) - return; + /* + * ST Micro variant: handle busy detection. + */ + if (host->variant->busy_detect) { + bool busy_resp = !!(cmd->flags & MMC_RSP_BUSY); - /* Enable busy completion if needed and supported. */ - if (!host->busy_status && busy_resp && - !(status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT)) && - (readl(base + MMCISTATUS) & MCI_ST_CARDBUSY)) { - writel(readl(base + MMCIMASK0) | MCI_ST_BUSYEND, - base + MMCIMASK0); - host->busy_status = status & (MCI_CMDSENT|MCI_CMDRESPEND); - return; - } + /* We are busy with a command, return */ + if (host->busy_status && + (status & host->variant->busy_detect_flag)) + return; + + /* + * We were not busy, but we now got a busy response on + * something that was not an error, and we double-check + * that the special busy status bit is still set before + * proceeding. + */ + if (!host->busy_status && busy_resp && + !(status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT)) && + (readl(base + MMCISTATUS) & host->variant->busy_detect_flag)) { + /* Unmask the busy IRQ */ + writel(readl(base + MMCIMASK0) | + host->variant->busy_detect_mask, + base + MMCIMASK0); + /* + * Now cache the last response status code (until + * the busy bit goes low), and return. + */ + host->busy_status = + status & (MCI_CMDSENT|MCI_CMDRESPEND); + return; + } - /* At busy completion, mask the IRQ and complete the request. */ - if (host->busy_status) { - writel(readl(base + MMCIMASK0) & ~MCI_ST_BUSYEND, - base + MMCIMASK0); - host->busy_status = 0; + /* + * At this point we are not busy with a command, we have + * not recieved a new busy request, mask the busy IRQ and + * fall through to process the IRQ. + */ + if (host->busy_status) { + writel(readl(base + MMCIMASK0) & + ~host->variant->busy_detect_mask, + base + MMCIMASK0); + host->busy_status = 0; + } } host->cmd = NULL; @@ -1257,9 +1301,11 @@ static irqreturn_t mmci_irq(int irq, void *dev_id) mmci_data_irq(host, host->data, status); } - /* Don't poll for busy completion in irq context. */ - if (host->busy_status) - status &= ~MCI_ST_CARDBUSY; + /* + * Don't poll for busy completion in irq context. + */ + if (host->variant->busy_detect && host->busy_status) + status &= ~host->variant->busy_detect_flag; ret = 1; } while (status); @@ -1612,9 +1658,18 @@ static int mmci_probe(struct amba_device *dev, /* We support these capabilities. */ mmc->caps |= MMC_CAP_CMD23; + /* + * Enable busy detection. + */ if (variant->busy_detect) { mmci_ops.card_busy = mmci_card_busy; - mmci_write_datactrlreg(host, MCI_ST_DPSM_BUSYMODE); + /* + * Not all variants have a flag to enable busy detection + * in the DPSM, but if they do, set it here. + */ + if (variant->busy_dpsm_flag) + mmci_write_datactrlreg(host, + host->variant->busy_dpsm_flag); mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY; mmc->max_busy_timeout = 0; } diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index 8952285196cd..38f6f1365ec4 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -174,7 +174,7 @@ /* Extended status bits for the ST Micro variants */ #define MCI_ST_SDIOITMASK (1 << 22) #define MCI_ST_CEATAENDMASK (1 << 23) -#define MCI_ST_BUSYEND (1 << 24) +#define MCI_ST_BUSYENDMASK (1 << 24) #define MMCIMASK1 0x040 #define MMCIFIFOCNT 0x048