[PATCH V3 2/3] clk: Fixup errorhandling for clk_set_parent
Rajagopal Venkat
rajagopal.venkat at linaro.org
Thu Mar 28 06:13:54 EDT 2013
On 27 March 2013 15:20, Ulf Hansson <ulf.hansson at stericsson.com> wrote:
> From: Ulf Hansson <ulf.hansson at linaro.org>
>
> Fixup the broken feature of allowing reparent of a clk to the
> orhpan list and vice verse. When operating on a single-parent
> clk, the .set_parent callback for the clk hw is optional to
> implement, but for a multi-parent clk it is mandatory.
>
> Moreover improve the errorhandling by verifying the prerequisites
> before triggering clk notifiers. This will prevent unnecessary
> rollback with ABORT_RATE_CHANGE.
>
> Signed-off-by: Ulf Hansson <ulf.hansson at linaro.org>
> Cc: Rajagopal Venkat <rajagopal.venkat at linaro.org>
> ---
> drivers/clk/clk.c | 56 ++++++++++++++++++++++++++++++++++-------------------
> 1 file changed, 36 insertions(+), 20 deletions(-)
>
> diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
> index 97d681b..6fa301b 100644
> --- a/drivers/clk/clk.c
> +++ b/drivers/clk/clk.c
> @@ -1269,15 +1269,10 @@ void __clk_reparent(struct clk *clk, struct clk *new_parent)
> __clk_recalc_rates(clk, POST_RATE_CHANGE);
> }
>
> -static int __clk_set_parent(struct clk *clk, struct clk *parent)
> +static u8 clk_fetch_parent_index(struct clk *clk, struct clk *parent)
> {
> - struct clk *old_parent;
> - unsigned long flags;
> - int ret = -EINVAL;
> u8 i;
>
> - old_parent = clk->parent;
> -
> if (!clk->parents)
> clk->parents = kzalloc((sizeof(struct clk*) * clk->num_parents),
> GFP_KERNEL);
> @@ -1297,11 +1292,14 @@ static int __clk_set_parent(struct clk *clk, struct clk *parent)
> }
> }
>
> - if (i == clk->num_parents) {
> - pr_debug("%s: clock %s is not a possible parent of clock %s\n",
> - __func__, parent->name, clk->name);
> - goto out;
> - }
> + return i;
> +}
> +
> +static int __clk_set_parent(struct clk *clk, struct clk *parent, u8 p_index)
> +{
> + unsigned long flags;
> + int ret = 0;
> + struct clk *old_parent = clk->parent;
>
> /* migrate prepare and enable */
> if (clk->prepare_count)
> @@ -1314,7 +1312,8 @@ static int __clk_set_parent(struct clk *clk, struct clk *parent)
> spin_unlock_irqrestore(&enable_lock, flags);
>
> /* change clock input source */
> - ret = clk->ops->set_parent(clk->hw, i);
> + if (parent && clk->ops->set_parent)
> + ret = clk->ops->set_parent(clk->hw, p_index);
What if clk->ops->set_parent() fails? should this clk undefined state
be handled?
>
> /* clean up old prepare and enable */
> spin_lock_irqsave(&enable_lock, flags);
> @@ -1325,7 +1324,6 @@ static int __clk_set_parent(struct clk *clk, struct clk *parent)
> if (clk->prepare_count)
> __clk_unprepare(old_parent);
>
> -out:
> return ret;
> }
>
> @@ -1344,11 +1342,14 @@ out:
> int clk_set_parent(struct clk *clk, struct clk *parent)
> {
> int ret = 0;
> + u8 p_index = 0;
> + unsigned long p_rate = 0;
>
> if (!clk || !clk->ops)
> return -EINVAL;
>
> - if (!clk->ops->set_parent)
> + /* verify ops for for multi-parent clks */
> + if ((clk->num_parents > 1) && (!clk->ops->set_parent))
> return -ENOSYS;
Should this check be moved to clk registration path? clk ops
validation is done in __clk_init().
>
> /* prevent racing with updates to the clock topology */
> @@ -1357,19 +1358,34 @@ int clk_set_parent(struct clk *clk, struct clk *parent)
> if (clk->parent == parent)
> goto out;
>
> + /* check that we are allowed to re-parent if the clock is in use */
> + if ((clk->flags & CLK_SET_PARENT_GATE) && clk->prepare_count) {
> + ret = -EBUSY;
> + goto out;
> + }
> +
> + /* try finding the new parent index */
> + if (parent) {
> + p_index = clk_fetch_parent_index(clk, parent);
> + p_rate = parent->rate;
> + if (p_index == clk->num_parents) {
> + pr_debug("%s: clk %s can not be parent of clk %s\n",
> + __func__, parent->name, clk->name);
> + ret = -EINVAL;
> + goto out;
> + }
> + }
> +
> /* propagate PRE_RATE_CHANGE notifications */
> if (clk->notifier_count)
> - ret = __clk_speculate_rates(clk, parent->rate);
> + ret = __clk_speculate_rates(clk, p_rate);
>
> /* abort if a driver objects */
> if (ret == NOTIFY_STOP)
> goto out;
>
> - /* only re-parent if the clock is not in use */
> - if ((clk->flags & CLK_SET_PARENT_GATE) && clk->prepare_count)
> - ret = -EBUSY;
> - else
> - ret = __clk_set_parent(clk, parent);
> + /* do the re-parent */
> + ret = __clk_set_parent(clk, parent, p_index);
>
> /* propagate ABORT_RATE_CHANGE if .set_parent failed */
> if (ret) {
> --
> 1.7.10
>
In addition, this fix is required to allow reparenting of clk to orphan list.
---
drivers/clk/clk.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index fb5d08a..ec436e0 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -1221,7 +1221,7 @@ void __clk_reparent(struct clk *clk, struct clk
*new_parent)
struct dentry *new_parent_d;
#endif
- if (!clk || !new_parent)
+ if (!clk)
return;
hlist_del(&clk->child_node);
--
1.7.10.4
--
Regards,
Rajagopal
More information about the linux-arm-kernel
mailing list