Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

clk: sunxi-ng: mux: Add clk notifier functions

On sunxi we support cpufreq by changing the clock rate of PLL-CPU.
It's possible the clock output of the PLL goes out of the CPU's
operational limits when the PLL's multipliers / dividers are changed
and it hasn't stabilized yet. This would result in the CPU hanging.

To circumvent this, we temporarily switch the CPU mux clock to another
stable clock before the rate change, and switch it back after the PLL
stabilizes. This is done with clk notifiers registered on the PLL.

This patch adds common functions for notifiers to reparent mux clocks.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>

authored by

Chen-Yu Tsai and committed by
Maxime Ripard
8adfb086 ff5294db

+50
+36
drivers/clk/sunxi-ng/ccu_mux.c
··· 8 8 * the License, or (at your option) any later version. 9 9 */ 10 10 11 + #include <linux/clk.h> 11 12 #include <linux/clk-provider.h> 13 + #include <linux/delay.h> 12 14 13 15 #include "ccu_gate.h" 14 16 #include "ccu_mux.h" ··· 201 199 .determine_rate = __clk_mux_determine_rate, 202 200 .recalc_rate = ccu_mux_recalc_rate, 203 201 }; 202 + 203 + /* 204 + * This clock notifier is called when the frequency of the of the parent 205 + * PLL clock is to be changed. The idea is to switch the parent to a 206 + * stable clock, such as the main oscillator, while the PLL frequency 207 + * stabilizes. 208 + */ 209 + static int ccu_mux_notifier_cb(struct notifier_block *nb, 210 + unsigned long event, void *data) 211 + { 212 + struct ccu_mux_nb *mux = to_ccu_mux_nb(nb); 213 + int ret = 0; 214 + 215 + if (event == PRE_RATE_CHANGE) { 216 + mux->original_index = ccu_mux_helper_get_parent(mux->common, 217 + mux->cm); 218 + ret = ccu_mux_helper_set_parent(mux->common, mux->cm, 219 + mux->bypass_index); 220 + } else if (event == POST_RATE_CHANGE) { 221 + ret = ccu_mux_helper_set_parent(mux->common, mux->cm, 222 + mux->original_index); 223 + } 224 + 225 + udelay(mux->delay_us); 226 + 227 + return notifier_from_errno(ret); 228 + } 229 + 230 + int ccu_mux_notifier_register(struct clk *clk, struct ccu_mux_nb *mux_nb) 231 + { 232 + mux_nb->clk_nb.notifier_call = ccu_mux_notifier_cb; 233 + 234 + return clk_notifier_register(clk, &mux_nb->clk_nb); 235 + }
+14
drivers/clk/sunxi-ng/ccu_mux.h
··· 96 96 struct ccu_mux_internal *cm, 97 97 u8 index); 98 98 99 + struct ccu_mux_nb { 100 + struct notifier_block clk_nb; 101 + struct ccu_common *common; 102 + struct ccu_mux_internal *cm; 103 + 104 + u32 delay_us; /* How many us to wait after reparenting */ 105 + u8 bypass_index; /* Which parent to temporarily use */ 106 + u8 original_index; /* This is set by the notifier callback */ 107 + }; 108 + 109 + #define to_ccu_mux_nb(_nb) container_of(_nb, struct ccu_mux_nb, clk_nb) 110 + 111 + int ccu_mux_notifier_register(struct clk *clk, struct ccu_mux_nb *mux_nb); 112 + 99 113 #endif /* _CCU_MUX_H_ */