From patchwork Thu Nov 22 06:34:45 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yinbo Zhu X-Patchwork-Id: 10693573 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 3CA9618F0 for ; Thu, 22 Nov 2018 06:39:21 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 2F61E2C689 for ; Thu, 22 Nov 2018 06:39:21 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 20A692C693; Thu, 22 Nov 2018 06:39:21 +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=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI 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 76C012C692 for ; Thu, 22 Nov 2018 06:39:20 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727736AbeKVRRT (ORCPT ); Thu, 22 Nov 2018 12:17:19 -0500 Received: from inva021.nxp.com ([92.121.34.21]:45010 "EHLO inva021.nxp.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726958AbeKVRRT (ORCPT ); Thu, 22 Nov 2018 12:17:19 -0500 Received: from inva021.nxp.com (localhost [127.0.0.1]) by inva021.eu-rdc02.nxp.com (Postfix) with ESMTP id 100BB200181; Thu, 22 Nov 2018 07:39:17 +0100 (CET) Received: from invc005.ap-rdc01.nxp.com (invc005.ap-rdc01.nxp.com [165.114.16.14]) by inva021.eu-rdc02.nxp.com (Postfix) with ESMTP id 7DE77200179; Thu, 22 Nov 2018 07:39:13 +0100 (CET) Received: from titan.ap.freescale.net (TITAN.ap.freescale.net [10.192.208.233]) by invc005.ap-rdc01.nxp.com (Postfix) with ESMTP id 69FB04031B; Thu, 22 Nov 2018 14:39:09 +0800 (SGT) From: Yinbo Zhu To: linux-mmc@vger.kernel.org, Adrian Hunter , ulf.hansson@linaro.org Cc: yinbo.zhu@nxp.com, xiaobo.xie@nxp.com, leoyang.li@nxp.com, yangbo.lu@nxp.com Subject: [PATCH v5 2/5] mmc: sdhci-of-esdhc: add hs400 mode support Date: Thu, 22 Nov 2018 14:34:45 +0800 Message-Id: <20181122063448.12351-2-yinbo.zhu@nxp.com> X-Mailer: git-send-email 2.14.1 In-Reply-To: <20181122063448.12351-1-yinbo.zhu@nxp.com> References: <20181122063448.12351-1-yinbo.zhu@nxp.com> X-Virus-Scanned: ClamAV using ClamSMTP 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 From: Yangbo Lu 1. Perform the Tuning Process at the HS400 target operating frequency. Latched the clock division value. 2. if read transaction, then set the SDTIMNGCTL[FLW_CTL_BG]. 3. Switch to High Speed mode and then set the card clock frequency to a value not greater than 52Mhz 4. Clear TBCTL[TB_EN],tuning block enable bit. 5. Change to 8 bit DDR Mode 6. Switch the card to HS400 mode. 7. Set TBCTL[TB_EN], tuning block enable bit. 8. Clear SYSCTL[SDCLKEN] 9. Wait for PRSSTAT[SDSTB] to be set 10. Change the clock division to latched value.Set TBCTL[HS 400 mode] and Set SDCLKCTL[CMD_CLK_CTRL] 11. Set SYSCTL[SDCLKEN] 12. Wait for PRSSTAT[SDSTB] to be set 13. Set DLLCFG0[DLL_ENABLE] and DLLCFG0[DLL_FREQ_SEL]. 14. Wait for delay chain to lock. 15. Set TBCTL[HS400_WNDW_ADJUST] 16. Again clear SYSCTL[SDCLKEN] 17. Wait for PRSSTAT[SDSTB] to be set 18. Set ESDHCCTL[FAF] 19. Wait for ESDHCCTL[FAF] to be cleared 20. Set SYSCTL[SDCLKEN] 21. Wait for PRSSTAT[SDSTB] to be set. Signed-off-by: Yangbo Lu Signed-off-by: Yinbo Zhu Acked-by: Adrian Hunter --- Change in v5: rebase this patch drivers/mmc/host/sdhci-esdhc.h | 20 +++++++++ drivers/mmc/host/sdhci-of-esdhc.c | 78 ++++++++++++++++++++++++++++++++----- 2 files changed, 88 insertions(+), 10 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h index 3f16d9c..721a635 100644 --- a/drivers/mmc/host/sdhci-esdhc.h +++ b/drivers/mmc/host/sdhci-esdhc.h @@ -59,9 +59,29 @@ /* Tuning Block Control Register */ #define ESDHC_TBCTL 0x120 +#define ESDHC_HS400_WNDW_ADJUST 0x00000040 +#define ESDHC_HS400_MODE 0x00000010 #define ESDHC_TB_EN 0x00000004 #define ESDHC_TBPTR 0x128 +/* SD Clock Control Register */ +#define ESDHC_SDCLKCTL 0x144 +#define ESDHC_LPBK_CLK_SEL 0x80000000 +#define ESDHC_CMD_CLK_CTL 0x00008000 + +/* SD Timing Control Register */ +#define ESDHC_SDTIMNGCTL 0x148 +#define ESDHC_FLW_CTL_BG 0x00008000 + +/* DLL Config 0 Register */ +#define ESDHC_DLLCFG0 0x160 +#define ESDHC_DLL_ENABLE 0x80000000 +#define ESDHC_DLL_FREQ_SEL 0x08000000 + +/* DLL Status 0 Register */ +#define ESDHC_DLLSTAT0 0x170 +#define ESDHC_DLL_STS_SLV_LOCK 0x08000000 + /* Control Register for DMA transfer */ #define ESDHC_DMA_SYSCTL 0x40c #define ESDHC_PERIPHERAL_CLK_SEL 0x00080000 diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 86fc9f0..65419cb 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -592,6 +592,26 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) | (pre_div << ESDHC_PREDIV_SHIFT)); sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); + if (host->mmc->ios.timing == MMC_TIMING_MMC_HS400 && + clock == MMC_HS200_MAX_DTR) { + temp = sdhci_readl(host, ESDHC_TBCTL); + sdhci_writel(host, temp | ESDHC_HS400_MODE, ESDHC_TBCTL); + temp = sdhci_readl(host, ESDHC_SDCLKCTL); + sdhci_writel(host, temp | ESDHC_CMD_CLK_CTL, ESDHC_SDCLKCTL); + esdhc_clock_enable(host, true); + + temp = sdhci_readl(host, ESDHC_DLLCFG0); + temp |= ESDHC_DLL_ENABLE | ESDHC_DLL_FREQ_SEL; + sdhci_writel(host, temp, ESDHC_DLLCFG0); + temp = sdhci_readl(host, ESDHC_TBCTL); + sdhci_writel(host, temp | ESDHC_HS400_WNDW_ADJUST, ESDHC_TBCTL); + + esdhc_clock_enable(host, false); + temp = sdhci_readl(host, ESDHC_DMA_SYSCTL); + temp |= ESDHC_FLUSH_ASYNC_FIFO; + sdhci_writel(host, temp, ESDHC_DMA_SYSCTL); + } + /* Wait max 20 ms */ timeout = ktime_add_ms(ktime_get(), 20); while (!(sdhci_readl(host, ESDHC_PRSSTAT) & ESDHC_CLOCK_STABLE)) { @@ -603,6 +623,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) udelay(10); } + temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); temp |= ESDHC_CLOCK_SDCLKEN; sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); } @@ -728,25 +749,46 @@ static int esdhc_signal_voltage_switch(struct mmc_host *mmc, { }, }; -static int esdhc_execute_tuning(struct mmc_host *mmc, u32 opcode) +static void esdhc_tuning_block_enable(struct sdhci_host *host, bool enable) { - struct sdhci_host *host = mmc_priv(mmc); - struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); - struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host); u32 val; - /* Use tuning block for tuning procedure */ esdhc_clock_enable(host, false); + val = sdhci_readl(host, ESDHC_DMA_SYSCTL); val |= ESDHC_FLUSH_ASYNC_FIFO; sdhci_writel(host, val, ESDHC_DMA_SYSCTL); val = sdhci_readl(host, ESDHC_TBCTL); - val |= ESDHC_TB_EN; + if (enable) + val |= ESDHC_TB_EN; + else + val &= ~ESDHC_TB_EN; sdhci_writel(host, val, ESDHC_TBCTL); + esdhc_clock_enable(host, true); +} + +static int esdhc_execute_tuning(struct mmc_host *mmc, u32 opcode) +{ + struct sdhci_host *host = mmc_priv(mmc); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host); + bool hs400_tuning; + u32 val; + int ret; + + esdhc_tuning_block_enable(host, true); + + hs400_tuning = host->flags & SDHCI_HS400_TUNING; + ret = sdhci_execute_tuning(mmc, opcode); + + if (hs400_tuning) { + val = sdhci_readl(host, ESDHC_SDTIMNGCTL); + val |= ESDHC_FLW_CTL_BG; + sdhci_writel(host, val, ESDHC_SDTIMNGCTL); + } - sdhci_execute_tuning(mmc, opcode); if (host->tuning_err == -EAGAIN && esdhc->quirk_fixup_tuning) { /* program TBPTR[TB_WNDW_END_PTR] = 3*DIV_RATIO and @@ -765,7 +807,16 @@ static int esdhc_execute_tuning(struct mmc_host *mmc, u32 opcode) sdhci_writel(host, val, ESDHC_TBCTL); sdhci_execute_tuning(mmc, opcode); } - return 0; + return ret; +} + +static void esdhc_set_uhs_signaling(struct sdhci_host *host, + unsigned int timing) +{ + if (timing == MMC_TIMING_MMC_HS400) + esdhc_tuning_block_enable(host, true); + else + sdhci_set_uhs_signaling(host, timing); } #ifdef CONFIG_PM_SLEEP @@ -814,7 +865,7 @@ static SIMPLE_DEV_PM_OPS(esdhc_of_dev_pm_ops, .adma_workaround = esdhc_of_adma_workaround, .set_bus_width = esdhc_pltfm_set_bus_width, .reset = esdhc_reset, - .set_uhs_signaling = sdhci_set_uhs_signaling, + .set_uhs_signaling = esdhc_set_uhs_signaling, }; static const struct sdhci_ops sdhci_esdhc_le_ops = { @@ -831,7 +882,7 @@ static SIMPLE_DEV_PM_OPS(esdhc_of_dev_pm_ops, .adma_workaround = esdhc_of_adma_workaround, .set_bus_width = esdhc_pltfm_set_bus_width, .reset = esdhc_reset, - .set_uhs_signaling = sdhci_set_uhs_signaling, + .set_uhs_signaling = esdhc_set_uhs_signaling, }; static const struct sdhci_pltfm_data sdhci_esdhc_be_pdata = { @@ -909,6 +960,12 @@ static void esdhc_init(struct platform_device *pdev, struct sdhci_host *host) } } +static int esdhc_prepare_ddr_to_hs400(struct mmc_host *mmc) +{ + esdhc_tuning_block_enable(mmc_priv(mmc), false); + return 0; +} + static int sdhci_esdhc_probe(struct platform_device *pdev) { struct sdhci_host *host; @@ -932,6 +989,7 @@ static int sdhci_esdhc_probe(struct platform_device *pdev) host->mmc_host_ops.start_signal_voltage_switch = esdhc_signal_voltage_switch; host->mmc_host_ops.execute_tuning = esdhc_execute_tuning; + host->mmc_host_ops.prepare_ddr_to_hs400 = esdhc_prepare_ddr_to_hs400; host->tuning_delay = 1; esdhc_init(pdev, host);