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); }; /**