[PATCH v2 05/11] clk: k210: Re-add support for setting rate

Sean Anderson seanga2 at gmail.com
Fri Jun 4 06:17:52 CEST 2021


On 6/3/21 11:58 PM, Sean Anderson wrote:
> This adds support for setting clock rates, which was left out of the
> initial CCF expunging. There are several tricky bits here, mostly related
> to the PLLS:
> 
> * The PLL's bypass is broken. If the PLL is reconfigured, any child clocks
>    will be stopped.
> * PLL0 is the parent of ACLK which is the CPU and SRAM's clock. To prevent
>    stopping the CPU while we configure PLL0's rate, ACLK is reparented
>    to IN0 while PLL0 is disabled.
> * PLL1 is the parent of the AISRAM clock. This clock cannot be reparented,
>    so we instead just disallow changing PLL1's rate after relocation (when
>    we are using the AISRAM).
> 
> Signed-off-by: Sean Anderson <seanga2 at gmail.com>
> ---
> 
> Changes in v2:
> - Only force probe clocks pre-reloc
> 
>   drivers/clk/kendryte/clk.c | 89 +++++++++++++++++++++++++++++++++++---
>   1 file changed, 84 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/clk/kendryte/clk.c b/drivers/clk/kendryte/clk.c
> index 203d5f741c..a1742eb856 100644
> --- a/drivers/clk/kendryte/clk.c
> +++ b/drivers/clk/kendryte/clk.c
> @@ -17,6 +17,8 @@
>   #include <kendryte/pll.h>
>   #include <linux/bitfield.h>
>   
> +DECLARE_GLOBAL_DATA_PTR;
> +
>   /**
>    * struct k210_clk_priv - K210 clock driver private data
>    * @base: The base address of the sysctl device
> @@ -1059,11 +1061,6 @@ static ulong k210_clk_get_rate(struct clk *clk)
>   	return do_k210_clk_get_rate(dev_get_priv(clk->dev), clk->id);
>   }
>   
> -static ulong k210_clk_set_rate(struct clk *clk, unsigned long rate)
> -{
> -	return -ENOSYS;
> -}
> -
>   static int do_k210_clk_set_parent(struct k210_clk_priv *priv, int id, int new)
>   {
>   	int i;
> @@ -1089,6 +1086,81 @@ static int k210_clk_set_parent(struct clk *clk, struct clk *parent)
>   				      parent->id);
>   }
>   
> +static ulong k210_clk_set_rate(struct clk *clk, unsigned long rate)
> +{
> +	int parent, ret, err;
> +	ulong rate_in, val;
> +	const struct k210_div_params *div;
> +	struct k210_clk_priv *priv = dev_get_priv(clk->dev);
> +
> +	if (clk->id == K210_CLK_IN0)
> +		return clk_set_rate(&priv->in0, rate);
> +
> +	parent = k210_clk_get_parent(priv, clk->id);
> +	rate_in = do_k210_clk_get_rate(priv, parent);
> +
> +	log_debug("id=%ld rate=%lu rate_in=%lu\n", clk->id, rate, rate_in);
> +
> +	if (clk->id == K210_CLK_PLL0) {
> +		/* Bypass ACLK so the CPU keeps going */
> +		ret = do_k210_clk_set_parent(priv, K210_CLK_ACLK, K210_CLK_IN0);
> +		if (ret)
> +			return ret;
> +	} else if (clk->id == K210_CLK_PLL1 && gd->flags & GD_FLG_RELOC) {
> +		/*
> +		 * We can't bypass the AI clock like we can ACLK, and after
> +		 * relocation we are using the AI ram.
> +		 */
> +		return -EPERM;
> +	}
> +
> +	if (k210_clks[clk->id].flags & K210_CLKF_PLL) {
> +		ret = k210_pll_set_rate(priv, k210_clks[clk->id].pll, rate,
> +					rate_in);
> +		if (!IS_ERR_VALUE(ret) && clk->id == K210_CLK_PLL0) {
> +			/*
> +			 * This may have the side effect of reparenting ACLK,
> +			 * but I don't really want to keep track of what the old
> +			 * parent was.
> +			 */
> +			err = do_k210_clk_set_parent(priv, K210_CLK_ACLK,
> +						     K210_CLK_PLL0);
> +			if (err)
> +				return err;
> +		}
> +		return ret;
> +	}
> +
> +	if (k210_clks[clk->id].div == K210_CLK_DIV_NONE)
> +		return -ENOSYS;
> +	div = &k210_divs[k210_clks[clk->id].div];
> +
> +	switch (div->type) {
> +	case K210_DIV_ONE:
> +		val = DIV_ROUND_CLOSEST_ULL((u64)rate_in, rate);
> +		val = val ? val - 1 : 0;
> +		break;
> +	case K210_DIV_EVEN:
> +		val = DIV_ROUND_CLOSEST_ULL((u64)rate_in, 2 * rate);
> +		break;
> +	case K210_DIV_POWER:
> +		/* This is ACLK, which has no divider on IN0 */
> +		if (parent == K210_CLK_IN0)
> +			return -ENOSYS;
> +
> +		DIV_ROUND_CLOSEST_ULL((u64)rate_in, rate);
> +		val = __ffs(val);
> +		break;
> +	default:
> +		assert(false);
> +		return -EINVAL;
> +	};
> +
> +	val = val ? val - 1 : 0;
> +	k210_clk_writel(priv, div->off, div->shift, div->width, val);
> +	return do_k210_clk_get_rate(priv, clk->id);
> +}
> +
>   static int k210_clk_endisable(struct k210_clk_priv *priv, int id, bool enable)
>   {
>   	int parent = k210_clk_get_parent(priv, id);
> @@ -1163,6 +1235,13 @@ static int k210_clk_probe(struct udevice *dev)
>   	if (ret)
>   		return ret;
>   
> +	/*
> +	 * Force setting defaults, even before relocation. This is so we can
> +	 * set the clock rate for PLL1 before we relocate into aisram.
> +	 */
> +	if (gd->flags & GD_FLG_RELOC)

The polarity of this if is inverted, should be if (!(...))

> +		clk_set_defaults(dev, CLK_DEFAULTS_POST_FORCE);
> +
>   	return 0;
>   }
>   
> 



More information about the U-Boot mailing list