msm: clock: Support recursive handoffs

It is necessary to ensure that a clock's parents are handed off
before the clock itself. Otherwise, the clk_prepare_enable()
call on the child may fail if the parent's rate has not yet
been set.

CRs-Fixed: 362534
Change-Id: I7687708f3c85aab64bfc6284d2cf1ca0ad5cc520
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 317511e..cbc9fba 100644
--- a/arch/arm/mach-msm/clock.c
+++ b/arch/arm/mach-msm/clock.c
@@ -419,14 +419,60 @@
 
 static struct clock_init_data __initdata *clk_init_data;
 
+static enum handoff __init __handoff_clk(struct clk *clk)
+{
+	enum handoff ret;
+	struct handoff_clk *h;
+	int err = 0;
+
+	/*
+	 * Tree roots don't have parents, but need to be handed off. So,
+	 * terminate recursion by returning "enabled". Also return "enabled"
+	 * for clocks with non-zero enable counts since they must have already
+	 * been handed off.
+	 */
+	if (clk == NULL || clk->count)
+		return HANDOFF_ENABLED_CLK;
+
+	/* Clocks without handoff functions are assumed to be disabled. */
+	if (!clk->ops->handoff || (clk->flags & CLKFLAG_SKIP_HANDOFF))
+		return HANDOFF_DISABLED_CLK;
+
+	/*
+	 * Handoff functions for children must be called before their parents'
+	 * so that the correct parent is returned by the clk_get_parent() below.
+	 */
+	ret = clk->ops->handoff(clk);
+	if (ret == HANDOFF_ENABLED_CLK) {
+		ret = __handoff_clk(clk_get_parent(clk));
+		if (ret == HANDOFF_ENABLED_CLK) {
+			h = kmalloc(sizeof(*h), GFP_KERNEL);
+			if (!h) {
+				err = -ENOMEM;
+				goto out;
+			}
+			err = clk_prepare_enable(clk);
+			if (err)
+				goto out;
+			h->clk = clk;
+			list_add_tail(&h->list, &handoff_list);
+		}
+	}
+out:
+	if (err) {
+		pr_err("%s handoff failed (%d)\n", clk->dbg_name, err);
+		kfree(h);
+		ret = HANDOFF_DISABLED_CLK;
+	}
+	return ret;
+}
+
 void __init msm_clock_init(struct clock_init_data *data)
 {
 	unsigned n;
 	struct clk_lookup *clock_tbl;
-	struct handoff_clk *h;
 	size_t num_clocks;
 	struct clk *clk;
-	int ret;
 
 	clk_init_data = data;
 	if (clk_init_data->pre_init)
@@ -447,32 +493,8 @@
 	 * Detect and preserve initial clock state until clock_late_init() or
 	 * a driver explicitly changes it, whichever is first.
 	 */
-	for (n = 0; n < num_clocks; n++) {
-		clk = clock_tbl[n].clk;
-		/*
-		 * Perform the handoffs, skipping clocks that don't support it
-		 * or have already been handed off because they have multiple
-		 * entries in the clock table.
-		 */
-		if (clk->ops->handoff && !clk->count &&
-		    !(clk->flags & CLKFLAG_SKIP_HANDOFF) &&
-		    clk->ops->handoff(clk) == HANDOFF_ENABLED_CLK) {
-			h = kmalloc(sizeof(*h), GFP_KERNEL);
-			if (!h)
-				ret = -ENOMEM;
-			else
-				ret = clk_prepare_enable(clk);
-
-			if (ret) {
-				pr_err("%s handoff failed (%d)\n",
-				       clk->dbg_name, ret);
-				kfree(h);
-			} else {
-				h->clk = clk;
-				list_add_tail(&h->list, &handoff_list);
-			}
-		}
-	}
+	for (n = 0; n < num_clocks; n++)
+		__handoff_clk(clock_tbl[n].clk);
 
 	clkdev_add_table(clock_tbl, num_clocks);