From patchwork Sat Jul 30 09:36:52 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Icenowy Zheng X-Patchwork-Id: 9253071 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 8FC6260869 for ; Sat, 30 Jul 2016 09:39:36 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 80CFE28408 for ; Sat, 30 Jul 2016 09:39:36 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 71A4327D76; Sat, 30 Jul 2016 09:39:36 +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 CE1EB27D76 for ; Sat, 30 Jul 2016 09:39:34 +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 1bTQiW-0008Ol-73; Sat, 30 Jul 2016 09:38:00 +0000 Received: from forward19o.cmail.yandex.net ([2a02:6b8:0:1a72::1e9]) by bombadil.infradead.org with esmtps (Exim 4.85_2 #1 (Red Hat Linux)) id 1bTQiR-0008Ku-Vm for linux-arm-kernel@lists.infradead.org; Sat, 30 Jul 2016 09:37:57 +0000 Received: from smtp3m.mail.yandex.net (smtp3m.mail.yandex.net [IPv6:2a02:6b8:0:2519::125]) by forward19o.cmail.yandex.net (Yandex) with ESMTP id 9356D2115D; Sat, 30 Jul 2016 12:37:30 +0300 (MSK) Received: from smtp3m.mail.yandex.net (localhost.localdomain [127.0.0.1]) by smtp3m.mail.yandex.net (Yandex) with ESMTP id 8C8612840C29; Sat, 30 Jul 2016 12:37:30 +0300 (MSK) Received: by smtp3m.mail.yandex.net (nwsmtp/Yandex) with ESMTPSA id 8Nc5VPSw41-bEBqY0pG; Sat, 30 Jul 2016 12:37:17 +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=1469871450; bh=JLUTltlQzey8L+bENEMa86cHrw6sFM6w2ZIfVSSbkCA=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References; b=eUn4l68DjxZxqSdu/zGlZ7rGhY+hVUhzf1QE+PfSewrs6Q/LRk229Np77pZgxQJVL +krB86/oy6N8SXiDpiR0QlP2+zLXGQyXzMQ+TGgn71mMvPkgJ7/Qei0CjPpTp1I4h7 JKhQxPRVv1YnQ9DYBQe+ZpY4SjHxkQSAdyLOkoos= Authentication-Results: smtp3m.mail.yandex.net; dkim=pass header.i=@aosc.xyz X-Yandex-ForeignMX: FR 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 2/2] mmc: host: sunxi: add support for A64 mmc controller Date: Sat, 30 Jul 2016 17:36:52 +0800 Message-Id: <20160730093652.10578-2-icenowy@aosc.xyz> X-Mailer: git-send-email 2.9.0 In-Reply-To: <20160730093652.10578-1-icenowy@aosc.xyz> References: <20160730093652.10578-1-icenowy@aosc.xyz> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20160730_023756_464770_18EA20CE X-CRM114-Status: GOOD ( 16.85 ) 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. Signed-off-by: Icenowy Zheng --- drivers/mmc/host/sunxi-mmc.c | 166 +++++++++++++++++++++++++++++++------------ 1 file changed, 122 insertions(+), 44 deletions(-) diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index 2ee4c21..ac56bcf 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; @@ -261,6 +278,9 @@ struct sunxi_mmc_host { /* vqmmc */ bool vqmmc_enabled; + + /* does the IP block support autocalibration? */ + bool can_calibrate; }; static int sunxi_mmc_reset_host(struct sunxi_mmc_host *host) @@ -653,10 +673,66 @@ 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_determine_delays(struct sunxi_mmc_host *host, + struct mmc_ios *ios, int rate) +{ + int index; + + if (rate <= 400000) { + index = SDXC_CLK_400K; + } else if (rate <= 25000000) { + index = SDXC_CLK_25M; + } else if (rate <= 52000000) { + if (ios->timing != MMC_TIMING_UHS_DDR50 && + ios->timing != MMC_TIMING_MMC_DDR52) { + index = SDXC_CLK_50M; + } else if (ios->bus_width == MMC_BUS_WIDTH_8) { + index = SDXC_CLK_50M_DDR_8BIT; + } else { + index = SDXC_CLK_50M_DDR; + } + } else { + return -EINVAL; + } + + clk_set_phase(host->clk_sample, host->clk_delays[index].sample); + clk_set_phase(host->clk_output, host->clk_delays[index].output); + + return 0; +} + static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host, struct mmc_ios *ios) { - u32 rate, oclk_dly, rval, sclk_dly; + u32 rate, rval; u32 clock = ios->clock; int ret; @@ -692,32 +768,18 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host, } mmc_writel(host, REG_CLKCR, rval); - /* determine delays */ - if (rate <= 400000) { - oclk_dly = host->clk_delays[SDXC_CLK_400K].output; - sclk_dly = host->clk_delays[SDXC_CLK_400K].sample; - } else if (rate <= 25000000) { - oclk_dly = host->clk_delays[SDXC_CLK_25M].output; - sclk_dly = host->clk_delays[SDXC_CLK_25M].sample; - } else if (rate <= 52000000) { - if (ios->timing != MMC_TIMING_UHS_DDR50 && - ios->timing != MMC_TIMING_MMC_DDR52) { - oclk_dly = host->clk_delays[SDXC_CLK_50M].output; - sclk_dly = host->clk_delays[SDXC_CLK_50M].sample; - } else if (ios->bus_width == MMC_BUS_WIDTH_8) { - oclk_dly = host->clk_delays[SDXC_CLK_50M_DDR_8BIT].output; - sclk_dly = host->clk_delays[SDXC_CLK_50M_DDR_8BIT].sample; - } else { - oclk_dly = host->clk_delays[SDXC_CLK_50M_DDR].output; - sclk_dly = host->clk_delays[SDXC_CLK_50M_DDR].sample; - } + if (host->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 { - return -EINVAL; + ret = sunxi_mmc_determine_delays(host, ios, rate); + if (ret) + return ret; } - clk_set_phase(host->clk_sample, sclk_dly); - clk_set_phase(host->clk_output, oclk_dly); - return sunxi_mmc_oclk_onoff(host, 1); } @@ -942,6 +1004,7 @@ static const struct of_device_id sunxi_mmc_of_match[] = { { .compatible = "allwinner,sun4i-a10-mmc", }, { .compatible = "allwinner,sun5i-a13-mmc", }, { .compatible = "allwinner,sun9i-a80-mmc", }, + { .compatible = "allwinner,sun50i-a64-mmc", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, sunxi_mmc_of_match); @@ -990,6 +1053,11 @@ static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host, else host->clk_delays = sunxi_mmc_clk_delays; + if (of_device_is_compatible(np, "allwinner,sun50i-a64-mmc")) + host->can_calibrate = true; + else + host->can_calibrate = false; + ret = mmc_regulator_get_supply(host->mmc); if (ret) { if (ret != -EPROBE_DEFER) @@ -1014,16 +1082,22 @@ static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host, return PTR_ERR(host->clk_mmc); } - host->clk_output = devm_clk_get(&pdev->dev, "output"); - if (IS_ERR(host->clk_output)) { - dev_err(&pdev->dev, "Could not get output clock\n"); - return PTR_ERR(host->clk_output); - } + /* self-calibrate version of the IP block needs no output/sample */ + if (!host->can_calibrate) { + host->clk_output = devm_clk_get(&pdev->dev, "output"); + if (IS_ERR(host->clk_output)) { + dev_err(&pdev->dev, "Could not get output clock\n"); + return PTR_ERR(host->clk_output); + } - host->clk_sample = devm_clk_get(&pdev->dev, "sample"); - if (IS_ERR(host->clk_sample)) { - dev_err(&pdev->dev, "Could not get sample clock\n"); - return PTR_ERR(host->clk_sample); + host->clk_sample = devm_clk_get(&pdev->dev, "sample"); + if (IS_ERR(host->clk_sample)) { + dev_err(&pdev->dev, "Could not get sample clock\n"); + return PTR_ERR(host->clk_sample); + } + } else { + host->clk_sample = NULL; + host->clk_output = NULL; } host->reset = devm_reset_control_get_optional(&pdev->dev, "ahb"); @@ -1042,16 +1116,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->can_calibrate) { + 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)) { @@ -1078,9 +1154,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->can_calibrate) + clk_disable_unprepare(host->clk_sample); error_disable_clk_output: - clk_disable_unprepare(host->clk_output); + if (!host->can_calibrate) + clk_disable_unprepare(host->clk_output); error_disable_clk_mmc: clk_disable_unprepare(host->clk_mmc); error_disable_clk_ahb: