[PATCH 10/12] soc: xilinx: vcu: make the PLL configurable

Michal Simek michal.simek at xilinx.com
Wed Dec 2 09:54:15 EST 2020



On 16. 11. 20 8:55, Michael Tretter wrote:
> Do not configure the PLL when probing the driver, but register the clock
> in the clock framework and do the configuration based on the respective
> callbacks.
> 
> This is necessary to allow the consumers, i.e., encoder and decoder
> drivers, of the xlnx_vcu clock provider to set the clock rate and
> actually enable the clocks without relying on some pre-configuration.
> 
> Signed-off-by: Michael Tretter <m.tretter at pengutronix.de>
> ---
>  drivers/soc/xilinx/xlnx_vcu.c | 137 ++++++++++++++++++++++++++--------
>  1 file changed, 106 insertions(+), 31 deletions(-)
> 
> diff --git a/drivers/soc/xilinx/xlnx_vcu.c b/drivers/soc/xilinx/xlnx_vcu.c
> index cf8456b4ef78..84d7c46cd42f 100644
> --- a/drivers/soc/xilinx/xlnx_vcu.c
> +++ b/drivers/soc/xilinx/xlnx_vcu.c
> @@ -257,9 +257,18 @@ static void xvcu_write_field_reg(void __iomem *iomem, int offset,
>  	xvcu_write(iomem, offset, val);
>  }
>  
> -static int xvcu_pll_wait_for_lock(struct xvcu_device *xvcu)
> +#define to_vcu_pll(_hw) container_of(_hw, struct vcu_pll, hw)
> +
> +struct vcu_pll {
> +	struct clk_hw hw;
> +	void __iomem *reg_base;
> +	unsigned long fvco_min;
> +	unsigned long fvco_max;
> +};
> +
> +static int xvcu_pll_wait_for_lock(struct vcu_pll *pll)
>  {
> -	void __iomem *base = xvcu->vcu_slcr_ba;
> +	void __iomem *base = pll->reg_base;
>  	unsigned long timeout;
>  	u32 lock_status;
>  
> @@ -307,9 +316,9 @@ static const struct xvcu_pll_cfg *xvcu_find_cfg(int div)
>  	return cfg;
>  }
>  
> -static int xvcu_pll_set_div(struct xvcu_device *xvcu, int div)
> +static int xvcu_pll_set_div(struct vcu_pll *pll, int div)
>  {
> -	void __iomem *base = xvcu->vcu_slcr_ba;
> +	void __iomem *base = pll->reg_base;
>  	const struct xvcu_pll_cfg *cfg = NULL;
>  	u32 vcu_pll_ctrl;
>  	u32 cfg_val;
> @@ -334,24 +343,49 @@ static int xvcu_pll_set_div(struct xvcu_device *xvcu, int div)
>  	return 0;
>  }
>  
> -static int xvcu_pll_set_rate(struct xvcu_device *xvcu,
> +static long xvcu_pll_round_rate(struct clk_hw *hw,
> +				unsigned long rate, unsigned long *parent_rate)
> +{
> +	struct vcu_pll *pll = to_vcu_pll(hw);
> +	unsigned int feedback_div;
> +
> +	rate = clamp_t(unsigned long, rate, pll->fvco_min, pll->fvco_max);
> +
> +	feedback_div = DIV_ROUND_CLOSEST_ULL(rate, *parent_rate);
> +	feedback_div = clamp_t(unsigned int, feedback_div, 25, 125);
> +
> +	return *parent_rate * feedback_div;
> +}
> +
> +static unsigned long xvcu_pll_recalc_rate(struct clk_hw *hw,
> +					  unsigned long parent_rate)
> +{
> +	struct vcu_pll *pll = to_vcu_pll(hw);
> +	void __iomem *base = pll->reg_base;
> +	unsigned int div;
> +	u32 vcu_pll_ctrl;
> +
> +	vcu_pll_ctrl = xvcu_read(base, VCU_PLL_CTRL);
> +	div = (vcu_pll_ctrl >> VCU_PLL_CTRL_FBDIV_SHIFT) & VCU_PLL_CTRL_FBDIV_MASK;
> +
> +	return div * parent_rate;
> +}
> +
> +static int xvcu_pll_set_rate(struct clk_hw *hw,
>  			     unsigned long rate, unsigned long parent_rate)
>  {
> -	return xvcu_pll_set_div(xvcu, rate / parent_rate);
> +	struct vcu_pll *pll = to_vcu_pll(hw);
> +
> +	return xvcu_pll_set_div(pll, rate / parent_rate);
>  }
>  
> -static int xvcu_pll_enable(struct xvcu_device *xvcu)
> +static int xvcu_pll_enable(struct clk_hw *hw)
>  {
> -	void __iomem *base = xvcu->vcu_slcr_ba;
> +	struct vcu_pll *pll = to_vcu_pll(hw);
> +	void __iomem *base = pll->reg_base;
>  	u32 vcu_pll_ctrl;
>  	int ret;
>  
> -	ret = clk_prepare_enable(xvcu->pll_ref);
> -	if (ret) {
> -		dev_err(xvcu->dev, "failed to enable pll_ref clock source\n");
> -		return ret;
> -	}
> -
>  	xvcu_write_field_reg(base, VCU_PLL_CTRL,
>  			     1, VCU_PLL_CTRL_BYPASS_MASK,
>  			     VCU_PLL_CTRL_BYPASS_SHIFT);
> @@ -371,9 +405,9 @@ static int xvcu_pll_enable(struct xvcu_device *xvcu)
>  	vcu_pll_ctrl |= (0 & VCU_PLL_CTRL_RESET_MASK) << VCU_PLL_CTRL_RESET_SHIFT;
>  	xvcu_write(base, VCU_PLL_CTRL, vcu_pll_ctrl);
>  
> -	ret = xvcu_pll_wait_for_lock(xvcu);
> +	ret = xvcu_pll_wait_for_lock(pll);
>  	if (ret) {
> -		dev_err(xvcu->dev, "PLL is not locked\n");
> +		pr_err("VCU PLL is not locked\n");
>  		goto err;
>  	}
>  
> @@ -381,15 +415,14 @@ static int xvcu_pll_enable(struct xvcu_device *xvcu)
>  			     0, VCU_PLL_CTRL_BYPASS_MASK,
>  			     VCU_PLL_CTRL_BYPASS_SHIFT);
>  
> -	return ret;
>  err:
> -	clk_disable_unprepare(xvcu->pll_ref);
>  	return ret;
>  }
>  
> -static void xvcu_pll_disable(struct xvcu_device *xvcu)
> +static void xvcu_pll_disable(struct clk_hw *hw)
>  {
> -	void __iomem *base = xvcu->vcu_slcr_ba;
> +	struct vcu_pll *pll = to_vcu_pll(hw);
> +	void __iomem *base = pll->reg_base;
>  	u32 vcu_pll_ctrl;
>  
>  	vcu_pll_ctrl = xvcu_read(base, VCU_PLL_CTRL);
> @@ -400,8 +433,49 @@ static void xvcu_pll_disable(struct xvcu_device *xvcu)
>  	vcu_pll_ctrl &= ~(VCU_PLL_CTRL_RESET_MASK << VCU_PLL_CTRL_RESET_SHIFT);
>  	vcu_pll_ctrl |= (1 & VCU_PLL_CTRL_RESET_MASK) << VCU_PLL_CTRL_RESET_SHIFT;
>  	xvcu_write(base, VCU_PLL_CTRL, vcu_pll_ctrl);
> +}
> +
> +static const struct clk_ops vcu_pll_ops = {
> +	.enable = xvcu_pll_enable,
> +	.disable = xvcu_pll_disable,
> +	.round_rate = xvcu_pll_round_rate,
> +	.recalc_rate = xvcu_pll_recalc_rate,
> +	.set_rate = xvcu_pll_set_rate,
> +};
> +
> +static struct clk_hw *xvcu_register_pll(struct device *dev,
> +					void __iomem *reg_base,
> +					const char *name, const char *parent,
> +					unsigned long flags)
> +{
> +	struct vcu_pll *pll;
> +	struct clk_hw *hw;
> +	struct clk_init_data init;
> +	int ret;
> +
> +	init.name = name;
> +	init.parent_names = &parent;
> +	init.ops = &vcu_pll_ops;
> +	init.num_parents = 1;
> +	init.flags = flags;
> +
> +	pll = devm_kmalloc(dev, sizeof(*pll), GFP_KERNEL);
> +	if (!pll)
> +		return ERR_PTR(-ENOMEM);
> +
> +	pll->hw.init = &init;
> +	pll->reg_base = reg_base;
> +	pll->fvco_min = FVCO_MIN;
> +	pll->fvco_max = FVCO_MAX;
> +
> +	hw = &pll->hw;
> +	ret = devm_clk_hw_register(dev, hw);
> +	if (ret)
> +		return ERR_PTR(ret);
> +
> +	clk_hw_set_rate_range(hw, pll->fvco_min, pll->fvco_max);
>  
> -	clk_disable_unprepare(xvcu->pll_ref);
> +	return hw;
>  }
>  
>  /**
> @@ -426,7 +500,6 @@ static int xvcu_set_vcu_pll_info(struct xvcu_device *xvcu)
>  	u32 pll_clk;
>  	u32 mod;
>  	int i;
> -	int ret;
>  	const struct xvcu_pll_cfg *found = NULL;
>  	struct clk_hw *hw;
>  
> @@ -486,13 +559,9 @@ static int xvcu_set_vcu_pll_info(struct xvcu_device *xvcu)
>  	dev_dbg(xvcu->dev, "Actual Core clock freq is %uHz\n", coreclk);
>  	dev_dbg(xvcu->dev, "Actual Mcu clock freq is %uHz\n", mcuclk);
>  
> -	ret = xvcu_pll_set_rate(xvcu, fvco, refclk);
> -	if (ret)
> -		return ret;
> -
> -	hw = clk_hw_register_fixed_rate(xvcu->dev, "vcu_pll",
> -					__clk_get_name(xvcu->pll_ref),
> -					0, fvco);
> +	hw = xvcu_register_pll(xvcu->dev, xvcu,
> +			       "vcu_pll", __clk_get_name(xvcu->pll_ref),
> +			       CLK_SET_RATE_NO_REPARENT);

You should be passing address not xvcu structure itself.

Reported by sparse.
drivers/soc/xilinx/xlnx_vcu.c:562:43: warning: incorrect type in
argument 2 (different address spaces)
drivers/soc/xilinx/xlnx_vcu.c:562:43:    expected void [noderef] __iomem
*reg_base
drivers/soc/xilinx/xlnx_vcu.c:562:43:    got struct xvcu_device *xvcu

Thanks,
Michal



More information about the linux-arm-kernel mailing list