diff --git a/drivers/clk/mediatek/clk-mux.c b/drivers/clk/mediatek/clk-mux.c index cd5f9fd8cb98e..4421e48592576 100644 --- a/drivers/clk/mediatek/clk-mux.c +++ b/drivers/clk/mediatek/clk-mux.c @@ -4,6 +4,7 @@ * Author: Owen Chen */ +#include #include #include #include @@ -259,4 +260,41 @@ void mtk_clk_unregister_muxes(const struct mtk_mux *muxes, int num, } EXPORT_SYMBOL_GPL(mtk_clk_unregister_muxes); +/* + * This clock notifier is called when the frequency 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 mtk_clk_mux_notifier_cb(struct notifier_block *nb, + unsigned long event, void *_data) +{ + struct clk_notifier_data *data = _data; + struct clk_hw *hw = __clk_get_hw(data->clk); + struct mtk_mux_nb *mux_nb = to_mtk_mux_nb(nb); + int ret = 0; + + switch (event) { + case PRE_RATE_CHANGE: + mux_nb->original_index = mux_nb->ops->get_parent(hw); + ret = mux_nb->ops->set_parent(hw, mux_nb->bypass_index); + break; + case POST_RATE_CHANGE: + case ABORT_RATE_CHANGE: + ret = mux_nb->ops->set_parent(hw, mux_nb->original_index); + break; + } + + return notifier_from_errno(ret); +} + +int devm_mtk_clk_mux_notifier_register(struct device *dev, struct clk *clk, + struct mtk_mux_nb *mux_nb) +{ + mux_nb->nb.notifier_call = mtk_clk_mux_notifier_cb; + + return devm_clk_notifier_register(dev, clk, &mux_nb->nb); +} +EXPORT_SYMBOL_GPL(devm_mtk_clk_mux_notifier_register); + MODULE_LICENSE("GPL"); diff --git a/drivers/clk/mediatek/clk-mux.h b/drivers/clk/mediatek/clk-mux.h index 6539c58f5d7df..83ff420f4ebe6 100644 --- a/drivers/clk/mediatek/clk-mux.h +++ b/drivers/clk/mediatek/clk-mux.h @@ -7,12 +7,14 @@ #ifndef __DRV_CLK_MTK_MUX_H #define __DRV_CLK_MTK_MUX_H +#include #include #include struct clk; struct clk_hw_onecell_data; struct clk_ops; +struct device; struct device_node; struct mtk_mux { @@ -89,4 +91,17 @@ int mtk_clk_register_muxes(const struct mtk_mux *muxes, void mtk_clk_unregister_muxes(const struct mtk_mux *muxes, int num, struct clk_hw_onecell_data *clk_data); +struct mtk_mux_nb { + struct notifier_block nb; + const struct clk_ops *ops; + + u8 bypass_index; /* Which parent to temporarily use */ + u8 original_index; /* Set by notifier callback */ +}; + +#define to_mtk_mux_nb(_nb) container_of(_nb, struct mtk_mux_nb, nb) + +int devm_mtk_clk_mux_notifier_register(struct device *dev, struct clk *clk, + struct mtk_mux_nb *mux_nb); + #endif /* __DRV_CLK_MTK_MUX_H */