[PATCH RFC 1/2] clk: introduce clk_invalidate_rate()

From: Neil Armstrong
Date: Thu Sep 19 2019 - 06:25:31 EST


This introduces the clk_invalidate_rate() call used to recalculate the
rate and parent tree of a particular clock if it's known that the
underlying registers set has been altered by the firmware, like from
a suspend/resume handler running in trusted cpu mode.

The call refreshes the actual parent and when changed, instructs CCF
the parent has changed. Finally the call will recalculate the rate of
each part of the tree to make sure the CCF cached tree is in sync with
the hardware.

Signed-off-by: Neil Armstrong <narmstrong@xxxxxxxxxxxx>
---
drivers/clk/clk.c | 70 +++++++++++++++++++++++++++++++++++++++++++++
include/linux/clk.h | 13 +++++++++
2 files changed, 83 insertions(+)

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index ca99e9db6575..8acf38ce3cc4 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -2557,6 +2557,76 @@ int clk_set_parent(struct clk *clk, struct clk *parent)
}
EXPORT_SYMBOL_GPL(clk_set_parent);

+/**
+ * __clk_invalidate_tree
+ * @core: first clk in the subtree
+ *
+ * Walks the subtree of clks starting with clk and recalculates the parents,
+ * then accuracies and rates as it goes.
+ */
+static int __clk_invalidate_tree(struct clk_core *core)
+{
+ struct clk_core *parent, *old_parent;
+ int ret, i, num_parents;
+
+ num_parents = core->num_parents;
+
+ for (i = 0; i < num_parents; i++) {
+ parent = clk_core_get_parent_by_index(core, i);
+ if (!parent)
+ continue;
+
+ ret = __clk_invalidate_tree(parent);
+ if (ret)
+ return ret;
+ }
+
+ parent = __clk_init_parent(core);
+
+ if (parent != core->parent) {
+ old_parent = __clk_set_parent_before(core, parent);
+ __clk_set_parent_after(core, parent, old_parent);
+ }
+
+ __clk_recalc_accuracies(core);
+ __clk_recalc_rates(core, 0);
+
+ return 0;
+}
+
+static int clk_core_invalidate_rate(struct clk_core *core)
+{
+ int ret;
+
+ clk_prepare_lock();
+
+ ret = __clk_invalidate_tree(core);
+
+ clk_prepare_unlock();
+
+ return ret;
+}
+
+/**
+ * clk_invalidate_rate - invalidate and recalc rate of the clock and it's tree
+ * @clk: the clk whose rate is too be invalidated
+ *
+ * If it's known the actual hardware state of a clock tree has changed,
+ * this call will invalidate the cached rate of the clk and it's possible
+ * parents tree to permit recalculation of the actual rate.
+ *
+ * Returns 0 on success, -EERROR otherwise.
+ * If clk is NULL then returns 0.
+ */
+int clk_invalidate_rate(struct clk *clk)
+{
+ if (!clk)
+ return 0;
+
+ return clk_core_invalidate_rate(clk->core);
+}
+EXPORT_SYMBOL_GPL(clk_invalidate_rate);
+
static int clk_core_set_phase_nolock(struct clk_core *core, int degrees)
{
int ret = -EINVAL;
diff --git a/include/linux/clk.h b/include/linux/clk.h
index 853a8f181394..46db47ffb7b2 100644
--- a/include/linux/clk.h
+++ b/include/linux/clk.h
@@ -629,6 +629,19 @@ long clk_round_rate(struct clk *clk, unsigned long rate);
*/
int clk_set_rate(struct clk *clk, unsigned long rate);

+/**
+ * clk_invalidate_rate - invalidate and recalc rate of the clock and it's tree
+ * @clk: the clk whose rate is too be invalidated
+ *
+ * If it's known the actual hardware state of a clock tree has changed,
+ * this call will invalidate the cached rate of the clk and it's possible
+ * parents tree to permit recalculation of the actual rate.
+ *
+ * Returns 0 on success, -EERROR otherwise.
+ * If clk is NULL then returns 0.
+ */
+int clk_invalidate_rate(struct clk *clk);
+
/**
* clk_set_rate_exclusive- set the clock rate and claim exclusivity over
* clock source
--
2.22.0