From ddfe77df15bc1db9180b261b9c701abf4712452c Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Thu, 22 Jun 2017 23:47:11 +0200 Subject: rockchip: clk: rk3368: implement bandwidth adjust for PLLs The RK3368 TRM recommends to configure the bandwith adjustment (CON2) for PLLs to NF/2. This implements this for all reconfigurations of PLLs and removes the 'has_bwadj' flag (as the RK3368 always has the bandwidth-adjustment feature according to its manual). Signed-off-by: Philipp Tomsich Reviewed-by: Simon Glass --- drivers/clk/rockchip/clk_rk3368.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'drivers/clk/rockchip/clk_rk3368.c') diff --git a/drivers/clk/rockchip/clk_rk3368.c b/drivers/clk/rockchip/clk_rk3368.c index e1d9aeb8e5..d8f06d5856 100644 --- a/drivers/clk/rockchip/clk_rk3368.c +++ b/drivers/clk/rockchip/clk_rk3368.c @@ -1,6 +1,7 @@ /* * (C) Copyright 2017 Rockchip Electronics Co., Ltd * Author: Andy Yan + * (C) Copyright 2017 Theobroma Systems Design und Consulting GmbH * SPDX-License-Identifier: GPL-2.0 */ @@ -74,7 +75,7 @@ static uint32_t rkclk_pll_get_rate(struct rk3368_cru *cru, } static int rkclk_set_pll(struct rk3368_cru *cru, enum rk3368_pll_id pll_id, - const struct pll_div *div, bool has_bwadj) + const struct pll_div *div) { struct rk3368_pll *pll = &cru->pll[pll_id]; /* All PLLs have same VCO and output frequency range restrictions*/ @@ -92,6 +93,12 @@ static int rkclk_set_pll(struct rk3368_cru *cru, enum rk3368_pll_id pll_id, ((div->nr - 1) << PLL_NR_SHIFT) | ((div->no - 1) << PLL_OD_SHIFT)); writel((div->nf - 1) << PLL_NF_SHIFT, &pll->con1); + /* + * BWADJ should be set to NF / 2 to ensure the nominal bandwidth. + * Compare the RK3368 TRM, section "3.6.4 PLL Bandwidth Adjustment". + */ + clrsetbits_le32(&pll->con2, PLL_BWADJ_MASK, (div->nf >> 1) - 1); + udelay(10); /* return from reset */ @@ -111,10 +118,10 @@ static void rkclk_init(struct rk3368_cru *cru) { u32 apllb, aplll, dpll, cpll, gpll; - rkclk_set_pll(cru, APLLB, &apll_b_init_cfg, false); - rkclk_set_pll(cru, APLLL, &apll_l_init_cfg, false); - rkclk_set_pll(cru, GPLL, &gpll_init_cfg, false); - rkclk_set_pll(cru, CPLL, &cpll_init_cfg, false); + rkclk_set_pll(cru, APLLB, &apll_b_init_cfg); + rkclk_set_pll(cru, APLLL, &apll_l_init_cfg); + rkclk_set_pll(cru, GPLL, &gpll_init_cfg); + rkclk_set_pll(cru, CPLL, &cpll_init_cfg); apllb = rkclk_pll_get_rate(cru, APLLB); aplll = rkclk_pll_get_rate(cru, APLLL); -- cgit From bee61801264988d4250e8536bf62336621d2e9fb Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Thu, 22 Jun 2017 23:51:37 +0200 Subject: rockchip: clk: rk3368: support OF_PLATDATA for the RK3368 clk driver With the RK3368's limited TPL size, we'll want to use OF_PLATFDATA for the SPL stage. This implements support for OF_PLATDATA in the clock driver for the RK3368. Signed-off-by: Philipp Tomsich Reviewed-by: Simon Glass --- drivers/clk/rockchip/clk_rk3368.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'drivers/clk/rockchip/clk_rk3368.c') diff --git a/drivers/clk/rockchip/clk_rk3368.c b/drivers/clk/rockchip/clk_rk3368.c index d8f06d5856..809ad1990f 100644 --- a/drivers/clk/rockchip/clk_rk3368.c +++ b/drivers/clk/rockchip/clk_rk3368.c @@ -8,7 +8,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -19,6 +21,12 @@ DECLARE_GLOBAL_DATA_PTR; +#if CONFIG_IS_ENABLED(OF_PLATDATA) +struct rk3368_clk_plat { + struct dtd_rockchip_rk3368_cru dtd; +}; +#endif + struct pll_div { u32 nr; u32 nf; @@ -254,7 +262,11 @@ static struct clk_ops rk3368_clk_ops = { static int rk3368_clk_probe(struct udevice *dev) { struct rk3368_clk_priv *priv = dev_get_priv(dev); +#if CONFIG_IS_ENABLED(OF_PLATDATA) + struct rk3368_clk_plat *plat = dev_get_platdata(dev); + priv->cru = map_sysmem(plat->dtd.reg[1], plat->dtd.reg[3]); +#endif rkclk_init(priv->cru); return 0; @@ -262,9 +274,11 @@ static int rk3368_clk_probe(struct udevice *dev) static int rk3368_clk_ofdata_to_platdata(struct udevice *dev) { +#if !CONFIG_IS_ENABLED(OF_PLATDATA) struct rk3368_clk_priv *priv = dev_get_priv(dev); priv->cru = (struct rk3368_cru *)devfdt_get_addr(dev); +#endif return 0; } @@ -291,6 +305,9 @@ U_BOOT_DRIVER(rockchip_rk3368_cru) = { .id = UCLASS_CLK, .of_match = rk3368_clk_ids, .priv_auto_alloc_size = sizeof(struct rk3368_clk_priv), +#if CONFIG_IS_ENABLED(OF_PLATDATA) + .platdata_auto_alloc_size = sizeof(struct rk3368_clk_plat), +#endif .ofdata_to_platdata = rk3368_clk_ofdata_to_platdata, .ops = &rk3368_clk_ops, .bind = rk3368_clk_bind, -- cgit From 4bebf94e8544399d040e0dc46d7ec72d64853237 Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Thu, 22 Jun 2017 23:53:44 +0200 Subject: rockchip: clk: rk3368: do not change CPLL/GPLL before returning to BROM The RK3368 has a somewhat temperamental BootROM (which I learned the hard way) when it comes to reconfiguring the CPLL and GPLL (in fact, experiments show that changing the GPLL broke things for me, while changing the CPLL seems to be more benign). These should not be modified by the SPL stage, if we intend to return to the BootROM for chain booting the next stage. This commit changes the clock initialisation to not change CPLL/GPLL before returning to the BootROM (i.e. in TPL). As it's safe to change these settings if we no longer intend to return to U-Boot, we'll run the full PLL setup a little later (i.e. in SPL). Signed-off-by: Philipp Tomsich Reviewed-by: Simon Glass --- drivers/clk/rockchip/clk_rk3368.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'drivers/clk/rockchip/clk_rk3368.c') diff --git a/drivers/clk/rockchip/clk_rk3368.c b/drivers/clk/rockchip/clk_rk3368.c index 809ad1990f..d05be72c9c 100644 --- a/drivers/clk/rockchip/clk_rk3368.c +++ b/drivers/clk/rockchip/clk_rk3368.c @@ -50,10 +50,14 @@ struct pll_div { (_nr * _no) == hz, #hz "Hz cannot be hit with PLL " \ "divisors on line " __stringify(__LINE__)); +#if IS_ENABLED(CONFIG_SPL_BUILD) || IS_ENABLED(CONFIG_TPL_BUILD) static const struct pll_div apll_l_init_cfg = PLL_DIVISORS(APLL_L_HZ, 12, 2); static const struct pll_div apll_b_init_cfg = PLL_DIVISORS(APLL_B_HZ, 1, 2); +#if !defined(CONFIG_TPL_BUILD) static const struct pll_div gpll_init_cfg = PLL_DIVISORS(GPLL_HZ, 1, 2); static const struct pll_div cpll_init_cfg = PLL_DIVISORS(CPLL_HZ, 1, 6); +#endif +#endif /* Get pll rate by id */ static uint32_t rkclk_pll_get_rate(struct rk3368_cru *cru, @@ -82,6 +86,7 @@ static uint32_t rkclk_pll_get_rate(struct rk3368_cru *cru, } } +#if IS_ENABLED(CONFIG_SPL_BUILD) || IS_ENABLED(CONFIG_TPL_BUILD) static int rkclk_set_pll(struct rk3368_cru *cru, enum rk3368_pll_id pll_id, const struct pll_div *div) { @@ -121,15 +126,23 @@ static int rkclk_set_pll(struct rk3368_cru *cru, enum rk3368_pll_id pll_id, return 0; } +#endif +#if IS_ENABLED(CONFIG_SPL_BUILD) || IS_ENABLED(CONFIG_TPL_BUILD) static void rkclk_init(struct rk3368_cru *cru) { u32 apllb, aplll, dpll, cpll, gpll; rkclk_set_pll(cru, APLLB, &apll_b_init_cfg); rkclk_set_pll(cru, APLLL, &apll_l_init_cfg); +#if !defined(CONFIG_TPL_BUILD) + /* + * If we plan to return to the boot ROM, we can't increase the + * GPLL rate from the SPL stage. + */ rkclk_set_pll(cru, GPLL, &gpll_init_cfg); rkclk_set_pll(cru, CPLL, &cpll_init_cfg); +#endif apllb = rkclk_pll_get_rate(cru, APLLB); aplll = rkclk_pll_get_rate(cru, APLLL); @@ -140,6 +153,7 @@ static void rkclk_init(struct rk3368_cru *cru) debug("%s apllb(%d) apll(%d) dpll(%d) cpll(%d) gpll(%d)\n", __func__, apllb, aplll, dpll, cpll, gpll); } +#endif static ulong rk3368_mmc_get_clk(struct rk3368_cru *cru, uint clk_id) { @@ -261,13 +275,15 @@ static struct clk_ops rk3368_clk_ops = { static int rk3368_clk_probe(struct udevice *dev) { - struct rk3368_clk_priv *priv = dev_get_priv(dev); + struct rk3368_clk_priv __maybe_unused *priv = dev_get_priv(dev); #if CONFIG_IS_ENABLED(OF_PLATDATA) struct rk3368_clk_plat *plat = dev_get_platdata(dev); priv->cru = map_sysmem(plat->dtd.reg[1], plat->dtd.reg[3]); #endif +#if IS_ENABLED(CONFIG_SPL_BUILD) || IS_ENABLED(CONFIG_TPL_BUILD) rkclk_init(priv->cru); +#endif return 0; } -- cgit From a00dfa042d3eecbe96308d87f38710e79a29e00c Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Fri, 23 Jun 2017 00:01:10 +0200 Subject: rockchip: clk: rk3368: implement DPLL (DRAM PLL) support To implement a TPL stage (incl. its DRAM controller setup) for the RK3368, we'll want to configure the DPLL (DRAM PLL). This commit implements setting the DPLL (CLK_DDR) and provides PLL configuration details for the common DRAM operating speeds found on RK3368 boards. Signed-off-by: Philipp Tomsich Reviewed-by: Simon Glass --- drivers/clk/rockchip/clk_rk3368.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'drivers/clk/rockchip/clk_rk3368.c') diff --git a/drivers/clk/rockchip/clk_rk3368.c b/drivers/clk/rockchip/clk_rk3368.c index d05be72c9c..33d29464fb 100644 --- a/drivers/clk/rockchip/clk_rk3368.c +++ b/drivers/clk/rockchip/clk_rk3368.c @@ -250,6 +250,37 @@ static ulong rk3368_clk_get_rate(struct clk *clk) return rate; } +static ulong rk3368_ddr_set_clk(struct rk3368_cru *cru, ulong set_rate) +{ + const struct pll_div *dpll_cfg = NULL; + const ulong MHz = 1000000; + + /* Fout = ((Fin /NR) * NF )/ NO */ + static const struct pll_div dpll_1200 = + PLL_DIVISORS(1200 * MHz, 1, 1); + static const struct pll_div dpll_1332 = + PLL_DIVISORS(1332 * MHz, 2, 1); + static const struct pll_div dpll_1600 = + PLL_DIVISORS(1600 * MHz, 3, 2); + + switch (set_rate) { + case 1200*MHz: + dpll_cfg = &dpll_1200; + break; + case 1332*MHz: + dpll_cfg = &dpll_1332; + break; + case 1600*MHz: + dpll_cfg = &dpll_1600; + break; + default: + error("Unsupported SDRAM frequency!,%ld\n", set_rate); + } + rkclk_set_pll(cru, DPLL, dpll_cfg); + + return set_rate; +} + static ulong rk3368_clk_set_rate(struct clk *clk, ulong rate) { struct rk3368_clk_priv *priv = dev_get_priv(clk->dev); @@ -257,6 +288,10 @@ static ulong rk3368_clk_set_rate(struct clk *clk, ulong rate) debug("%s id:%ld rate:%ld\n", __func__, clk->id, rate); switch (clk->id) { + 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); -- cgit From f5a432959aaf02a4c87067c9ae756ab21e98f8b8 Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Tue, 4 Jul 2017 14:49:38 +0200 Subject: rockchip: clk: rk3368: implement MMC/SD clock reparenting 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 --- drivers/clk/rockchip/clk_rk3368.c | 119 +++++++++++++++++++++++++++++--------- 1 file changed, 91 insertions(+), 28 deletions(-) (limited to 'drivers/clk/rockchip/clk_rk3368.c') diff --git a/drivers/clk/rockchip/clk_rk3368.c b/drivers/clk/rockchip/clk_rk3368.c index 33d29464fb..1327116f19 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; -- cgit From 629246907312390bbc281e531b60ac981842670f Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Wed, 5 Jul 2017 11:55:23 +0200 Subject: rockchip: clk: rk3368: support configuring the DRAM PLL (from TPL) As part of the DRAM initialisation process (running as part of the TPL stage) on the RK3368, we need to set up the DRAM PLL. This implements support for configuring the PLL to for 1200, 1332 or 1600 MHz (i.e. for DDR3-1200, DDR3-1333, DDR3-1600 operating modes). Signed-off-by: Philipp Tomsich Reviewed-by: Simon Glass --- drivers/clk/rockchip/clk_rk3368.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers/clk/rockchip/clk_rk3368.c') diff --git a/drivers/clk/rockchip/clk_rk3368.c b/drivers/clk/rockchip/clk_rk3368.c index 1327116f19..1bed4e20bf 100644 --- a/drivers/clk/rockchip/clk_rk3368.c +++ b/drivers/clk/rockchip/clk_rk3368.c @@ -308,18 +308,16 @@ static ulong rk3368_clk_get_rate(struct clk *clk) return rate; } +#if IS_ENABLED(CONFIG_TPL_BUILD) static ulong rk3368_ddr_set_clk(struct rk3368_cru *cru, ulong set_rate) { const struct pll_div *dpll_cfg = NULL; const ulong MHz = 1000000; /* Fout = ((Fin /NR) * NF )/ NO */ - static const struct pll_div dpll_1200 = - PLL_DIVISORS(1200 * MHz, 1, 1); - static const struct pll_div dpll_1332 = - PLL_DIVISORS(1332 * MHz, 2, 1); - static const struct pll_div dpll_1600 = - PLL_DIVISORS(1600 * MHz, 3, 2); + static const struct pll_div dpll_1200 = PLL_DIVISORS(1200 * MHz, 1, 1); + static const struct pll_div dpll_1332 = PLL_DIVISORS(1332 * MHz, 2, 1); + static const struct pll_div dpll_1600 = PLL_DIVISORS(1600 * MHz, 3, 2); switch (set_rate) { case 1200*MHz: @@ -338,6 +336,7 @@ static ulong rk3368_ddr_set_clk(struct rk3368_cru *cru, ulong set_rate) return set_rate; } +#endif static ulong rk3368_clk_set_rate(struct clk *clk, ulong rate) { @@ -346,9 +345,11 @@ static ulong rk3368_clk_set_rate(struct clk *clk, ulong rate) debug("%s id:%ld rate:%ld\n", __func__, clk->id, rate); switch (clk->id) { +#if IS_ENABLED(CONFIG_TPL_BUILD) case CLK_DDR: ret = rk3368_ddr_set_clk(priv->cru, rate); break; +#endif #if !IS_ENABLED(CONFIG_SPL_BUILD) || CONFIG_IS_ENABLED(MMC_SUPPORT) case HCLK_SDMMC: case HCLK_EMMC: -- cgit From df0ae00041c62709917ad600999fd2945dc69426 Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Fri, 14 Jul 2017 19:57:39 +0200 Subject: rockchip: clk: rk3368: add support for GMAC (SLCK_MAC) clock To enable the GMAC on the RK3368, we need to set up the clocking appropriately to generate a tx_clk for the MAC. This adds an implementation that implements the use of the <&ext_gmac> clock (i.e. an external 125MHz clock for RGMII provided by the PHY). This is the clock setup used by the boards currently supported by U-Boot (i.e. Geekbox, Sheep and RK3368-uQ7). This includes the change from commit - rockchip: clk: rk3368: define GMAC_MUX_SEL_EXTCLK Signed-off-by: Philipp Tomsich Reviewed-by: Simon Glass --- drivers/clk/rockchip/clk_rk3368.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) (limited to 'drivers/clk/rockchip/clk_rk3368.c') diff --git a/drivers/clk/rockchip/clk_rk3368.c b/drivers/clk/rockchip/clk_rk3368.c index 1bed4e20bf..2b6c8dabf8 100644 --- a/drivers/clk/rockchip/clk_rk3368.c +++ b/drivers/clk/rockchip/clk_rk3368.c @@ -338,6 +338,19 @@ static ulong rk3368_ddr_set_clk(struct rk3368_cru *cru, ulong set_rate) } #endif +#if CONFIG_IS_ENABLED(GMAC_ROCKCHIP) +static ulong rk3368_gmac_set_clk(struct rk3368_cru *cru, + ulong clk_id, ulong set_rate) +{ + /* + * This models the 'assigned-clock-parents = <&ext_gmac>' from + * the DTS and switches to the 'ext_gmac' clock parent. + */ + rk_setreg(&cru->clksel_con[43], GMAC_MUX_SEL_EXTCLK); + return set_rate; +} +#endif + static ulong rk3368_clk_set_rate(struct clk *clk, ulong rate) { struct rk3368_clk_priv *priv = dev_get_priv(clk->dev); @@ -356,10 +369,12 @@ static ulong rk3368_clk_set_rate(struct clk *clk, ulong rate) ret = rk3368_mmc_set_clk(clk, rate); break; #endif +#if CONFIG_IS_ENABLED(GMAC_ROCKCHIP) case SCLK_MAC: - /* nothing to do, as this is an external clock */ - ret = rate; + /* select the external clock */ + ret = rk3368_gmac_set_clk(priv->cru, clk->id, rate); break; +#endif default: return -ENOENT; } -- cgit From 4e4c40df3065df285e3bf60460ade2adbaabce22 Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Wed, 5 Jul 2017 12:11:58 +0200 Subject: rockchip: clk: rk3368: mark 'priv' __maybe_unused in rk3368_clk_set_rate() With the clock support in rk3368_clk_set_rate() conditionalized on various feature definitions, 'priv' can remain unused (e.g. in the SPL build when only MMC is enabled). Signed-off-by: Philipp Tomsich Reviewed-by: Simon Glass --- drivers/clk/rockchip/clk_rk3368.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/clk/rockchip/clk_rk3368.c') diff --git a/drivers/clk/rockchip/clk_rk3368.c b/drivers/clk/rockchip/clk_rk3368.c index 2b6c8dabf8..d3f6c29ef9 100644 --- a/drivers/clk/rockchip/clk_rk3368.c +++ b/drivers/clk/rockchip/clk_rk3368.c @@ -353,7 +353,7 @@ static ulong rk3368_gmac_set_clk(struct rk3368_cru *cru, static ulong rk3368_clk_set_rate(struct clk *clk, ulong rate) { - struct rk3368_clk_priv *priv = dev_get_priv(clk->dev); + __maybe_unused struct rk3368_clk_priv *priv = dev_get_priv(clk->dev); ulong ret = 0; debug("%s id:%ld rate:%ld\n", __func__, clk->id, rate); -- cgit From cf8aceb1c96943b992449fdc9ef697620643aeb9 Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Tue, 25 Jul 2017 16:48:16 +0200 Subject: rockchip: clk: rk3368: add support for configuring the SPI clocks As SPI support may be useful in the boot-flow, this adds support for configuring the SPI controller's clocks in the RK3368 clock driver. Signed-off-by: Philipp Tomsich Reviewed-by: Simon Glass --- drivers/clk/rockchip/clk_rk3368.c | 133 ++++++++++++++++++++++++++++++-------- 1 file changed, 107 insertions(+), 26 deletions(-) (limited to 'drivers/clk/rockchip/clk_rk3368.c') diff --git a/drivers/clk/rockchip/clk_rk3368.c b/drivers/clk/rockchip/clk_rk3368.c index d3f6c29ef9..9ef5badf56 100644 --- a/drivers/clk/rockchip/clk_rk3368.c +++ b/drivers/clk/rockchip/clk_rk3368.c @@ -282,32 +282,6 @@ static ulong rk3368_mmc_set_clk(struct clk *clk, ulong rate) } #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); - 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; - } - - return rate; -} - #if IS_ENABLED(CONFIG_TPL_BUILD) static ulong rk3368_ddr_set_clk(struct rk3368_cru *cru, ulong set_rate) { @@ -351,6 +325,110 @@ static ulong rk3368_gmac_set_clk(struct rk3368_cru *cru, } #endif +/* + * RK3368 SPI clocks have a common divider-width (7 bits) and a single bit + * to select either CPLL or GPLL as the clock-parent. The location within + * the enclosing CLKSEL_CON (i.e. div_shift and sel_shift) are variable. + */ + +struct spi_clkreg { + uint8_t reg; /* CLKSEL_CON[reg] register in CRU */ + uint8_t div_shift; + uint8_t sel_shift; +}; + +/* + * The entries are numbered relative to their offset from SCLK_SPI0. + */ +static const struct spi_clkreg spi_clkregs[] = { + [0] = { .reg = 45, .div_shift = 0, .sel_shift = 7, }, + [1] = { .reg = 45, .div_shift = 8, .sel_shift = 15, }, + [2] = { .reg = 46, .div_shift = 8, .sel_shift = 15, }, +}; + +static inline u32 extract_bits(u32 val, unsigned width, unsigned shift) +{ + return (val >> shift) & ((1 << width) - 1); +} + +static ulong rk3368_spi_get_clk(struct rk3368_cru *cru, ulong clk_id) +{ + const struct spi_clkreg *spiclk = NULL; + u32 div, val; + + switch (clk_id) { + case SCLK_SPI0 ... SCLK_SPI2: + spiclk = &spi_clkregs[clk_id - SCLK_SPI0]; + break; + + default: + error("%s: SPI clk-id %ld not supported\n", __func__, clk_id); + return -EINVAL; + } + + val = readl(&cru->clksel_con[spiclk->reg]); + div = extract_bits(val, 7, spiclk->div_shift); + + debug("%s: div 0x%x\n", __func__, div); + return DIV_TO_RATE(GPLL_HZ, div); +} + +static ulong rk3368_spi_set_clk(struct rk3368_cru *cru, ulong clk_id, uint hz) +{ + const struct spi_clkreg *spiclk = NULL; + int src_clk_div; + + src_clk_div = DIV_ROUND_UP(GPLL_HZ, hz); + assert(src_clk_div < 127); + + switch (clk_id) { + case SCLK_SPI0 ... SCLK_SPI2: + spiclk = &spi_clkregs[clk_id - SCLK_SPI0]; + break; + + default: + error("%s: SPI clk-id %ld not supported\n", __func__, clk_id); + return -EINVAL; + } + + rk_clrsetreg(&cru->clksel_con[spiclk->reg], + ((0x7f << spiclk->div_shift) | + (0x1 << spiclk->sel_shift)), + ((src_clk_div << spiclk->div_shift) | + (1 << spiclk->sel_shift))); + + return rk3368_spi_get_clk(cru, clk_id); +} + +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); + 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; + case SCLK_SPI0 ... SCLK_SPI2: + rate = rk3368_spi_get_clk(priv->cru, clk->id); + 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; + } + + return rate; +} + static ulong rk3368_clk_set_rate(struct clk *clk, ulong rate) { __maybe_unused struct rk3368_clk_priv *priv = dev_get_priv(clk->dev); @@ -358,6 +436,9 @@ static ulong rk3368_clk_set_rate(struct clk *clk, ulong rate) debug("%s id:%ld rate:%ld\n", __func__, clk->id, rate); switch (clk->id) { + case SCLK_SPI0 ... SCLK_SPI2: + ret = rk3368_spi_set_clk(priv->cru, clk->id, rate); + break; #if IS_ENABLED(CONFIG_TPL_BUILD) case CLK_DDR: ret = rk3368_ddr_set_clk(priv->cru, rate); -- cgit From 217273cd441fe3d00a1bdad143dcb656854963f9 Mon Sep 17 00:00:00 2001 From: Kever Yang Date: Thu, 27 Jul 2017 12:54:02 +0800 Subject: rockchip: clk: remove RATE_TO_DIV Use DIV_ROUND_UP instead RATE_TO_DIV for all Rockchip SoC clock driver. Add or fix the div-field overflow check at the same time. Signed-off-by: Kever Yang Reviewed-by: Philipp Tomsich Acked-by: Philipp Tomsich --- drivers/clk/rockchip/clk_rk3368.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/clk/rockchip/clk_rk3368.c') diff --git a/drivers/clk/rockchip/clk_rk3368.c b/drivers/clk/rockchip/clk_rk3368.c index 9ef5badf56..2be1f572d7 100644 --- a/drivers/clk/rockchip/clk_rk3368.c +++ b/drivers/clk/rockchip/clk_rk3368.c @@ -39,9 +39,6 @@ struct pll_div { #define GPLL_HZ (576 * 1000 * 1000) #define CPLL_HZ (400 * 1000 * 1000) -#define RATE_TO_DIV(input_rate, output_rate) \ - ((input_rate) / (output_rate) - 1); - #define DIV_TO_RATE(input_rate, div) ((input_rate) / ((div) + 1)) #define PLL_DIVISORS(hz, _nr, _no) { \ -- cgit