msm: clock-8x60: Allow hand-offs of already-running RCG clocks at boot
Inherit the rate of running rate-settable clocks at boot if the
same rate is supported by the Linux kernel. These clocks will
still be disabled by the auto-off code in late-init if no driver
explicitly enables them before that.
This feature allows Linux drivers to keep a clock enabled all the
way through boot. For glitch-free clocks, drivers can set the
rate of the clock to any supported value at boot and enable it
with no interruption of the clock signal. Non-glitch-free clocks
will still experience some interruption, but this can be avoided
by calling clk_set_rate() with the same rate that was set by the
bootloader (which can be returned by calling clk_get_rate()).
Signed-off-by: Matt Wagantall <mattw@codeaurora.org>
diff --git a/arch/arm/mach-msm/clock-local.c b/arch/arm/mach-msm/clock-local.c
index 92132b2..6f29803 100644
--- a/arch/arm/mach-msm/clock-local.c
+++ b/arch/arm/mach-msm/clock-local.c
@@ -721,6 +721,46 @@
return to_rcg_clk(clk)->current_freq->src_clk;
}
+void rcg_clk_handoff(struct clk *c)
+{
+ struct rcg_clk *clk = to_rcg_clk(c);
+ uint32_t ctl_val, ns_val, md_val, ns_mask;
+ struct clk_freq_tbl *freq;
+
+ ctl_val = readl_relaxed(clk->b.ctl_reg);
+ if (!(ctl_val & clk->root_en_mask))
+ return;
+
+ if (clk->bank_masks) {
+ const struct bank_mask_info *bank_info;
+ if (!(ctl_val & clk->bank_masks->bank_sel_mask))
+ bank_info = &clk->bank_masks->bank0_mask;
+ else
+ bank_info = &clk->bank_masks->bank1_mask;
+
+ ns_mask = bank_info->ns_mask;
+ md_val = readl_relaxed(bank_info->md_reg);
+ } else {
+ ns_mask = clk->ns_mask;
+ md_val = clk->md_reg ? readl_relaxed(clk->md_reg) : 0;
+ }
+
+ ns_val = readl_relaxed(clk->ns_reg) & ns_mask;
+ for (freq = clk->freq_tbl; freq->freq_hz != FREQ_END; freq++) {
+ if ((freq->ns_val & ns_mask) == ns_val &&
+ (freq->mnd_en_mask || freq->md_val == md_val)) {
+ pr_info("%s rate=%d\n", clk->c.dbg_name, freq->freq_hz);
+ break;
+ }
+ }
+ if (freq->freq_hz == FREQ_END)
+ return;
+
+ clk->current_freq = freq;
+ c->flags |= CLKFLAG_HANDOFF_RATE;
+ clk_enable(c);
+}
+
static int pll_vote_clk_enable(struct clk *clk)
{
u32 ena;