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.
next prev parent 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).