rk3228: initialize platform data
[firefly-linux-kernel-4.4.55.git] / drivers / clk / rockchip / clk-pll.c
old mode 100755 (executable)
new mode 100644 (file)
index c092f83..5943ca1
@@ -31,8 +31,10 @@ static const struct pll_clk_set rk3188plus_pll_com_table[] = {
        _RK3188PLUS_PLL_SET_CLKS(891000,        8,      594,    2),
        _RK3188PLUS_PLL_SET_CLKS(768000,        1,      64,     2),
        _RK3188PLUS_PLL_SET_CLKS(594000,        2,      198,    4),
+       _RK3188PLUS_PLL_SET_CLKS(576000,        1,      48,     2),
        _RK3188PLUS_PLL_SET_CLKS(500000,        3,      250,    4),
        _RK3188PLUS_PLL_SET_CLKS(408000,        1,      68,     4),
+       _RK3188PLUS_PLL_SET_CLKS(400000,        3,      200,    4),
        _RK3188PLUS_PLL_SET_CLKS(396000,        1,      66,     4),
        _RK3188PLUS_PLL_SET_CLKS(384000,        2,      128,    4),
        _RK3188PLUS_PLL_SET_CLKS(360000,        1,      60,     4),
@@ -178,47 +180,47 @@ static const struct apll_clk_set rk3288_apll_table[] = {
 };
 
 static const struct apll_clk_set rk3036_apll_table[] = {
-       _RK3036_APLL_SET_CLKS(1608, 1, 67, 1, 1, 1, 0, 81, 41, 41, 21, 21),
-       _RK3036_APLL_SET_CLKS(1584, 1, 66, 1, 1, 1, 0, 81, 41, 41, 21, 21),
-       _RK3036_APLL_SET_CLKS(1560, 1, 65, 1, 1, 1, 0, 81, 41, 41, 21, 21),
-       _RK3036_APLL_SET_CLKS(1536, 1, 64, 1, 1, 1, 0, 81, 41, 41, 21, 21),
-       _RK3036_APLL_SET_CLKS(1512, 1, 63, 1, 1, 1, 0, 81, 41, 41, 21, 21),
-       _RK3036_APLL_SET_CLKS(1488, 1, 62, 1, 1, 1, 0, 81, 41, 41, 21, 21),
-       _RK3036_APLL_SET_CLKS(1464, 1, 61, 1, 1, 1, 0, 81, 41, 41, 21, 21),
-       _RK3036_APLL_SET_CLKS(1440, 1, 60, 1, 1, 1, 0, 81, 41, 41, 21, 21),
-       _RK3036_APLL_SET_CLKS(1416, 1, 59, 1, 1, 1, 0, 81, 41, 41, 21, 21),
-       _RK3036_APLL_SET_CLKS(1392, 1, 58, 1, 1, 1, 0, 81, 41, 41, 21, 21),
-       _RK3036_APLL_SET_CLKS(1368, 1, 57, 1, 1, 1, 0, 81, 41, 41, 21, 21),
-       _RK3036_APLL_SET_CLKS(1344, 1, 56, 1, 1, 1, 0, 81, 41, 41, 21, 21),
-       _RK3036_APLL_SET_CLKS(1320, 1, 55, 1, 1, 1, 0, 81, 41, 41, 21, 21),
-       _RK3036_APLL_SET_CLKS(1296, 1, 54, 1, 1, 1, 0, 81, 81, 41, 21, 21),
-       _RK3036_APLL_SET_CLKS(1272, 1, 53, 1, 1, 1, 0, 81, 41, 41, 21, 21),
-       _RK3036_APLL_SET_CLKS(1248, 1, 52, 1, 1, 1, 0, 81, 41, 41, 21, 21),
-       _RK3036_APLL_SET_CLKS(1200, 1, 50, 1, 1, 1, 0, 81, 41, 41, 21, 21),
-       _RK3036_APLL_SET_CLKS(1104, 1, 46, 1, 1, 1, 0, 81, 41, 41, 21, 21),
-       _RK3036_APLL_SET_CLKS(1100, 12, 550, 1, 1, 1, 0, 81, 41, 41, 21, 21),
-       _RK3036_APLL_SET_CLKS(1008, 1, 84, 2, 1, 1, 0, 81, 41, 41, 21, 21),
-       _RK3036_APLL_SET_CLKS(1000, 6, 500, 2, 1, 1, 0, 81, 41, 41, 21, 21),
-       _RK3036_APLL_SET_CLKS(984, 1, 82, 2, 1, 1, 0, 81, 41, 41, 21, 21),
-       _RK3036_APLL_SET_CLKS(960, 1, 80, 2, 1, 1, 0, 81, 41, 41, 21, 21),
-       _RK3036_APLL_SET_CLKS(936, 1, 78, 2, 1, 1, 0, 81, 41, 41, 21, 21),
-       _RK3036_APLL_SET_CLKS(912, 1, 76, 2, 1, 1, 0, 41, 41, 41, 21, 21),
-       _RK3036_APLL_SET_CLKS(900, 4, 300, 2, 1, 1, 0, 41, 41, 41, 21, 21),
-       _RK3036_APLL_SET_CLKS(888, 1, 74, 2, 1, 1, 0, 41, 41, 41, 21, 21),
-       _RK3036_APLL_SET_CLKS(864, 1, 72, 2, 1, 1, 0, 41, 41, 41, 21, 21),
-       _RK3036_APLL_SET_CLKS(840, 1, 70, 2, 1, 1, 0, 41, 41, 41, 21, 21),
-       _RK3036_APLL_SET_CLKS(816, 1, 68, 2, 1, 1, 0, 41, 41, 41, 21, 21),
-       _RK3036_APLL_SET_CLKS(800, 6, 400, 2, 1, 1, 0, 41, 41, 41, 21, 21),
-       _RK3036_APLL_SET_CLKS(700, 6, 350, 2, 1, 1, 0, 41, 41, 41, 21, 21),
-       _RK3036_APLL_SET_CLKS(696, 1, 58, 2, 1, 1, 0, 41, 41, 41, 21, 21),
-       _RK3036_APLL_SET_CLKS(600, 1, 75, 3, 1, 1, 0, 41, 21, 41, 21, 21),
-       _RK3036_APLL_SET_CLKS(504, 1, 63, 3, 1, 1, 0, 41, 21, 41, 21, 21),
-       _RK3036_APLL_SET_CLKS(500, 6, 250, 2, 1, 1, 0, 41, 21, 41, 21, 21),
-       _RK3036_APLL_SET_CLKS(408, 1, 68, 2, 2, 1, 0, 41, 21, 41, 21, 21),
-       _RK3036_APLL_SET_CLKS(312, 1, 52, 2, 2, 1, 0, 41, 21, 41, 21, 21),
-       _RK3036_APLL_SET_CLKS(216, 1, 72, 4, 2, 1, 0, 41, 21, 41, 21, 21),
-       _RK3036_APLL_SET_CLKS(96, 1, 64, 4, 4, 1, 0, 21, 21, 41, 21, 21),
-       _RK3036_APLL_SET_CLKS(0, 1, 0, 1, 1, 1, 0, 21, 21, 41, 21, 21),
+       _RK3036_APLL_SET_CLKS(1608, 1, 67, 1, 1, 1, 0, 81),
+       _RK3036_APLL_SET_CLKS(1584, 1, 66, 1, 1, 1, 0, 81),
+       _RK3036_APLL_SET_CLKS(1560, 1, 65, 1, 1, 1, 0, 81),
+       _RK3036_APLL_SET_CLKS(1536, 1, 64, 1, 1, 1, 0, 81),
+       _RK3036_APLL_SET_CLKS(1512, 1, 63, 1, 1, 1, 0, 81),
+       _RK3036_APLL_SET_CLKS(1488, 1, 62, 1, 1, 1, 0, 81),
+       _RK3036_APLL_SET_CLKS(1464, 1, 61, 1, 1, 1, 0, 81),
+       _RK3036_APLL_SET_CLKS(1440, 1, 60, 1, 1, 1, 0, 81),
+       _RK3036_APLL_SET_CLKS(1416, 1, 59, 1, 1, 1, 0, 81),
+       _RK3036_APLL_SET_CLKS(1392, 1, 58, 1, 1, 1, 0, 81),
+       _RK3036_APLL_SET_CLKS(1368, 1, 57, 1, 1, 1, 0, 81),
+       _RK3036_APLL_SET_CLKS(1344, 1, 56, 1, 1, 1, 0, 81),
+       _RK3036_APLL_SET_CLKS(1320, 1, 55, 1, 1, 1, 0, 81),
+       _RK3036_APLL_SET_CLKS(1296, 1, 54, 1, 1, 1, 0, 81),
+       _RK3036_APLL_SET_CLKS(1272, 1, 53, 1, 1, 1, 0, 81),
+       _RK3036_APLL_SET_CLKS(1248, 1, 52, 1, 1, 1, 0, 81),
+       _RK3036_APLL_SET_CLKS(1200, 1, 50, 1, 1, 1, 0, 81),
+       _RK3036_APLL_SET_CLKS(1104, 1, 46, 1, 1, 1, 0, 81),
+       _RK3036_APLL_SET_CLKS(1100, 12, 550, 1, 1, 1, 0, 81),
+       _RK3036_APLL_SET_CLKS(1008, 1, 84, 2, 1, 1, 0, 81),
+       _RK3036_APLL_SET_CLKS(1000, 6, 500, 2, 1, 1, 0, 81),
+       _RK3036_APLL_SET_CLKS(984, 1, 82, 2, 1, 1, 0, 81),
+       _RK3036_APLL_SET_CLKS(960, 1, 80, 2, 1, 1, 0, 81),
+       _RK3036_APLL_SET_CLKS(936, 1, 78, 2, 1, 1, 0, 81),
+       _RK3036_APLL_SET_CLKS(912, 1, 76, 2, 1, 1, 0, 41),
+       _RK3036_APLL_SET_CLKS(900, 4, 300, 2, 1, 1, 0, 41),
+       _RK3036_APLL_SET_CLKS(888, 1, 74, 2, 1, 1, 0, 41),
+       _RK3036_APLL_SET_CLKS(864, 1, 72, 2, 1, 1, 0, 41),
+       _RK3036_APLL_SET_CLKS(840, 1, 70, 2, 1, 1, 0, 41),
+       _RK3036_APLL_SET_CLKS(816, 1, 68, 2, 1, 1, 0, 41),
+       _RK3036_APLL_SET_CLKS(800, 6, 400, 2, 1, 1, 0, 41),
+       _RK3036_APLL_SET_CLKS(700, 6, 350, 2, 1, 1, 0, 41),
+       _RK3036_APLL_SET_CLKS(696, 1, 58, 2, 1, 1, 0, 41),
+       _RK3036_APLL_SET_CLKS(600, 1, 75, 3, 1, 1, 0, 41),
+       _RK3036_APLL_SET_CLKS(504, 1, 63, 3, 1, 1, 0, 41),
+       _RK3036_APLL_SET_CLKS(500, 6, 250, 2, 1, 1, 0, 41),
+       _RK3036_APLL_SET_CLKS(408, 1, 68, 2, 2, 1, 0, 41),
+       _RK3036_APLL_SET_CLKS(312, 1, 52, 2, 2, 1, 0, 41),
+       _RK3036_APLL_SET_CLKS(216, 1, 72, 4, 2, 1, 0, 41),
+       _RK3036_APLL_SET_CLKS(96, 1, 64, 4, 4, 1, 0, 21),
+       _RK3036_APLL_SET_CLKS(0, 1, 0, 1, 1, 1, 0, 21),
 };
 
 static const struct pll_clk_set rk3036plus_pll_com_table[] = {
@@ -230,9 +232,109 @@ static const struct pll_clk_set rk3036plus_pll_com_table[] = {
 static const struct pll_clk_set rk312xplus_pll_com_table[] = {
        /*_RK3036_PLL_SET_CLKS(1064000, 3, 133, 1, 1, 1, 0),*/
        /*_RK3036_PLL_SET_CLKS(798000, 2, 133, 2, 1, 1, 0),*/
+       _RK3036_PLL_SET_CLKS(1000000, 3, 125, 1,  1, 1, 0),
        _RK3036_PLL_SET_CLKS(594000, 2, 99, 2, 1, 1, 0),
-       _RK3036_PLL_SET_CLKS(500000, 6, 250, 2, 1, 1, 0),
-       _RK3036_PLL_SET_CLKS(400000, 6, 400, 2, 2, 1, 0),
+       _RK3036_PLL_SET_CLKS(500000, 3, 125, 2, 1, 1, 0),
+       _RK3036_PLL_SET_CLKS(400000, 3, 200, 2, 2, 1, 0),
+};
+
+static const struct apll_clk_set rk3368_apllb_table[] = {
+                       /*(_mhz,        nr,     nf,     no,     aclkm,  atclk,  pclk_dbg)*/
+       _RK3368_APLL_SET_CLKS(1608,     1,      67,     1,      2,      6,      6),
+       _RK3368_APLL_SET_CLKS(1560,     1,      65,     1,      2,      6,      6),
+       _RK3368_APLL_SET_CLKS(1512,     1,      63,     1,      2,      6,      6),
+       _RK3368_APLL_SET_CLKS(1488,     1,      62,     1,      2,      5,      5),
+       _RK3368_APLL_SET_CLKS(1464,     1,      61,     1,      2,      5,      5),
+       _RK3368_APLL_SET_CLKS(1440,     1,      60,     1,      2,      5,      5),
+       _RK3368_APLL_SET_CLKS(1416,     1,      59,     1,      2,      5,      5),
+       _RK3368_APLL_SET_CLKS(1392,     1,      58,     1,      2,      5,      5),
+       _RK3368_APLL_SET_CLKS(1368,     1,      57,     1,      2,      5,      5),
+       _RK3368_APLL_SET_CLKS(1344,     1,      56,     1,      2,      5,      5),
+       _RK3368_APLL_SET_CLKS(1320,     1,      55,     1,      2,      5,      5),
+       _RK3368_APLL_SET_CLKS(1296,     1,      54,     1,      2,      5,      5),
+       _RK3368_APLL_SET_CLKS(1272,     1,      53,     1,      2,      5,      5),
+       _RK3368_APLL_SET_CLKS(1248,     1,      52,     1,      2,      5,      5),
+       _RK3368_APLL_SET_CLKS(1224,     1,      51,     1,      2,      5,      5),
+       _RK3368_APLL_SET_CLKS(1200,     1,      50,     1,      2,      4,      4),
+       _RK3368_APLL_SET_CLKS(1176,     1,      49,     1,      2,      4,      4),
+       _RK3368_APLL_SET_CLKS(1128,     1,      47,     1,      2,      4,      4),
+       _RK3368_APLL_SET_CLKS(1104,     1,      46,     1,      2,      4,      4),
+       _RK3368_APLL_SET_CLKS(1008,     1,      84,     2,      2,      4,      4),
+       _RK3368_APLL_SET_CLKS(912,      1,      76,     2,      2,      4,      4),
+       _RK3368_APLL_SET_CLKS(888,      1,      74,     2,      2,      3,      3),
+       _RK3368_APLL_SET_CLKS(816,      1,      68,     2,      2,      3,      3),
+       _RK3368_APLL_SET_CLKS(792,      1,      66,     2,      2,      3,      3),
+       _RK3368_APLL_SET_CLKS(696,      1,      58,     2,      2,      3,      3),
+       _RK3368_APLL_SET_CLKS(672,      1,      56,     2,      2,      3,      3),
+       _RK3368_APLL_SET_CLKS(648,      1,      54,     2,      2,      3,      3),
+       _RK3368_APLL_SET_CLKS(624,      1,      52,     2,      2,      3,      3),
+       _RK3368_APLL_SET_CLKS(600,      1,      50,     2,      2,      2,      2),
+       _RK3368_APLL_SET_CLKS(576,      1,      48,     2,      2,      2,      2),
+       _RK3368_APLL_SET_CLKS(552,      1,      92,     4,      2,      2,      2),
+       _RK3368_APLL_SET_CLKS(528,      1,      88,     4,      2,      2,      2),
+       _RK3368_APLL_SET_CLKS(504,      1,      84,     4,      2,      2,      2),
+       _RK3368_APLL_SET_CLKS(480,      1,      80,     4,      2,      2,      2),
+       _RK3368_APLL_SET_CLKS(456,      1,      76,     4,      2,      2,      2),
+       _RK3368_APLL_SET_CLKS(408,      1,      68,     4,      2,      2,      2),
+       _RK3368_APLL_SET_CLKS(312,      1,      52,     4,      2,      2,      2),
+       _RK3368_APLL_SET_CLKS(252,      1,      84,     8,      2,      1,      1),
+       _RK3368_APLL_SET_CLKS(216,      1,      72,     8,      2,      1,      1),
+       _RK3368_APLL_SET_CLKS(126,      2,      84,     8,      2,      1,      1),
+       _RK3368_APLL_SET_CLKS(48,       2,      32,     8,      2,      1,      1),
+       _RK3368_APLL_SET_CLKS(0,        1,      32,     16,     2,      1,      1),
+};
+
+static const struct apll_clk_set rk3368_aplll_table[] = {
+                       /*(_mhz,        nr,     nf,     no,     aclkm,  atclk,  pclk_dbg)*/
+       _RK3368_APLL_SET_CLKS(1608,     1,      67,     1,      2,      7,      7),
+       _RK3368_APLL_SET_CLKS(1560,     1,      65,     1,      2,      7,      7),
+       _RK3368_APLL_SET_CLKS(1512,     1,      63,     1,      2,      7,      7),
+       _RK3368_APLL_SET_CLKS(1488,     1,      62,     1,      2,      6,      6),
+       _RK3368_APLL_SET_CLKS(1464,     1,      61,     1,      2,      6,      6),
+       _RK3368_APLL_SET_CLKS(1440,     1,      60,     1,      2,      6,      6),
+       _RK3368_APLL_SET_CLKS(1416,     1,      59,     1,      2,      6,      6),
+       _RK3368_APLL_SET_CLKS(1392,     1,      58,     1,      2,      6,      6),
+       _RK3368_APLL_SET_CLKS(1368,     1,      57,     1,      2,      6,      6),
+       _RK3368_APLL_SET_CLKS(1344,     1,      56,     1,      2,      6,      6),
+       _RK3368_APLL_SET_CLKS(1320,     1,      55,     1,      2,      6,      6),
+       _RK3368_APLL_SET_CLKS(1296,     1,      54,     1,      2,      6,      6),
+       _RK3368_APLL_SET_CLKS(1272,     1,      53,     1,      2,      6,      6),
+       _RK3368_APLL_SET_CLKS(1248,     1,      52,     1,      2,      5,      5),
+       _RK3368_APLL_SET_CLKS(1224,     1,      51,     1,      2,      5,      5),
+       _RK3368_APLL_SET_CLKS(1200,     1,      50,     1,      2,      5,      5),
+       _RK3368_APLL_SET_CLKS(1176,     1,      49,     1,      2,      5,      5),
+       _RK3368_APLL_SET_CLKS(1128,     1,      47,     1,      2,      5,      5),
+       _RK3368_APLL_SET_CLKS(1104,     1,      46,     1,      2,      5,      5),
+       _RK3368_APLL_SET_CLKS(1008,     1,      84,     2,      2,      5,      5),
+       _RK3368_APLL_SET_CLKS(912,      1,      76,     2,      2,      4,      4),
+       _RK3368_APLL_SET_CLKS(888,      1,      74,     2,      2,      4,      4),
+       _RK3368_APLL_SET_CLKS(816,      1,      68,     2,      2,      4,      4),
+       _RK3368_APLL_SET_CLKS(792,      1,      66,     2,      2,      4,      4),
+       _RK3368_APLL_SET_CLKS(696,      1,      58,     2,      2,      3,      3),
+       _RK3368_APLL_SET_CLKS(672,      1,      56,     2,      2,      3,      3),
+       _RK3368_APLL_SET_CLKS(648,      1,      54,     2,      2,      3,      3),
+       _RK3368_APLL_SET_CLKS(624,      1,      52,     2,      2,      3,      3),
+       _RK3368_APLL_SET_CLKS(600,      1,      50,     2,      2,      3,      3),
+       _RK3368_APLL_SET_CLKS(576,      1,      48,     2,      2,      3,      3),
+       _RK3368_APLL_SET_CLKS(552,      1,      92,     4,      2,      3,      3),
+       _RK3368_APLL_SET_CLKS(528,      1,      88,     4,      2,      3,      3),
+       _RK3368_APLL_SET_CLKS(504,      1,      84,     4,      2,      3,      3),
+       _RK3368_APLL_SET_CLKS(480,      1,      80,     4,      2,      2,      2),
+       _RK3368_APLL_SET_CLKS(456,      1,      76,     4,      2,      2,      2),
+       _RK3368_APLL_SET_CLKS(408,      1,      68,     4,      2,      2,      2),
+       _RK3368_APLL_SET_CLKS(312,      1,      52,     4,      2,      2,      2),
+       _RK3368_APLL_SET_CLKS(252,      1,      84,     8,      2,      2,      2),
+       _RK3368_APLL_SET_CLKS(216,      1,      72,     8,      2,      1,      1),
+       _RK3368_APLL_SET_CLKS(126,      2,      84,     8,      2,      1,      1),
+       _RK3368_APLL_SET_CLKS(48,       2,      32,     8,      2,      1,      1),
+       _RK3368_APLL_SET_CLKS(0,        1,      32,     16,     2,      1,      1),
+};
+
+static const struct pll_clk_set rk3368_pll_table_low_jitter[] = {
+       /*                             _khz, nr,  nf, no, nb */
+       _RK3188PLUS_PLL_SET_CLKS_NB(1188000,  1,  99,  2,  1),
+       _RK3188PLUS_PLL_SET_CLKS_NB(400000,  1,  100,  6,  1),
+       _RK3188PLUS_PLL_SET_CLKS(         0,  0,   0,  0),
 };
 
 static void pll_wait_lock(struct clk_hw *hw)
@@ -240,7 +342,6 @@ static void pll_wait_lock(struct clk_hw *hw)
        struct clk_pll *pll = to_clk_pll(hw);
        int delay = 24000000;
 
-
        while (delay > 0) {
                if (grf_readl(pll->status_offset) & (1 << pll->status_shift))
                        break;
@@ -394,12 +495,12 @@ static int _pll_clk_set_rate_3188(struct pll_clk_set *clk_set,
        cru_writel(_RK3188_PLL_MODE_SLOW_SET(pll->mode_shift), pll->mode_offset);
        //pll power down
        cru_writel((0x1 << (16+1)) | (0x1<<1), pll->reg + RK3188_PLL_CON(3));
-       dsb();
-       dsb();
-       dsb();
-       dsb();
-       dsb();
-       dsb();
+       dsb(sy);
+       dsb(sy);
+       dsb(sy);
+       dsb(sy);
+       dsb(sy);
+       dsb(sy);
        cru_writel(clk_set->pllcon0, pll->reg + RK3188_PLL_CON(0));
        cru_writel(clk_set->pllcon1, pll->reg + RK3188_PLL_CON(1));
 
@@ -604,12 +705,12 @@ CHANGE_APLL:
 
        /* PLL power down */
        cru_writel((0x1 << (16+1)) | (0x1<<1), pll->reg + RK3188_PLL_CON(3));
-       dsb();
-       dsb();
-       dsb();
-       dsb();
-       dsb();
-       dsb();
+       dsb(sy);
+       dsb(sy);
+       dsb(sy);
+       dsb(sy);
+       dsb(sy);
+       dsb(sy);
        cru_writel(ps->pllcon0, pll->reg + RK3188_PLL_CON(0));
        cru_writel(ps->pllcon1, pll->reg + RK3188_PLL_CON(1));
 
@@ -798,7 +899,7 @@ static int clk_pll_set_rate_3188plus(struct clk_hw *hw, unsigned long rate,
                clk_set++;
        }
 
-       if (cpu_is_rk3288() && (rate == 297*MHZ)) {
+       if (cpu_is_rk3288() && ((rate == 297*MHZ) || (rate == 594*MHZ))) {
                if((strncmp(__clk_get_name(hw->clk), "clk_gpll",
                        strlen("clk_gpll")) == 0)) {
 
@@ -820,10 +921,113 @@ static int clk_pll_set_rate_3188plus(struct clk_hw *hw, unsigned long rate,
        return ret;
 }
 
+static int clk_pll_is_enabled_3188plus(struct clk_hw *hw)
+{
+       unsigned long flags;
+       struct clk_pll *pll = to_clk_pll(hw);
+       int ret;
+
+       if(pll->lock)
+               spin_lock_irqsave(pll->lock, flags);
+
+       if (_RK3188_PLL_MODE_IS_NORM(pll->mode_offset, pll->mode_shift))
+               ret = 1;
+       else
+               ret = 0;
+
+       if (pll->lock)
+               spin_unlock_irqrestore(pll->lock, flags);
+
+       return ret;
+}
+
+static int clk_pll_enable_3188plus(struct clk_hw *hw)
+{
+       struct clk_pll *pll = to_clk_pll(hw);
+       unsigned long flags;
+       unsigned long rst_dly;
+       u32 nr;
+
+       clk_debug("%s enter\n", __func__);
+
+       if (clk_pll_is_enabled_3188plus(hw)) {
+               clk_debug("pll has been enabled\n");
+               return 0;
+       }
+
+       if(pll->lock)
+               spin_lock_irqsave(pll->lock, flags);
+
+       //enter slowmode
+       cru_writel(_RK3188_PLL_MODE_SLOW_SET(pll->mode_shift), pll->mode_offset);
+
+       //power up
+       cru_writel(_RK3188PLUS_PLL_POWERDOWN_SET(0), pll->reg + RK3188_PLL_CON(3));
+
+       //enter reset
+       cru_writel(_RK3188PLUS_PLL_RESET_SET(1), pll->reg + RK3188_PLL_CON(3));
+
+       //cru_writel(clk_set->pllcon0, pll->reg + RK3188_PLL_CON(0));
+       //cru_writel(clk_set->pllcon1, pll->reg + RK3188_PLL_CON(1));
+       //cru_writel(clk_set->pllcon2, pll->reg + RK3188_PLL_CON(2));
+
+       udelay(5);
+
+       //return from reset
+       cru_writel(_RK3188PLUS_PLL_RESET_SET(0), pll->reg + RK3188_PLL_CON(3));
+
+       //wating lock state
+       nr = RK3188PLUS_PLL_NR(cru_readl(pll->reg + RK3188_PLL_CON(0)));
+       rst_dly = ((nr*500)/24+1);
+       udelay(rst_dly);
+
+       pll_wait_lock(hw);
+
+       //return from slow
+       cru_writel(_RK3188_PLL_MODE_NORM_SET(pll->mode_shift), pll->mode_offset);
+
+       if (pll->lock)
+               spin_unlock_irqrestore(pll->lock, flags);
+
+       clk_debug("pll %s dump reg:\n con0=0x%08x,\n con1=0x%08x,\n con2=0x%08x,\n"
+                       "con3=0x%08x,\n mode=0x%08x\n",
+                       __clk_get_name(hw->clk),
+                       cru_readl(pll->reg + RK3188_PLL_CON(0)),
+                       cru_readl(pll->reg + RK3188_PLL_CON(1)),
+                       cru_readl(pll->reg + RK3188_PLL_CON(2)),
+                       cru_readl(pll->reg + RK3188_PLL_CON(3)),
+                       cru_readl(pll->mode_offset));
+
+       return 0;
+}
+
+static void clk_pll_disable_3188plus(struct clk_hw *hw)
+{
+       struct clk_pll *pll = to_clk_pll(hw);
+       unsigned long flags;
+
+       clk_debug("%s enter\n", __func__);
+
+       if(pll->lock)
+               spin_lock_irqsave(pll->lock, flags);
+
+       //enter slowmode
+       cru_writel(_RK3188_PLL_MODE_SLOW_SET(pll->mode_shift), pll->mode_offset);
+
+       //power down
+       cru_writel(_RK3188PLUS_PLL_POWERDOWN_SET(1), pll->reg + RK3188_PLL_CON(3));
+
+       if (pll->lock)
+               spin_unlock_irqrestore(pll->lock, flags);
+}
+
 static const struct clk_ops clk_pll_ops_3188plus = {
        .recalc_rate = clk_pll_recalc_rate_3188plus,
        .round_rate = clk_pll_round_rate_3188plus,
        .set_rate = clk_pll_set_rate_3188plus,
+       .enable = clk_pll_enable_3188plus,
+       .disable = clk_pll_disable_3188plus,
+       .is_enabled = clk_pll_is_enabled_3188plus,
 };
 
 /* CLK_PLL_3188PLUS_AUTO type ops */
@@ -842,100 +1046,89 @@ static const struct clk_ops clk_pll_ops_3188plus = {
 
 static u32 clk_gcd(u32 numerator, u32 denominator)
 {
-        u32 a, b;
-
-        if (!numerator || !denominator)
-                return 0;
-        if (numerator > denominator) {
-                a = numerator;
-                b = denominator;
-        } else {
-                a = denominator;
-                b = numerator;
-        }
-        while (b != 0) {
-                int r = b;
-                b = a % b;
-                a = r;
-        }
-
-        return a;
+       u32 a, b;
+
+       if (!numerator || !denominator)
+               return 0;
+       if (numerator > denominator) {
+               a = numerator;
+               b = denominator;
+       } else {
+               a = denominator;
+               b = numerator;
+       }
+       while (b != 0) {
+               int r = b;
+
+               b = a % b;
+               a = r;
+       }
+
+       return a;
 }
 
-/* FIXME: calc using u64 */
 static int pll_clk_get_best_set(unsigned long fin_hz, unsigned long fout_hz,
                                u32 *best_nr, u32 *best_nf, u32 *best_no)
 {
-        u32 nr, nf, no, nonr;
-        u32 nr_out, nf_out, no_out;
-        u32 n;
-        u32 YFfenzi;
-        u32 YFfenmu;
-        u64 fref, fvco, fout;
-        u32 gcd_val = 0;
-
-
-        nr_out = PLL_NR_MAX + 1;
-        no_out = 0;
-
-//     printk("pll_clk_get_set fin=%lu,fout=%lu\n", fin_hz, fout_hz);
-        if(!fin_hz || !fout_hz || fout_hz == fin_hz)
-                return -EINVAL;
-        gcd_val = clk_gcd(fin_hz, fout_hz);
-
-//      printk("gcd_val = %d\n",gcd_val);
-
-        YFfenzi = fout_hz / gcd_val;
-        YFfenmu = fin_hz / gcd_val;
-
-//      printk("YFfenzi = %d, YFfenmu = %d\n",YFfenzi,YFfenmu);
-
-       for(n = 1;; n++) {
-              nf = YFfenzi * n;
-              nonr = YFfenmu * n;
-              if(nf > PLL_NF_MAX || nonr > (PLL_NO_MAX * PLL_NR_MAX))
-                      break;
-              for(no = 1; no <= PLL_NO_MAX; no++) {
-                      if(!(no == 1 || !(no % 2)))
-                              continue;
-
-                      if(nonr % no)
-                              continue;
-                      nr = nonr / no;
-
-                      if(nr > PLL_NR_MAX) //PLL_NR_MAX
-                              continue;
-
-                      fref = fin_hz / nr;
-                      if(fref < PLL_FREF_MIN || fref > PLL_FREF_MAX)
-                              continue;
-
-                      fvco = fref * nf;
-                      if(fvco < PLL_FVCO_MIN || fvco > PLL_FVCO_MAX)
-                              continue;
-                      fout = fvco / no;
-                      if(fout < PLL_FOUT_MIN || fout > PLL_FOUT_MAX)
-                              continue;
-
-                      /* output all available PLL settings */
-                      //printk("nr=%d,\tnf=%d,\tno=%d\n",nr,nf,no);
-                      //printk("_PLL_SET_CLKS(%lu,\t%d,\t%d,\t%d),\n",fout_hz/KHZ,nr,nf,no);
-
-                      /* select the best from all available PLL settings */
-                      if((nr < nr_out) || ((nr == nr_out)&&(no > no_out)))
-                      {
-                              nr_out = nr;
-                              nf_out = nf;
-                              no_out = no;
-                      }
-              }
-
-       }
-
-        /* output the best PLL setting */
-        if((nr_out <= PLL_NR_MAX) && (no_out > 0)){
-                //printk("_PLL_SET_CLKS(%lu,\t%d,\t%d,\t%d),\n",fout_hz/KHZ,nr_out,nf_out,no_out);
-               if(best_nr && best_nf && best_no){
+       u32 nr, nf, no, nonr;
+       u32 nr_out, nf_out, no_out;
+       u32 n;
+       u32 YFfenzi;
+       u32 YFfenmu;
+       u64 fref, fvco, fout;
+       u32 gcd_val = 0;
+
+       nr_out = PLL_NR_MAX + 1;
+       no_out = 0;
+
+       if (!fin_hz || !fout_hz || fout_hz == fin_hz)
+               return -EINVAL;
+       gcd_val = clk_gcd(fin_hz, fout_hz);
+
+       YFfenzi = fout_hz / gcd_val;
+       YFfenmu = fin_hz / gcd_val;
+
+       for (n = 1;; n++) {
+               nf = YFfenzi * n;
+               nonr = YFfenmu * n;
+               if (nf > PLL_NF_MAX || nonr > (PLL_NO_MAX * PLL_NR_MAX))
+                       break;
+
+               for (no = 1; no <= PLL_NO_MAX; no++) {
+                       if (!(no == 1 || !(no % 2)))
+                               continue;
+
+                       if (nonr % no)
+                               continue;
+                       nr = nonr / no;
+
+                       if (nr > PLL_NR_MAX)
+                               continue;
+
+                       fref = fin_hz / nr;
+                       if (fref < PLL_FREF_MIN || fref > PLL_FREF_MAX)
+                               continue;
+
+                       fvco = fref * nf;
+                       if (fvco < PLL_FVCO_MIN || fvco > PLL_FVCO_MAX)
+                               continue;
+
+                       fout = fvco / no;
+                       if (fout < PLL_FOUT_MIN || fout > PLL_FOUT_MAX)
+                               continue;
+
+                       /* select the best from all available PLL settings */
+                       if ((no > no_out) || ((no == no_out) && (nr < nr_out))) {
+                               nr_out = nr;
+                               nf_out = nf;
+                               no_out = no;
+                       }
+               }
+       }
+
+       /* output the best PLL setting */
+       if ((nr_out <= PLL_NR_MAX) && (no_out > 0)) {
+               if (best_nr && best_nf && best_no) {
                        *best_nr = nr_out;
                        *best_nf = nf_out;
                        *best_no = no_out;
@@ -999,8 +1192,95 @@ static const struct clk_ops clk_pll_ops_3188plus_auto = {
        .recalc_rate = clk_pll_recalc_rate_3188plus_auto,
        .round_rate = clk_pll_round_rate_3188plus_auto,
        .set_rate = clk_pll_set_rate_3188plus_auto,
+       .enable = clk_pll_enable_3188plus,
+       .disable = clk_pll_disable_3188plus,
+       .is_enabled = clk_pll_is_enabled_3188plus,
 };
 
+static long clk_pll_round_rate_3368_low_jitter(struct clk_hw *hw,
+                                              unsigned long rate,
+                                              unsigned long *prate)
+{
+       unsigned long best;
+       struct pll_clk_set *p_clk_set;
+
+       p_clk_set = (struct pll_clk_set *)(rk3368_pll_table_low_jitter);
+
+       while (p_clk_set->rate) {
+               if (p_clk_set->rate == rate)
+                       break;
+               p_clk_set++;
+       }
+
+       if (p_clk_set->rate == rate) {
+               clk_debug("get rate from table\n");
+               return rate;
+       }
+
+       for (best = rate; best > 0; best--) {
+               if (!pll_clk_get_best_set(*prate, best, NULL, NULL, NULL))
+                       return best;
+       }
+
+       clk_err("%s: can't round rate %lu\n", __func__, rate);
+       return 0;
+}
+
+
+static int clk_pll_set_rate_3368_low_jitter(struct clk_hw *hw,
+                                           unsigned long rate,
+                                           unsigned long parent_rate)
+{
+       unsigned long best;
+       u32 nr, nf, no;
+       struct pll_clk_set clk_set, *p_clk_set;
+       int ret;
+
+       p_clk_set = (struct pll_clk_set *)(rk3368_pll_table_low_jitter);
+
+       while (p_clk_set->rate) {
+               if (p_clk_set->rate == rate)
+                       break;
+               p_clk_set++;
+       }
+
+       if (p_clk_set->rate == rate) {
+               clk_debug("get rate from table\n");
+               goto set_rate;
+       }
+
+       best = clk_pll_round_rate_3188plus_auto(hw, rate, &parent_rate);
+
+       if (!best)
+               return -EINVAL;
+
+       pll_clk_get_best_set(parent_rate, best, &nr, &nf, &no);
+
+       /* prepare clk_set */
+       clk_set.rate = best;
+       clk_set.pllcon0 = RK3188PLUS_PLL_CLKR_SET(nr)|RK3188PLUS_PLL_CLKOD_SET(no);
+       clk_set.pllcon1 = RK3188PLUS_PLL_CLKF_SET(nf);
+       clk_set.pllcon2 = RK3188PLUS_PLL_CLK_BWADJ_SET(nf >> 1);
+       clk_set.rst_dly = ((nr*500)/24+1);
+
+       p_clk_set = &clk_set;
+
+set_rate:
+       ret = _pll_clk_set_rate_3188plus(p_clk_set, hw);
+       clk_debug("pll %s set rate=%lu OK!\n", __clk_get_name(hw->clk),
+                 p_clk_set->rate);
+
+       return ret;
+}
+
+static const struct clk_ops clk_pll_ops_3368_low_jitter = {
+       .recalc_rate = clk_pll_recalc_rate_3188plus_auto,
+       .round_rate = clk_pll_round_rate_3368_low_jitter,
+       .set_rate = clk_pll_set_rate_3368_low_jitter,
+       .enable = clk_pll_enable_3188plus,
+       .disable = clk_pll_disable_3188plus,
+       .is_enabled = clk_pll_is_enabled_3188plus,
+};
 
 /* CLK_PLL_3188PLUS_APLL type ops */
 static unsigned long clk_pll_recalc_rate_3188plus_apll(struct clk_hw *hw,
@@ -1371,7 +1651,7 @@ CHANGE_APLL:
        /* reparent to apll, and set div to 1 */
        if (sel_gpll) {
                if (temp_div == 1) {
-                       /* when rate/2 < (old_rate-arm_gpll_rate),
+                       /* when rate/2 < (rate-arm_gpll_rate),
                           we can set div to make rate change more gently */
                        if (rate > (2*arm_gpll_rate)) {
                                cru_writel(RK3288_CORE_CLK_DIV(2), RK3288_CRU_CLKSELS_CON(0));
@@ -1612,9 +1892,11 @@ static int clk_pll_set_rate_3036_apll(struct clk_hw *hw, unsigned long rate,
                unsigned long parent_rate)
 {
        struct clk_pll *pll = to_clk_pll(hw);
-       unsigned long flags;
-       u32 refdiv, fbdiv, postdiv1, postdiv2, frac;
        struct apll_clk_set *ps = (struct apll_clk_set *)(rk3036_apll_table);
+       struct clk *arm_gpll = __clk_lookup("clk_gpll");
+       struct clk *clk = hw->clk;
+       unsigned long flags, arm_gpll_rate, old_rate, temp_rate;
+       u32 temp_div;
 
        while (ps->rate) {
                if (ps->rate == rate) {
@@ -1623,46 +1905,73 @@ static int clk_pll_set_rate_3036_apll(struct clk_hw *hw, unsigned long rate,
                ps++;
        }
 
-       clk_debug("%s %lu\n", __func__,  rate);
+       if (ps->rate != rate) {
+               clk_err("%s: unsupport arm rate %lu\n", __func__, rate);
+               return 0;
+       }
+
+       if (!arm_gpll) {
+               clk_err("clk arm_gpll is NULL!\n");
+               return 0;
+       }
+
+       old_rate = __clk_get_rate(clk);
+       arm_gpll_rate = __clk_get_rate(arm_gpll);
+       if (soc_is_rk3128() || soc_is_rk3126())
+               arm_gpll_rate /= 2;
+
+       temp_rate = (old_rate > rate) ? old_rate : rate;
+       temp_div = DIV_ROUND_UP(arm_gpll_rate, temp_rate);
+
+       local_irq_save(flags);
+
+       if (rate >= old_rate) {
+               cru_writel(ps->clksel0, RK3036_CRU_CLKSELS_CON(0));
+               cru_writel(ps->clksel1, RK3036_CRU_CLKSELS_CON(1));
+       }
+
+       /* set div first, then select gpll */
+       if (temp_div > 1)
+               cru_writel(RK3036_CLK_CORE_DIV(temp_div), RK3036_CRU_CLKSELS_CON(0));
+       cru_writel(RK3036_CORE_SEL_PLL(1), RK3036_CRU_CLKSELS_CON(0));
+
+       clk_debug("temp select arm_gpll path, get rate %lu\n",
+                 arm_gpll_rate/temp_div);
+       clk_debug("from arm_gpll rate %lu, temp_div %d\n", arm_gpll_rate,
+                 temp_div);
+
+       /**************enter slow mode 24M***********/
+       /*cru_writel(_RK3188_PLL_MODE_SLOW_SET(pll->mode_shift), pll->mode_offset);*/
+       loops_per_jiffy = LPJ_24M;
+
+       cru_writel(ps->pllcon0, pll->reg + RK3188_PLL_CON(0));
+       cru_writel(ps->pllcon1, pll->reg + RK3188_PLL_CON(1));
+       cru_writel(ps->pllcon2, pll->reg + RK3188_PLL_CON(2));
+
        clk_debug("pllcon0 %08x\n", cru_readl(pll->reg + RK3188_PLL_CON(0)));
        clk_debug("pllcon1 %08x\n", cru_readl(pll->reg + RK3188_PLL_CON(1)));
        clk_debug("pllcon2 %08x\n", cru_readl(pll->reg + RK3188_PLL_CON(2)));
        clk_debug("clksel0 %08x\n", cru_readl(RK3036_CRU_CLKSELS_CON(0)));
        clk_debug("clksel1 %08x\n", cru_readl(RK3036_CRU_CLKSELS_CON(1)));
-       if (ps->rate == rate) {
-               clk_debug("apll get a rate\n");
-
-               /*enter slowmode*/
-               local_irq_save(flags);
-               cru_writel(_RK3188_PLL_MODE_SLOW_SET(pll->mode_shift), pll->mode_offset);
-               loops_per_jiffy = LPJ_24M;
 
-               cru_writel(ps->pllcon0, pll->reg + RK3188_PLL_CON(0));
-               cru_writel(ps->pllcon1, pll->reg + RK3188_PLL_CON(1));
-               cru_writel(ps->pllcon2, pll->reg + RK3188_PLL_CON(2));
-               cru_writel(ps->clksel0, RK3036_CRU_CLKSELS_CON(0));
-               cru_writel(ps->clksel1, RK3036_CRU_CLKSELS_CON(1));
+       /*wating lock state*/
+       udelay(ps->rst_dly);
+       rk3036_pll_wait_lock(hw);
 
-               clk_debug("pllcon0 %08x\n", cru_readl(pll->reg + RK3188_PLL_CON(0)));
-               clk_debug("pllcon1 %08x\n", cru_readl(pll->reg + RK3188_PLL_CON(1)));
-               clk_debug("pllcon2 %08x\n", cru_readl(pll->reg + RK3188_PLL_CON(2)));
-               clk_debug("clksel0 %08x\n", cru_readl(RK3036_CRU_CLKSELS_CON(0)));
-               clk_debug("clksel1 %08x\n", cru_readl(RK3036_CRU_CLKSELS_CON(1)));
+       /************select apll******************/
+       cru_writel(RK3036_CORE_SEL_PLL(0), RK3036_CRU_CLKSELS_CON(0));
+       /**************return slow mode***********/
+       cru_writel(_RK3188_PLL_MODE_NORM_SET(pll->mode_shift), pll->mode_offset);
 
-               /*wating lock state*/
-               udelay(ps->rst_dly);
-               rk3036_pll_wait_lock(hw);
+       cru_writel(RK3036_CLK_CORE_DIV(1), RK3036_CRU_CLKSELS_CON(0));
 
-               /*return form slow*/
-               cru_writel(_RK3188_PLL_MODE_NORM_SET(pll->mode_shift), pll->mode_offset);
-               loops_per_jiffy = ps->lpj;
-               local_irq_restore(flags);
-       } else {
-               /*FIXME*/
-               rk3036_pll_clk_get_set(parent_rate, rate, &refdiv, &fbdiv, &postdiv1, &postdiv2, &frac);
-               rk3036_pll_set_con(hw, refdiv, fbdiv, postdiv1, postdiv2, frac);
+       if (rate < old_rate) {
+               cru_writel(ps->clksel0, RK3036_CRU_CLKSELS_CON(0));
+               cru_writel(ps->clksel1, RK3036_CRU_CLKSELS_CON(1));
        }
-       clk_debug("setting OK\n");
+
+       loops_per_jiffy = ps->lpj;
+       local_irq_restore(flags);
 
        return 0;       
 }
@@ -1746,7 +2055,7 @@ static int clk_cpll_set_rate_312xplus(struct clk_hw *hw, unsigned long rate,
        }
 
        if (clk_set->rate == rate) {
-               clk_debug("cpll get a rate\n");
+               clk_debug("cpll get a rate %ld\n", rate);
                rk3036_pll_clk_set_rate(clk_set, hw);
 
        } else {
@@ -1771,6 +2080,410 @@ static const struct clk_ops clk_pll_ops_312xplus = {
        .set_rate = clk_cpll_set_rate_312xplus,
 };
 
+static long clk_pll_round_rate_3368_apllb(struct clk_hw *hw, unsigned long rate,
+                                         unsigned long *prate)
+{
+       struct clk *parent = __clk_get_parent(hw->clk);
+
+       if (parent && (rate == __clk_get_rate(parent))) {
+               clk_debug("pll %s round rate=%lu equal to parent rate\n",
+                         __clk_get_name(hw->clk), rate);
+               return rate;
+       }
+
+       return (apll_get_best_set(rate, rk3368_apllb_table)->rate);
+}
+
+/* 1: use, 0: no use */
+#define RK3368_APLLB_USE_GPLL  1
+
+/* when define 1, we will set div to make rate change gently, but it will cost
+ more time */
+#define RK3368_APLLB_DIV_MORE  1
+
+static int clk_pll_set_rate_3368_apllb(struct clk_hw *hw, unsigned long rate,
+                                      unsigned long parent_rate)
+{
+       struct clk_pll *pll = to_clk_pll(hw);
+       struct clk *clk = hw->clk;
+       struct clk *arm_gpll = __clk_lookup("clk_gpll");
+       unsigned long arm_gpll_rate, temp_rate, old_rate;
+       const struct apll_clk_set *ps;
+       u32 temp_div;
+       unsigned long flags;
+       int sel_gpll = 0;
+
+       ps = apll_get_best_set(rate, rk3368_apllb_table);
+       clk_debug("apllb will set rate %lu\n", ps->rate);
+       clk_debug("table con:%08x,%08x,%08x, sel:%08x,%08x\n",
+                 ps->pllcon0, ps->pllcon1, ps->pllcon2,
+                 ps->clksel0, ps->clksel1);
+
+#if !RK3368_APLLB_USE_GPLL
+       goto CHANGE_APLL;
+#endif
+
+       /* prepare arm_gpll before reparent clk_core to it */
+       if (!arm_gpll) {
+               clk_err("clk arm_gpll is NULL!\n");
+               goto CHANGE_APLL;
+       }
+
+       arm_gpll_rate = __clk_get_rate(arm_gpll);
+       old_rate = __clk_get_rate(clk);
+
+       temp_rate = (old_rate > rate) ? old_rate : rate;
+       temp_div = DIV_ROUND_UP(arm_gpll_rate, temp_rate);
+
+       if (temp_div > RK3368_CORE_CLK_MAX_DIV) {
+               clk_debug("temp_div %d > max_div %d\n", temp_div,
+                         RK3368_CORE_CLK_MAX_DIV);
+               clk_debug("can't get rate %lu from arm_gpll rate %lu\n",
+                         __clk_get_rate(clk), arm_gpll_rate);
+               goto CHANGE_APLL;
+       }
+
+       local_irq_save(flags);
+
+       if (rate >= old_rate) {
+               cru_writel(ps->clksel0, RK3368_CRU_CLKSELS_CON(0));
+               cru_writel(ps->clksel1, RK3368_CRU_CLKSELS_CON(1));
+       }
+
+       /* select gpll */
+#if RK3368_APLLB_DIV_MORE
+       if (temp_div == 1) {
+               /* when old_rate/2 < (old_rate-arm_gpll_rate),
+                  we can set div to make rate change more gently */
+               if (old_rate > (2*arm_gpll_rate)) {
+                       cru_writel(RK3368_CORE_CLK_DIV(2), RK3368_CRU_CLKSELS_CON(0));
+                       udelay(10);
+                       cru_writel(RK3368_CORE_CLK_DIV(3), RK3368_CRU_CLKSELS_CON(0));
+                       udelay(10);
+                       cru_writel(RK3368_CORE_SEL_PLL_W_MSK|RK3368_CORE_SEL_GPLL,
+                                  RK3368_CRU_CLKSELS_CON(0));
+                       udelay(10);
+                       cru_writel(RK3368_CORE_CLK_DIV(2), RK3368_CRU_CLKSELS_CON(0));
+                       udelay(10);
+                       cru_writel(RK3368_CORE_CLK_DIV(1), RK3368_CRU_CLKSELS_CON(0));
+               } else {
+                       cru_writel(RK3368_CORE_SEL_PLL_W_MSK|RK3368_CORE_SEL_GPLL,
+                                  RK3368_CRU_CLKSELS_CON(0));
+               }
+       } else {
+               cru_writel(RK3368_CORE_CLK_DIV(temp_div), RK3368_CRU_CLKSELS_CON(0));
+               cru_writel(RK3368_CORE_SEL_PLL_W_MSK|RK3368_CORE_SEL_GPLL,
+                          RK3368_CRU_CLKSELS_CON(0));
+       }
+#else
+       cru_writel(RK3368_CORE_CLK_DIV(temp_div), RK3368_CRU_CLKSELS_CON(0));
+       cru_writel(RK3368_CORE_SEL_PLL_W_MSK|RK3368_CORE_SEL_GPLL,
+                  RK3368_CRU_CLKSELS_CON(0));
+#endif
+
+       sel_gpll = 1;
+
+       smp_wmb();
+
+       local_irq_restore(flags);
+
+       clk_debug("temp select arm_gpll path, get rate %lu\n",
+                 arm_gpll_rate/temp_div);
+       clk_debug("from arm_gpll rate %lu, temp_div %d\n", arm_gpll_rate,
+                 temp_div);
+
+CHANGE_APLL:
+       local_irq_save(flags);
+
+       /* apll enter slow mode */
+       cru_writel(_RK3188_PLL_MODE_SLOW_SET(pll->mode_shift),
+                  pll->mode_offset);
+
+       /* PLL enter reset */
+       cru_writel(_RK3188PLUS_PLL_RESET_SET(1), pll->reg + RK3188_PLL_CON(3));
+
+       cru_writel(ps->pllcon0, pll->reg + RK3188_PLL_CON(0));
+       cru_writel(ps->pllcon1, pll->reg + RK3188_PLL_CON(1));
+       cru_writel(ps->pllcon2, pll->reg + RK3188_PLL_CON(2));
+
+       udelay(5);
+
+       /* return from rest */
+       cru_writel(_RK3188PLUS_PLL_RESET_SET(0), pll->reg + RK3188_PLL_CON(3));
+
+       /* wating lock state */
+       udelay(ps->rst_dly);
+       pll_wait_lock(hw);
+
+       if (!sel_gpll) {
+               if (rate >= old_rate) {
+                       cru_writel(ps->clksel0, RK3368_CRU_CLKSELS_CON(0));
+                       cru_writel(ps->clksel1, RK3368_CRU_CLKSELS_CON(1));
+               }
+       }
+
+       /* apll return from slow mode */
+       cru_writel(_RK3188_PLL_MODE_NORM_SET(pll->mode_shift),
+                  pll->mode_offset);
+
+       /* reparent to apll, and set div to 1 */
+       if (sel_gpll) {
+#if RK3368_APLLB_DIV_MORE
+               /* when rate/2 < (rate-arm_gpll_rate), we can set div to make
+                  rate change more gently */
+               if ((temp_div == 1) && (rate > (2*arm_gpll_rate))) {
+                       cru_writel(RK3368_CORE_CLK_DIV(2), RK3368_CRU_CLKSELS_CON(0));
+                       udelay(10);
+                       cru_writel(RK3368_CORE_CLK_DIV(3), RK3368_CRU_CLKSELS_CON(0));
+                       udelay(10);
+                       cru_writel(RK3368_CORE_SEL_PLL_W_MSK|RK3368_CORE_SEL_APLL,
+                                  RK3368_CRU_CLKSELS_CON(0));
+                       udelay(10);
+                       cru_writel(RK3368_CORE_CLK_DIV(2), RK3368_CRU_CLKSELS_CON(0));
+                       udelay(10);
+               } else
+                       cru_writel(RK3368_CORE_SEL_PLL_W_MSK|RK3368_CORE_SEL_APLL,
+                                  RK3368_CRU_CLKSELS_CON(0));
+#else
+               cru_writel(RK3368_CORE_SEL_PLL_W_MSK|RK3368_CORE_SEL_APLL,
+                          RK3368_CRU_CLKSELS_CON(0));
+#endif
+       }
+
+       cru_writel(RK3368_CORE_CLK_DIV(1), RK3368_CRU_CLKSELS_CON(0));
+
+       if (rate < old_rate) {
+               cru_writel(ps->clksel0, RK3368_CRU_CLKSELS_CON(0));
+               cru_writel(ps->clksel1, RK3368_CRU_CLKSELS_CON(1));
+       }
+
+       smp_wmb();
+
+       local_irq_restore(flags);
+
+       if (sel_gpll)
+               sel_gpll = 0;
+
+       clk_debug("apll set rate %lu, con(%x,%x,%x,%x), sel(%x,%x)\n",
+                 ps->rate,
+                 cru_readl(pll->reg + RK3188_PLL_CON(0)),
+                 cru_readl(pll->reg + RK3188_PLL_CON(1)),
+                 cru_readl(pll->reg + RK3188_PLL_CON(2)),
+                 cru_readl(pll->reg + RK3188_PLL_CON(3)),
+                 cru_readl(RK3368_CRU_CLKSELS_CON(0)),
+                 cru_readl(RK3368_CRU_CLKSELS_CON(1)));
+
+       return 0;
+}
+
+static const struct clk_ops clk_pll_ops_3368_apllb = {
+       .recalc_rate = clk_pll_recalc_rate_3188plus,
+       .round_rate = clk_pll_round_rate_3368_apllb,
+       .set_rate = clk_pll_set_rate_3368_apllb,
+};
+
+static long clk_pll_round_rate_3368_aplll(struct clk_hw *hw, unsigned long rate,
+                                         unsigned long *prate)
+{
+       struct clk *parent = __clk_get_parent(hw->clk);
+
+       if (parent && (rate == __clk_get_rate(parent))) {
+               clk_debug("pll %s round rate=%lu equal to parent rate\n",
+                         __clk_get_name(hw->clk), rate);
+               return rate;
+       }
+
+       return (apll_get_best_set(rate, rk3368_aplll_table)->rate);
+}
+
+/* 1: use, 0: no use */
+#define RK3368_APLLL_USE_GPLL  1
+
+/* when define 1, we will set div to make rate change gently, but it will cost
+ more time */
+#define RK3368_APLLL_DIV_MORE  1
+
+static int clk_pll_set_rate_3368_aplll(struct clk_hw *hw, unsigned long rate,
+                                      unsigned long parent_rate)
+{
+       struct clk_pll *pll = to_clk_pll(hw);
+       struct clk *clk = hw->clk;
+       struct clk *arm_gpll = __clk_lookup("clk_gpll");
+       unsigned long arm_gpll_rate, temp_rate, old_rate;
+       const struct apll_clk_set *ps;
+       u32 temp_div;
+       unsigned long flags;
+       int sel_gpll = 0;
+
+       ps = apll_get_best_set(rate, rk3368_aplll_table);
+       clk_debug("aplll will set rate %lu\n", ps->rate);
+       clk_debug("table con:%08x,%08x,%08x, sel:%08x,%08x\n",
+                 ps->pllcon0, ps->pllcon1, ps->pllcon2,
+                 ps->clksel0, ps->clksel1);
+
+#if !RK3368_APLLL_USE_GPLL
+       goto CHANGE_APLL;
+#endif
+
+       /* prepare arm_gpll before reparent clk_core to it */
+       if (!arm_gpll) {
+               clk_err("clk arm_gpll is NULL!\n");
+               goto CHANGE_APLL;
+       }
+
+       arm_gpll_rate = __clk_get_rate(arm_gpll);
+       old_rate = __clk_get_rate(clk);
+
+       temp_rate = (old_rate > rate) ? old_rate : rate;
+       temp_div = DIV_ROUND_UP(arm_gpll_rate, temp_rate);
+
+       if (temp_div > RK3368_CORE_CLK_MAX_DIV) {
+               clk_debug("temp_div %d > max_div %d\n", temp_div,
+                         RK3368_CORE_CLK_MAX_DIV);
+               clk_debug("can't get rate %lu from arm_gpll rate %lu\n",
+                         __clk_get_rate(clk), arm_gpll_rate);
+               goto CHANGE_APLL;
+       }
+
+       local_irq_save(flags);
+
+       if (rate >= old_rate) {
+               cru_writel(ps->clksel0, RK3368_CRU_CLKSELS_CON(2));
+               cru_writel(ps->clksel1, RK3368_CRU_CLKSELS_CON(3));
+       }
+
+       /* select gpll */
+#if RK3368_APLLL_DIV_MORE
+       if (temp_div == 1) {
+               /* when old_rate/2 < (old_rate-arm_gpll_rate),
+                  we can set div to make rate change more gently */
+               if (old_rate > (2*arm_gpll_rate)) {
+                       cru_writel(RK3368_CORE_CLK_DIV(2), RK3368_CRU_CLKSELS_CON(2));
+                       udelay(10);
+                       cru_writel(RK3368_CORE_CLK_DIV(3), RK3368_CRU_CLKSELS_CON(2));
+                       udelay(10);
+                       cru_writel(RK3368_CORE_SEL_PLL_W_MSK|RK3368_CORE_SEL_GPLL,
+                                  RK3368_CRU_CLKSELS_CON(2));
+                       udelay(10);
+                       cru_writel(RK3368_CORE_CLK_DIV(2), RK3368_CRU_CLKSELS_CON(2));
+                       udelay(10);
+                       cru_writel(RK3368_CORE_CLK_DIV(1), RK3368_CRU_CLKSELS_CON(2));
+               } else {
+                       cru_writel(RK3368_CORE_SEL_PLL_W_MSK|RK3368_CORE_SEL_GPLL,
+                                  RK3368_CRU_CLKSELS_CON(2));
+               }
+       } else {
+               cru_writel(RK3368_CORE_CLK_DIV(temp_div), RK3368_CRU_CLKSELS_CON(2));
+               cru_writel(RK3368_CORE_SEL_PLL_W_MSK|RK3368_CORE_SEL_GPLL,
+                          RK3368_CRU_CLKSELS_CON(2));
+       }
+#else
+               cru_writel(RK3368_CORE_CLK_DIV(temp_div), RK3368_CRU_CLKSELS_CON(2));
+               cru_writel(RK3368_CORE_SEL_PLL_W_MSK|RK3368_CORE_SEL_GPLL,
+                          RK3368_CRU_CLKSELS_CON(2));
+#endif
+
+       sel_gpll = 1;
+
+       smp_wmb();
+
+       local_irq_restore(flags);
+
+       clk_debug("temp select arm_gpll path, get rate %lu\n",
+                 arm_gpll_rate/temp_div);
+       clk_debug("from arm_gpll rate %lu, temp_div %d\n", arm_gpll_rate,
+                 temp_div);
+
+CHANGE_APLL:
+       local_irq_save(flags);
+
+       /* apll enter slow mode */
+       cru_writel(_RK3188_PLL_MODE_SLOW_SET(pll->mode_shift),
+                  pll->mode_offset);
+
+       /* PLL enter reset */
+       cru_writel(_RK3188PLUS_PLL_RESET_SET(1), pll->reg + RK3188_PLL_CON(3));
+
+       cru_writel(ps->pllcon0, pll->reg + RK3188_PLL_CON(0));
+       cru_writel(ps->pllcon1, pll->reg + RK3188_PLL_CON(1));
+       cru_writel(ps->pllcon2, pll->reg + RK3188_PLL_CON(2));
+
+       udelay(5);
+
+       /* return from rest */
+       cru_writel(_RK3188PLUS_PLL_RESET_SET(0), pll->reg + RK3188_PLL_CON(3));
+
+       /* wating lock state */
+       udelay(ps->rst_dly);
+       pll_wait_lock(hw);
+
+       if (!sel_gpll) {
+               if (rate >= old_rate) {
+                       cru_writel(ps->clksel0, RK3368_CRU_CLKSELS_CON(2));
+                       cru_writel(ps->clksel1, RK3368_CRU_CLKSELS_CON(3));
+               }
+       }
+
+       /* apll return from slow mode */
+       cru_writel(_RK3188_PLL_MODE_NORM_SET(pll->mode_shift),
+                  pll->mode_offset);
+
+       /* reparent to apll, and set div to 1 */
+       if (sel_gpll) {
+#if RK3368_APLLL_DIV_MORE
+               /* when rate/2 < (rate-arm_gpll_rate), we can set div to make
+                  rate change more gently */
+               if ((temp_div == 1) && (rate > (2*arm_gpll_rate))) {
+                       cru_writel(RK3368_CORE_CLK_DIV(2), RK3368_CRU_CLKSELS_CON(2));
+                       udelay(10);
+                       cru_writel(RK3368_CORE_CLK_DIV(3), RK3368_CRU_CLKSELS_CON(2));
+                       udelay(10);
+                       cru_writel(RK3368_CORE_SEL_PLL_W_MSK|RK3368_CORE_SEL_APLL,
+                                  RK3368_CRU_CLKSELS_CON(2));
+                       udelay(10);
+                       cru_writel(RK3368_CORE_CLK_DIV(2), RK3368_CRU_CLKSELS_CON(2));
+                       udelay(10);
+               } else
+                       cru_writel(RK3368_CORE_SEL_PLL_W_MSK|RK3368_CORE_SEL_APLL,
+                                  RK3368_CRU_CLKSELS_CON(2));
+#else
+               cru_writel(RK3368_CORE_SEL_PLL_W_MSK|RK3368_CORE_SEL_APLL,
+                          RK3368_CRU_CLKSELS_CON(2));
+#endif
+       }
+
+       cru_writel(RK3368_CORE_CLK_DIV(1), RK3368_CRU_CLKSELS_CON(2));
+
+       if (rate < old_rate) {
+               cru_writel(ps->clksel0, RK3368_CRU_CLKSELS_CON(2));
+               cru_writel(ps->clksel1, RK3368_CRU_CLKSELS_CON(3));
+       }
+
+       smp_wmb();
+
+       local_irq_restore(flags);
+
+       if (sel_gpll)
+               sel_gpll = 0;
+
+       clk_debug("apll set rate %lu, con(%x,%x,%x,%x), sel(%x,%x)\n",
+                 ps->rate,
+                 cru_readl(pll->reg + RK3188_PLL_CON(0)),
+                 cru_readl(pll->reg + RK3188_PLL_CON(1)),
+                 cru_readl(pll->reg + RK3188_PLL_CON(2)),
+                 cru_readl(pll->reg + RK3188_PLL_CON(3)),
+                 cru_readl(RK3368_CRU_CLKSELS_CON(2)),
+                 cru_readl(RK3368_CRU_CLKSELS_CON(3)));
+
+       return 0;
+}
+
+static const struct clk_ops clk_pll_ops_3368_aplll = {
+       .recalc_rate = clk_pll_recalc_rate_3188plus,
+       .round_rate = clk_pll_round_rate_3368_aplll,
+       .set_rate = clk_pll_set_rate_3368_aplll,
+};
+
 const struct clk_ops *rk_get_pll_ops(u32 pll_flags)
 {
        switch (pll_flags) {
@@ -1801,6 +2514,15 @@ const struct clk_ops *rk_get_pll_ops(u32 pll_flags)
                case CLK_PLL_312XPLUS:
                        return &clk_pll_ops_312xplus;
 
+               case CLK_PLL_3368_APLLB:
+                       return &clk_pll_ops_3368_apllb;
+
+               case CLK_PLL_3368_APLLL:
+                       return &clk_pll_ops_3368_aplll;
+
+               case CLK_PLL_3368_LOW_JITTER:
+                       return &clk_pll_ops_3368_low_jitter;
+
                default:
                        clk_err("%s: unknown pll_flags!\n", __func__);
                        return NULL;