From patchwork Sun Jul 31 11:02:34 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Icenowy Zheng X-Patchwork-Id: 9253341 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 BBFC1601C0 for ; Sun, 31 Jul 2016 11:05:54 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id AC4C028387 for ; Sun, 31 Jul 2016 11:05:54 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id A11F22845D; Sun, 31 Jul 2016 11:05:54 +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=-4.1 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_MED,T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id B21EE28387 for ; Sun, 31 Jul 2016 11:05:51 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.85_2 #1 (Red Hat Linux)) id 1bToXF-0003B0-A8; Sun, 31 Jul 2016 11:03:57 +0000 Received: from forward8h.cmail.yandex.net ([87.250.230.219]) by bombadil.infradead.org with esmtps (Exim 4.85_2 #1 (Red Hat Linux)) id 1bToX9-00033W-6H for linux-arm-kernel@lists.infradead.org; Sun, 31 Jul 2016 11:03:53 +0000 Received: from smtp1p.mail.yandex.net (smtp1p.mail.yandex.net [77.88.29.84]) by forward8h.cmail.yandex.net (Yandex) with ESMTP id E78F1217C9; Sun, 31 Jul 2016 14:03:27 +0300 (MSK) Received: from smtp1p.mail.yandex.net (localhost.localdomain [127.0.0.1]) by smtp1p.mail.yandex.net (Yandex) with ESMTP id E31A217804D7; Sun, 31 Jul 2016 14:03:27 +0300 (MSK) Received: by smtp1p.mail.yandex.net (nwsmtp/Yandex) with ESMTPSA id Q6Flkv0TLz-3FoWNjXI; Sun, 31 Jul 2016 14:03:20 +0300 (using TLSv1.2 with cipher ECDHE-RSA-AES128-SHA256 (128/128 bits)) (Client certificate not present) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=aosc.xyz; s=mail; t=1469963007; bh=XeEMfU5sPRajrf8Ny6XNXh1jy6BcNosyOkq0FMJRFjo=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References; b=RLc+TN+FelDTT6Ct7LJ8k46JU7SYhsPG1NImw7HOadxNyNcqNbKb0A9NjxMxmjQl7 LNhYTqu/a/VbZKi7/BP2hWMQrzEnSQzAreKG8QT62/yN6HiFnRkOkjG8cl/G5gCHhR JxDEAmGknRokefBIyxHCFcvlc10no0BtU2o6R9tc= Authentication-Results: smtp1p.mail.yandex.net; dkim=pass header.i=@aosc.xyz X-Yandex-ForeignMX: US X-Yandex-Suid-Status: 1 0, 1 0, 1 0, 1 0, 1 0, 1 0, 1 0, 1 0, 1 0, 1 0, 1 0, 1 0, 1 1130000036118848 From: Icenowy Zheng To: Rob Herring , Maxime Ripard , Chen-Yu Tsai , Ulf Hansson , Hans de Goede Subject: [PATCH v2 2/2] mmc: host: sunxi: add support for A64 mmc controller Date: Sun, 31 Jul 2016 19:02:34 +0800 Message-Id: <20160731110234.3593-3-icenowy@aosc.xyz> X-Mailer: git-send-email 2.9.0 In-Reply-To: <20160731110234.3593-1-icenowy@aosc.xyz> References: <20160731110234.3593-1-icenowy@aosc.xyz> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20160731_040351_707448_1DFBBE28 X-CRM114-Status: GOOD ( 16.28 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Mark Rutland , devicetree@vger.kernel.org, Michal Suchanek , linux-mmc@vger.kernel.org, linux-kernel@vger.kernel.org, Jaehoon Chung , Icenowy Zheng , linux-arm-kernel@lists.infradead.org MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP A64 SoC features a MMC controller which need only the mod clock, and can calibrate delay by itself. This patch adds support for the new MMC controller IP core. Based on work by Andre Przywara . Signed-off-by: Icenowy Zheng --- drivers/mmc/host/sunxi-mmc.c | 106 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 90 insertions(+), 16 deletions(-) diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index 2ec91ce..aa2abf3 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -72,6 +72,14 @@ #define SDXC_REG_CHDA (0x90) #define SDXC_REG_CBDA (0x94) +/* New registers introduced in A64 */ +#define SDXC_REG_A12A 0x058 /* SMC Auto Command 12 Register */ +#define SDXC_REG_SD_NTSR 0x05C /* SMC New Timing Set Register */ +#define SDXC_REG_DRV_DL 0x140 /* Drive Delay Control Register */ +#define SDXC_REG_SAMP_DL_REG 0x144 /* SMC sample delay control */ +#define SDXC_REG_DS_DL_REG 0x148 /* SMC data strobe delay control */ + + #define mmc_readl(host, reg) \ readl((host)->reg_base + SDXC_##reg) #define mmc_writel(host, reg, value) \ @@ -217,6 +225,15 @@ #define SDXC_CLK_50M_DDR 3 #define SDXC_CLK_50M_DDR_8BIT 4 +#define SDXC_2X_TIMING_MODE BIT(31) + +#define SDXC_CAL_START BIT(15) +#define SDXC_CAL_DONE BIT(14) +#define SDXC_CAL_DL_SHIFT 8 +#define SDXC_CAL_DL_SW_EN BIT(7) +#define SDXC_CAL_DL_SW_SHIFT 0 +#define SDXC_CAL_DL_MASK 0x3f + struct sunxi_mmc_clk_delay { u32 output; u32 sample; @@ -232,6 +249,9 @@ struct sunxi_idma_des { struct sunxi_mmc_cfg { u32 idma_des_size_bits; const struct sunxi_mmc_clk_delay *clk_delays; + + /* does the IP block support autocalibration? */ + bool can_calibrate; }; struct sunxi_mmc_host { @@ -657,6 +677,34 @@ static int sunxi_mmc_oclk_onoff(struct sunxi_mmc_host *host, u32 oclk_en) return 0; } +static int sunxi_mmc_calibrate(struct sunxi_mmc_host *host, + struct mmc_ios *ios, int reg_off) +{ + u32 reg = readl(host->reg_base + reg_off); + u32 delay; + + reg &= ~(SDXC_CAL_DL_MASK << SDXC_CAL_DL_SW_SHIFT); + reg &= ~SDXC_CAL_DL_SW_EN; + + writel(reg | SDXC_CAL_START, host->reg_base + reg_off); + + dev_dbg(mmc_dev(host->mmc), "calibration started\n"); + + while (!((reg = readl(host->reg_base + reg_off)) & SDXC_CAL_DONE)) + cpu_relax(); + + delay = (reg >> SDXC_CAL_DL_SHIFT) & SDXC_CAL_DL_MASK; + + reg &= ~SDXC_CAL_START; + reg |= (delay << SDXC_CAL_DL_SW_SHIFT) | SDXC_CAL_DL_SW_EN; + + writel(reg, host->reg_base + reg_off); + + dev_dbg(mmc_dev(host->mmc), "calibration ended, res is 0x%x\n", reg); + + return 0; +} + static int sunxi_mmc_clk_set_phase(struct sunxi_mmc_host *host, struct mmc_ios *ios, u32 rate) { @@ -727,9 +775,17 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host, } mmc_writel(host, REG_CLKCR, rval); - ret = sunxi_mmc_clk_set_phase(host, ios, rate); - if (ret) - return ret; + if (host->cfg->can_calibrate) { + ret = sunxi_mmc_calibrate(host, ios, SDXC_REG_SAMP_DL_REG); + if (ret) + return ret; + + /* TODO: enable calibrate on sdc2 SDXC_REG_DS_DL_REG of A64 */ + } else { + ret = sunxi_mmc_clk_set_phase(host, ios, rate); + if (ret) + return ret; + } return sunxi_mmc_oclk_onoff(host, 1); } @@ -982,21 +1038,31 @@ static const struct sunxi_mmc_clk_delay sun9i_mmc_clk_delays[] = { static const struct sunxi_mmc_cfg sun4i_a10_cfg = { .idma_des_size_bits = 13, .clk_delays = NULL, + .can_calibrate = false, }; static const struct sunxi_mmc_cfg sun5i_a13_cfg = { .idma_des_size_bits = 16, .clk_delays = NULL, + .can_calibrate = false, }; static const struct sunxi_mmc_cfg sun7i_a20_cfg = { .idma_des_size_bits = 16, .clk_delays = sunxi_mmc_clk_delays, + .can_calibrate = false, }; static const struct sunxi_mmc_cfg sun9i_a80_cfg = { .idma_des_size_bits = 16, .clk_delays = sun9i_mmc_clk_delays, + .can_calibrate = false, +}; + +static const struct sunxi_mmc_cfg sun50i_a64_cfg = { + .idma_des_size_bits = 16, + .clk_delays = NULL, + .can_calibrate = true, }; static const struct of_device_id sunxi_mmc_of_match[] = { @@ -1004,6 +1070,7 @@ static const struct of_device_id sunxi_mmc_of_match[] = { { .compatible = "allwinner,sun5i-a13-mmc", .data = &sun5i_a13_cfg }, { .compatible = "allwinner,sun7i-a20-mmc", .data = &sun7i_a20_cfg }, { .compatible = "allwinner,sun9i-a80-mmc", .data = &sun9i_a80_cfg }, + { .compatible = "allwinner,sun50i-a64-mmc", .data = &sun50i_a64_cfg }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, sunxi_mmc_of_match); @@ -1071,16 +1138,18 @@ static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host, goto error_disable_clk_ahb; } - ret = clk_prepare_enable(host->clk_output); - if (ret) { - dev_err(&pdev->dev, "Enable output clk err %d\n", ret); - goto error_disable_clk_mmc; - } + if (host->cfg->clk_delays) { + ret = clk_prepare_enable(host->clk_output); + if (ret) { + dev_err(&pdev->dev, "Enable output clk err %d\n", ret); + goto error_disable_clk_mmc; + } - ret = clk_prepare_enable(host->clk_sample); - if (ret) { - dev_err(&pdev->dev, "Enable sample clk err %d\n", ret); - goto error_disable_clk_output; + ret = clk_prepare_enable(host->clk_sample); + if (ret) { + dev_err(&pdev->dev, "Enable sample clk err %d\n", ret); + goto error_disable_clk_output; + } } if (!IS_ERR(host->reset)) { @@ -1107,9 +1176,11 @@ error_assert_reset: if (!IS_ERR(host->reset)) reset_control_assert(host->reset); error_disable_clk_sample: - clk_disable_unprepare(host->clk_sample); + if (host->cfg->clk_delays) + clk_disable_unprepare(host->clk_sample); error_disable_clk_output: - clk_disable_unprepare(host->clk_output); + if (host->cfg->clk_delays) + clk_disable_unprepare(host->clk_output); error_disable_clk_mmc: clk_disable_unprepare(host->clk_mmc); error_disable_clk_ahb: @@ -1191,8 +1262,11 @@ static int sunxi_mmc_remove(struct platform_device *pdev) if (!IS_ERR(host->reset)) reset_control_assert(host->reset); - clk_disable_unprepare(host->clk_sample); - clk_disable_unprepare(host->clk_output); + if (host->cfg->clk_delays) { + clk_disable_unprepare(host->clk_sample); + clk_disable_unprepare(host->clk_output); + } + clk_disable_unprepare(host->clk_mmc); clk_disable_unprepare(host->clk_ahb);