[PATCH 2/3] clk: rockchip: handle mux dependency of fractional dividers

Sjoerd Simons sjoerd.simons at collabora.co.uk
Mon Oct 5 12:09:51 PDT 2015


On Fri, 2015-08-21 at 19:47 +0200, Heiko Stuebner wrote:
> The fractional dividers of Rockchip SoCs contain an "auto-gating
> -feature"
> that requires the downstream mux to actually point to the fractional
> divider and the fractional divider gate to be enabled, for it to
> really
> accept changes to the divider ratio.
> 
> The downstream muxes themselfs are not generic enough to include them
> directly into the fractional divider, as they have varying sources of
> parent clocks including not only clocks related to the fractional
> dividers but other clocks as well.
> 
> To solve this, allow our clock branches to specify direct child clock
> -
> branches in the new child property, let the fractional divider
> register
> its downstream mux through this and add a clock notifier that
> temporarily
> switches the mux setting when it notices rate changes to the
> fractional
> divider.
> 
> Signed-off-by: Heiko Stuebner <heiko at sntech.de>

Tested-by: Sjoerd Simons <sjoerd.simons at collabora.co.uk>
Reviewed-by: Sjoerd Simons <sjoerd.simons at collabora.co.uk>


>  drivers/clk/rockchip/clk.c | 137
> ++++++++++++++++++++++++++++++++++++++++-----
>  drivers/clk/rockchip/clk.h |  19 +++++++
>  2 files changed, 142 insertions(+), 14 deletions(-)
> 
> diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c
> index 2493881..8c7fd2b 100644
> --- a/drivers/clk/rockchip/clk.c
> +++ b/drivers/clk/rockchip/clk.c
> @@ -102,22 +102,82 @@ static struct clk
> *rockchip_clk_register_branch(const char *name,
>  	return clk;
>  }
>  
> +struct rockchip_clk_frac {
> +	struct notifier_block			clk_nb;
> +	struct clk_fractional_divider		div;
> +	struct clk_gate				gate;
> +
> +	struct clk_mux				mux;
> +	const struct clk_ops			*mux_ops;
> +	int					mux_frac_idx;
> +
> +	bool					rate_change_remu
> xed;
> +	int					rate_change_idx;
> +};
> +
> +#define to_rockchip_clk_frac_nb(nb) \
> +			container_of(nb, struct rockchip_clk_frac,
> clk_nb)
> +
> +static int rockchip_clk_frac_notifier_cb(struct notifier_block *nb,
> +					 unsigned long event, void
> *data)
> +{
> +	struct clk_notifier_data *ndata = data;
> +	struct rockchip_clk_frac *frac =
> to_rockchip_clk_frac_nb(nb);
> +	struct clk_mux *frac_mux = &frac->mux;
> +	int ret = 0;
> +
> +	pr_debug("%s: event %lu, old_rate %lu, new_rate: %lu\n",
> +		 __func__, event, ndata->old_rate, ndata->new_rate);
> +	if (event == PRE_RATE_CHANGE) {
> +		frac->rate_change_idx = frac->mux_ops
> ->get_parent(&frac_mux->hw);
> +		if (frac->rate_change_idx != frac->mux_frac_idx) {
> +			frac->mux_ops->set_parent(&frac_mux->hw,
> frac->mux_frac_idx);
> +			frac->rate_change_remuxed = 1;
> +		}
> +	} else if (event == POST_RATE_CHANGE) {
> +		/*
> +		 * The POST_RATE_CHANGE notifier runs directly after
> the
> +		 * divider clock is set in clk_change_rate, so we'll
> have
> +		 * remuxed back to the original parent before
> clk_change_rate
> +		 * reaches the mux itself.
> +		 */
> +		if (frac->rate_change_remuxed) {
> +			frac->mux_ops->set_parent(&frac_mux->hw,
> frac->rate_change_idx);
> +			frac->rate_change_remuxed = 0;
> +		}
> +	}
> +
> +	return notifier_from_errno(ret);
> +}
> +
>  static struct clk *rockchip_clk_register_frac_branch(const char
> *name,
>  		const char *const *parent_names, u8 num_parents,
>  		void __iomem *base, int muxdiv_offset, u8 div_flags,
>  		int gate_offset, u8 gate_shift, u8 gate_flags,
> -		unsigned long flags, spinlock_t *lock)
> +		unsigned long flags, struct rockchip_clk_branch
> *child,
> +		spinlock_t *lock)
>  {
> +	struct rockchip_clk_frac *frac;
>  	struct clk *clk;
>  	struct clk_gate *gate = NULL;
>  	struct clk_fractional_divider *div = NULL;
>  	const struct clk_ops *div_ops = NULL, *gate_ops = NULL;
>  
> -	if (gate_offset >= 0) {
> -		gate = kzalloc(sizeof(*gate), GFP_KERNEL);
> -		if (!gate)
> -			return ERR_PTR(-ENOMEM);
> +	if (muxdiv_offset < 0)
> +		return ERR_PTR(-EINVAL);
>  
> +	if (child && child->branch_type != branch_mux) {
> +		pr_err("%s: fractional child clock for %s can only
> be a mux\n",
> +		       __func__, name);
> +		return ERR_PTR(-EINVAL);
> +	}
> +
> +	frac = kzalloc(sizeof(*frac), GFP_KERNEL);
> +	if (!frac)
> +		return ERR_PTR(-ENOMEM);
> +
> +	if (gate_offset >= 0) {
> +		gate = &frac->gate;
>  		gate->flags = gate_flags;
>  		gate->reg = base + gate_offset;
>  		gate->bit_idx = gate_shift;
> @@ -125,13 +185,7 @@ static struct clk
> *rockchip_clk_register_frac_branch(const char *name,
>  		gate_ops = &clk_gate_ops;
>  	}
>  
> -	if (muxdiv_offset < 0)
> -		return ERR_PTR(-EINVAL);
> -
> -	div = kzalloc(sizeof(*div), GFP_KERNEL);
> -	if (!div)
> -		return ERR_PTR(-ENOMEM);
> -
> +	div = &frac->div;
>  	div->flags = div_flags;
>  	div->reg = base + muxdiv_offset;
>  	div->mshift = 16;
> @@ -145,7 +199,61 @@ static struct clk
> *rockchip_clk_register_frac_branch(const char *name,
>  				     NULL, NULL,
>  				     &div->hw, div_ops,
>  				     gate ? &gate->hw : NULL,
> gate_ops,
> -				     flags);
> +				     flags | CLK_SET_RATE_UNGATE);
> +	if (IS_ERR(clk)) {
> +		kfree(frac);
> +		return clk;
> +	}
> +
> +	if (child) {
> +		struct clk_mux *frac_mux = &frac->mux;
> +		struct clk_init_data init;
> +		struct clk *mux_clk;
> +		int i, ret;
> +
> +		frac->mux_frac_idx = -1;
> +		for (i = 0; i < child->num_parents; i++) {
> +			if (!strcmp(name, child->parent_names[i])) {
> +				pr_debug("%s: found fractional
> parent in mux at pos %d\n",
> +					 __func__, i);
> +				frac->mux_frac_idx = i;
> +				break;
> +			}
> +		}
> +
> +		frac->mux_ops = &clk_mux_ops;
> +		frac->clk_nb.notifier_call =
> rockchip_clk_frac_notifier_cb;
> +
> +		frac_mux->reg = base + child->muxdiv_offset;
> +		frac_mux->shift = child->mux_shift;
> +		frac_mux->mask = BIT(child->mux_width) - 1;
> +		frac_mux->flags = child->mux_flags;
> +		frac_mux->lock = lock;
> +		frac_mux->hw.init = &init;
> +
> +		init.name = child->name;
> +		init.flags = child->flags | CLK_SET_RATE_PARENT;
> +		init.ops = frac->mux_ops;
> +		init.parent_names = child->parent_names;
> +		init.num_parents = child->num_parents;
> +
> +		mux_clk = clk_register(NULL, &frac_mux->hw);
> +		if (IS_ERR(mux_clk))
> +			return clk;
> +
> +		rockchip_clk_add_lookup(mux_clk, child->id);
> +
> +		/* notifier on the fraction divider to catch rate
> changes */
> +		if (frac->mux_frac_idx >= 0) {
> +			ret = clk_notifier_register(clk, &frac
> ->clk_nb);
> +			if (ret)
> +				pr_err("%s: failed to register clock
> notifier for %s\n",
> +						__func__, name);
> +		} else {
> +			pr_warn("%s: could not find %s as parent of
> %s, rate changes may not work\n",
> +				__func__, name, child->name);
> +		}
> +	}
>  
>  	return clk;
>  }
> @@ -249,7 +357,8 @@ void __init rockchip_clk_register_branches(
>  				list->parent_names, list
> ->num_parents,
>  				reg_base, list->muxdiv_offset, list
> ->div_flags,
>  				list->gate_offset, list->gate_shift,
> -				list->gate_flags, flags, &clk_lock);
> +				list->gate_flags, flags, list
> ->child,
> +				&clk_lock);
>  			break;
>  		case branch_gate:
>  			flags |= CLK_SET_RATE_PARENT;
> diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h
> index dc8ecb2..147db79 100644
> --- a/drivers/clk/rockchip/clk.h
> +++ b/drivers/clk/rockchip/clk.h
> @@ -235,6 +235,7 @@ struct rockchip_clk_branch {
>  	int				gate_offset;
>  	u8				gate_shift;
>  	u8				gate_flags;
> +	struct rockchip_clk_branch	*child;
>  };
>  
>  #define COMPOSITE(_id, cname, pnames, f, mo, ms, mw, mf, ds, dw,\
> @@ -369,6 +370,24 @@ struct rockchip_clk_branch {
>  		.gate_flags	= gf,				
> \
>  	}
>  
> +#define COMPOSITE_FRACMUX(_id, cname, pname, f, mo, df, go, gs, gf,
> ch) \
> +	{							\
> +		.id		= _id,				
> \
> +		.branch_type	= branch_fraction_divider,	
> \
> +		.name		= cname,			
> \
> +		.parent_names	= (const char *[]){ pname },	
> \
> +		.num_parents	= 1,				
> \
> +		.flags		= f,				
> \
> +		.muxdiv_offset	= mo,				
> \
> +		.div_shift	= 16,				
> \
> +		.div_width	= 16,				
> \
> +		.div_flags	= df,				
> \
> +		.gate_offset	= go,				
> \
> +		.gate_shift	= gs,				
> \
> +		.gate_flags	= gf,				
> \
> +		.child		= &(struct
> rockchip_clk_branch)ch, \
> +	}
> +
>  #define MUX(_id, cname, pnames, f, o, s, w, mf)			
> \
>  	{							\
>  		.id		= _id,				
> \

-- 
Sjoerd Simons
Collabora Ltd.



More information about the linux-arm-kernel mailing list