|
@@ -1332,15 +1332,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);
|
|
@@ -1360,11 +1355,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)
|
|
@@ -1377,7 +1375,8 @@ static int __clk_set_parent(struct clk *clk, struct clk *parent)
|
|
|
clk_enable_unlock(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);
|
|
|
|
|
|
/* clean up old prepare and enable */
|
|
|
flags = clk_enable_lock();
|
|
@@ -1388,7 +1387,6 @@ static int __clk_set_parent(struct clk *clk, struct clk *parent)
|
|
|
if (clk->prepare_count)
|
|
|
__clk_unprepare(old_parent);
|
|
|
|
|
|
-out:
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
@@ -1407,11 +1405,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;
|
|
|
|
|
|
/* prevent racing with updates to the clock topology */
|
|
@@ -1420,19 +1421,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) {
|