[RFC PATCH 1/3] clk: add support for temporary parent clock migration
Mike Turquette
mturquette at linaro.org
Thu Aug 22 02:16:49 EDT 2013
Quoting Chander Kashyap (2013-08-06 01:34:23)
> Some platforms use to migrate temporarily to another parent during cpu frequency
> scaling, e.g. Exynos and Tegra. Once the frequency is changed the latch on to
> original parent.
>
> The generic cpufreq-cpu0 driver use CCF to scale cpu frequency. This patch is an
> attempt to address the above mentioned requirement.
The generic cpufreq-cpu0 driver does not use CCF per se. It uses the
long-standing clock api written by Russell. Coincidentally I think that
all the platforms using cpufreq-cpu0 have converted to the common struct
clk.
>
> This is achieved as follows:
>
> Add a clk flag "CLK_SET_RATE_ALTERNATE" for clocks which need to migrate to
> another parent during set_rate operation on them.
We discussed the naming a bit already. I think I like
CLK_SET_RATE_TEMP_PARENT more. It's really long but it gets the idea
across easily.
>
> Add "alternate_parent_name" and "alternate_parent" fields to clk structure
> i.e the name of alternate_parent_clock and reference to alternate_parent_clock.
Similarly temp_parent_name and temp_parent here.
>
> Hence in clk_set_rate API check for the "CLK_SET_RATE_ALTERNATE" flag, then
> latch on to alternate parent clock temporarily. Once the requested rate is set
> on the clk, re-parent back to original parent clock.
>
> This property is applicable only for mux-clocks, where it is guaranteed that
> set_rate will actually execute on parent clock.
Can you clarify what you mean by this?
>
> Signed-off-by: Chander Kashyap <chander.kashyap at linaro.org>
> ---
> drivers/clk/clk-mux.c | 13 ++++++------
> drivers/clk/clk.c | 45 +++++++++++++++++++++++++++++++++++++++---
> include/linux/clk-private.h | 17 +++++++++-------
> include/linux/clk-provider.h | 10 ++++++----
> 4 files changed, 65 insertions(+), 20 deletions(-)
>
> diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c
> index 614444c..47cb77f 100644
> --- a/drivers/clk/clk-mux.c
> +++ b/drivers/clk/clk-mux.c
> @@ -109,8 +109,8 @@ EXPORT_SYMBOL_GPL(clk_mux_ops);
>
> struct clk *clk_register_mux_table(struct device *dev, const char *name,
> const char **parent_names, u8 num_parents, unsigned long flags,
> - void __iomem *reg, u8 shift, u32 mask,
> - u8 clk_mux_flags, u32 *table, spinlock_t *lock)
> + const char *alternate_parent_name, void __iomem *reg, u8 shift,
> + u32 mask, u8 clk_mux_flags, u32 *table, spinlock_t *lock)
> {
> struct clk_mux *mux;
> struct clk *clk;
> @@ -137,6 +137,7 @@ struct clk *clk_register_mux_table(struct device *dev, const char *name,
> init.flags = flags | CLK_IS_BASIC;
> init.parent_names = parent_names;
> init.num_parents = num_parents;
> + init.alternate_parent_name = alternate_parent_name;
>
> /* struct clk_mux assignments */
> mux->reg = reg;
> @@ -157,12 +158,12 @@ struct clk *clk_register_mux_table(struct device *dev, const char *name,
>
> struct clk *clk_register_mux(struct device *dev, const char *name,
> const char **parent_names, u8 num_parents, unsigned long flags,
> - void __iomem *reg, u8 shift, u8 width,
> - u8 clk_mux_flags, spinlock_t *lock)
> + const char *alternate_parent_name, void __iomem *reg, u8 shift,
> + u8 width, u8 clk_mux_flags, spinlock_t *lock)
> {
> u32 mask = BIT(width) - 1;
>
> return clk_register_mux_table(dev, name, parent_names, num_parents,
> - flags, reg, shift, mask, clk_mux_flags,
> - NULL, lock);
> + flags, alternate_parent_name, reg, shift,
> + mask, clk_mux_flags, NULL, lock);
> }
> diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
> index 54a191c..0f18a45 100644
> --- a/drivers/clk/clk.c
> +++ b/drivers/clk/clk.c
> @@ -1209,8 +1209,8 @@ static void clk_change_rate(struct clk *clk)
> */
> int clk_set_rate(struct clk *clk, unsigned long rate)
> {
> - struct clk *top, *fail_clk;
> - int ret = 0;
> + struct clk *top, *fail_clk, *parent = NULL;
> + int ret = 0, index;
>
> /* prevent racing with updates to the clock topology */
> clk_prepare_lock();
> @@ -1231,6 +1231,36 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
> goto out;
> }
>
> + /* Latch on to alternate parent temporarily if needed */
> + if ((clk->flags & CLK_SET_RATE_ALTERNATE)
> + && clk->alternate_parent_name) {
> + /* Save current parent before latching on to alternate parent */
> + parent = clk->parent;
> +
> + if (!clk->alternate_parent) {
> + for (index = 0; index < clk->num_parents; index++) {
> + if (!strcmp(clk->parent_names[index],
> + clk->alternate_parent_name))
> + clk->alternate_parent =
> + __clk_lookup(clk->alternate_parent_name);
> + }
> +
> + if (!clk->alternate_parent) {
> + pr_warn("%s: wrong alternate_parent_name %s",
> + __func__, clk->alternate_parent_name);
> + ret = -EINVAL;
> + goto out;
> + }
> + }
> +
> + ret = clk_set_parent(clk, clk->alternate_parent);
> + if (ret) {
> + pr_warn("%s: failed to set %s parent\n",
> + __func__, clk->alternate_parent->name);
> + goto out;
> + }
> + }
I have two concerns here. First is making the core code more complex for
handling this case. I think that the .set_rate callback for a clock type
could just as easily call clk_set_parent() twice instead of introducing
a new flag and doing it here inside clk_set_rate().
My second concern is the case where there could be more than one
temporary parent. If a clock has 3 possible inputs, one being the
reference input and the other two being alternate/temporary inputs then
there might be a time to use one of the other of the alternates. This is
purely hypothetical but it shows how the CLK_SET_RATE_ALTERNATE flag is
less flexible than just leaving these details up to the .set_rate
callback implementation.
Regards,
Mike
More information about the linux-arm-kernel
mailing list