linux-rockchip.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
From: Sjoerd Simons <sjoerd.simons@collabora.co.uk>
To: Heiko Stuebner <heiko@sntech.de>,
	mturquette@baylibre.com, sboyd@codeaurora.org
Cc: linux-rockchip@lists.infradead.org, linux-clk@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org
Subject: Re: [PATCH 2/3] clk: rockchip: handle mux dependency of fractional dividers
Date: Mon, 05 Oct 2015 21:09:51 +0200	[thread overview]
Message-ID: <1444072191.9895.27.camel@collabora.co.uk> (raw)
In-Reply-To: <1990916.Xur8Q3rQIb@phil>

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@sntech.de>

Tested-by: Sjoerd Simons <sjoerd.simons@collabora.co.uk>
Reviewed-by: Sjoerd Simons <sjoerd.simons@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.

  reply	other threads:[~2015-10-05 19:09 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-08-21 17:46 [PATCH 1/3] clk: add flag for clocks that need to be enabled on rate changes Heiko Stuebner
2015-08-21 17:47 ` [PATCH 2/3] clk: rockchip: handle mux dependency of fractional dividers Heiko Stuebner
2015-10-05 19:09   ` Sjoerd Simons [this message]
2015-08-21 17:48 ` [PATCH 3/3] clk: rockchip: include downstream muxes into " Heiko Stuebner
2015-10-05 19:09   ` Sjoerd Simons
2015-12-12  3:35   ` Caesar Wang
2015-10-02 14:12 ` [PATCH 1/3] clk: add flag for clocks that need to be enabled on rate changes Heiko Stübner
2015-10-08 21:58   ` Stephen Boyd
2015-10-11 10:41     ` Heiko Stübner
2015-10-12 16:03       ` Heiko Stübner
2015-10-13  3:34         ` Xing Zheng
2015-10-05 19:09 ` Sjoerd Simons

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1444072191.9895.27.camel@collabora.co.uk \
    --to=sjoerd.simons@collabora.co.uk \
    --cc=heiko@sntech.de \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-clk@vger.kernel.org \
    --cc=linux-rockchip@lists.infradead.org \
    --cc=mturquette@baylibre.com \
    --cc=sboyd@codeaurora.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).