diff options
Diffstat (limited to 'drivers/clk/sunxi-ng/ccu_mux.c')
-rw-r--r-- | drivers/clk/sunxi-ng/ccu_mux.c | 56 |
1 files changed, 53 insertions, 3 deletions
diff --git a/drivers/clk/sunxi-ng/ccu_mux.c b/drivers/clk/sunxi-ng/ccu_mux.c index 58fc36e7dcce..a43ad52a957d 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 <linux/clk.h> #include <linux/clk-provider.h> +#include <linux/delay.h> #include "ccu_gate.h" #include "ccu_mux.h" @@ -18,8 +20,9 @@ void ccu_mux_helper_adjust_parent_for_prediv(struct ccu_common *common, int parent_index, unsigned long *parent_rate) { - u8 prediv = 1; + u16 prediv = 1; u32 reg; + int i; if (!((common->features & CCU_FEATURE_FIXED_PREDIV) || (common->features & CCU_FEATURE_VARIABLE_PREDIV))) @@ -32,8 +35,9 @@ void ccu_mux_helper_adjust_parent_for_prediv(struct ccu_common *common, } if (common->features & CCU_FEATURE_FIXED_PREDIV) - if (parent_index == cm->fixed_prediv.index) - prediv = cm->fixed_prediv.div; + for (i = 0; i < cm->n_predivs; i++) + if (parent_index == cm->fixed_predivs[i].index) + prediv = cm->fixed_predivs[i].div; if (common->features & CCU_FEATURE_VARIABLE_PREDIV) if (parent_index == cm->variable_prediv.index) { @@ -107,6 +111,15 @@ u8 ccu_mux_helper_get_parent(struct ccu_common *common, parent = reg >> cm->shift; parent &= (1 << cm->width) - 1; + if (cm->table) { + int num_parents = clk_hw_get_num_parents(&common->hw); + int i; + + for (i = 0; i < num_parents; i++) + if (cm->table[i] == parent) + return i; + } + return parent; } @@ -117,6 +130,9 @@ int ccu_mux_helper_set_parent(struct ccu_common *common, unsigned long flags; u32 reg; + if (cm->table) + index = cm->table[index]; + spin_lock_irqsave(common->lock, flags); reg = readl(common->base + common->reg); @@ -185,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); +} |