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.c b/arch/arm/mach-msm/clock.c
index c145240..a29e9cd 100644
--- a/arch/arm/mach-msm/clock.c
+++ b/arch/arm/mach-msm/clock.c
@@ -49,6 +49,15 @@
 			clk_disable(parent);
 			goto out;
 		}
+	} else if (clk->flags & CLKFLAG_HANDOFF_RATE) {
+		/*
+		 * The clock was already enabled by handoff code so there is no
+		 * need to enable it again here. Clearing the handoff flag will
+		 * prevent the lateinit handoff code from disabling the clock if
+		 * a client driver still has it enabled.
+		 */
+		clk->flags &= ~CLKFLAG_HANDOFF_RATE;
+		goto out;
 	}
 	clk->count++;
 out:
@@ -180,6 +189,8 @@
 		struct clk *clk = clock_tbl[n].clk;
 		struct clk *parent = clk_get_parent(clk);
 		clk_set_parent(clk, parent);
+		if (clk->ops->handoff)
+			clk->ops->handoff(clk);
 	}
 
 	clkdev_add_table(clock_tbl, num_clocks);
@@ -199,6 +210,7 @@
 	clock_debug_init(clk_init_data);
 	for (n = 0; n < clk_init_data->size; n++) {
 		struct clk *clk = clk_init_data->table[n].clk;
+		bool handoff = false;
 
 		clock_debug_add(clk);
 		if (!(clk->flags & CLKFLAG_SKIP_AUTO_OFF)) {
@@ -207,7 +219,17 @@
 				count++;
 				clk->ops->auto_off(clk);
 			}
+			if (clk->flags & CLKFLAG_HANDOFF_RATE) {
+				clk->flags &= ~CLKFLAG_HANDOFF_RATE;
+				handoff = true;
+			}
 			spin_unlock_irqrestore(&clk->lock, flags);
+			/*
+			 * Calling clk_disable() outside the lock is safe since
+			 * it doesn't need to be atomic with the flag change.
+			 */
+			if (handoff)
+				clk_disable(clk);
 		}
 	}
 	pr_info("clock_late_init() disabled %d unused clocks\n", count);