From ad11a1286b9b5ac52cb83b7b4db8c7d27db9f182 Mon Sep 17 00:00:00 2001
From: dkl <dkl@rock-chips.com>
Date: Thu, 24 Apr 2014 09:30:12 +0800
Subject: [PATCH] clk: rockchip: add CLK_SET_RATE_PARENT_IN_ORDER

If the flag CLK_SET_RATE_PARENT_IN_ORDER is set, consider the
order of .set_parent and .set_rate, to prevent a too large
temporary rate on rate change. This will fix the bug of clk_gpu
in rk3288.
---
 arch/arm/boot/dts/rk3288-clocks.dtsi |  1 +
 drivers/clk/clk.c                    | 13 +++++++++++--
 include/dt-bindings/clock/rockchip.h |  4 ++++
 include/linux/clk-provider.h         |  3 +++
 4 files changed, 19 insertions(+), 2 deletions(-)

diff --git a/arch/arm/boot/dts/rk3288-clocks.dtsi b/arch/arm/boot/dts/rk3288-clocks.dtsi
index f3e007fe34d3..1649d729f81f 100755
--- a/arch/arm/boot/dts/rk3288-clocks.dtsi
+++ b/arch/arm/boot/dts/rk3288-clocks.dtsi
@@ -1732,6 +1732,7 @@
 						#clock-cells = <0>;
 						rockchip,clkops-idx =
 							<CLKOPS_RATE_MUX_DIV>;
+						rockchip,flags = <CLK_SET_RATE_PARENT_IN_ORDER>;
 					};
 
 					/* reg[5]: reserved */
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 2cf2ea6b77a1..251c10b63c41 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -1374,14 +1374,23 @@ static struct clk *clk_propagate_rate_change(struct clk *clk, unsigned long even
 static void clk_change_rate(struct clk *clk)
 {
 	struct clk *child;
-	unsigned long old_rate;
+	unsigned long old_rate, tmp_rate;
 	unsigned long best_parent_rate = 0;
 
 	old_rate = clk->rate;
 
 	/* set parent */
-	if (clk->new_parent && clk->new_parent != clk->parent)
+	if (clk->new_parent && clk->new_parent != clk->parent) {
+		if (clk->flags & CLK_SET_RATE_PARENT_IN_ORDER) {
+			tmp_rate = clk->ops->recalc_rate(clk->hw, clk->new_parent->rate);
+			if ((tmp_rate > clk->rate) && (tmp_rate > clk->new_rate)) {
+				if (clk->ops->set_rate)
+					clk->ops->set_rate(clk->hw, clk->new_rate, clk->new_parent->rate);
+			}
+		}
+
 		__clk_set_parent(clk, clk->new_parent, clk->new_parent_index);
+	}
 
 	if (clk->parent)
 		best_parent_rate = clk->parent->rate;
diff --git a/include/dt-bindings/clock/rockchip.h b/include/dt-bindings/clock/rockchip.h
index c216a0e9c109..ffd38921c4b0 100644
--- a/include/dt-bindings/clock/rockchip.h
+++ b/include/dt-bindings/clock/rockchip.h
@@ -28,6 +28,10 @@
 #define CLK_IS_BASIC		BIT(5) /* Basic clk, can't do a to_clk_foo() */
 #define CLK_GET_RATE_NOCACHE	BIT(6) /* do not use the cached clk rate */
 #define CLK_SET_RATE_NO_REPARENT BIT(7) /* don't re-parent on rate change */
+#define CLK_SET_RATE_PARENT_IN_ORDER BIT(8) /* consider the order of re-parent
+						and set_div on rate change */
+
+
 
 /* Rockchip pll flags */
 #define CLK_PLL_3188		BIT(0)
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index dce14cd2a410..6ee9fbf109ef 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -28,6 +28,9 @@
 #define CLK_IS_BASIC		BIT(5) /* Basic clk, can't do a to_clk_foo() */
 #define CLK_GET_RATE_NOCACHE	BIT(6) /* do not use the cached clk rate */
 #define CLK_SET_RATE_NO_REPARENT BIT(7) /* don't re-parent on rate change */
+#define CLK_SET_RATE_PARENT_IN_ORDER BIT(8) /* consider the order of re-parent
+						and set_div on rate change */
+
 
 struct clk_hw;
 
-- 
2.34.1