From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: David Dai Date: Mon, 19 Aug 2019 16:35:44 -0700 Subject: clk: add pre and post change rate callbacks There are scenarios where a rate change could result in a configuration change for both the targeted clock and its parent. For example, setting the rate for a clock could require both slewing its parent PLL as well as adjusting the clock's divider values. Due to the fact that rate change propagation always occurs from parent to child, we could exceed the allowed operating frequencies for the clock as the parent slews to a higher frequency before increasing the downstream divider. Add a pre change call back which allows the clock to adjust its divider appropriately before any rate change has occurred from its parents to ensure that the clock's requirements are always within safe frequencies during parent rate changes. The symmetrical post change call back handles the scenario where the divider adjusts to a lower value and can only be safely adjusted after the parent rate changes. Change-Id: I4f8cf9df6fc256d065599de86a34cf99eae4d853 Signed-off-by: David Dai --- drivers/clk/clk.c | 10 ++++++++++ include/linux/clk-provider.h | 13 +++++++++++++ 2 files changed, 23 insertions(+) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 1c677d7f7f53..020fdb8105cc 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1980,6 +1980,13 @@ static struct clk_core *clk_propagate_rate_change(struct clk_core *core, fail_clk = core; } + if (core->ops->pre_rate_change) { + ret = core->ops->pre_rate_change(core->hw, core->rate, + core->new_rate); + if (ret) + fail_clk = core; + } + hlist_for_each_entry(child, &core->children, child_node) { /* Skip children who will be reparented to another clock */ if (child->new_parent && child->new_parent != core) @@ -2082,6 +2089,9 @@ static void clk_change_rate(struct clk_core *core) if (core->flags & CLK_RECALC_NEW_RATES) (void)clk_calc_new_rates(core, core->new_rate); + if (core->ops->post_rate_change) + core->ops->post_rate_change(core->hw, old_rate, core->rate); + /* * Use safe iteration, as change_rate can actually swap parents * for certain clock types. diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 2fdfe8061363..73e4369e8408 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -199,6 +199,13 @@ struct clk_duty { * directory is provided as an argument. Called with * prepare_lock held. Returns 0 on success, -EERROR otherwise. * + * @pre_rate_change: Optional callback for a clock to fulfill its rate + * change requirements before any rate change has occurred in + * its clock tree. Returns 0 on success, -EERROR otherwise. + * + * @post_rate_change: Optional callback for a clock to clean up any + * requirements that were needed while the clock and its tree + * was changing states. Returns 0 on success, -EERROR otherwise. * * The clk_enable/clk_disable and clk_prepare/clk_unprepare pairs allow * implementations to split any work between atomic (enable) and sleepable @@ -245,6 +252,12 @@ struct clk_ops { struct clk_duty *duty); void (*init)(struct clk_hw *hw); void (*debug_init)(struct clk_hw *hw, struct dentry *dentry); + int (*pre_rate_change)(struct clk_hw *hw, + unsigned long rate, + unsigned long new_rate); + int (*post_rate_change)(struct clk_hw *hw, + unsigned long old_rate, + unsigned long rate); }; /**