From patchwork Tue Apr 14 10:05:43 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: yangbo lu X-Patchwork-Id: 6214631 Return-Path: X-Original-To: patchwork-linux-mmc@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 51155BF4A6 for ; Tue, 14 Apr 2015 10:24:31 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 15D08202EB for ; Tue, 14 Apr 2015 10:24:30 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id BA6CF202DD for ; Tue, 14 Apr 2015 10:24:28 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753533AbbDNKY1 (ORCPT ); Tue, 14 Apr 2015 06:24:27 -0400 Received: from mail-by2on0135.outbound.protection.outlook.com ([207.46.100.135]:38200 "EHLO na01-by2-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1753532AbbDNKY0 (ORCPT ); Tue, 14 Apr 2015 06:24:26 -0400 Received: from DM2PR03CA0032.namprd03.prod.outlook.com (10.141.96.31) by DM2PR0301MB1197.namprd03.prod.outlook.com (0.160.217.147) with Microsoft SMTP Server (TLS) id 15.1.136.25; Tue, 14 Apr 2015 10:08:58 +0000 Received: from BN1BFFO11FD048.protection.gbl (2a01:111:f400:7c10::1:129) by DM2PR03CA0032.outlook.office365.com (2a01:111:e400:2428::31) with Microsoft SMTP Server (TLS) id 15.1.136.25 via Frontend Transport; Tue, 14 Apr 2015 10:08:57 +0000 Authentication-Results: spf=fail (sender IP is 192.88.168.50) smtp.mailfrom=freescale.com; freescale.mail.onmicrosoft.com; dkim=none (message not signed) header.d=none; Received-SPF: Fail (protection.outlook.com: domain of freescale.com does not designate 192.88.168.50 as permitted sender) receiver=protection.outlook.com; client-ip=192.88.168.50; helo=tx30smr01.am.freescale.net; Received: from tx30smr01.am.freescale.net (192.88.168.50) by BN1BFFO11FD048.mail.protection.outlook.com (10.58.145.3) with Microsoft SMTP Server (TLS) id 15.1.142.12 via Frontend Transport; Tue, 14 Apr 2015 10:08:57 +0000 Received: from titan.ap.freescale.net ([10.192.208.233]) by tx30smr01.am.freescale.net (8.14.3/8.14.0) with ESMTP id t3EA8rie023960; Tue, 14 Apr 2015 03:08:55 -0700 From: Yangbo Lu To: , , CC: Yangbo Lu Subject: [v2, 4/4] mmc: esdhc: add eMMC DDR mode support Date: Tue, 14 Apr 2015 18:05:43 +0800 Message-ID: <1429005943-21235-1-git-send-email-yangbo.lu@freescale.com> X-Mailer: git-send-email 2.1.0.27.g96db324 X-EOPAttributedMessage: 0 X-Forefront-Antispam-Report: CIP:192.88.168.50; CTRY:US; IPV:NLI; EFV:NLI; BMV:1; SFV:NSPM; SFS:(10019020)(6009001)(339900001)(199003)(189002)(2201001)(86362001)(92566002)(50226001)(105606002)(47776003)(4001410100001)(77156002)(85426001)(106466001)(104016003)(19580405001)(50986999)(6806004)(87936001)(19580395003)(46102003)(33646002)(229853001)(36756003)(77096005)(62966003)(48376002)(50466002)(4001430100001); DIR:OUT; SFP:1102; SCL:1; SRVR:DM2PR0301MB1197; H:tx30smr01.am.freescale.net; FPR:; SPF:Fail; MLV:sfv; A:1; MX:1; LANG:en; MIME-Version: 1.0 X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:;SRVR:DM2PR0301MB1197; X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:; X-Exchange-Antispam-Report-CFA-Test: BCL:0; PCL:0; RULEID:(601004)(5005006)(5002010); SRVR:DM2PR0301MB1197; BCL:0; PCL:0; RULEID:; SRVR:DM2PR0301MB1197; X-Forefront-PRVS: 054642504A X-OriginatorOrg: freescale.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 14 Apr 2015 10:08:57.8165 (UTC) X-MS-Exchange-CrossTenant-Id: 710a03f5-10f6-4d38-9ff4-a80b81da590d X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=710a03f5-10f6-4d38-9ff4-a80b81da590d; Ip=[192.88.168.50]; Helo=[tx30smr01.am.freescale.net] X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: DM2PR0301MB1197 Sender: linux-mmc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-mmc@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add eMMC DDR mode support for Freescale SDHC adapter card. The u-boot should provide device tree properties 'adapter-type' and 'periperal-frequency' for this feature, if not, the card would not use DDR mode. Signed-off-by: Yangbo Lu --- drivers/mmc/host/sdhci-esdhc.h | 24 +++++++ drivers/mmc/host/sdhci-of-esdhc.c | 132 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 152 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h index 3497cfa..a6f887e 100644 --- a/drivers/mmc/host/sdhci-esdhc.h +++ b/drivers/mmc/host/sdhci-esdhc.h @@ -27,10 +27,32 @@ #define ESDHC_CLOCK_MASK 0x0000fff0 #define ESDHC_PREDIV_SHIFT 8 #define ESDHC_DIVIDER_SHIFT 4 +#define ESDHC_CLOCK_CRDEN 0x00000008 #define ESDHC_CLOCK_PEREN 0x00000004 #define ESDHC_CLOCK_HCKEN 0x00000002 #define ESDHC_CLOCK_IPGEN 0x00000001 +#define ESDHCI_PRESENT_STATE 0x24 +#define ESDHC_CLK_STABLE 0x00000008 + +#define ESDHC_CAPABILITIES_1 0x114 +#define ESDHC_MODE_MASK 0x00000007 +#define ESDHC_MODE_DDR50_SEL 0xfffffffc +#define ESDHC_MODE_DDR50 0x00000004 + +#define ESDHC_CLOCK_CONTROL 0x144 +#define ESDHC_CLKLPBK_EXTPIN 0x80000000 +#define ESDHC_CMDCLK_SHIFTED 0x00008000 + +/* SDHC Adapter Card Type */ +#define ESDHC_ADAPTER_TYPE_EMMC45 0x1 /* eMMC Card Rev4.5 */ +#define ESDHC_ADAPTER_TYPE_SDMMC_LEGACY 0x2 /* SD/MMC Legacy Card */ +#define ESDHC_ADAPTER_TYPE_EMMC44 0x3 /* eMMC Card Rev4.4 */ +#define ESDHC_ADAPTER_TYPE_RSV 0x4 /* Reserved */ +#define ESDHC_ADAPTER_TYPE_MMC 0x5 /* MMC Card */ +#define ESDHC_ADAPTER_TYPE_SD 0x6 /* SD Card Rev2.0 Rev3.0 */ +#define ESDHC_NO_ADAPTER 0x7 /* No Card is Present*/ + /* pltfm-specific */ #define ESDHC_HOST_CONTROL_LE 0x20 @@ -44,6 +66,8 @@ /* OF-specific */ #define ESDHC_DMA_SYSCTL 0x40c #define ESDHC_DMA_SNOOP 0x00000040 +#define ESDHC_FLUSH_ASYNC_FIFO 0x00040000 +#define ESDHC_USE_PERIPHERAL_CLK 0x00080000 #define ESDHC_HOST_CONTROL_RES 0x05 diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 858d65d..284094c 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -24,11 +24,30 @@ #define VENDOR_V_22 0x12 #define VENDOR_V_23 0x13 + +static u32 adapter_type; +static bool peripheral_clk_available; + static u32 esdhc_readl(struct sdhci_host *host, int reg) { u32 ret; - ret = sdhci_32bs_readl(host, reg); + if (reg == SDHCI_CAPABILITIES_1) { + ret = sdhci_32bs_readl(host, ESDHC_CAPABILITIES_1); + switch (adapter_type) { + case ESDHC_ADAPTER_TYPE_EMMC44: + if (ret & ESDHC_MODE_DDR50) { + ret &= ESDHC_MODE_DDR50_SEL; + /* enable 1/8V DDR capable */ + host->mmc->caps |= MMC_CAP_1_8V_DDR; + } else + ret &= ~ESDHC_MODE_MASK; + break; + default: + ret &= ~ESDHC_MODE_MASK; + } + } else + ret = sdhci_32bs_readl(host, reg); /* * The bit of ADMA flag in eSDHC is not compatible with standard * SDHC register, so set fake flag SDHCI_CAN_DO_ADMA2 when ADMA is @@ -159,8 +178,11 @@ static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg) } /* Prevent SDHCI core from writing reserved bits (e.g. HISPD). */ - if (reg == SDHCI_HOST_CONTROL) + if (reg == SDHCI_HOST_CONTROL) { val &= ~ESDHC_HOST_CONTROL_RES; + val &= ~SDHCI_CTRL_HISPD; + val |= (sdhci_32bs_readl(host, reg) & SDHCI_CTRL_HISPD); + } sdhci_clrsetbits(host, 0xff, val, reg); } @@ -307,6 +329,84 @@ static void esdhc_reset(struct sdhci_host *host, u8 mask) sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); } +static void esdhc_clock_control(struct sdhci_host *host, bool enable) +{ + u32 value; + u32 time_out; + + value = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); + + if (enable) + value |= ESDHC_CLOCK_CRDEN; + else + value &= ~ESDHC_CLOCK_CRDEN; + + sdhci_writel(host, value, ESDHC_SYSTEM_CONTROL); + + time_out = 20; + value = ESDHC_CLK_STABLE; + while (!(sdhci_readl(host, ESDHCI_PRESENT_STATE) & value)) { + if (time_out == 0) { + pr_err("%s: Internal clock never stabilised.\n", + mmc_hostname(host->mmc)); + break; + } + time_out--; + mdelay(1); + } +} + +static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) +{ + u16 ctrl_2; + u32 time_out; + u32 value; + + ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); + /* Select Bus Speed Mode for host */ + ctrl_2 &= ~SDHCI_CTRL_UHS_MASK; + if ((uhs == MMC_TIMING_MMC_HS200) || + (uhs == MMC_TIMING_UHS_SDR104)) + ctrl_2 |= SDHCI_CTRL_UHS_SDR104; + else if (uhs == MMC_TIMING_UHS_SDR12) + ctrl_2 |= SDHCI_CTRL_UHS_SDR12; + else if (uhs == MMC_TIMING_UHS_SDR25) + ctrl_2 |= SDHCI_CTRL_UHS_SDR25; + else if (uhs == MMC_TIMING_UHS_SDR50) + ctrl_2 |= SDHCI_CTRL_UHS_SDR50; + else if (uhs == MMC_TIMING_UHS_DDR50) + ctrl_2 |= SDHCI_CTRL_UHS_DDR50; + + if (uhs == MMC_TIMING_UHS_DDR50) { + esdhc_clock_control(host, false); + sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); + value = sdhci_readl(host, ESDHC_CLOCK_CONTROL); + value |= (ESDHC_CLKLPBK_EXTPIN | ESDHC_CMDCLK_SHIFTED); + sdhci_writel(host, value, ESDHC_CLOCK_CONTROL); + esdhc_clock_control(host, true); + + esdhc_clock_control(host, false); + value = sdhci_readl(host, ESDHC_DMA_SYSCTL); + value |= ESDHC_FLUSH_ASYNC_FIFO; + sdhci_writel(host, value, ESDHC_DMA_SYSCTL); + /* Wait max 20 ms */ + time_out = 20; + value = ESDHC_FLUSH_ASYNC_FIFO; + while (sdhci_readl(host, ESDHC_DMA_SYSCTL) & value) { + if (time_out == 0) { + pr_err("%s: FAF bit is auto cleaned failed.\n", + mmc_hostname(host->mmc)); + + break; + } + time_out--; + mdelay(1); + } + esdhc_clock_control(host, true); + } else + sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); +} + static const struct sdhci_ops sdhci_esdhc_ops = { .read_l = esdhc_readl, .read_w = esdhc_readw, @@ -322,7 +422,7 @@ static const struct sdhci_ops sdhci_esdhc_ops = { .adma_workaround = esdhci_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, }; #ifdef CONFIG_PM @@ -376,6 +476,8 @@ static void esdhc_get_property(struct platform_device *pdev) struct device_node *np = pdev->dev.of_node; struct sdhci_host *host = platform_get_drvdata(pdev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + const __be32 *value; + int size; sdhci_get_of_property(pdev); @@ -383,6 +485,18 @@ static void esdhc_get_property(struct platform_device *pdev) mmc_of_parse(host->mmc); mmc_of_parse_voltage(np, &host->ocr_mask); + value = of_get_property(np, "adapter-type", &size); + if (value && size == sizeof(*value) && *value) + adapter_type = be32_to_cpup(value); + + /* If getting a peripheral-frequency, use it instead */ + value = of_get_property(np, "peripheral-frequency", &size); + if (value && size == sizeof(*value) && *value) { + pltfm_host->clock = be32_to_cpup(value); + peripheral_clk_available = true; + } else + peripheral_clk_available = false; + if (of_device_is_compatible(np, "fsl,p2020-esdhc")) { /* * Freescale messed up with P2020 as it has a non-standard @@ -402,13 +516,23 @@ static int sdhci_esdhc_probe(struct platform_device *pdev) { struct sdhci_host *host; int ret; - + u32 value; host = sdhci_pltfm_init(pdev, &sdhci_esdhc_pdata, 0); if (IS_ERR(host)) return PTR_ERR(host); esdhc_get_property(pdev); + + /* Select peripheral clock as the eSDHC clock */ + if (peripheral_clk_available) { + esdhc_clock_control(host, false); + value = sdhci_readl(host, ESDHC_DMA_SYSCTL); + value |= ESDHC_USE_PERIPHERAL_CLK; + sdhci_writel(host, value, ESDHC_DMA_SYSCTL); + esdhc_clock_control(host, true); + } + ret = sdhci_add_host(host); if (ret) goto err;