diff --git a/drivers/clk/sunxi-ng/ccu_mux.c b/drivers/clk/sunxi-ng/ccu_mux.c index 7b17e0c26b0101fa9a4d642fbf62ea06a15fa2a4..a43ad52a957dcbd104ae29a67ecdf4e057038bf1 100644 --- a/drivers/clk/sunxi-ng/ccu_mux.c +++ b/drivers/clk/sunxi-ng/ccu_mux.c @@ -8,7 +8,9 @@ * the License, or (at your option) any later version. */ +#include #include +#include #include "ccu_gate.h" #include "ccu_mux.h" @@ -199,3 +201,37 @@ const struct clk_ops ccu_mux_ops = { .determine_rate = __clk_mux_determine_rate, .recalc_rate = ccu_mux_recalc_rate, }; + +/* + * This clock notifier is called when the frequency of the of the parent + * PLL clock is to be changed. The idea is to switch the parent to a + * stable clock, such as the main oscillator, while the PLL frequency + * stabilizes. + */ +static int ccu_mux_notifier_cb(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct ccu_mux_nb *mux = to_ccu_mux_nb(nb); + int ret = 0; + + if (event == PRE_RATE_CHANGE) { + mux->original_index = ccu_mux_helper_get_parent(mux->common, + mux->cm); + ret = ccu_mux_helper_set_parent(mux->common, mux->cm, + mux->bypass_index); + } else if (event == POST_RATE_CHANGE) { + ret = ccu_mux_helper_set_parent(mux->common, mux->cm, + mux->original_index); + } + + udelay(mux->delay_us); + + return notifier_from_errno(ret); +} + +int ccu_mux_notifier_register(struct clk *clk, struct ccu_mux_nb *mux_nb) +{ + mux_nb->clk_nb.notifier_call = ccu_mux_notifier_cb; + + return clk_notifier_register(clk, &mux_nb->clk_nb); +} diff --git a/drivers/clk/sunxi-ng/ccu_mux.h b/drivers/clk/sunxi-ng/ccu_mux.h index 8eab1733eeac0528b975bc7ffdfcd6edf2c2f539..f5123a1a6603b1b289dddb196da05a69b9b05c78 100644 --- a/drivers/clk/sunxi-ng/ccu_mux.h +++ b/drivers/clk/sunxi-ng/ccu_mux.h @@ -96,4 +96,18 @@ int ccu_mux_helper_set_parent(struct ccu_common *common, struct ccu_mux_internal *cm, u8 index); +struct ccu_mux_nb { + struct notifier_block clk_nb; + struct ccu_common *common; + struct ccu_mux_internal *cm; + + u32 delay_us; /* How many us to wait after reparenting */ + u8 bypass_index; /* Which parent to temporarily use */ + u8 original_index; /* This is set by the notifier callback */ +}; + +#define to_ccu_mux_nb(_nb) container_of(_nb, struct ccu_mux_nb, clk_nb) + +int ccu_mux_notifier_register(struct clk *clk, struct ccu_mux_nb *mux_nb); + #endif /* _CCU_MUX_H_ */