From patchwork Sat Dec 19 19:16:00 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lucas Stach X-Patchwork-Id: 7890701 Return-Path: X-Original-To: patchwork-linux-mmc@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id DC0039F349 for ; Sat, 19 Dec 2015 19:23:37 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id D68172050E for ; Sat, 19 Dec 2015 19:23:36 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id EC88B20519 for ; Sat, 19 Dec 2015 19:23:35 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932724AbbLSTX0 (ORCPT ); Sat, 19 Dec 2015 14:23:26 -0500 Received: from ns.lynxeye.de ([87.118.118.114]:40484 "EHLO lynxeye.de" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S932366AbbLSTXK (ORCPT ); Sat, 19 Dec 2015 14:23:10 -0500 Received: by lynxeye.de (Postfix, from userid 501) id DA9DF26C2005; Sat, 19 Dec 2015 20:16:10 +0100 (CET) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Spam-Level: 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 Received: from tellur.intern.lynxeye.de (p57B5F3A9.dip0.t-ipconnect.de [87.181.243.169]) by lynxeye.de (Postfix) with ESMTPA id D8A8C26C2002; Sat, 19 Dec 2015 20:16:08 +0100 (CET) From: Lucas Stach To: Ulf Hansson Cc: Stephen Warren , Thierry Reding , Alexandre Courbot , linux-mmc@vger.kernel.org, linux-tegra@vger.kernel.org Subject: [PATCH 1/5] mmc: tegra: implement module external clock change Date: Sat, 19 Dec 2015 20:16:00 +0100 Message-Id: <1450552564-32697-2-git-send-email-dev@lynxeye.de> X-Mailer: git-send-email 2.5.0 In-Reply-To: <1450552564-32697-1-git-send-email-dev@lynxeye.de> References: <1450552564-32697-1-git-send-email-dev@lynxeye.de> 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 Allow the the driver to change the clock supplied from the CAR directly, minimizing the need to divide the clock inside the SDMMC module itself. This allows for higher clock speeds than the default 48MHz supplied to the module and is a prerequisite to support DDR signaling modes, where the Tegra host needs to be run with a fixed internal divider of 2 for data to be sampled correctly. (Tegra K1 TRM v03p chapter 29.7.1.1) Also enable the broken preset value quirk as the preset values need to be adapted to the changed clocking. While Tegra114+ allows this through vendor registers, there is no such way for Tegra30. Takes the easy way out and keep things consistent between the different SoC generations by flagging the preset registers as unusable. Signed-off-by: Lucas Stach --- drivers/mmc/host/sdhci-tegra.c | 56 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 6 deletions(-) diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index ad28b49..877daf5 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -49,6 +49,7 @@ struct sdhci_tegra_soc_data { struct sdhci_tegra { const struct sdhci_tegra_soc_data *soc_data; struct gpio_desc *power_gpio; + bool ddr_signaling; }; static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg) @@ -143,6 +144,8 @@ static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask) if (soc_data->nvquirks & NVQUIRK_DISABLE_SDR104) misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_SDR104; sdhci_writew(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL); + + tegra_host->ddr_signaling = false; } static void tegra_sdhci_set_bus_width(struct sdhci_host *host, int bus_width) @@ -164,15 +167,53 @@ static void tegra_sdhci_set_bus_width(struct sdhci_host *host, int bus_width) sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); } +static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_tegra *tegra_host = pltfm_host->priv; + unsigned long host_clk; + + if (!clock) + return; + + host_clk = tegra_host->ddr_signaling ? clock * 2 : clock; + clk_set_rate(pltfm_host->clk, host_clk); + host->max_clk = clk_get_rate(pltfm_host->clk); + + return sdhci_set_clock(host, clock); +} + +static void tegra_sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_tegra *tegra_host = pltfm_host->priv; + + if (timing == MMC_TIMING_UHS_DDR50) + tegra_host->ddr_signaling = true; + + return sdhci_set_uhs_signaling(host, timing); +} + +static unsigned int tegra_sdhci_get_max_clock(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + + /* + * DDR modes require the host to run at double the card frequency, so + * the maximum rate we can support is half of the module input clock. + */ + return clk_round_rate(pltfm_host->clk, UINT_MAX) / 2; +} + static const struct sdhci_ops tegra_sdhci_ops = { .get_ro = tegra_sdhci_get_ro, .read_w = tegra_sdhci_readw, .write_l = tegra_sdhci_writel, - .set_clock = sdhci_set_clock, + .set_clock = tegra_sdhci_set_clock, .set_bus_width = tegra_sdhci_set_bus_width, .reset = tegra_sdhci_reset, - .set_uhs_signaling = sdhci_set_uhs_signaling, - .get_max_clock = sdhci_pltfm_clk_get_max_clock, + .set_uhs_signaling = tegra_sdhci_set_uhs_signaling, + .get_max_clock = tegra_sdhci_get_max_clock, }; static const struct sdhci_pltfm_data sdhci_tegra20_pdata = { @@ -197,6 +238,7 @@ static const struct sdhci_pltfm_data sdhci_tegra30_pdata = { SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, .ops = &tegra_sdhci_ops, }; @@ -212,11 +254,11 @@ static const struct sdhci_ops tegra114_sdhci_ops = { .read_w = tegra_sdhci_readw, .write_w = tegra_sdhci_writew, .write_l = tegra_sdhci_writel, - .set_clock = sdhci_set_clock, + .set_clock = tegra_sdhci_set_clock, .set_bus_width = tegra_sdhci_set_bus_width, .reset = tegra_sdhci_reset, - .set_uhs_signaling = sdhci_set_uhs_signaling, - .get_max_clock = sdhci_pltfm_clk_get_max_clock, + .set_uhs_signaling = tegra_sdhci_set_uhs_signaling, + .get_max_clock = tegra_sdhci_get_max_clock, }; static const struct sdhci_pltfm_data sdhci_tegra114_pdata = { @@ -226,6 +268,7 @@ static const struct sdhci_pltfm_data sdhci_tegra114_pdata = { SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, .ops = &tegra114_sdhci_ops, }; @@ -271,6 +314,7 @@ static int sdhci_tegra_probe(struct platform_device *pdev) rc = -ENOMEM; goto err_alloc_tegra_host; } + tegra_host->ddr_signaling = false; tegra_host->soc_data = soc_data; pltfm_host->priv = tegra_host;