diff options
Diffstat (limited to 'drivers/clk/mediatek/clk-mux.c')
-rw-r--r-- | drivers/clk/mediatek/clk-mux.c | 48 |
1 files changed, 48 insertions, 0 deletions
diff --git a/drivers/clk/mediatek/clk-mux.c b/drivers/clk/mediatek/clk-mux.c index cd5f9fd8cb98..ba1720b9e231 100644 --- a/drivers/clk/mediatek/clk-mux.c +++ b/drivers/clk/mediatek/clk-mux.c @@ -4,6 +4,7 @@ * Author: Owen Chen <owen.chen@mediatek.com> */ +#include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/compiler_types.h> #include <linux/container_of.h> @@ -128,9 +129,18 @@ static int mtk_clk_mux_set_parent_setclr_lock(struct clk_hw *hw, u8 index) return 0; } +static int mtk_clk_mux_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct mtk_clk_mux *mux = to_mtk_clk_mux(hw); + + return clk_mux_determine_rate_flags(hw, req, mux->data->flags); +} + const struct clk_ops mtk_mux_clr_set_upd_ops = { .get_parent = mtk_clk_mux_get_parent, .set_parent = mtk_clk_mux_set_parent_setclr_lock, + .determine_rate = mtk_clk_mux_determine_rate, }; EXPORT_SYMBOL_GPL(mtk_mux_clr_set_upd_ops); @@ -140,6 +150,7 @@ const struct clk_ops mtk_mux_gate_clr_set_upd_ops = { .is_enabled = mtk_clk_mux_is_enabled, .get_parent = mtk_clk_mux_get_parent, .set_parent = mtk_clk_mux_set_parent_setclr_lock, + .determine_rate = mtk_clk_mux_determine_rate, }; EXPORT_SYMBOL_GPL(mtk_mux_gate_clr_set_upd_ops); @@ -259,4 +270,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"); |