|
@@ -1218,10 +1218,9 @@ static void clk_reparent(struct clk *clk, struct clk *new_parent)
|
|
|
clk->parent = new_parent;
|
|
|
}
|
|
|
|
|
|
-static int __clk_set_parent(struct clk *clk, struct clk *parent, u8 p_index)
|
|
|
+static struct clk *__clk_set_parent_before(struct clk *clk, struct clk *parent)
|
|
|
{
|
|
|
unsigned long flags;
|
|
|
- int ret = 0;
|
|
|
struct clk *old_parent = clk->parent;
|
|
|
|
|
|
/*
|
|
@@ -1252,6 +1251,34 @@ static int __clk_set_parent(struct clk *clk, struct clk *parent, u8 p_index)
|
|
|
clk_reparent(clk, parent);
|
|
|
clk_enable_unlock(flags);
|
|
|
|
|
|
+ return old_parent;
|
|
|
+}
|
|
|
+
|
|
|
+static void __clk_set_parent_after(struct clk *clk, struct clk *parent,
|
|
|
+ struct clk *old_parent)
|
|
|
+{
|
|
|
+ /*
|
|
|
+ * Finish the migration of prepare state and undo the changes done
|
|
|
+ * for preventing a race with clk_enable().
|
|
|
+ */
|
|
|
+ if (clk->prepare_count) {
|
|
|
+ clk_disable(clk);
|
|
|
+ clk_disable(old_parent);
|
|
|
+ __clk_unprepare(old_parent);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* update debugfs with new clk tree topology */
|
|
|
+ clk_debug_reparent(clk, parent);
|
|
|
+}
|
|
|
+
|
|
|
+static int __clk_set_parent(struct clk *clk, struct clk *parent, u8 p_index)
|
|
|
+{
|
|
|
+ unsigned long flags;
|
|
|
+ int ret = 0;
|
|
|
+ struct clk *old_parent;
|
|
|
+
|
|
|
+ old_parent = __clk_set_parent_before(clk, parent);
|
|
|
+
|
|
|
/* change clock input source */
|
|
|
if (parent && clk->ops->set_parent)
|
|
|
ret = clk->ops->set_parent(clk->hw, p_index);
|
|
@@ -1269,18 +1296,8 @@ static int __clk_set_parent(struct clk *clk, struct clk *parent, u8 p_index)
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
- /*
|
|
|
- * Finish the migration of prepare state and undo the changes done
|
|
|
- * for preventing a race with clk_enable().
|
|
|
- */
|
|
|
- if (clk->prepare_count) {
|
|
|
- clk_disable(clk);
|
|
|
- clk_disable(old_parent);
|
|
|
- __clk_unprepare(old_parent);
|
|
|
- }
|
|
|
+ __clk_set_parent_after(clk, parent, old_parent);
|
|
|
|
|
|
- /* update debugfs with new clk tree topology */
|
|
|
- clk_debug_reparent(clk, parent);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -1465,17 +1482,32 @@ static void clk_change_rate(struct clk *clk)
|
|
|
struct clk *child;
|
|
|
unsigned long old_rate;
|
|
|
unsigned long best_parent_rate = 0;
|
|
|
+ bool skip_set_rate = false;
|
|
|
+ struct clk *old_parent;
|
|
|
|
|
|
old_rate = clk->rate;
|
|
|
|
|
|
- /* set parent */
|
|
|
- if (clk->new_parent && clk->new_parent != clk->parent)
|
|
|
- __clk_set_parent(clk, clk->new_parent, clk->new_parent_index);
|
|
|
-
|
|
|
- if (clk->parent)
|
|
|
+ if (clk->new_parent)
|
|
|
+ best_parent_rate = clk->new_parent->rate;
|
|
|
+ else if (clk->parent)
|
|
|
best_parent_rate = clk->parent->rate;
|
|
|
|
|
|
- if (clk->ops->set_rate)
|
|
|
+ if (clk->new_parent && clk->new_parent != clk->parent) {
|
|
|
+ old_parent = __clk_set_parent_before(clk, clk->new_parent);
|
|
|
+
|
|
|
+ if (clk->ops->set_rate_and_parent) {
|
|
|
+ skip_set_rate = true;
|
|
|
+ clk->ops->set_rate_and_parent(clk->hw, clk->new_rate,
|
|
|
+ best_parent_rate,
|
|
|
+ clk->new_parent_index);
|
|
|
+ } else if (clk->ops->set_parent) {
|
|
|
+ clk->ops->set_parent(clk->hw, clk->new_parent_index);
|
|
|
+ }
|
|
|
+
|
|
|
+ __clk_set_parent_after(clk, clk->new_parent, old_parent);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!skip_set_rate && clk->ops->set_rate)
|
|
|
clk->ops->set_rate(clk->hw, clk->new_rate, best_parent_rate);
|
|
|
|
|
|
if (clk->ops->recalc_rate)
|
|
@@ -1770,6 +1802,14 @@ int __clk_init(struct device *dev, struct clk *clk)
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
|
+ if (clk->ops->set_rate_and_parent &&
|
|
|
+ !(clk->ops->set_parent && clk->ops->set_rate)) {
|
|
|
+ pr_warn("%s: %s must implement .set_parent & .set_rate\n",
|
|
|
+ __func__, clk->name);
|
|
|
+ ret = -EINVAL;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
/* throw a WARN if any entries in parent_names are NULL */
|
|
|
for (i = 0; i < clk->num_parents; i++)
|
|
|
WARN(!clk->parent_names[i],
|