From patchwork Tue Mar 19 09:12:57 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Shawn Lin X-Patchwork-Id: 10859145 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 3ACAF1708 for ; Tue, 19 Mar 2019 09:15:07 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 210B2294AB for ; Tue, 19 Mar 2019 09:15:07 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 1584E295DA; Tue, 19 Mar 2019 09:15:07 +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.4 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI,RCVD_IN_SORBS_WEB 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 88DFA294AB for ; Tue, 19 Mar 2019 09:15:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726910AbfCSJPG (ORCPT ); Tue, 19 Mar 2019 05:15:06 -0400 Received: from lucky1.263xmail.com ([211.157.147.135]:35442 "EHLO lucky1.263xmail.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725906AbfCSJPF (ORCPT ); Tue, 19 Mar 2019 05:15:05 -0400 Received: from shawn.lin?rock-chips.com (unknown [192.168.167.230]) by lucky1.263xmail.com (Postfix) with ESMTP id AC74D2F020; Tue, 19 Mar 2019 17:14:59 +0800 (CST) X-263anti-spam: KSV:0; X-MAIL-GRAY: 1 X-MAIL-DELIVERY: 0 X-KSVirus-check: 0 X-ABS-CHECKED: 4 Received: from localhost.localdomain (unknown [58.22.7.114]) by smtp.263.net (postfix) whith ESMTP id P5590T140485355104000S1552986885594231_; Tue, 19 Mar 2019 17:14:59 +0800 (CST) X-IP-DOMAINF: 1 X-UNIQUE-TAG: <20fe962be3ba0bfa01cf34872c698821> X-RL-SENDER: shawn.lin@rock-chips.com X-SENDER: lintao@rock-chips.com X-LOGIN-NAME: shawn.lin@rock-chips.com X-FST-TO: jh80.chung@samsung.com X-SENDER-IP: 58.22.7.114 X-ATTACHMENT-NUM: 0 X-DNS-TYPE: 0 From: Shawn Lin To: Jaehoon Chung , Ulf Hansson Cc: linux-mmc@vger.kernel.org, linux-rockchip@lists.infradead.org, Douglas Anderson , Shawn Lin Subject: [PATCH v3 2/3] mmc: dw_mmc: Add hardware unbusy interrupt support Date: Tue, 19 Mar 2019 17:12:57 +0800 Message-Id: <1552986778-33904-3-git-send-email-shawn.lin@rock-chips.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1552986778-33904-1-git-send-email-shawn.lin@rock-chips.com> References: <1552986778-33904-1-git-send-email-shawn.lin@rock-chips.com> 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 So that we don't need to busy checking the busy state, but relinguish the CPU and rely on the hareware interrupt if available. Signed-off-by: Shawn Lin Tested-by: Ziyuan Xu --- Changes in v3: - remove unused irqflags and check hw_unbusy_int in dw_mci_wait_hw_unbusy() as well. Changes in v2: - add tag from Ziyuan Xu - remove lock for checking event since it's already atomic operation drivers/mmc/host/dw_mmc.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++ drivers/mmc/host/dw_mmc.h | 6 ++++++ 2 files changed, 57 insertions(+) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 703dedf..bb6b243 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -107,6 +107,8 @@ struct idmac_desc { /* Each descriptor can transfer up to 4KB of data in chained mode */ #define DW_MCI_DESC_DATA_LENGTH 0x1000 +DECLARE_WAIT_QUEUE_HEAD(unbusy_waiter); + #if defined(CONFIG_DEBUG_FS) static int dw_mci_req_show(struct seq_file *s, void *v) { @@ -231,9 +233,34 @@ static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset) return true; } +static inline int dw_mci_wait_hw_unbusy(struct dw_mci *host, + unsigned long timeout) +{ + int err; + + set_bit(EVENT_UNBUSY_COMPLETE, &host->pending_events); + err = host->drv_data->prepare_hw_unbusy(host, true); + if (err) + return err; + + wait_event_interruptible_timeout(unbusy_waiter, + !test_bit(EVENT_UNBUSY_COMPLETE, + &host->pending_events), + timeout); + + if (test_and_clear_bit(EVENT_UNBUSY_COMPLETE, + &host->pending_events)) { + dev_err(host->dev, "Busy; trying anyway\n"); + host->drv_data->prepare_hw_unbusy(host, false); + } + + return 0; +} + static void dw_mci_wait_while_busy(struct dw_mci *host, u32 cmd_flags) { u32 status; + int err; /* * Databook says that before issuing a new data transfer command @@ -245,6 +272,15 @@ static void dw_mci_wait_while_busy(struct dw_mci *host, u32 cmd_flags) */ if ((cmd_flags & SDMMC_CMD_PRV_DAT_WAIT) && !(cmd_flags & SDMMC_CMD_VOLT_SWITCH)) { + /* Resort to hw unbusy interrupt first */ + if (host->drv_data->prepare_hw_unbusy && + host->hw_unbusy_int != -EINVAL) { + err = dw_mci_wait_hw_unbusy(host, + jiffies + msecs_to_jiffies(500)); + if (!err) + return; + } + if (readl_poll_timeout_atomic(host->regs + SDMMC_STATUS, status, !(status & SDMMC_STATUS_BUSY), @@ -2734,6 +2770,14 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) dw_mci_handle_cd(host); } + /* Check hardware unbusy interrupt */ + if (host->hw_unbusy_int != -EINVAL && + pending & BIT(host->hw_unbusy_int)) { + mci_writel(host, RINTSTS, BIT(host->hw_unbusy_int)); + clear_bit(EVENT_UNBUSY_COMPLETE, &host->pending_events); + wake_up_interruptible(&unbusy_waiter); + } + if (pending & SDMMC_INT_SDIO(slot->sdio_id)) { mci_writel(host, RINTSTS, SDMMC_INT_SDIO(slot->sdio_id)); @@ -3249,6 +3293,8 @@ int dw_mci_probe(struct dw_mci *host) reset_control_deassert(host->pdata->rstc); } + host->hw_unbusy_int = -EINVAL; + if (drv_data && drv_data->init) { ret = drv_data->init(host); if (ret) { @@ -3360,6 +3406,11 @@ int dw_mci_probe(struct dw_mci *host) mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER | SDMMC_INT_TXDR | SDMMC_INT_RXDR | DW_MCI_ERROR_FLAGS); + + if (host->hw_unbusy_int != -EINVAL) + mci_writel(host, INTMASK, + mci_readl(host, INTMASK) | BIT(host->hw_unbusy_int)); + /* Enable mci interrupt */ mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 46e9f8e..fb4c173 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -33,6 +33,7 @@ enum dw_mci_state { enum { EVENT_CMD_COMPLETE = 0, + EVENT_UNBUSY_COMPLETE, EVENT_XFER_COMPLETE, EVENT_DATA_COMPLETE, EVENT_DATA_ERROR, @@ -124,6 +125,7 @@ struct dw_mci_dma_slave { * @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. + * @hw_unbusy_int: Number of unbusy interrupt in the interrupt registers. * @cmd11_timer: Timer for SD3.0 voltage switch over scheme. * @cto_timer: Timer for broken command transfer over scheme. * @dto_timer: Timer for broken data transfer over scheme. @@ -230,6 +232,7 @@ struct dw_mci { int irq; int sdio_id0; + int hw_unbusy_int; struct timer_list cmd11_timer; struct timer_list cto_timer; @@ -551,6 +554,8 @@ struct dw_mci_slot { * @set_ios: handle bus specific extensions. * @parse_dt: parse implementation specific device tree properties. * @execute_tuning: implementation specific tuning procedure. + * @prepare_hw_unbusy: implementation specific procedure for + * controlling hw unbusy interrupt. * * Provide controller implementation specific extensions. The usage of this * data structure is fully optional and usage of each member in this structure @@ -565,6 +570,7 @@ struct dw_mci_drv_data { int (*execute_tuning)(struct dw_mci_slot *slot, u32 opcode); int (*prepare_hs400_tuning)(struct dw_mci *host, struct mmc_ios *ios); + int (*prepare_hw_unbusy)(struct dw_mci *host, bool enable); int (*switch_voltage)(struct mmc_host *mmc, struct mmc_ios *ios); };