From patchwork Fri Jul 28 19:22:15 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Philipp Tomsich X-Patchwork-Id: 795067 X-Patchwork-Delegate: philipp.tomsich@theobroma-systems.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 3xK0Fb6RHkz9s75 for ; Sat, 29 Jul 2017 06:05:23 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id A0A26C22052; Fri, 28 Jul 2017 19:53:53 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=none autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 342DDC220D3; Fri, 28 Jul 2017 19:37:02 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id EC1FAC22335; Fri, 28 Jul 2017 19:29:50 +0000 (UTC) Received: from mail.theobroma-systems.com (vegas.theobroma-systems.com [144.76.126.164]) by lists.denx.de (Postfix) with ESMTPS id 44B5AC22334 for ; Fri, 28 Jul 2017 19:29:43 +0000 (UTC) Received: from [86.59.122.178] (port=51261 helo=android.lan) by mail.theobroma-systems.com with esmtpsa (TLS1.2:RSA_AES_128_CBC_SHA256:128) (Exim 4.80) (envelope-from ) id 1dbAre-0003yU-9s; Fri, 28 Jul 2017 21:23:58 +0200 From: Philipp Tomsich To: u-boot@lists.denx.de Date: Fri, 28 Jul 2017 21:22:15 +0200 Message-Id: <1501269764-13969-44-git-send-email-philipp.tomsich@theobroma-systems.com> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1501269764-13969-1-git-send-email-philipp.tomsich@theobroma-systems.com> References: <1501269764-13969-1-git-send-email-philipp.tomsich@theobroma-systems.com> Cc: Albert Aribaud , Klaus Goger , Andy Yan Subject: [U-Boot] [PATCH v3 43/66] rockchip: clk: rk3368: implement MMC/SD clock reparenting X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" The original clock support for MMC/SD cards on the RK3368 suffered from a tendency to select a divider less-or-equal to the the one giving the requested clock-rate: this can lead to higher-than-expected (or rather: higher than supported) clock rates for the MMC/SD communiction. This change rewrites the MMC/SD clock generation to: * always generate a clock less-than-or-equal to the requested clock * support reparenting among the CPLL, GPLL and OSC24M parents to generate the highest clock that does not exceed the requested rate In addition to this, the Linux DTS uses HCLK_MMC/HCLK_SDMMC instead of SCLK_MMC/SCLK_SDMMC: to match this (and to ensure that clock setup always works), we adjust the driver appropriately. This includes the changes from: - rockchip: clk: rk3368: convert MMC_PLL_SEL_* definitions to shifted-value form Signed-off-by: Philipp Tomsich Reviewed-by: Simon Glass --- Changes in v3: None Changes in v2: None arch/arm/include/asm/arch-rockchip/cru_rk3368.h | 8 +- drivers/clk/rockchip/clk_rk3368.c | 119 ++++++++++++++++++------ 2 files changed, 95 insertions(+), 32 deletions(-) diff --git a/arch/arm/include/asm/arch-rockchip/cru_rk3368.h b/arch/arm/include/asm/arch-rockchip/cru_rk3368.h index bf09e2f..21f11e0 100644 --- a/arch/arm/include/asm/arch-rockchip/cru_rk3368.h +++ b/arch/arm/include/asm/arch-rockchip/cru_rk3368.h @@ -92,10 +92,10 @@ enum { /* CLKSEL51_CON */ MMC_PLL_SEL_SHIFT = 8, MMC_PLL_SEL_MASK = GENMASK(9, 8), - MMC_PLL_SEL_CPLL = 0, - MMC_PLL_SEL_GPLL, - MMC_PLL_SEL_USBPHY_480M, - MMC_PLL_SEL_24M, + MMC_PLL_SEL_CPLL = (0 << MMC_PLL_SEL_SHIFT), + MMC_PLL_SEL_GPLL = (1 << MMC_PLL_SEL_SHIFT), + MMC_PLL_SEL_USBPHY_480M = (2 << MMC_PLL_SEL_SHIFT), + MMC_PLL_SEL_24M = (3 << MMC_PLL_SEL_SHIFT), MMC_CLK_DIV_SHIFT = 0, MMC_CLK_DIV_MASK = GENMASK(6, 0), diff --git a/drivers/clk/rockchip/clk_rk3368.c b/drivers/clk/rockchip/clk_rk3368.c index 33d2946..1327116 100644 --- a/drivers/clk/rockchip/clk_rk3368.c +++ b/drivers/clk/rockchip/clk_rk3368.c @@ -59,6 +59,8 @@ static const struct pll_div cpll_init_cfg = PLL_DIVISORS(CPLL_HZ, 1, 6); #endif #endif +static ulong rk3368_clk_get_rate(struct clk *clk); + /* Get pll rate by id */ static uint32_t rkclk_pll_get_rate(struct rk3368_cru *cru, enum rk3368_pll_id pll_id) @@ -155,16 +157,17 @@ static void rkclk_init(struct rk3368_cru *cru) } #endif +#if !IS_ENABLED(CONFIG_SPL_BUILD) || CONFIG_IS_ENABLED(MMC_SUPPORT) static ulong rk3368_mmc_get_clk(struct rk3368_cru *cru, uint clk_id) { u32 div, con, con_id, rate; u32 pll_rate; switch (clk_id) { - case SCLK_SDMMC: + case HCLK_SDMMC: con_id = 50; break; - case SCLK_EMMC: + case HCLK_EMMC: con_id = 51; break; case SCLK_SDIO0: @@ -175,7 +178,7 @@ static ulong rk3368_mmc_get_clk(struct rk3368_cru *cru, uint clk_id) } con = readl(&cru->clksel_con[con_id]); - switch ((con & MMC_PLL_SEL_MASK) >> MMC_PLL_SEL_SHIFT) { + switch (con & MMC_PLL_SEL_MASK) { case MMC_PLL_SEL_GPLL: pll_rate = rkclk_pll_get_rate(cru, GPLL); break; @@ -183,6 +186,8 @@ static ulong rk3368_mmc_get_clk(struct rk3368_cru *cru, uint clk_id) pll_rate = OSC_HZ; break; case MMC_PLL_SEL_CPLL: + pll_rate = rkclk_pll_get_rate(cru, CPLL); + break; case MMC_PLL_SEL_USBPHY_480M: default: return -EINVAL; @@ -190,23 +195,76 @@ static ulong rk3368_mmc_get_clk(struct rk3368_cru *cru, uint clk_id) div = (con & MMC_CLK_DIV_MASK) >> MMC_CLK_DIV_SHIFT; rate = DIV_TO_RATE(pll_rate, div); + debug("%s: raw rate %d (post-divide by 2)\n", __func__, rate); return rate >> 1; } -static ulong rk3368_mmc_set_clk(struct rk3368_cru *cru, - ulong clk_id, ulong rate) +static ulong rk3368_mmc_find_best_rate_and_parent(struct clk *clk, + ulong rate, + u32 *best_mux, + u32 *best_div) +{ + int i; + ulong best_rate = 0; + const ulong MHz = 1000000; + const struct { + u32 mux; + ulong rate; + } parents[] = { + { .mux = MMC_PLL_SEL_CPLL, .rate = CPLL_HZ }, + { .mux = MMC_PLL_SEL_GPLL, .rate = GPLL_HZ }, + { .mux = MMC_PLL_SEL_24M, .rate = 24 * MHz } + }; + + debug("%s: target rate %ld\n", __func__, rate); + for (i = 0; i < ARRAY_SIZE(parents); ++i) { + /* + * Find the largest rate no larger than the target-rate for + * the current parent. + */ + ulong parent_rate = parents[i].rate; + u32 div = DIV_ROUND_UP(parent_rate, rate); + u32 adj_div = div; + ulong new_rate = parent_rate / adj_div; + + debug("%s: rate %ld, parent-mux %d, parent-rate %ld, div %d\n", + __func__, rate, parents[i].mux, parents[i].rate, div); + + /* Skip, if not representable */ + if ((div - 1) > MMC_CLK_DIV_MASK) + continue; + + /* Skip, if we already have a better (or equal) solution */ + if (new_rate <= best_rate) + continue; + + /* This is our new best rate. */ + best_rate = new_rate; + *best_mux = parents[i].mux; + *best_div = div - 1; + } + + debug("%s: best_mux = %x, best_div = %d, best_rate = %ld\n", + __func__, *best_mux, *best_div, best_rate); + + return best_rate; +} + +static ulong rk3368_mmc_set_clk(struct clk *clk, ulong rate) { - u32 div; - u32 con_id; - u32 gpll_rate = rkclk_pll_get_rate(cru, GPLL); + struct rk3368_clk_priv *priv = dev_get_priv(clk->dev); + struct rk3368_cru *cru = priv->cru; + ulong clk_id = clk->id; + u32 con_id, mux = 0, div = 0; - div = RATE_TO_DIV(gpll_rate, rate << 1); + /* Find the best parent and rate */ + rk3368_mmc_find_best_rate_and_parent(clk, rate << 1, &mux, &div); switch (clk_id) { - case SCLK_SDMMC: + case HCLK_SDMMC: con_id = 50; break; - case SCLK_EMMC: + case HCLK_EMMC: con_id = 51; break; case SCLK_SDIO0: @@ -216,33 +274,33 @@ static ulong rk3368_mmc_set_clk(struct rk3368_cru *cru, return -EINVAL; } - if (div > 0x3f) { - div = RATE_TO_DIV(OSC_HZ, rate); - rk_clrsetreg(&cru->clksel_con[con_id], - MMC_PLL_SEL_MASK | MMC_CLK_DIV_MASK, - (MMC_PLL_SEL_24M << MMC_PLL_SEL_SHIFT) | - (div << MMC_CLK_DIV_SHIFT)); - } else { - rk_clrsetreg(&cru->clksel_con[con_id], - MMC_PLL_SEL_MASK | MMC_CLK_DIV_MASK, - (MMC_PLL_SEL_GPLL << MMC_PLL_SEL_SHIFT) | - div << MMC_CLK_DIV_SHIFT); - } + rk_clrsetreg(&cru->clksel_con[con_id], + MMC_PLL_SEL_MASK | MMC_CLK_DIV_MASK, + mux | div); return rk3368_mmc_get_clk(cru, clk_id); } +#endif static ulong rk3368_clk_get_rate(struct clk *clk) { struct rk3368_clk_priv *priv = dev_get_priv(clk->dev); ulong rate = 0; - debug("%s id:%ld\n", __func__, clk->id); + debug("%s: id %ld\n", __func__, clk->id); switch (clk->id) { + case PLL_CPLL: + rate = rkclk_pll_get_rate(priv->cru, CPLL); + break; + case PLL_GPLL: + rate = rkclk_pll_get_rate(priv->cru, GPLL); + break; +#if !IS_ENABLED(CONFIG_SPL_BUILD) || CONFIG_IS_ENABLED(MMC_SUPPORT) case HCLK_SDMMC: case HCLK_EMMC: rate = rk3368_mmc_get_clk(priv->cru, clk->id); break; +#endif default: return -ENOENT; } @@ -291,10 +349,15 @@ static ulong rk3368_clk_set_rate(struct clk *clk, ulong rate) case CLK_DDR: ret = rk3368_ddr_set_clk(priv->cru, rate); break; - - case SCLK_SDMMC: - case SCLK_EMMC: - ret = rk3368_mmc_set_clk(priv->cru, clk->id, rate); +#if !IS_ENABLED(CONFIG_SPL_BUILD) || CONFIG_IS_ENABLED(MMC_SUPPORT) + case HCLK_SDMMC: + case HCLK_EMMC: + ret = rk3368_mmc_set_clk(clk, rate); + break; +#endif + case SCLK_MAC: + /* nothing to do, as this is an external clock */ + ret = rate; break; default: return -ENOENT;