From patchwork Mon Jun 13 23:04:34 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Doug Anderson X-Patchwork-Id: 9174687 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 B09FC60573 for ; Mon, 13 Jun 2016 23:09:22 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A20C626861 for ; Mon, 13 Jun 2016 23:09:22 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 96D022723E; Mon, 13 Jun 2016 23:09:22 +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=-6.8 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI,T_DKIM_INVALID autolearn=unavailable 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 14F8E26861 for ; Mon, 13 Jun 2016 23:09:22 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1423763AbcFMXJD (ORCPT ); Mon, 13 Jun 2016 19:09:03 -0400 Received: from mail-pf0-f176.google.com ([209.85.192.176]:32827 "EHLO mail-pf0-f176.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1423789AbcFMXFG (ORCPT ); Mon, 13 Jun 2016 19:05:06 -0400 Received: by mail-pf0-f176.google.com with SMTP id y124so50577270pfy.0 for ; Mon, 13 Jun 2016 16:05:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=Lg+G/qAaBssCIFDcwWGHP4uJOwOCazX31mOp8macXaE=; b=dXpynqA7GgSDyIqypewIcIOS+bPJkkMWQxtFNvsoyQJVbywEFoQWKrCtEvmQgruza7 cLMHJUDMTZmAZXIKMxLftRKEYv6bgIOTxMM1dsVhJR+S2s+7RVtkPFRRNtAj3zWtRI2h I8Ao25EQnuk2RU3J9tfhRCWmkOnsLAm3bCnNg= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=Lg+G/qAaBssCIFDcwWGHP4uJOwOCazX31mOp8macXaE=; b=T5PCDiRirSCUpLHoYiLU/a0eXCaEl56Rj9dXsCBmBbXOgclJN7TyeaF1P5ycwev0OE 1Ti+wWDDwoY01nf8vfhFnpz2DvOI5ZOKAEDl4n/psma3x7K3FqT7SCDZ7f/MdDPPLYM9 8ehVKCuWZHZjRP28M2aGdMg04FJ46QUgg/UMZnmwFLB68GRjCq7WYNCur8iy3Wu0gPfT E0387RkMwd/AsW/TZG68wLWwcoS7zHv7lKGI2glk5jS9sFkaXDlSMjxFEEcMQ5E9E//M X43istQbdE1jFe4mrVU3JzbGImbXSxbl3ohbJKNftrbC8Dp9KwS6/szh9yuBeD1dbnvj RZmg== X-Gm-Message-State: ALyK8tJG7S3YdTR5tRFRPyoJwl/R0F5ESOfO4DiiKAS95SKZ92hL/rR7f5skUKGZPhiAI25E X-Received: by 10.98.5.196 with SMTP id 187mr24284923pff.133.1465859105956; Mon, 13 Jun 2016 16:05:05 -0700 (PDT) Received: from tictac.mtv.corp.google.com ([172.22.65.76]) by smtp.gmail.com with ESMTPSA id pk18sm8906434pab.27.2016.06.13.16.05.04 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 13 Jun 2016 16:05:04 -0700 (PDT) From: Douglas Anderson To: ulf.hansson@linaro.org, kishon@ti.com, Heiko Stuebner , robh+dt@kernel.org Cc: shawn.lin@rock-chips.com, xzy.xu@rock-chips.com, briannorris@chromium.org, adrian.hunter@intel.com, linux-rockchip@lists.infradead.org, linux-mmc@vger.kernel.org, devicetree@vger.kernel.org, Douglas Anderson , linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org Subject: [PATCH v2 10/11] phy: rockchip-emmc: Set phyctrl_frqsel based on card clock Date: Mon, 13 Jun 2016 16:04:34 -0700 Message-Id: <1465859076-4868-11-git-send-email-dianders@chromium.org> X-Mailer: git-send-email 2.8.0.rc3.226.g39d4020 In-Reply-To: <1465859076-4868-1-git-send-email-dianders@chromium.org> References: <1465859076-4868-1-git-send-email-dianders@chromium.org> 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 The "phyctrl_frqsel" is described in the Arasan datasheet [1] as "the frequency range of DLL operation". Although the Rockchip variant of this PHY has different ranges than the reference Arasan PHY it appears as if the functionality is similar. We should set this phyctrl field properly. Note: as per Rockchip engineers, apparently the "phyctrl_frqsel" is actually only useful in HS200 / HS400 modes even though the DLL itself it used for some purposes in all modes. See the discussion in the earlier change in this series: ("mmc: sdhci-of-arasan: Always power the PHY off/on when clock changes"). In any case, it shouldn't hurt to set this always. Note that this change should allow boards to run at HS200 / HS400 speed modes while running at 100 MHz or 150 MHz. In fact, running HS400 at 150 MHz (giving 300 MB/s) is the main motivation of this series, since performance is still good but signal integrity problems are less prevelant at 150 MHz. [1]: https://arasan.com/wp-content/media/eMMC-5-1-Total-Solution_Rev-1-3.pdf Signed-off-by: Douglas Anderson Acked-by: Kishon Vijay Abraham I --- Changes in v2: - Warn if we're more than 15 MHz from ideal rate (Shawn) - Move code cleanup before set phyctrl_frqsel based on card clock (Shawn) - Fix typo USB => SDHCI (Shawn) drivers/phy/phy-rockchip-emmc.c | 82 ++++++++++++++++++++++++++++++++++------- 1 file changed, 69 insertions(+), 13 deletions(-) diff --git a/drivers/phy/phy-rockchip-emmc.c b/drivers/phy/phy-rockchip-emmc.c index 23fe50864526..51ddd543fd04 100644 --- a/drivers/phy/phy-rockchip-emmc.c +++ b/drivers/phy/phy-rockchip-emmc.c @@ -14,6 +14,7 @@ * GNU General Public License for more details. */ +#include #include #include #include @@ -78,16 +79,73 @@ struct rockchip_emmc_phy { unsigned int reg_offset; struct regmap *reg_base; + struct clk *emmcclk; }; -static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy, - bool on_off) +static int rockchip_emmc_phy_power(struct phy *phy, bool on_off) { + struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); unsigned int caldone; unsigned int dllrdy; + unsigned int freqsel = PHYCTRL_FREQSEL_200M; unsigned long timeout; /* + * We purposely get the clock here and not in probe to avoid the + * circular dependency problem. We expect: + * - PHY driver to probe + * - SDHCI driver to start probe + * - SDHCI driver to register it's clock + * - SDHCI driver to get the PHY + * - SDHCI driver to power on the PHY + */ + if (!rk_phy->emmcclk) { + rk_phy->emmcclk = devm_clk_get(&phy->dev, "emmcclk"); + + /* Don't expect defer at this point; try next time */ + if (PTR_ERR(rk_phy->emmcclk) == -EPROBE_DEFER) { + dev_warn(&phy->dev, "Unexpected emmcclk defer\n"); + rk_phy->emmcclk = NULL; + } + } + + if (!IS_ERR_OR_NULL(rk_phy->emmcclk)) { + unsigned long rate = clk_get_rate(rk_phy->emmcclk); + unsigned long ideal_rate; + unsigned long diff; + + switch (rate) { + case 0 ... 74999999: + ideal_rate = 50000000; + freqsel = PHYCTRL_FREQSEL_50M; + break; + case 75000000 ... 124999999: + ideal_rate = 100000000; + freqsel = PHYCTRL_FREQSEL_100M; + break; + case 125000000 ... 174999999: + ideal_rate = 150000000; + freqsel = PHYCTRL_FREQSEL_150M; + break; + default: + ideal_rate = 200000000; + break; + }; + + diff = (rate > ideal_rate) ? + rate - ideal_rate : ideal_rate - rate; + + /* + * In order for tuning delays to be accurate we need to be + * pretty spot on for the DLL range, so warn if we're too + * far off. Also warn if we're above the 200 MHz max. Don't + * warn for really slow rates since we won't be tuning then. + */ + if ((rate > 50000000 && diff > 15000000) || (rate > 200000000)) + dev_warn(&phy->dev, "Unsupported rate: %lu\n", rate); + } + + /* * Keep phyctrl_pdb and phyctrl_endll low to allow * initialization of CALIO state M/C DFFs */ @@ -132,6 +190,13 @@ static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy, return -ETIMEDOUT; } + /* Set the frequency of the DLL operation */ + regmap_write(rk_phy->reg_base, + rk_phy->reg_offset + GRF_EMMCPHY_CON0, + HIWORD_UPDATE(freqsel, PHYCTRL_FREQSEL_MASK, + PHYCTRL_FREQSEL_SHIFT)); + + /* Turn on the DLL */ regmap_write(rk_phy->reg_base, rk_phy->reg_offset + GRF_EMMCPHY_CON6, HIWORD_UPDATE(PHYCTRL_ENDLL_ENABLE, @@ -168,23 +233,14 @@ static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy, static int rockchip_emmc_phy_power_off(struct phy *phy) { - struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); - /* Power down emmc phy analog blocks */ - return rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_OFF); + return rockchip_emmc_phy_power(phy, PHYCTRL_PDB_PWR_OFF); } static int rockchip_emmc_phy_power_on(struct phy *phy) { struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); - /* DLL operation: 200 MHz */ - regmap_write(rk_phy->reg_base, - rk_phy->reg_offset + GRF_EMMCPHY_CON0, - HIWORD_UPDATE(PHYCTRL_FREQSEL_200M, - PHYCTRL_FREQSEL_MASK, - PHYCTRL_FREQSEL_SHIFT)); - /* Drive impedance: 50 Ohm */ regmap_write(rk_phy->reg_base, rk_phy->reg_offset + GRF_EMMCPHY_CON6, @@ -207,7 +263,7 @@ static int rockchip_emmc_phy_power_on(struct phy *phy) PHYCTRL_OTAPDLYSEL_SHIFT)); /* Power up emmc phy analog blocks */ - return rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_ON); + return rockchip_emmc_phy_power(phy, PHYCTRL_PDB_PWR_ON); } static const struct phy_ops ops = {