rk3188: add clock support
authorchenxing <chenxing@rock-chips.com>
Fri, 20 Dec 2013 11:07:23 +0000 (19:07 +0800)
committerchenxing <chenxing@rock-chips.com>
Fri, 20 Dec 2013 11:07:36 +0000 (19:07 +0800)
arch/arm/boot/dts/rk3188-clocks.dtsi [new file with mode: 0644]
arch/arm/boot/dts/rk3188-tb.dts
drivers/clk/Makefile
drivers/clk/clk-divider.c
drivers/clk/clk-mux.c
drivers/clk/rockchip/Makefile [new file with mode: 0644]
drivers/clk/rockchip/clk-ops.c [new file with mode: 0644]
drivers/clk/rockchip/clk-ops.h [new file with mode: 0644]
drivers/clk/rockchip/clk.c [new file with mode: 0644]
drivers/clk/rockchip/clkops-dtsi.h [new file with mode: 0644]

diff --git a/arch/arm/boot/dts/rk3188-clocks.dtsi b/arch/arm/boot/dts/rk3188-clocks.dtsi
new file mode 100644 (file)
index 0000000..342e2f4
--- /dev/null
@@ -0,0 +1,1369 @@
+/*
+ * Copyright (C) 2013 ROCKCHIP, Inc.
+ * Author: chenxing <chenxing@rock-chips.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include "../../../../drivers/clk/rockchip/clkops-dtsi.h"
+
+/{
+       clocks {
+               compatible = "rockchip,rk-clocks";
+               #address-cells = <1>;
+               #size-cells = <1>;
+               ranges;
+
+               xin24m: xin24m {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-output-names = "xin24m";
+                       clock-frequency = <24000000>;
+               };
+
+               xin12m: xin12m {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clocks = <&xin24m>;
+                       clock-output-names = "xin12m";
+                       clock-frequency = <12000000>;
+               };
+
+               dummy: dummy {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <0>;
+               };
+
+
+               rmii_clkin: rmii_clkin {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-output-names = "rmii_clkin";
+                       clock-frequency = <0>;
+               };
+
+               clk_hsadc_ext: clk_hsadc_ext {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-output-names = "clk_hsadc_ext";
+                       clock-frequency = <0>;
+               };
+
+               clk_cif_in: clk_cif_in {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-output-names = "clk_cif_in";
+                       clock-frequency = <0>;
+               };
+
+               clock_regs {
+                       compatible = "rockchip,rk-clock-regs";
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+                       ranges ;
+
+                       /* PLL control regs */
+                       pll_cons {
+                               compatible = "rockchip,rk-pll-cons";
+                               #address-cells = <1>;
+                               #size-cells = <1>;
+                               ranges ;
+
+                               clk_apll: pll-clk@0x20000000 {
+                                       compatible = "rockchip,rk3188-pll-clk";
+                                       reg = <0x20000000 0x10>;
+                                       clock-frequency = <24000000>;
+                                       clocks = <&xin24m>;
+                                       clock-output-names = "clk_apll";
+                                       #clock-cells = <0>;
+                               };
+
+                               clk_dpll: pll-clk@0x20000010 {
+                                       compatible = "rockchip,rk3188-pll-clk";
+                                       clock-frequency = <24000000>;
+                                       reg = <0x20000010 0x10>;
+                                       clocks = <&xin24m>;
+                                       clock-output-names = "clk_dpll";
+                                       #clock-cells = <0>;
+                               };
+
+                               clk_cpll: pll-clk@0x20000020 {
+                                       compatible = "rockchip,rk3188-pll-clk";
+                                       clock-frequency = <24000000>;
+                                       reg = <0x20000020 0x10>;
+                                       clocks = <&xin24m>;
+                                       clock-output-names = "clk_cpll";
+                                       #clock-cells = <0>;
+                               };
+
+                               clk_gpll: pll-clk@0x20000030 {
+                                       compatible = "rockchip,rk3188-pll-clk";
+                                       clock-frequency = <24000000>;
+                                       reg = <0x20000030 0x10>;
+                                       clocks = <&xin24m>;
+                                       clock-output-names = "clk_gpll";
+                                       #clock-cells = <0>;
+                               };
+                       };
+
+                       /* Select control regs */
+                       clk_sel_cons {
+                               compatible = "rockchip,rk-sel-cons";
+                               #address-cells = <1>;
+                               #size-cells = <1>;
+                               ranges ;
+
+                               clk_sel_con0: sel-con@20000044 {
+                                       compatible = "rockchip,rk3188-selcon";
+                                       reg = <0x20000044 0x4>;
+                                       #address-cells = <1>;
+                                       #size-cells = <1>;
+
+                                       aclk_cpu_div: aclk_cpu_div {
+                                               compatible = "rockchip,rk3188-div-con";
+                                               rockchip,bits = <0 5>;
+                                               clocks = <&aclk_cpu>;
+                                               rockchip,div-type = <CLK_DIVIDER_PLUS_ONE>;
+                                       };
+
+                                       aclk_cpu: clk_cpu_mux {
+                                               compatible = "rockchip,rk3188-mux-con";
+                                               rockchip,bits = <5 1>;
+                                               clocks = <&clk_apll>, <&clk_gpll>;
+                                               clock-output-names = "aclk_cpu";
+                                               #clock-cells = <0>;
+                                       };
+
+                                       clk_core_peri: clk_core_peri_div {
+                                               compatible = "rockchip,rk3188-div-con";
+                                               rockchip,bits = <6 2>;
+                                               clocks = <&clk_core>;
+                                               clock-output-names = "clk_core_peri";
+                                               rockchip,div-type = <CLK_DIVIDER_USER_DEFINE>;
+                                               #clock-cells = <0>;
+                                               rockchip,div-relations = <0x0 2
+                                                                0x1 4
+                                                                0x2 8
+                                                                0x3 16>;
+                                       };
+
+                                       clk_core: clk_core_mux {
+                                               compatible = "rockchip,rk3188-mux-con";
+                                               rockchip,bits = <8 1>;
+                                               clocks = <&clk_apll>,
+                                                      <&clk_gates0 1>;
+                                               clock-output-names = "clk_core";
+                                               #clock-cells = <0>;
+                                       };
+
+                                       clk_core_div: clk_core_div {
+                                               compatible = "rockchip,rk3188-div-con";
+                                               rockchip,bits = <9 5>;
+                                               clocks = <&clk_core>;
+                                               rockchip,div-type = <CLK_DIVIDER_PLUS_ONE>;
+                                       };
+
+                                       /* reg[15:14]: reserved */
+
+                               };
+
+                               clk_sel_con1: sel-con@20000048 {
+                                       compatible = "rockchip,rk3188-selcon";
+                                       reg = <0x20000048 0x4>;
+                                       #address-cells = <1>;
+                                       #size-cells = <1>;
+
+                                       /* reg[2:0]: reserved */
+
+                                       aclk_core: aclk_core_div {
+                                               compatible = "rockchip,rk3188-div-con";
+                                               rockchip,bits = <3 3>;
+                                               clocks = <&clk_core>;
+                                               clock-output-names = "aclk_core";
+                                               #clock-cells = <0>;
+                                               rockchip,div-type = <CLK_DIVIDER_USER_DEFINE>;
+                                               rockchip,div-relations = <0x0 1
+                                                                0x1 2
+                                                                0x2 3
+                                                                0x3 4
+                                                                0x4 8>;
+                                       };
+
+                                       /* reg[7:6]: reserved */
+
+                                       hclk_cpu: hclk_cpu_div {
+                                               compatible = "rockchip,rk3188-div-con";
+                                               rockchip,bits = <8 2>;
+                                               clocks = <&aclk_cpu>;
+                                               rockchip,div-type = <CLK_DIVIDER_POWER_OF_TWO>;
+                                               clock-output-names = "hclk_cpu";
+                                               #clock-cells = <0>;
+                                       };
+
+                                       /* reg[11:10]: reserved */
+
+                                       pclk_cpu: pclk_cpu_div {
+                                               compatible = "rockchip,rk3188-div-con";
+                                               rockchip,bits = <12 2>;
+                                               clocks = <&aclk_cpu>;
+                                               rockchip,div-type = <CLK_DIVIDER_POWER_OF_TWO>;
+                                               clock-output-names = "pclk_cpu";
+                                               #clock-cells = <0>;
+                                       };
+
+                                       pclk_ahb2apb: pclk_ahb2apb_div {
+                                               compatible = "rockchip,rk3188-div-con";
+                                               rockchip,bits = <14 2>;
+                                               clocks = <&hclk_cpu>;
+                                               rockchip,div-type = <CLK_DIVIDER_POWER_OF_TWO>;
+                                               clock-output-names = "pclk_ahb2apb";
+                                               #clock-cells = <0>;
+                                       };
+
+                               };
+                               clk_sel_con2: sel-con@2000004c {
+                                       compatible = "rockchip,rk3188-selcon";
+                                       reg = <0x2000004c 0x4>;
+                                       #address-cells = <1>;
+                                       #size-cells = <1>;
+
+                                       /* reg[14:0]: reserved */
+
+                                       clk_i2s_pll_mux: clk_i2s_pll_mux {
+                                               compatible = "rockchip,rk3188-mux-con";
+                                               rockchip,bits = <15 1>;
+                                               clocks = <&clk_gpll>, <&clk_cpll>;
+                                               clock-output-names = "clk_i2s_pll";
+                                               #clock-cells = <0>;
+                                       };
+                               };
+
+                               clk_sel_con3: sel-con@20000050 {
+                                       compatible = "rockchip,rk3188-selcon";
+                                       reg = <0x20000050 0x4>;
+                                       #address-cells = <1>;
+                                       #size-cells = <1>;
+
+                                       clk_i2s_div: clk_i2s_div {
+                                               compatible = "rockchip,rk3188-div-con";
+                                               rockchip,bits = <0 7>;
+                                               clocks = <&clk_i2s_pll_mux>;
+                                               rockchip,div-type = <CLK_DIVIDER_PLUS_ONE>;
+                                               clock-output-names = "clk_i2s_div";
+                                               #clock-cells = <0>;
+                                       };
+
+                                       /* reg[7]: reserved */
+
+                                       clk_i2s: clk_i2s_mux {
+                                               compatible = "rockchip,rk3188-mux-con";
+                                               rockchip,bits = <8 2>;
+                                               clocks = <&clk_i2s_div>, <&clk_i2s_frac>, <&xin12m>;
+                                               clock-output-names = "clk_i2s";
+                                               rockchip,clkops-idx = <CLKOPS_RATE_I2S>;
+                                               #clock-cells = <0>;
+                                       };
+
+                                       /* reg[15:10]: reserved */
+                               };
+
+                               /* clk_sel_con4: reserved */
+
+                               clk_sel_con5: sel-con@20000058 {
+                                       compatible = "rockchip,rk3188-selcon";
+                                       reg = <0x20000058 0x4>;
+                                       #address-cells = <1>;
+                                       #size-cells = <1>;
+
+                                       clk_spdif_div: clk_spdif_div {
+                                               compatible = "rockchip,rk3188-div-con";
+                                               rockchip,bits = <0 7>;
+                                               clocks = <&clk_i2s_pll_mux>;
+                                               rockchip,div-type = <CLK_DIVIDER_PLUS_ONE>;
+                                               clock-output-names = "clk_spdif_div";
+                                               /* spdif same as i2s */
+                                               #clock-cells = <0>;
+                                       };
+
+                                       /* reg[7]: reserved */
+
+                                       clk_spdif: clk_spdif_mux {
+                                               compatible = "rockchip,rk3188-mux-con";
+                                               rockchip,bits = <8 2>;
+                                               clocks = <&clk_spdif_div>, <&clk_spdif_frac>, <&xin12m>;
+                                               clock-output-names = "clk_spdif";
+                                               rockchip,clkops-idx = <CLKOPS_RATE_I2S>;
+                                               #clock-cells = <0>;
+                                       };
+
+                                       /* reg[15:10]: reserved */
+                               };
+
+                               /* clk_sel_con6: reserved */
+
+                               clk_sel_con7: sel-con@20000060 {
+                                       compatible = "rockchip,rk3188-selcon";
+                                       reg = <0x20000060 0x4>;
+                                       #address-cells = <1>;
+                                       #size-cells = <1>;
+                                       clk_i2s_frac: clk_i2s_frac {
+                                               compatible = "rockchip,rk3188-frac-con";
+                                               clocks = <&clk_i2s_div>;
+                                               clock-output-names = "clk_i2s_frac";
+                                               /* numerator    denominator */
+                                               rockchip,bits = <0 32>;
+                                               #clock-cells = <0>;
+                                               rockchip,clkops-idx =
+                                                       <CLKOPS_RATE_I2S_FRAC>;
+                                       };
+                               };
+
+                               /* clk_sel_con8: reserved */
+
+                               clk_sel_con9: sel-con@20000068 {
+                                       compatible = "rockchip,rk3188-selcon";
+                                       reg = <0x20000068 0x4>;
+                                       #address-cells = <1>;
+                                       #size-cells = <1>;
+                                       clk_spdif_frac: clk_spdif_frac {
+                                               compatible = "rockchip,rk3188-frac-con";
+                                               clocks = <&clk_spdif_div>;
+                                               clock-output-names = "clk_spdif_frac";
+                                               /* numerator    denominator */
+                                               rockchip,bits = <0 32>;
+                                               #clock-cells = <0>;
+                                               rockchip,clkops-idx =
+                                                       <CLKOPS_RATE_I2S_FRAC>;
+                                       };
+                               };
+
+                               clk_sel_con10: sel-con@2000006c {
+                                       compatible = "rockchip,rk3188-selcon";
+                                       reg = <0x2000006c 0x4>;
+                                       #address-cells = <1>;
+                                       #size-cells = <1>;
+
+                                       aclk_peri: aclk_peri_div {
+                                               compatible = "rockchip,rk3188-div-con";
+                                               rockchip,bits = <0 5>;
+                                               clocks = <&aclk_peri_mux>;
+                                               clock-output-names = "aclk_peri";
+                                               rockchip,div-type = <CLK_DIVIDER_PLUS_ONE>;
+                                               #clock-cells = <0>;
+                                       };
+
+                                       /* reg[7:5]: reserved */
+
+                                       hclk_peri: hclk_peri_div {
+                                               compatible = "rockchip,rk3188-div-con";
+                                               rockchip,bits = <8 2>;
+                                               clocks = <&aclk_peri>;
+                                               clock-output-names = "hclk_peri";
+                                               rockchip,div-type = <CLK_DIVIDER_POWER_OF_TWO>;
+                                               #clock-cells = <0>;
+                                       };
+
+                                       /* reg[11:10]: reserved */
+
+                                       pclk_peri: pclk_peri_div {
+                                               compatible = "rockchip,rk3188-div-con";
+                                               rockchip,bits = <12 2>;
+                                               clocks = <&aclk_peri>;
+                                               clock-output-names = "pclk_peri";
+                                               rockchip,div-type = <CLK_DIVIDER_POWER_OF_TWO>;
+                                               #clock-cells = <0>;
+                                       };
+
+                                       /* reg[14]: reserved */
+
+                                       aclk_peri_mux: aclk_peri_mux {
+                                               compatible = "rockchip,rk3188-mux-con";
+                                               rockchip,bits = <15 1>;
+                                               clocks = <&clk_cpll>, <&clk_gpll>;
+                                               clock-output-names = "aclk_peri_mux";
+                                               #clock-cells = <0>;
+                                       };
+                               };
+
+                               clk_sel_con11: sel-con@20000070 {
+                                       compatible = "rockchip,rk3188-selcon";
+                                       reg = <0x20000070 0x4>;
+                                       #address-cells = <1>;
+                                       #size-cells = <1>;
+
+                                       clk_sdmmc: clk_sdmmc_div {
+                                               compatible = "rockchip,rk3188-div-con";
+                                               rockchip,bits = <0 6>;
+                                               clocks = <&hclk_peri>;
+                                               clock-output-names = "clk_sdmmc";
+                                               rockchip,div-type = <CLK_DIVIDER_PLUS_ONE>;
+                                               rockchip,clkops-idx =
+                                                       <CLKOPS_RATE_EVENDIV>;
+                                               #clock-cells = <0>;
+                                       };
+
+                                       /* reg[7:6]: reserved */
+
+                                       hsic_phy_div: hsic_phy_div {
+                                               compatible = "rockchip,rk3188-div-con";
+                                               rockchip,bits = <8 6>;
+                                               clocks = <&clk_hsicphy480m_mux>;
+                                               rockchip,div-type = <CLK_DIVIDER_PLUS_ONE>;
+                                       };
+
+                                       /* reg[15:14]: reserved */
+
+                               };
+
+                               clk_sel_con12: sel-con@20000074 {
+                                       compatible = "rockchip,rk3188-selcon";
+                                       reg = <0x20000074 0x4>;
+                                       #address-cells = <1>;
+                                       #size-cells = <1>;
+
+                                       clk_sdio: clk_sdio_div {
+                                               compatible = "rockchip,rk3188-div-con";
+                                               rockchip,bits = <0 6>;
+                                               clocks = <&hclk_peri>;
+                                               clock-output-names = "clk_sdio";
+                                               rockchip,div-type = <CLK_DIVIDER_PLUS_ONE>;
+                                               rockchip,clkops-idx =
+                                                       <CLKOPS_RATE_EVENDIV>;
+                                               #clock-cells = <0>;
+                                       };
+
+                                       /* reg[7:6]: reserved */
+
+                                       clk_emmc: clk_emmc_div {
+                                               compatible = "rockchip,rk3188-div-con";
+                                               rockchip,bits = <8 6>;
+                                               clocks = <&hclk_peri>;
+                                               clock-output-names = "clk_emmc";
+                                               rockchip,div-type = <CLK_DIVIDER_PLUS_ONE>;
+                                               rockchip,clkops-idx =
+                                                       <CLKOPS_RATE_EVENDIV>;
+                                               #clock-cells = <0>;
+                                       };
+
+                                       /* reg[14]: reserved */
+
+                                       clk_uart_pll_mux: clk_uart_pll_mux {
+                                               compatible = "rockchip,rk3188-mux-con";
+                                               rockchip,bits = <15 1>;
+                                               clocks = <&clk_gpll>, <&clk_cpll>;
+                                               clock-output-names = "clk_uart_pll";
+                                               #clock-cells = <0>;
+                                       };
+                               };
+
+                               clk_sel_con13: sel-con@20000078 {
+                                       compatible = "rockchip,rk3188-selcon";
+                                       reg = <0x20000078 0x4>;
+                                       #address-cells = <1>;
+                                       #size-cells = <1>;
+
+                                       clk_uart0_div: clk_uart0_div {
+                                               compatible = "rockchip,rk3188-div-con";
+                                               rockchip,bits = <0 7>;
+                                               clocks = <&clk_uart_pll_mux>;
+                                               clock-output-names = "clk_uart0_div";
+                                               rockchip,div-type = <CLK_DIVIDER_PLUS_ONE>;
+                                               #clock-cells = <0>;
+                                       };
+
+                                       /* reg[7]: reserved */
+
+                                       clk_uart0: clk_uart0_mux {
+                                               compatible = "rockchip,rk3188-mux-con";
+                                               rockchip,bits = <8 2>;
+                                               clocks = <&clk_uart0_div>, <&clk_uart0_frac>,
+                                                      <&xin24m>;
+                                               rockchip,clkops-idx =
+                                                       <CLKOPS_RATE_UART>;
+                                               clock-output-names = "clk_uart0";
+                                               #clock-cells = <0>;
+                                       };
+
+                                       /* reg[15:10]: reserved */
+
+                               };
+
+                               clk_sel_con14: sel-con@2000007c {
+                                       compatible = "rockchip,rk3188-selcon";
+                                       reg = <0x2000007c 0x4>;
+                                       #address-cells = <1>;
+                                       #size-cells = <1>;
+
+                                       clk_uart1_div: clk_uart1_div {
+                                               compatible = "rockchip,rk3188-div-con";
+                                               rockchip,bits = <0 7>;
+                                               clocks = <&clk_uart_pll_mux>;
+                                               clock-output-names = "clk_uart1_div";
+                                               rockchip,div-type = <CLK_DIVIDER_PLUS_ONE>;
+                                               #clock-cells = <0>;
+                                       };
+
+                                       /* reg[7]: reserved */
+
+                                       clk_uart1: clk_uart1_mux {
+                                               compatible = "rockchip,rk3188-mux-con";
+                                               rockchip,bits = <8 2>;
+                                               clocks = <&clk_uart1_div>, <&clk_uart1_frac>,
+                                                      <&xin24m>;
+                                               rockchip,clkops-idx =
+                                                       <CLKOPS_RATE_UART>;
+                                               clock-output-names = "clk_uart1";
+                                               #clock-cells = <0>;
+                                       };
+
+                                       /* reg[15:10]: reserved */
+
+                               };
+
+                               clk_sel_con15: sel-con@20000080 {
+                                       compatible = "rockchip,rk3188-selcon";
+                                       reg = <0x20000080 0x4>;
+                                       #address-cells = <1>;
+                                       #size-cells = <1>;
+
+                                       clk_uart2_div: clk_uart2_div {
+                                               compatible = "rockchip,rk3188-div-con";
+                                               rockchip,bits = <0 7>;
+                                               clocks = <&clk_uart_pll_mux>;
+                                               clock-output-names = "clk_uart2_div";
+                                               rockchip,div-type = <CLK_DIVIDER_PLUS_ONE>;
+                                               #clock-cells = <0>;
+                                       };
+
+                                       /* reg[7]: reserved */
+
+                                       clk_uart2: clk_uart2_mux {
+                                               compatible = "rockchip,rk3188-mux-con";
+                                               rockchip,bits = <8 2>;
+                                               clocks = <&clk_uart2_div>, <&clk_uart2_frac>,
+                                                      <&xin24m>;
+                                               rockchip,clkops-idx =
+                                                       <CLKOPS_RATE_UART>;
+                                               clock-output-names = "clk_uart2";
+                                               #clock-cells = <0>;
+                                       };
+
+                                       /* reg[15:10]: reserved */
+
+                               };
+
+                               clk_sel_con16: sel-con@20000084 {
+                                       compatible = "rockchip,rk3188-selcon";
+                                       reg = <0x20000084 0x4>;
+                                       #address-cells = <1>;
+                                       #size-cells = <1>;
+
+                                       clk_uart3_div: clk_uart3_div {
+                                               compatible = "rockchip,rk3188-div-con";
+                                               rockchip,bits = <0 7>;
+                                               clocks = <&clk_uart_pll_mux>;
+                                               clock-output-names = "clk_uart3_div";
+                                               rockchip,div-type = <CLK_DIVIDER_PLUS_ONE>;
+                                               #clock-cells = <0>;
+                                       };
+
+                                       /* reg[7]: reserved */
+
+                                       clk_uart3: clk_uart3_mux {
+                                               compatible = "rockchip,rk3188-mux-con";
+                                               rockchip,bits = <8 2>;
+                                               clocks = <&clk_uart3_div>, <&clk_uart3_frac>,
+                                                      <&xin24m>;
+                                               rockchip,clkops-idx =
+                                                       <CLKOPS_RATE_UART>;
+                                               clock-output-names = "clk_uart3";
+                                               #clock-cells = <0>;
+                                       };
+
+                                       /* reg[15:10]: reserved */
+
+                               };
+
+                               clk_sel_con17: sel-con@20000088 {
+                                       compatible = "rockchip,rk3188-selcon";
+                                       reg = <0x20000088 0x4>;
+                                       #address-cells = <1>;
+                                       #size-cells = <1>;
+                                       clk_uart0_frac: clk_uart0_frac {
+                                               compatible = "rockchip,rk3188-frac-con";
+                                               clocks = <&clk_uart0_div>;
+                                               clock-output-names = "clk_uart0_frac";
+                                               /* numerator    denominator */
+                                               rockchip,bits = <0 32>;
+                                               rockchip,clkops-idx =
+                                                       <CLKOPS_RATE_UART_FRAC>;
+                                               #clock-cells = <0>;
+                                       };
+                               };
+                               clk_sel_con18: sel-con@2000008c {
+                                       compatible = "rockchip,rk3188-selcon";
+                                       reg = <0x2000008c 0x4>;
+                                       #address-cells = <1>;
+                                       #size-cells = <1>;
+                                       clk_uart1_frac: clk_uart1_frac {
+                                               compatible = "rockchip,rk3188-frac-con";
+                                               clocks = <&clk_uart1_div>;
+                                               clock-output-names = "clk_uart1_frac";
+                                               /* numerator    denominator */
+                                               rockchip,bits = <0 32>;
+                                               rockchip,clkops-idx =
+                                                       <CLKOPS_RATE_UART_FRAC>;
+                                               #clock-cells = <0>;
+                                       };
+                               };
+                               clk_sel_con19: sel-con@20000090 {
+                                       compatible = "rockchip,rk3188-selcon";
+                                       reg = <0x20000090 0x4>;
+                                       #address-cells = <1>;
+                                       #size-cells = <1>;
+                                       clk_uart2_frac: clk_uart2_frac {
+                                               compatible = "rockchip,rk3188-frac-con";
+                                               clocks = <&clk_uart2_div>;
+                                               clock-output-names = "clk_uart2_frac";
+                                               /* numerator    denominator */
+                                               rockchip,bits = <0 32>;
+                                               rockchip,clkops-idx =
+                                                       <CLKOPS_RATE_UART_FRAC>;
+                                               #clock-cells = <0>;
+                                       };
+                               };
+                               clk_sel_con20: sel-con@20000094 {
+                                       compatible = "rockchip,rk3188-selcon";
+                                       reg = <0x20000094 0x4>;
+                                       #address-cells = <1>;
+                                       #size-cells = <1>;
+                                       clk_uart3_frac: clk_uart3_frac {
+                                               compatible = "rockchip,rk3188-frac-con";
+                                               clocks = <&clk_uart3_div>;
+                                               clock-output-names = "clk_uart3_frac";
+                                               /* numerator    denominator */
+                                               rockchip,bits = <0 32>;
+                                               rockchip,clkops-idx =
+                                                       <CLKOPS_RATE_UART_FRAC>;
+                                               #clock-cells = <0>;
+                                       };
+                               };
+
+                               clk_sel_con21: sel-con@20000098 {
+                                       compatible = "rockchip,rk3188-selcon";
+                                       reg = <0x20000098 0x4>;
+                                       #address-cells = <1>;
+                                       #size-cells = <1>;
+
+                                       clk_mac_pll_mux: clk_mac_pll_mux {
+                                               compatible = "rockchip,rk3188-mux-con";
+                                               rockchip,bits = <0 1>;
+                                               clocks = <&clk_gpll>, <&clk_dpll>;
+                                               clock-output-names = "clk_mac_pll";
+                                               #clock-cells = <0>;
+                                       };
+
+                                       /* reg[3:1]: reserved */
+
+                                       clk_mac: clk_mac_mux {
+                                               compatible = "rockchip,rk3188-mux-con";
+                                               rockchip,bits = <4 1>;
+                                               clocks = <&clk_mac_pll_mux>, <&rmii_clkin>;
+                                               rockchip,clkops-idx =
+                                                       <CLKOPS_RATE_MAC_REF>;
+                                               clock-output-names = "clk_mac";
+                                               #clock-cells = <0>;
+                                       };
+
+                                       /* reg[7:5]: reserved */
+
+                                       clk_mac_pll_div: clk_mac_pll_div {
+                                               compatible = "rockchip,rk3188-div-con";
+                                               rockchip,bits = <8 5>;
+                                               clocks = <&clk_mac_pll_mux>;
+                                               rockchip,div-type = <CLK_DIVIDER_PLUS_ONE>;
+
+                                       };
+
+                                       /* reg[15:13]: reserved */
+                               };
+
+                               clk_sel_con22: sel-con@2000009c {
+                                       compatible = "rockchip,rk3188-selcon";
+                                       reg = <0x2000009c 0x4>;
+                                       #address-cells = <1>;
+                                       #size-cells = <1>;
+
+                                       clk_hsadc_pll_mux: clk_hsadc_pll_mux {
+                                               compatible = "rockchip,rk3188-mux-con";
+                                               rockchip,bits = <0 1>;
+                                               clocks = <&clk_gpll>, <&clk_cpll>;
+                                               clock-output-names = "clk_hsadc_pll";
+                                               #clock-cells = <0>;
+                                       };
+
+                                       /* reg[3:1]: reserved */
+
+                                       clk_hsadc: clk_hsadc_mux {
+                                               compatible = "rockchip,rk3188-mux-con";
+                                               rockchip,bits = <4 2>;
+                                               clocks = <&clk_hsadc_pll_mux>, <&clk_hsadc_frac>,
+                                                        <&clk_hsadc_ext>;
+                                               rockchip,clkops-idx =
+                                                       <CLKOPS_RATE_HSADC>;
+                                               clock-output-names = "clk_hsadc";
+                                               #clock-cells = <0>;
+                                       };
+
+                                       /* reg[6]: reserved */
+
+                                       clk_hsadc_inv: clk_hsadc_inv {
+                                               compatible = "rockchip,rk3188-inv-con";
+                                               rockchip,bits = <7 1>;
+                                               clocks = <&clk_hsadc>;
+                                               rockchip,div-type = <CLK_DIVIDER_FIXED>;
+                                               rockchip,div-relations = <1>;
+                                       };
+
+                                       clk_hsadc_div: clk_hsadc_div {
+                                               compatible = "rockchip,rk3188-div-con";
+                                               rockchip,bits = <8 8>;
+                                               clocks = <&clk_hsadc_pll_mux>;
+                                               rockchip,div-type = <CLK_DIVIDER_PLUS_ONE>;
+                                       };
+                               };
+
+                               clk_sel_con23: sel-con@200000a0 {
+                                       compatible = "rockchip,rk3188-selcon";
+                                       reg = <0x200000a0 0x4>;
+                                       #address-cells = <1>;
+                                       #size-cells = <1>;
+
+                                       clk_hsadc_frac: clk_hsadc_frac{
+                                               compatible = "rockchip,rk3188-frac-con";
+                                               clocks = <&clk_hsadc_pll_mux>;
+                                               clock-output-names = "clk_hsadc_frac";
+                                               /* numerator    denominator */
+                                               rockchip,bits = <0 32>;
+                                               rockchip,clkops-idx =
+                                                       <CLKOPS_RATE_HSADC_FRAC>;
+                                               #clock-cells = <0>;
+                                       };
+                               };
+
+                               clk_sel_con24: sel-con@200000a4 {
+                                       compatible = "rockchip,rk3188-selcon";
+                                       reg = <0x200000a4 0x4>;
+                                       #address-cells = <1>;
+                                       #size-cells = <1>;
+                                       clk_saradc: clk_saradc_div {
+                                               compatible = "rockchip,rk3188-div-con";
+                                               rockchip,bits = <8 8>;
+                                               clocks = <&xin24m>;
+                                               clock-output-names = "clk_saradc";
+                                               rockchip,div-type = <CLK_DIVIDER_PLUS_ONE>;
+                                               #clock-cells = <0>;
+                                       };
+                               };
+
+                               clk_sel_con25: sel-con@200000a8 {
+                                       compatible = "rockchip,rk3188-selcon";
+                                       reg = <0x200000a8 0x4>;
+                                       #address-cells = <1>;
+                                       #size-cells = <1>;
+
+                                       clk_spi0: clk_spi0_div {
+                                               compatible = "rockchip,rk3188-div-con";
+                                               rockchip,bits = <0 7>;
+                                               clocks = <&pclk_peri>;
+                                               clock-output-names = "clk_spi0";
+                                               rockchip,div-type = <CLK_DIVIDER_PLUS_ONE>;
+                                               #clock-cells = <0>;
+                                       };
+                                       /* reg[7]: reserved */
+                                       clk_spi1: clk_spi1_div {
+                                               compatible = "rockchip,rk3188-div-con";
+                                               rockchip,bits = <8 7>;
+                                               clocks = <&pclk_peri>;
+                                               clock-output-names = "clk_spi1";
+                                               rockchip,div-type = <CLK_DIVIDER_PLUS_ONE>;
+                                               #clock-cells = <0>;
+                                       };
+                                       /* reg[15]: reserved */
+                               };
+
+                               clk_sel_con26: sel-con@200000ac {
+                                       compatible = "rockchip,rk3188-selcon";
+                                       reg = <0x200000ac 0x4>;
+                                       #address-cells = <1>;
+                                       #size-cells = <1>;
+
+                                       clk_ddr_div: clk_ddr_div {
+                                               compatible = "rockchip,rk3188-div-con";
+                                               rockchip,bits = <0 2>;
+                                               clocks = <&clk_ddr>;
+                                               rockchip,div-type = <CLK_DIVIDER_POWER_OF_TWO>;
+                                       };
+
+                                       /* reg[7:2]: reserved */
+
+                                       clk_ddr: clk_ddr_mux {
+                                               compatible = "rockchip,rk3188-mux-con";
+                                               rockchip,bits = <8 1>;
+                                               clocks = <&clk_dpll>,
+                                                      <&clk_gates1 7>;
+                                               clock-output-names = "clk_ddr";
+                                               #clock-cells = <0>;
+                                       };
+
+                                       /* reg[15:9]: reserved */
+                               };
+
+                               clk_sel_con27: sel-con@200000b0 {
+                                       compatible = "rockchip,rk3188-selcon";
+                                       reg = <0x200000b0 0x4>;
+                                       #address-cells = <1>;
+                                       #size-cells = <1>;
+
+                                       dclk_lcdc0: dclk_lcdc0_mux {
+                                               compatible = "rockchip,rk3188-mux-con";
+                                               rockchip,bits = <0 1>;
+                                               clocks = <&clk_cpll>, <&clk_gpll>;
+                                               clock-output-names = "dclk_lcdc0";
+                                               #clock-cells = <0>;
+                                       };
+
+                                       /* reg[7:1]: reserved */
+
+                                       dclk_lcdc0_div: dclk_lcdc0_div {
+                                               compatible = "rockchip,rk3188-div-con";
+                                               rockchip,bits = <8 8>;
+                                               clocks = <&dclk_lcdc0>;
+                                               rockchip,div-type = <CLK_DIVIDER_PLUS_ONE>;
+                                               rockchip,clkops-idx =
+                                                       <CLKOPS_RATE_DCLK_LCDC>;
+                                       };
+                               };
+
+
+                               clk_sel_con28: sel-con@200000b4 {
+                                       compatible = "rockchip,rk3188-selcon";
+                                       reg = <0x200000b4 0x4>;
+                                       #address-cells = <1>;
+                                       #size-cells = <1>;
+
+                                       dclk_lcdc1: dclk_lcdc1_mux {
+                                               compatible = "rockchip,rk3188-mux-con";
+                                               rockchip,bits = <0 1>;
+                                               clocks = <&clk_cpll>, <&clk_gpll>;
+                                               clock-output-names = "dclk_lcdc1";
+                                               #clock-cells = <0>;
+                                       };
+
+                                       /* reg[7:1]: reserved */
+
+                                       dclk_lcdc1_div: dclk_lcdc1_div {
+                                               compatible = "rockchip,rk3188-div-con";
+                                               rockchip,bits = <8 8>;
+                                               clocks = <&dclk_lcdc1>;
+                                               rockchip,div-type = <CLK_DIVIDER_PLUS_ONE>;
+                                               rockchip,clkops-idx =
+                                                       <CLKOPS_RATE_DCLK_LCDC>;
+                                       };
+                               };
+
+                               clk_sel_con29: sel-con@200000b8 {
+                                       compatible = "rockchip,rk3188-selcon";
+                                       reg = <0x200000b8 0x4>;
+                                       #address-cells = <1>;
+                                       #size-cells = <1>;
+
+                                       cif_out_pll_mux: cif_out_pll_mux {
+                                               compatible = "rockchip,rk3188-mux-con";
+                                               rockchip,bits = <0 1>;
+                                               clocks = <&clk_cpll>, <&clk_gpll>;
+                                               clock-output-names = "cif_out_pll";
+                                               #clock-cells = <0>;
+                                       };
+
+                                       cif0_out_div: cif0_out_div {
+                                               compatible = "rockchip,rk3188-div-con";
+                                               rockchip,bits = <1 5>;
+                                               clocks = <&cif_out_pll_mux>;
+                                               rockchip,div-type = <CLK_DIVIDER_PLUS_ONE>;
+                                       };
+
+                                       /* reg[6]: reserved */
+
+                                       clk_cif0: cif0_out_mux {
+                                               compatible = "rockchip,rk3188-mux-con";
+                                               rockchip,bits = <7 1>;
+                                               clocks = <&cif_out_pll_mux>, <&xin24m>;
+                                               rockchip,clkops-idx =
+                                                       <CLKOPS_RATE_CIFOUT>;
+                                               clock-output-names = "clk_cif0";
+                                               #clock-cells = <0>;
+                                       };
+
+                                       /* reg[15:8]: reserved */
+                               };
+
+                               clk_sel_con30: sel-con@200000bc {
+                                       compatible = "rockchip,rk3188-selcon";
+                                       reg = <0x200000bc 0x4>;
+                                       #address-cells = <1>;
+                                       #size-cells = <1>;
+
+                                       clk_hsicphy480m_mux: clk_hsicphy480m_mux {
+                                               compatible = "rockchip,rk3188-mux-con";
+                                               rockchip,bits = <0 2>;
+                                               clocks = <&clk_gates1 5>, <&clk_gates1 6>,
+                                                        <&clk_gpll>, <&clk_cpll>;
+                                               clock-output-names = "clk_hsicphy480m";
+                                               #clock-cells = <0>;
+                                       };
+
+                                       /* reg[7:2]: reserved */
+
+                                       /* inv here?????? */
+
+                                       /* reg[15:9]: reserved */
+                               };
+
+                               clk_sel_con31: sel-con@200000c0 {
+                                       compatible = "rockchip,rk3188-selcon";
+                                       reg = <0x200000c0 0x4>;
+                                       #address-cells = <1>;
+                                       #size-cells = <1>;
+
+                                       aclk_lcdc0_pre_div: aclk_lcdc0_pre_div {
+                                               compatible = "rockchip,rk3188-div-con";
+                                               rockchip,bits = <0 5>;
+                                               clocks = <&aclk_lcdc0>;
+                                               rockchip,div-type = <CLK_DIVIDER_PLUS_ONE>;
+                                               rockchip,clkops-idx =
+                                                       <CLKOPS_RATE_MUX_DIV>;
+                                       };
+
+                                       /* reg[6:5]: reserved */
+
+                                       aclk_lcdc0: aclk_lcdc0_pre_mux {
+                                               compatible = "rockchip,rk3188-mux-con";
+                                               rockchip,bits = <7 1>;
+                                               clocks = <&clk_cpll>, <&clk_gpll>;
+                                               clock-output-names = "aclk_lcdc0";
+                                               #clock-cells = <0>;
+                                       };
+
+                                       aclk_lcdc1_pre_div: aclk_lcdc1_pre_div {
+                                               compatible = "rockchip,rk3188-div-con";
+                                               rockchip,bits = <8 5>;
+                                               clocks = <&aclk_lcdc1>;
+                                               rockchip,div-type = <CLK_DIVIDER_PLUS_ONE>;
+                                               rockchip,clkops-idx =
+                                                       <CLKOPS_RATE_MUX_DIV>;
+                                       };
+
+                                       /* reg[14:13]: reserved */
+
+                                       aclk_lcdc1: aclk_lcdc1_pre_mux {
+                                               compatible = "rockchip,rk3188-mux-con";
+                                               rockchip,bits = <15 1>;
+                                               clocks = <&clk_cpll>, <&clk_gpll>;
+                                               clock-output-names = "aclk_lcdc1";
+                                               #clock-cells = <0>;
+                                       };
+                               };
+
+                               clk_sel_con32: sel-con@200000c4 {
+                                       compatible = "rockchip,rk3188-selcon";
+                                       reg = <0x200000c4 0x4>;
+                                       #address-cells = <1>;
+                                       #size-cells = <1>;
+
+                                       aclk_vepu_div: aclk_vepu_div {
+                                               compatible = "rockchip,rk3188-div-con";
+                                               rockchip,bits = <0 5>;
+                                               clocks = <&clk_vepu>;
+                                               rockchip,div-type = <CLK_DIVIDER_PLUS_ONE>;
+                                               rockchip,clkops-idx =
+                                                       <CLKOPS_RATE_MUX_DIV>;
+                                       };
+
+                                       /* reg[6:5]: reserved */
+
+                                       clk_vepu: aclk_vepu_mux {
+                                               compatible = "rockchip,rk3188-mux-con";
+                                               rockchip,bits = <7 1>;
+                                               clocks = <&clk_cpll>, <&clk_gpll>;
+                                               clock-output-names = "clk_vepu";
+                                               #clock-cells = <0>;
+                                       };
+
+                                       aclk_vdpu_div: aclk_vdpu_div {
+                                               compatible = "rockchip,rk3188-div-con";
+                                               rockchip,bits = <8 5>;
+                                               clocks = <&clk_vdpu>;
+                                               rockchip,div-type = <CLK_DIVIDER_PLUS_ONE>;
+                                               rockchip,clkops-idx =
+                                                       <CLKOPS_RATE_MUX_DIV>;
+                                       };
+
+                                       /* reg[14:13]: reserved */
+
+                                       clk_vdpu: aclk_vdpu_mux {
+                                               compatible = "rockchip,rk3188-mux-con";
+                                               rockchip,bits = <15 1>;
+                                               clocks = <&clk_cpll>, <&clk_gpll>;
+                                               clock-output-names = "clk_vdpu";
+                                               #clock-cells = <0>;
+                                       };
+                               };
+
+                               clk_sel_con34: sel-con@200000cc {
+                                       compatible = "rockchip,rk3188-selcon";
+                                       reg = <0x200000cc 0x4>;
+                                       #address-cells = <1>;
+                                       #size-cells = <1>;
+
+                                       aclk_gpu_div: aclk_gpu_div {
+                                               compatible = "rockchip,rk3188-div-con";
+                                               rockchip,bits = <0 5>;
+                                               clocks = <&clk_gpu>;
+                                               rockchip,clkops-idx =
+                                                       <CLKOPS_RATE_MUX_DIV>;
+                                       };
+
+                                       /* reg[6:5]: reserved */
+
+                                       clk_gpu: aclk_gpu_mux {
+                                               compatible = "rockchip,rk3188-mux-con";
+                                               rockchip,bits = <7 1>;
+                                               clocks = <&clk_cpll>, <&clk_gpll>;
+                                               clock-output-names = "clk_gpu";
+                                               #clock-cells = <0>;
+                                       };
+
+                                       /* reg[15:8]: reserved */
+                               };
+                       };
+
+                       /* Gate control regs */
+                       clk_gate_cons {
+                               compatible = "rockchip,rk-gate-cons";
+                               #address-cells = <1>;
+                               #size-cells = <1>;
+                               ranges ;
+
+                               clk_gates0: gate-clk@200000d0 {
+                                       compatible = "rockchip,rk3188-gate-clk";
+                                       reg = <0x200000d0 0x4>;
+                                       clocks = <&clk_core_peri>,      <&clk_gpll>,
+                                                <&clk_dpll>,           <&aclk_cpu>,
+
+                                                <&hclk_cpu>,           <&pclk_cpu>,
+                                                <&pclk_cpu>,           <&aclk_core>,
+
+                                                <&dummy>,              <&clk_i2s_div>,
+                                                <&clk_i2s_frac>,       <&dummy>,
+
+                                                <&dummy>,              <&clk_spdif_div>,
+                                                <&clk_spdif_frac>,     <&dummy>;
+
+                                       clock-output-names =
+                                               "clk_core_peri",        "clk_arm_gpll",
+                                               "clk_dpll",             "aclk_cpu",
+
+                                               "hclk_cpu",             "pclk_cpu",
+                                               "g_atclk_cpu",          "aclk_core",
+
+                                               "reserved",             "clk_i2s_div",
+                                               "clk_i2s_frac",         "reserved",
+
+                                               "reserved",             "clk_spdif_div",
+                                               "clk_spdif_frac",       "g_testclk";
+
+                                       #clock-cells = <1>;
+                               };
+
+                               clk_gates1: gate-clk@200000d4 {
+                                       compatible = "rockchip,rk3188-gate-clk";
+                                       reg = <0x200000d4 0x4>;
+                                       clocks = <&xin24m>,             <&xin24m>,
+                                                <&xin24m>,             <&dummy>,
+
+                                                <&aclk_lcdc1>,         <&xin24m>,
+                                                <&xin24m>,             <&clk_gpll>,
+
+                                                <&clk_uart0_div>,      <&clk_uart0_frac>,
+                                                <&clk_uart1_div>,      <&clk_uart1_frac>,
+
+                                                <&clk_uart2_div>,      <&clk_uart2_frac>,
+                                                <&clk_uart3_div>,      <&clk_uart3_frac>;
+
+                                       clock-output-names =
+                                               "timer0",               "timer1",
+                                               "timer3",               "g_jtag",
+
+                                               "aclk_lcdc1",           "g_otgphy0",
+                                               "g_otgphy1",            "clk_ddr_gpll",
+
+                                               "clk_uart0_div",        "clk_uart0_frac",
+                                               "clk_uart1_div",        "clk_uart1_frac",
+
+                                               "clk_uart2_div",        "clk_uart2_frac",
+                                               "clk_uart3_div",        "clk_uart3_frac";
+
+                                       #clock-cells = <1>;
+                               };
+
+                               clk_gates2: gate-clk@200000d8 {
+                                       compatible = "rockchip,rk3188-gate-clk";
+                                       reg = <0x200000d8 0x4>;
+                                       clocks = <&aclk_peri_mux>,      <&aclk_peri>,
+                                                <&hclk_peri>,          <&pclk_peri>,
+
+                                                <&hclk_peri>,          <&clk_mac_pll_mux>,
+                                                <&clk_hsadc_pll_mux>,  <&clk_hsadc_frac>,
+
+                                                <&clk_saradc>,         <&clk_spi0>,
+                                                <&clk_spi1>,           <&clk_sdmmc>,
+
+                                                <&dummy>,              <&clk_sdio>,
+                                                <&clk_emmc>,           <&dummy>;
+
+                                       clock-output-names =
+                                               "aclk_peri_mux",        "aclk_peri",
+                                               "hclk_peri",            "pclk_peri",
+
+                                               "g_smc_src",            "clk_mac_pll",
+                                               "clk_hsadc_pll",        "clk_hsadc_frac",
+
+                                               "clk_saradc",           "clk_spi0",
+                                               "clk_spi1",             "clk_sdmmc",
+
+                                               "g_mac_lbtest",         "clk_sdio",
+                                               "clk_emmc",             "reserved";
+
+                                       #clock-cells = <1>;
+                               };
+
+                               clk_gates3: gate-clk@200000dc {
+                                       compatible = "rockchip,rk3188-gate-clk";
+                                       reg = <0x200000dc 0x4>;
+                                       clocks = <&aclk_lcdc0>,         <&dclk_lcdc0>,
+                                                <&dclk_lcdc1>,         <&clk_cif_in>,
+
+                                                <&xin24m>,                     <&xin24m>,
+                                                <&clk_hsicphy480m_mux>,        <&clk_cif0>,
+
+                                                <&xin24m>,             <&clk_vepu>,
+                                                <&clk_vepu>,           <&clk_vdpu>,
+
+                                                <&clk_vdpu>,           <&dummy>,
+                                                <&xin24m>,             <&clk_gpu>;
+
+                                       clock-output-names =
+                                               "aclk_lcdc0",           "dclk_lcdc0",
+                                               "dclk_lcdc1",           "g_clk_cif_in",
+
+                                               /*
+                                                * FIXME: cif_out_pll can be set to
+                                                * clk_cif as virtual
+                                                */
+                                               "timer2",               "timer4",
+                                               "clk_hsicphy480m",      "clk_cif0",
+
+                                               "timer5",               "clk_vepu",
+                                               "g_h_vepu",             "clk_vdpu",
+
+                                               "g_h_vdpu",             "reserved",
+                                               "timer6",               "clk_gpu";
+
+                                       #clock-cells = <1>;
+                               };
+
+                               clk_gates4: gate-clk@200000e0 {
+                                       compatible = "rockchip,rk3188-gate-clk";
+                                       reg = <0x200000e0 0x4>;
+                                       clocks = <&hclk_peri>,          <&pclk_peri>,
+                                                <&aclk_peri>,          <&aclk_peri>,
+
+                                                <&aclk_peri>,          <&hclk_peri>,
+                                                <&hclk_peri>,          <&hclk_peri>,
+
+                                                <&hclk_cpu>,           <&hclk_cpu>,
+                                                <&aclk_cpu>,           <&dummy>,
+
+                                                <&aclk_cpu>,           <&dummy>,
+                                                <&hclk_cpu>,           <&hclk_cpu>;
+
+                                       /*
+                                        * g_ap: gate_aclk_peri_...
+                                        * g_hp: gate_hclk_peri_...
+                                        * g_pp: gate_pclk_peri_...
+                                        */
+                                       clock-output-names =
+                                               "g_hp_axi_matrix",      "g_pp_axi_matrix",
+                                               "g_a_cpu_peri",         "g_ap_axi_matrix",
+
+                                               "g_a_peri_niu",         "g_h_usb_peri",
+                                               "g_hp_ahb_arbi",        "g_h_emem_peri",
+
+                                               "g_h_cpubus",           "g_h_ahb2apb",
+                                               "g_a_strc_sys",         "reserved",
+
+                                               "g_a_intmem",           "reserved",
+                                               "g_h_imem1",            "g_h_imem0";
+
+                                       #clock-cells = <1>;
+                               };
+
+                               clk_gates5: gate-clk@200000e4 {
+                                       compatible = "rockchip,rk3188-gate-clk";
+                                       reg = <0x200000e4 0x4>;
+                                       clocks = <&aclk_cpu>,           <&aclk_peri>,
+                                                <&pclk_cpu>,           <&pclk_cpu>,
+
+                                                <&pclk_cpu>,           <&pclk_cpu>,
+                                                <&hclk_cpu>,           <&pclk_cpu>,
+
+                                                <&aclk_peri>,          <&hclk_peri>,
+                                                <&hclk_peri>,          <&hclk_peri>,
+
+                                                <&hclk_peri>,          <&hclk_peri>;
+
+                                       clock-output-names =
+                                               "g_a_dmac1",            "g_a_dmac2",
+                                               "g_p_efuse",            "g_p_tzpc",
+
+                                               "g_p_grf",              "g_p_pmu",
+                                               "g_h_rom",              "g_p_ddrupctl",
+
+                                               "g_a_smc",              "g_h_nandc",
+                                               "g_h_sdmmc0",           "g_h_sdio",
+
+                                               "g_h_emmc",             "g_h_otg0";
+
+                                       #clock-cells = <1>;
+                               };
+
+                               clk_gates6: gate-clk@200000e8 {
+                                       compatible = "rockchip,rk3188-gate-clk";
+                                       reg = <0x200000e8 0x4>;
+                                       clocks = <&clk_gates6 13>,      <&hclk_cpu>,
+                                                <&hclk_cpu>,           <&clk_gates9 5>,
+
+                                                <&hclk_cpu>,           <&clk_gates6 13>,
+                                                <&dummy>,              <&dummy>,
+
+                                                <&clk_gates6 13>,      <&hclk_cpu>,
+                                                <&hclk_cpu>,           <&clk_gates9 5>,
+
+                                                <&hclk_cpu>,           <&aclk_lcdc0>;
+
+                                       clock-output-names =
+                                               "g_a_lcdc0",            "g_h_lcdc0",
+                                               "g_h_lcdc1",            "g_a_lcdc1",
+
+                                               "g_h_cif0",             "g_a_cif0",
+                                               "reserved",             "reserved",
+
+                                               "g_a_ipp",              "g_h_ipp",
+                                               "g_h_rga",              "g_a_rga",
+
+                                               "g_h_vio_bus",          "g_a_vio0";
+
+                                       #clock-cells = <1>;
+                               };
+
+                               clk_gates7: gate-clk@200000ec {
+                                       compatible = "rockchip,rk3188-gate-clk";
+                                       reg = <0x200000ec 0x4>;
+                                       clocks = <&hclk_peri>,          <&hclk_cpu>,
+                                                <&hclk_cpu>,           <&hclk_peri>,
+
+                                                <&hclk_peri>,          <&hclk_peri>,
+                                                <&hclk_peri>,          <&pclk_cpu>,
+
+                                                <&dummy>,              <&pclk_cpu>,
+                                                <&pclk_cpu>,           <&pclk_peri>,
+
+                                                <&pclk_peri>,          <&pclk_peri>,
+                                                <&pclk_peri>,          <&pclk_peri>;
+
+                                       clock-output-names =
+                                               "g_h_emac",             "g_h_spdif",
+                                               "g_h_i2s0_2ch",         "g_h_otg1",
+
+                                               "g_h_hsic",             "g_h_hsadc",
+                                               "g_h_pidf",             "g_p_timer0",
+
+                                               "reserved",             "g_p_timer2",
+                                               "g_p_pwm01",            "g_p_pwm23",
+
+                                               "g_p_spi0",             "g_p_spi1",
+                                               "g_p_saradc",           "g_p_wdt";
+
+                                       #clock-cells = <1>;
+                               };
+
+                               clk_gates8: gate-clk@200000f0 {
+                                       compatible = "rockchip,rk3188-gate-clk";
+                                       reg = <0x200000f0 0x4>;
+                                       clocks = <&pclk_ahb2apb>,       <&pclk_ahb2apb>,
+                                                <&pclk_peri>,          <&pclk_peri>,
+
+                                                <&pclk_cpu>,           <&pclk_cpu>,
+                                                <&pclk_peri>,          <&pclk_peri>,
+
+                                                <&pclk_peri>,          <&pclk_cpu>,
+                                                <&pclk_cpu>,           <&pclk_cpu>,
+
+                                                <&pclk_peri>,          <&aclk_peri>;
+
+                                       clock-output-names =
+                                               "g_p_uart0",            "g_p_uart1",
+                                               "g_p_uart2",            "g_p_uart3",
+
+                                               "g_p_i2c0",             "g_p_i2c1",
+                                               "g_p_i2c2",             "g_p_i2c3",
+
+                                               "g_p_i2c4",             "g_p_gpio0",
+                                               "g_p_gpio1",            "g_p_gpio2",
+
+                                               "g_p_gpio3",            "g_a_gps";
+
+                                       #clock-cells = <1>;
+                               };
+
+                               clk_gates9: gate-clk@200000f4 {
+                                       compatible = "rockchip,rk3188-gate-clk";
+                                       reg = <0x200000f4 0x4>;
+                                       clocks = <&clk_core>,           <&pclk_cpu>,
+                                                <&clk_gates0 6>,       <&clk_gates0 6>,
+
+                                                <&clk_core>,           <&aclk_lcdc1>,
+                                                <&pclk_cpu>,           <&clk_gpu>;
+
+                                       clock-output-names =
+                                               "g_clk_core_dbg",       "g_p_dbg",
+                                               "g_clk_trace",          "g_atclk",
+
+                                               "g_clk_l2c",            "g_a_vio1",
+                                               "g_p_ddrpubl",          "g_a_gpu";
+
+                                       #clock-cells = <1>;
+                               };
+                       };
+               };
+       };
+};
index 390a413d41f1300c557cb1c69093cf83ebb433f5..845505349275feb7cc967275e034db3c3bdce430 100644 (file)
@@ -1,6 +1,7 @@
 /dts-v1/;
 
 #include "rk3188.dtsi"
+#include "rk3188-clocks.dtsi"
 
 / {
        memory {
index 137d3e730f866a2f10f75701801179c53d6340a5..5eca625228fd9e8e884b6cf1d3ae6cae94f53877 100644 (file)
@@ -20,6 +20,7 @@ obj-$(CONFIG_ARCH_U300)               += clk-u300.o
 obj-$(CONFIG_COMMON_CLK_VERSATILE) += versatile/
 obj-$(CONFIG_ARCH_PRIMA2)      += clk-prima2.o
 obj-$(CONFIG_PLAT_ORION)       += mvebu/
+obj-$(CONFIG_ARCH_ROCKCHIP)    += rockchip/
 ifeq ($(CONFIG_COMMON_CLK), y)
 obj-$(CONFIG_ARCH_MMP)         += mmp/
 endif
index 6d9674160430db7456b58c1697272a801b68ba52..2c066abb2452282811deb75f9004c10e4c5ea4cd 100644 (file)
@@ -220,6 +220,7 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
        val = readl(divider->reg);
        val &= ~(div_mask(divider) << divider->shift);
        val |= value << divider->shift;
+       val |= (div_mask(divider) << (divider->shift + 16));
        writel(val, divider->reg);
 
        if (divider->lock)
index 25b1734560d0c99bbae8455c78d22d613ccb99c0..09f9e1e9f2de0956f5e9c2a93c8f6f5d8c8d3723 100644 (file)
@@ -89,6 +89,8 @@ static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
        val = readl(mux->reg);
        val &= ~(mux->mask << mux->shift);
        val |= index << mux->shift;
+       val |= (mux->mask << (mux->shift + 16));
+
        writel(val, mux->reg);
 
        if (mux->lock)
diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile
new file mode 100644 (file)
index 0000000..ae86b42
--- /dev/null
@@ -0,0 +1,2 @@
+obj-y  += clk.o
+obj-y  += clk-ops.o
diff --git a/drivers/clk/rockchip/clk-ops.c b/drivers/clk/rockchip/clk-ops.c
new file mode 100644 (file)
index 0000000..bf9bc8f
--- /dev/null
@@ -0,0 +1,814 @@
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/io.h>
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/clk-private.h>
+#include "clk-ops.h"
+#include <linux/delay.h>
+
+/* mux_ops */
+struct clk_ops_table rk_clk_mux_ops_table[] = {
+       {.index = CLKOPS_TABLE_END},
+};
+
+#define to_clk_divider(_hw) container_of(_hw, struct clk_divider, hw)
+#define div_mask(d)    ((1 << ((d)->width)) - 1)
+
+#define MHZ    (1000 * 1000)
+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;
+}
+
+static int clk_fracdiv_get_config(unsigned long rate_out, unsigned long rate,
+               u32 *numerator, u32 *denominator)
+{
+       u32 gcd_val;
+       gcd_val = clk_gcd(rate, rate_out);
+       clk_debug("%s: frac_get_seting rate=%lu, parent=%lu, gcd=%d\n",
+                       __func__, rate_out, rate, gcd_val);
+
+       if (!gcd_val) {
+               clk_err("gcd=0, i2s frac div is not be supported\n");
+               return -EINVAL;
+       }
+
+       *numerator = rate_out / gcd_val;
+       *denominator = rate / gcd_val;
+
+       clk_debug("%s: frac_get_seting numerator=%d, denominator=%d, times=%d\n",
+                       __func__, *numerator, *denominator,
+                       *denominator / *numerator);
+
+       if (*numerator > 0xffff || *denominator > 0xffff ||
+                       (*denominator / (*numerator)) < 20) {
+               clk_err("can't get a available nume and deno\n");
+               return -EINVAL;
+       }
+
+       return 0;
+
+}
+
+static int clk_fracdiv_set_rate(struct clk_hw *hw, unsigned long rate,
+               unsigned long parent_rate)
+{
+       u32 numerator, denominator;
+       struct clk_divider *div = to_clk_divider(hw);
+
+       struct clk *clk_parent = hw->clk->parent;
+       if(clk_fracdiv_get_config(rate, parent_rate,
+                               &numerator, &denominator) == 0) {
+
+               clk_parent->ops->set_rate(clk_parent->hw,
+                               clk_parent->parent->rate,
+                               clk_parent->parent->rate);
+               writel(numerator << 16 | denominator, div->reg);
+               clk_err("%s set rate=%lu,is ok\n", hw->clk->name, rate);
+
+       } else {
+               clk_err("clk_frac_div can't get rate=%lu,%s\n",
+                               rate, hw->clk->name);
+               return -ENOENT;
+       }
+       return 0;
+}
+
+static unsigned long clk_fracdiv_recalc(struct clk_hw *hw,
+               unsigned long parent_rate)
+{
+       unsigned long rate;
+       u64 rate64;
+       struct clk_divider *div = to_clk_divider(hw);
+       u32 numerator, denominator, reg_val;
+       reg_val = readl(div->reg);
+       if (reg_val == 0)
+               return parent_rate;
+       numerator = reg_val >> 16;
+       denominator = reg_val & 0xFFFF;
+       rate64 = (u64)parent_rate * numerator;
+       do_div(rate64, denominator);
+       rate = rate64;
+       clk_debug("%s: %s new clock rate is %lu, prate %lu (frac %u/%u)\n",
+                       __func__, hw->clk->name, rate, parent_rate,
+                       numerator, denominator);
+       return rate;
+}
+static long clk_fracdiv_round_rate(struct clk_hw *hw, unsigned long rate,
+               unsigned long *prate)
+{
+       return rate;
+}
+/*************************************************************************/
+/* rate_ops */
+#define PARENTS_NUM_MAX 3
+/*
+ * get the best rate from array of available rates, regarding rate which is smaller than
+ * and most close to the set_rate as the best.
+ */
+static long get_best_rate(unsigned long array[],unsigned int num, int *n, long rate)
+{
+       int i = 0;
+       unsigned long best_rate = 0;
+
+       for(i = 0;  i < num;  i++){
+               if(array[i] == rate){
+                       *n = i;
+                       return array[i];
+               }else if((array[i] < rate) && (array[i] > best_rate)){
+                       best_rate = array[i];
+                       *n = i;
+               }
+       }
+
+       if(best_rate == 0){
+               clk_err("NOT array rate is <= %lu\n", rate);
+       }else{
+               clk_debug("get the best available rate,but it != %lu  you want to set!\n", rate);
+       }
+
+       return best_rate;
+}
+
+static struct clk *clk_get_best_parent(struct clk_hw *hw, unsigned long rate,
+               unsigned int *div_out)
+{
+       struct clk *clk = hw->clk;
+       u32 div[PARENTS_NUM_MAX] = {0};
+       unsigned long new_rate[PARENTS_NUM_MAX] = {0};
+       unsigned long best_rate;
+       u32 i;
+
+       memset(div, 0, sizeof(div));
+       memset(new_rate, 0, sizeof(new_rate));
+
+       if(clk->rate == rate)
+               return clk->parent;
+
+       for(i = 0; i < clk->num_parents; i++) {
+               new_rate[i] = clk_divider_ops.round_rate(hw, rate,
+                               &(clk->parents[i]->rate));
+               div[i] = (clk->parents[i]->rate)/new_rate[i];
+               if(new_rate[i] == rate) {
+                       *div_out = div[i];
+                       return clk->parents[i];
+               }
+       }
+
+       best_rate = get_best_rate(new_rate, PARENTS_NUM_MAX, &i, rate);
+       if(best_rate == 0){
+               clk_err("NOT rate is good!\n");
+               return NULL;
+       }
+
+       *div_out = div[i];
+
+       return clk->parents[i];
+}
+
+static long clk_div_round_rate_autosel_parents(struct clk_hw *hw,
+               unsigned long rate, unsigned long *prate)
+{
+       struct clk *clk = hw->clk;
+       struct clk *new_parent;
+       int new_div;
+
+       if(clk->rate == rate)
+               return rate;
+
+       new_parent = clk_get_best_parent(hw, rate, &new_div);
+       if(!new_parent || (new_div <= 0)){
+               clk_err("%s: clk %s could not get new_parent or new_div\n",
+                               __func__,clk->name);
+               return -EINVAL;
+       }
+
+       return (new_parent->rate)/new_div;
+}
+
+
+static int clk_div_set_rate_autosel_parents(struct clk_hw *hw,
+               unsigned long rate, unsigned long parent_rate)
+{
+       //struct clk_divider *divider = to_clk_divider(hw);
+       struct clk *clk = hw->clk;
+       struct clk *new_parent;
+       unsigned int new_div,old_div;
+       unsigned long new_rate;
+       int ret = 0;
+       u8 index;
+       int i;
+
+       if(clk->rate == rate)
+               goto out;
+
+       new_parent = clk_get_best_parent(hw, rate, &new_div);
+       if(!new_parent || (new_div == 0)){
+               clk_err("%s: clk %s could not get new_parent or get "
+                               "new_div = 0\n", __func__,clk->name);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       old_div = (clk->parent->rate)/(clk->rate);
+
+       clk_debug("%s:%d: %s: %lu\n", __func__, __LINE__,
+                       clk->parent->name, new_parent->rate);
+       if(new_div > old_div){
+               new_rate = (clk->parent->rate)/new_div;
+               ret = clk_divider_ops.set_rate(hw, new_rate,
+                               (clk->parent->rate));
+               if(ret)
+                       goto out;
+       }
+
+       if(clk->parent != new_parent){
+               for(i=0; i<clk->num_parents; i++){
+                       if(new_parent == clk->parents[i]){
+                               index = i;
+                               break;
+                       }
+               }
+               /*
+                * ret = clk->ops->set_parent(clk->hw, index);
+                * if(ret)
+                *         goto out;
+                */
+               clk_set_parent(clk, new_parent);
+               clk->ops->recalc_rate(clk->hw, clk->parent->rate);
+       }
+
+       if(new_div <= old_div){
+               new_rate = (clk->parent->rate)/new_div;
+               ret = clk_divider_ops.set_rate(hw, new_rate,
+                               (clk->parent->rate));
+               if(ret)
+                       goto out;
+       }
+
+out:
+       return ret;
+}
+
+static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
+               unsigned long parent_rate)
+{
+       return clk_divider_ops.recalc_rate(hw, hw->clk->parent->rate);
+}
+
+const struct clk_ops clkops_rate_auto_parent = {
+       .recalc_rate    = clk_divider_recalc_rate,
+       .round_rate     = clk_div_round_rate_autosel_parents,
+       .set_rate       = clk_div_set_rate_autosel_parents,
+};
+
+static long clk_div_round_rate_even(struct clk_hw *hw, unsigned long rate,
+               unsigned long *prate)
+{
+       int i = 0;
+       struct clk_divider *divider =to_clk_divider(hw);
+       int max_div = 1 << divider->width;
+
+       for (i = 1; i < max_div; i++) {
+               if (i > 1 && (i % 2 != 0))
+                       continue;
+               if (rate >= *prate / i)
+                       return *prate / i;
+       }
+       return -EINVAL;
+}
+
+static int clk_div_set_rate_even(struct clk_hw *hw, unsigned long rate,
+               unsigned long parent_rate)
+{
+       return clk_divider_ops.set_rate(hw, rate, hw->clk->parent->rate);
+}
+
+const struct clk_ops clkops_rate_evendiv = {
+       .recalc_rate    = clk_divider_recalc_rate,
+       .round_rate     = clk_div_round_rate_even,
+       .set_rate       = clk_div_set_rate_even,
+};
+
+static long dclk_lcdc_round_rate(struct clk_hw *hw, unsigned long rate,
+               unsigned long *prate)
+{
+       long ret = 0;
+       if (rate == 27 * MHZ) {
+               ret = clk_div_round_rate_autosel_parents(hw, rate, prate);
+       } else {
+               ret = clk_div_round_rate_autosel_parents(hw, rate, prate);
+       }
+       return ret;
+}
+
+static int dclk_lcdc_set_rate(struct clk_hw *hw, unsigned long rate,
+               unsigned long parent_rate)
+{
+       return clk_div_set_rate_autosel_parents(hw, rate, parent_rate);
+}
+
+const struct clk_ops clkops_rate_dclk_lcdc = {
+       .recalc_rate    = clk_divider_recalc_rate,
+       .round_rate     = dclk_lcdc_round_rate,
+       .set_rate       = dclk_lcdc_set_rate,
+};
+
+#define CIF_OUT_SRC_DIV        (0x0)
+#define CIF_OUT_SRC_24M        (0x1)
+
+static unsigned long cif_out_recalc_rate(struct clk_hw *hw,
+               unsigned long parent_rate)
+{
+       return hw->clk->parent->rate;
+}
+
+static long cif_out_round_rate(struct clk_hw *hw, unsigned long rate,
+               unsigned long *prate)
+{
+       struct clk *clk = hw->clk;
+       struct clk *parent;
+
+       if (rate == clk->parents[CIF_OUT_SRC_24M]->rate) {
+               return rate;
+       } else {
+               parent = clk->parents[CIF_OUT_SRC_DIV];
+               return parent->ops->round_rate(parent->hw, rate,
+                               &(parent->parent->rate));
+       }
+}
+
+static int cif_out_set_rate(struct clk_hw *hw, unsigned long rate,
+               unsigned long parent_rate)
+{
+       struct clk *clk = hw->clk;
+       struct clk *parent;
+       int ret = 0;
+
+       if (rate == clk->parents[CIF_OUT_SRC_24M]->rate) {
+               parent = clk->parents[CIF_OUT_SRC_24M];
+       } else {
+               parent = clk->parents[CIF_OUT_SRC_DIV];
+               ret = parent->ops->set_rate(parent->hw, rate,
+                               parent->parent->rate);
+               if (ret)
+                       goto out;
+               else
+                       parent->rate = rate;
+       }
+
+       if(clk->parent != parent){
+               ret = clk_set_parent(clk, parent);
+#if 0
+               for(i=0; i<clk->num_parents; i++){
+                       if(parent == clk->parents[i]){
+                               index = i;
+                               break;
+                       }
+               }
+               ret = clk->ops->set_parent(clk->hw, index);
+#endif
+               if(ret)
+                       goto out;
+       }
+out:
+       return ret;
+}
+const struct clk_ops clkops_rate_cif_out = {
+       .recalc_rate    = cif_out_recalc_rate,
+       .round_rate     = cif_out_round_rate,
+       .set_rate       = cif_out_set_rate,
+};
+
+static int clk_i2s_fracdiv_set_rate(struct clk_hw *hw, unsigned long rate,
+               unsigned long parent_rate)
+{
+       u32 numerator, denominator;
+       struct clk *clk_parent;
+       int i = 10;
+       struct clk_divider *div = to_clk_divider(hw);
+
+       clk_parent = hw->clk->parent;
+       if(clk_fracdiv_get_config(rate, parent_rate,
+                               &numerator, &denominator) == 0) {
+
+               clk_parent->ops->set_rate(clk_parent->hw,
+                               clk_parent->parent->rate,
+                               clk_parent->parent->rate);
+               while (i--) {
+                       writel((numerator - 1) << 16 | denominator, div->reg);
+                       mdelay(1);
+                       writel(numerator << 16 | denominator, div->reg);
+                       mdelay(1);
+               }
+               clk_err("%s set rate=%lu,is ok\n", hw->clk->name, rate);
+
+       } else {
+               clk_err("%s: can't get rate=%lu,%s\n", __func__, rate, hw->clk->name);
+               return -ENOENT;
+       }
+       return 0;
+}
+
+const struct clk_ops clkops_rate_i2s_frac = {
+       .recalc_rate    = clk_fracdiv_recalc,
+       .round_rate     = clk_fracdiv_round_rate,
+       .set_rate       = clk_i2s_fracdiv_set_rate,
+};
+static unsigned long clk_i2s_recalc_rate(struct clk_hw *hw,
+               unsigned long parent_rate)
+{
+       return hw->clk->parent->rate;
+}
+
+static long clk_i2s_round_rate(struct clk_hw *hw, unsigned long rate,
+               unsigned long *prate)
+{
+       return rate;
+}
+
+#define I2S_SRC_DIV    (0x0)
+#define I2S_SRC_FRAC   (0x1)
+#define I2S_SRC_12M    (0x2)
+static int clk_i2s_set_rate(struct clk_hw *hw, unsigned long rate,
+               unsigned long parent_rate)
+{
+       int ret = -EINVAL;
+       u8 p_index = 0;
+       struct clk *parent_tmp, *parent;
+       struct clk *clk = hw->clk;
+
+
+       if (rate == clk->parents[I2S_SRC_12M]->rate) {
+               parent = clk->parents[I2S_SRC_12M];
+               p_index = I2S_SRC_12M;
+               goto set_parent;
+       }
+
+       parent_tmp = clk->parents[I2S_SRC_DIV];
+
+       if(parent_tmp->ops->round_rate(parent_tmp->hw, rate,
+                               &parent_tmp->parent->rate) == rate) {
+               parent = clk->parents[I2S_SRC_DIV];
+               p_index = I2S_SRC_DIV;
+               goto set;
+       }
+
+       parent = clk->parents[I2S_SRC_FRAC];
+       p_index = I2S_SRC_FRAC;
+       //ret = clk_set_rate(parent_tmp, parent_tmp->parent->rate);
+       ret = parent_tmp->ops->set_rate(parent_tmp->hw,
+                       parent_tmp->parent->rate,
+                       parent_tmp->parent->rate);
+       parent_tmp->rate = parent_tmp->ops->recalc_rate(parent_tmp->hw,
+                       parent_tmp->parent->rate);
+       //ret = parent->ops->set_rate(parent->hw, rate, parent->parent->rate);
+       if (ret) {
+               clk_debug("%s set rate%lu err\n", clk->name, rate);
+               return ret;
+       }
+
+set:
+       clk_debug(" %s set rate=%lu parent %s(old %s)\n",
+                       clk->name, rate, parent->name, clk->parent->name);
+
+       ret = clk_set_rate(parent, rate);
+       //ret = parent->ops->set_rate(parent->hw, rate, parent->parent->rate);
+       if (ret) {
+               clk_debug("%s set rate%lu err\n", clk->name, rate);
+               return ret;
+       }
+
+set_parent:
+       clk_debug("%s: set parent\n", __func__);
+       if (clk->parent != parent) {
+               ret = clk_set_parent(clk, parent);
+               /*
+                * clk->ops->set_parent(hw, p_index);
+                */
+               if (ret) {
+                       clk_debug("%s can't get rate%lu,reparent err\n",
+                                       clk->name, rate);
+                       return ret;
+               }
+       }
+
+       return ret;
+}
+
+const struct clk_ops clkops_rate_i2s = {
+       .recalc_rate    = clk_i2s_recalc_rate,
+       .round_rate     = clk_i2s_round_rate,
+       .set_rate       = clk_i2s_set_rate,
+};
+const struct clk_ops clkops_rate_hsadc_frac = {
+       .recalc_rate    = clk_fracdiv_recalc,
+       .round_rate     = clk_fracdiv_round_rate,
+       .set_rate       = clk_fracdiv_set_rate,
+};
+
+
+const struct clk_ops clkops_rate_uart_frac = {
+       .recalc_rate    = clk_fracdiv_recalc,
+       .round_rate     = clk_fracdiv_round_rate,
+       .set_rate       = clk_fracdiv_set_rate,
+};
+
+static unsigned long clk_uart_recalc_rate(struct clk_hw *hw,
+               unsigned long parent_rate)
+{
+       return hw->clk->parent->rate;
+
+}
+static long clk_uart_round_rate(struct clk_hw *hw, unsigned long rate,
+               unsigned long *prate)
+{
+       return rate;
+}
+
+#define UART_SRC_DIV   (0x0)
+#define UART_SRC_FRAC  (0x1)
+#define UART_SRC_24M   (0x2)
+static int clk_uart_set_rate(struct clk_hw *hw, unsigned long rate,
+               unsigned long parent_rate)
+{
+       int ret = -EINVAL;
+       u8 p_index = 0;
+       struct clk *parent_tmp, *parent;
+       struct clk *clk = hw->clk;
+
+
+       if (rate == clk->parents[UART_SRC_24M]->rate) {
+               parent = clk->parents[UART_SRC_24M];
+               p_index = UART_SRC_24M;
+               goto set_parent;
+       }
+
+       parent_tmp = clk->parents[UART_SRC_DIV];
+
+       if(parent_tmp->ops->round_rate(parent_tmp->hw, rate,
+                               &parent_tmp->parent->rate) == rate) {
+               parent = clk->parents[UART_SRC_DIV];
+               p_index = UART_SRC_DIV;
+               goto set;
+       }
+
+       parent = clk->parents[UART_SRC_FRAC];
+       p_index = UART_SRC_FRAC;
+       /*
+        * ret = clk_set_rate(parent_tmp, parent_tmp->parent->rate);
+        */
+       ret = parent_tmp->ops->set_rate(parent_tmp->hw,
+                       parent_tmp->parent->rate,
+                       parent_tmp->parent->rate);
+       parent_tmp->rate = parent_tmp->ops->recalc_rate(parent_tmp->hw,
+                       parent_tmp->parent->rate);
+       //ret = parent->ops->set_rate(parent->hw, rate, parent->parent->rate);
+       if (ret) {
+               clk_debug("%s set rate%lu err\n", clk->name, rate);
+               return ret;
+       }
+
+set:
+       clk_debug(" %s set rate=%lu parent %s(old %s)\n",
+                       clk->name, rate, parent->name, clk->parent->name);
+
+       ret = clk_set_rate(parent, rate);
+       //ret = parent->ops->set_rate(parent->hw, rate, parent->parent->rate);
+       if (ret) {
+               clk_debug("%s set rate%lu err\n", clk->name, rate);
+               return ret;
+       }
+
+set_parent:
+       clk_debug("%s: set parent\n", __func__);
+       if (clk->parent != parent) {
+               ret = clk_set_parent(clk, parent);
+               /*
+                * clk->ops->set_parent(hw, p_index);
+                */
+               if (ret) {
+                       clk_debug("%s can't get rate%lu,reparent err\n",
+                                       clk->name, rate);
+                       return ret;
+               }
+       }
+
+       return ret;
+}
+
+
+const struct clk_ops clkops_rate_uart = {
+       .recalc_rate    = clk_uart_recalc_rate,
+       .round_rate     = clk_uart_round_rate,
+       .set_rate       = clk_uart_set_rate,
+};
+
+
+static unsigned long clk_hsadc_recalc_rate(struct clk_hw *hw,
+               unsigned long parent_rate)
+{
+       return hw->clk->parent->rate;
+
+}
+static long clk_hsadc_round_rate(struct clk_hw *hw, unsigned long rate,
+               unsigned long *prate)
+{
+       return rate;
+}
+
+#define HSADC_SRC_DIV  (0x0)
+#define HSADC_SRC_FRAC (0x1)
+#define HSADC_SRC_EXT  (0x2)
+static int clk_hsadc_set_rate(struct clk_hw *hw, unsigned long rate,
+               unsigned long parent_rate)
+{
+       int ret = -EINVAL;
+       u8 p_index = 0;
+       struct clk *parent_tmp, *parent;
+       struct clk *clk = hw->clk;
+
+
+       if (rate == clk->parents[HSADC_SRC_EXT]->rate) {
+               parent = clk->parents[HSADC_SRC_EXT];
+               p_index = HSADC_SRC_EXT;
+               goto set_parent;
+       }
+
+       parent_tmp = clk->parents[HSADC_SRC_DIV];
+
+       if(parent_tmp->ops->round_rate(parent_tmp->hw, rate,
+                               &parent_tmp->parent->rate) == rate) {
+               parent = clk->parents[HSADC_SRC_DIV];
+               p_index = HSADC_SRC_DIV;
+               goto set;
+       }
+
+       parent = clk->parents[HSADC_SRC_FRAC];
+       p_index = HSADC_SRC_FRAC;
+       /*
+        * ret = clk_set_rate(parent_tmp, parent_tmp->parent->rate);
+        */
+       ret = parent_tmp->ops->set_rate(parent_tmp->hw,
+                       parent_tmp->parent->rate,
+                       parent_tmp->parent->rate);
+       parent_tmp->rate = parent_tmp->ops->recalc_rate(parent_tmp->hw,
+                       parent_tmp->parent->rate);
+       //ret = parent->ops->set_rate(parent->hw, rate, parent->parent->rate);
+       if (ret) {
+               clk_debug("%s set rate%lu err\n", clk->name, rate);
+               return ret;
+       }
+
+
+
+set:
+       clk_debug(" %s set rate=%lu parent %s(old %s)\n",
+                       clk->name, rate, parent->name, clk->parent->name);
+
+       ret = clk_set_rate(parent, rate);
+       //ret = parent->ops->set_rate(parent->hw, rate, parent->parent->rate);
+       if (ret) {
+               clk_debug("%s set rate%lu err\n", clk->name, rate);
+               return ret;
+       }
+
+set_parent:
+       clk_debug("%s: set parent\n", __func__);
+       if (clk->parent != parent) {
+               ret = clk_set_parent(clk, parent);
+               /*
+                * clk->ops->set_parent(hw, p_index);
+                */
+               if (ret) {
+                       clk_debug("%s can't get rate%lu,reparent err\n",
+                                       clk->name, rate);
+                       return ret;
+               }
+       }
+
+       return ret;
+}
+
+
+const struct clk_ops clkops_rate_hsadc = {
+       .recalc_rate    = clk_hsadc_recalc_rate,
+       .round_rate     = clk_hsadc_round_rate,
+       .set_rate       = clk_hsadc_set_rate,
+};
+
+static unsigned long clk_mac_ref_recalc_rate(struct clk_hw *hw,
+               unsigned long parent_rate)
+{
+       return hw->clk->parent->rate;
+
+}
+static long clk_mac_ref_round_rate(struct clk_hw *hw, unsigned long rate,
+               unsigned long *prate)
+{
+       return rate;
+}
+
+#define MAC_SRC_DIV    (0x0)
+#define RMII_CLKIN     (0x1)
+static int clk_mac_ref_set_rate(struct clk_hw *hw, unsigned long rate,
+               unsigned long parent_rate)
+{
+       int ret = -EINVAL;
+       u8 p_index = 0;
+       struct clk *parent;
+       struct clk *clk = hw->clk;
+
+       clk_debug("%s: rate %lu\n", __func__, rate);
+
+       if (rate == clk->parents[RMII_CLKIN]->rate) {
+               parent = clk->parents[RMII_CLKIN];
+               p_index = RMII_CLKIN;
+               goto set_parent;
+       }
+
+       parent = clk->parents[MAC_SRC_DIV];
+       p_index = MAC_SRC_DIV;
+
+       clk_debug(" %s set rate=%lu parent %s(old %s)\n",
+                       clk->name, rate, parent->name, clk->parent->name);
+
+       /*
+        * ret = clk_set_rate(parent, rate);
+        */
+       ret = parent->ops->set_rate(parent->hw,
+                       rate,
+                       parent->parent->rate);
+       parent->rate = parent->ops->recalc_rate(parent->hw,
+                       parent->parent->rate);
+       //ret = parent->ops->set_rate(parent->hw, rate, parent->parent->rate);
+       if (ret) {
+               clk_debug("%s set rate%lu err\n", clk->name, rate);
+               return ret;
+       }
+
+set_parent:
+       clk_debug("%s: set parent\n", __func__);
+       if (clk->parent != parent) {
+               ret = clk_set_parent(clk, parent);
+               /*
+                * clk->ops->set_parent(hw, p_index);
+                */
+               if (ret) {
+                       clk_debug("%s can't get rate%lu,reparent err\n",
+                                       clk->name, rate);
+                       return ret;
+               }
+       }
+
+       return ret;
+}
+
+
+const struct clk_ops clkops_rate_mac_ref = {
+       .recalc_rate    = clk_mac_ref_recalc_rate,
+       .round_rate     = clk_mac_ref_round_rate,
+       .set_rate       = clk_mac_ref_set_rate,
+};
+
+
+struct clk_ops_table rk_clkops_rate_table[] = {
+       {.index = CLKOPS_RATE_MUX_DIV,          .clk_ops = &clkops_rate_auto_parent},
+       {.index = CLKOPS_RATE_EVENDIV,          .clk_ops = &clkops_rate_evendiv},
+       {.index = CLKOPS_RATE_DCLK_LCDC,        .clk_ops = &clkops_rate_dclk_lcdc},
+       {.index = CLKOPS_RATE_CIFOUT,           .clk_ops = &clkops_rate_cif_out},
+       {.index = CLKOPS_RATE_I2S_FRAC,         .clk_ops = &clkops_rate_i2s_frac},
+       {.index = CLKOPS_RATE_I2S,              .clk_ops = &clkops_rate_i2s},
+       {.index = CLKOPS_RATE_HSADC_FRAC,       .clk_ops = &clkops_rate_hsadc_frac},
+       {.index = CLKOPS_RATE_UART_FRAC,        .clk_ops = &clkops_rate_uart_frac},
+       {.index = CLKOPS_RATE_UART,             .clk_ops = &clkops_rate_uart},
+       {.index = CLKOPS_RATE_HSADC,            .clk_ops = &clkops_rate_hsadc},
+       {.index = CLKOPS_RATE_MAC_REF,          .clk_ops = &clkops_rate_mac_ref},
+
+
+       {.index = CLKOPS_TABLE_END},
+};
+const struct clk_ops *rk_get_clkops(u32 idx)
+{
+       return rk_clkops_rate_table[idx].clk_ops;
+}
+EXPORT_SYMBOL_GPL(rk_get_clkops);
diff --git a/drivers/clk/rockchip/clk-ops.h b/drivers/clk/rockchip/clk-ops.h
new file mode 100644 (file)
index 0000000..e3bfb85
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef __RK_CLK_OPS_H
+#define __RK_CLK_OPS_H
+#include "clkops-dtsi.h"
+struct clk_ops_table {
+       unsigned int            index;
+       const struct clk_ops    *clk_ops;
+};
+const struct clk_ops *rk_get_clkops(u32 idx);
+//#define RKCLK_DEBUG
+
+#if defined(RKCLK_DEBUG)
+#define clk_debug(fmt, args...) printk(KERN_INFO "rkclk: "fmt, ##args)
+#else
+#define clk_debug(fmt, args...) do {} while(0)
+#endif
+
+#define clk_err(fmt, args...) printk(KERN_ERR "rkclk: "fmt, ##args)
+
+#endif /* __RK_CLKOPS_H */
diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c
new file mode 100644 (file)
index 0000000..c058683
--- /dev/null
@@ -0,0 +1,924 @@
+/*
+ * Copyright (C) 2013 ROCKCHIP, Inc.
+ * Author: chenxing <chenxing@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include "clk-ops.h"
+#include <linux/clk-private.h>
+#include <asm/io.h>
+
+static DEFINE_SPINLOCK(clk_lock);
+
+struct rkclk_divmap_table {
+       u32             reg_val;
+       u32             div_val;
+};
+
+struct rkclk_divinfo {
+       struct clk_divider *div;
+       void __iomem    *addr;
+       u32             shift;
+       u32             width;
+       u32             div_type;
+       u32             max_div;
+       u32             fixed_div_val;
+       u32             clkops_idx;
+       const char      *clk_name;
+       const char      *parent_name;
+       struct clk_div_table            *div_table;
+       struct list_head                node;
+};
+
+struct rkclk_muxinfo {
+       struct clk_mux  *mux;
+       void __iomem    *addr;
+       u32             shift;
+       u32             width;
+       u32             parent_num;
+       u32             clkops_idx;
+       const char      *clk_name;
+       const char      **parent_names;
+       struct list_head        node;
+};
+
+struct rkclk_fracinfo {
+       struct clk_hw   hw;
+       void __iomem    *addr;
+       u32             shift;
+       u32             width;
+       u32             frac_type;
+       u32             clkops_idx;
+       const char      *clk_name;
+       const char      *parent_name;
+       struct list_head        node;
+};
+
+struct rkclk_gateinfo {
+       struct clk_gate *gate;
+       void __iomem    *addr;
+       u32             shift;
+       u32             clkops_idx;
+       const char      *clk_name;
+       const char      *parent_name;
+};
+
+struct rkclk_pllinfo {
+       struct clk_hw   hw;
+       struct clk_ops  *ops;
+       void __iomem    *addr;
+       u32             width;
+       const char      *clk_name;
+       const char      *parent_name;
+       /*
+        * const char   **clkout_names;
+        */
+       struct list_head        node;
+};
+
+struct rkclk {
+       const char      *clk_name;
+       u32             clk_type;
+       /*
+        * store nodes creat this rkclk
+        * */
+       struct device_node              *np;
+       struct rkclk_pllinfo            *pll_info;
+       struct rkclk_muxinfo            *mux_info;
+       struct rkclk_divinfo            *div_info;
+       struct rkclk_fracinfo           *frac_info;
+       struct rkclk_gateinfo           *gate_info;
+       struct list_head                node;
+};
+
+LIST_HEAD(rk_clks);
+void __iomem *reg_start = 0;
+#define RKCLK_PLL_TYPE (1 << 0)
+#define RKCLK_MUX_TYPE (1 << 1)
+#define RKCLK_DIV_TYPE (1 << 2)
+#define RKCLK_FRAC_TYPE        (1 << 3)
+#define RKCLK_GATE_TYPE        (1 << 4)
+
+#define CLK_GATE_SET_TO_DISABLE                BIT(0)
+#define CLK_GATE_HIWORD_MASK           BIT(1)
+static int rkclk_init_muxinfo(struct device_node *np,
+               struct rkclk_muxinfo *mux, void __iomem *addr)
+{
+       int cnt, i, ret = 0;
+       u8 found = 0;
+       struct rkclk *rkclk;
+
+       mux = kzalloc(sizeof(struct rkclk_muxinfo), GFP_KERNEL);
+       if (!mux)
+               return -ENOMEM;
+       /*
+        * Get control bit addr
+        */
+       ret = of_property_read_u32_index(np, "rockchip,bits", 0, &mux->shift);
+       if (ret != 0)
+               return -EINVAL;
+       ret = of_property_read_u32(np, "rockchip,clkops-idx", &mux->clkops_idx);
+       if (ret != 0)
+               mux->clkops_idx = CLKOPS_TABLE_END;
+
+       ret = of_property_read_u32_index(np, "rockchip,bits", 1, &mux->width);
+       if (ret != 0)
+               return -EINVAL;
+       mux->addr = addr;
+
+       ret = of_property_read_string(np, "clock-output-names", &mux->clk_name);
+       if (ret != 0)
+               return -EINVAL;
+
+       /*
+        * Get parents' cnt
+        */
+       cnt = of_count_phandle_with_args(np, "clocks", "#clock-cells");
+       if (cnt< 0)
+               return -EINVAL;
+
+       mux->parent_num = cnt;
+       mux->parent_names = kzalloc(cnt * sizeof(char *), GFP_KERNEL);
+
+       clk_debug("%s: parent cnt = %d\n", __func__, cnt);
+       for (i = 0; i < cnt ; i++) {
+
+               mux->parent_names[i] = of_clk_get_parent_name(np, i);
+       }
+
+       found = 0;
+       list_for_each_entry(rkclk, &rk_clks, node) {
+               if (strcmp(mux->clk_name, rkclk->clk_name) == 0) {
+                       if (rkclk->mux_info != NULL)
+                               clk_err("%s(%d): This clk(%s) has been used\n",
+                                               __func__, __LINE__, mux->clk_name);
+                       clk_debug("%s: find match %s\n", __func__, rkclk->clk_name);
+                       found = 1;
+                       rkclk->mux_info = mux;
+                       rkclk->clk_type |= RKCLK_MUX_TYPE;
+                       break;
+               }
+       }
+       if (!found) {
+               rkclk = kzalloc(sizeof(struct rkclk), GFP_KERNEL);
+               rkclk->clk_name = mux->clk_name;
+               rkclk->mux_info = mux;
+               rkclk->clk_type |= RKCLK_MUX_TYPE;
+               rkclk->np = np;
+               clk_debug("%s: creat %s\n", __func__, rkclk->clk_name);
+
+               list_add_tail(&rkclk->node, &rk_clks);
+       }
+       return 0;
+}
+static int rkclk_init_divinfo(struct device_node *np,
+               struct rkclk_divinfo *div, void __iomem *addr)
+{
+       int cnt = 0, i = 0, ret = 0;
+       struct rkclk *rkclk;
+       u8 found = 0;
+
+       div = kzalloc(sizeof(struct rkclk_divinfo), GFP_KERNEL);
+       if (!div)
+               return -ENOMEM;
+
+       of_property_read_u32_index(np, "rockchip,bits", 0, &div->shift);
+       of_property_read_u32_index(np, "rockchip,bits", 1, &div->width);
+       div->addr = addr;
+
+       of_property_read_u32(np, "rockchip,div-type", &div->div_type);
+       ret = of_property_read_u32(np, "rockchip,clkops-idx", &div->clkops_idx);
+       if (ret != 0)
+               div->clkops_idx = CLKOPS_TABLE_END;
+
+       cnt = of_property_count_strings(np, "clock-output-names");
+       if (cnt <= 0)
+               div->clk_name = of_clk_get_parent_name(np, 0);
+       else {
+               ret = of_property_read_string(np, "clock-output-names", &div->clk_name);
+               if (ret != 0)
+                       return -EINVAL;
+               div->parent_name = of_clk_get_parent_name(np, 0);
+       }
+
+       switch (div->div_type) {
+               case CLK_DIVIDER_PLUS_ONE:
+               case CLK_DIVIDER_ONE_BASED:
+               case CLK_DIVIDER_POWER_OF_TWO:
+                       break;
+               case CLK_DIVIDER_FIXED:
+                       of_property_read_u32_index(np, "rockchip,div-relations", 0,
+                                       &div->fixed_div_val);
+                       clk_debug("%s:%s fixed_div = %d\n", __func__,
+                                       div->clk_name, div->fixed_div_val);
+                       break;
+               case CLK_DIVIDER_USER_DEFINE:
+                       of_get_property(np, "rockchip,div-relations", &cnt);
+                       cnt /= 4 * 2;
+                       div->div_table = kzalloc(cnt * sizeof(struct clk_div_table),
+                                       GFP_KERNEL);
+
+                       for (i = 0; i < cnt; i++) {
+                               of_property_read_u32_index(np, "rockchip,div-relations", i * 2,
+                                               &div->div_table[i].val);
+                               of_property_read_u32_index(np, "rockchip,div-relations", i * 2 + 1,
+                                               &div->div_table[i].div);
+                               clk_debug("\tGet div table %d: val=%d, div=%d\n",
+                                               i, div->div_table[i].val,
+                                               div->div_table[i].div);
+                       }
+                       break;
+               default:
+                       clk_err("%s: %s: unknown rockchip,div-type, please check dtsi\n",
+                                       __func__, div->clk_name);
+                       break;
+       }
+
+       found = 0;
+       list_for_each_entry(rkclk, &rk_clks, node) {
+               if (strcmp(div->clk_name, rkclk->clk_name) == 0) {
+                       if (rkclk->div_info != NULL)
+                               clk_err("%s(Line %d): This clk(%s) has been used\n",
+                                               __func__, __LINE__, rkclk->clk_name);
+                       clk_debug("%s: find match %s\n", __func__, rkclk->clk_name);
+                       found = 1;
+                       rkclk->div_info = div;
+                       rkclk->clk_type |= RKCLK_DIV_TYPE;
+                       break;
+               }
+       }
+       if (!found) {
+               rkclk = kzalloc(sizeof(struct rkclk), GFP_KERNEL);
+               rkclk->clk_name = div->clk_name;
+               rkclk->div_info = div;
+               rkclk->clk_type |= RKCLK_DIV_TYPE;
+               rkclk->np = np;
+               clk_debug("%s: creat %s\n", __func__, rkclk->clk_name);
+
+               list_add_tail(&rkclk->node, &rk_clks);
+       }
+       return 0;
+
+
+}
+static int rkclk_init_fracinfo(struct device_node *np,
+               struct rkclk_fracinfo *frac, void __iomem *addr)
+{
+       struct rkclk *rkclk;
+       u8 found = 0;
+       int ret = 0;
+
+       frac = kzalloc(sizeof(struct rkclk_fracinfo), GFP_KERNEL);
+       if (!frac)
+               return -ENOMEM;
+
+       of_property_read_u32_index(np, "rockchip,bits", 0, &frac->shift);
+       of_property_read_u32_index(np, "rockchip,bits", 1, &frac->width);
+       frac->addr = addr;
+
+       ret = of_property_read_u32(np, "rockchip,clkops-idx", &frac->clkops_idx);
+       if (ret != 0)
+               frac->clkops_idx = CLKOPS_TABLE_END;
+
+       frac->parent_name = of_clk_get_parent_name(np, 0);
+       ret = of_property_read_string(np, "clock-output-names", &frac->clk_name);
+       if (ret != 0)
+               return -EINVAL;
+
+       found = 0;
+       list_for_each_entry(rkclk, &rk_clks, node) {
+               if (strcmp(frac->clk_name, rkclk->clk_name) == 0) {
+                       if (rkclk->frac_info != NULL)
+                               clk_err("%s(%d): This clk(%s) has been used\n",
+                                               __func__, __LINE__, frac->clk_name);
+                       clk_debug("%s: find match %s\n", __func__, rkclk->clk_name);
+                       found = 1;
+                       rkclk->frac_info = frac;
+                       rkclk->clk_type |= RKCLK_FRAC_TYPE;
+                       break;
+               }
+       }
+       if (!found) {
+               rkclk = kzalloc(sizeof(struct rkclk), GFP_KERNEL);
+               rkclk->clk_name = frac->clk_name;
+               rkclk->frac_info = frac;
+               rkclk->clk_type |= RKCLK_FRAC_TYPE;
+               rkclk->np = np;
+               clk_debug("%s: creat %s\n", __func__, rkclk->clk_name);
+
+               list_add_tail(&rkclk->node, &rk_clks);
+       }
+       return 0;
+}
+
+static int __init rkclk_init_selcon(struct device_node *np)
+{
+       struct device_node *node_con, *node;
+       void __iomem *reg = 0;
+
+       struct rkclk_divinfo *divinfo;
+       struct rkclk_muxinfo *muxinfo;
+       struct rkclk_fracinfo *fracinfo;
+
+       for_each_available_child_of_node(np, node_con) {
+
+               reg = of_iomap(node_con, 0);
+
+               for_each_available_child_of_node(node_con, node) {
+
+                       if (of_device_is_compatible(node, "rockchip,rk3188-div-con"))
+                               rkclk_init_divinfo(node, divinfo, reg);
+
+                       else if (of_device_is_compatible(node, "rockchip,rk3188-mux-con"))
+                               rkclk_init_muxinfo(node, muxinfo, reg);
+
+                       else if (of_device_is_compatible(node, "rockchip,rk3188-frac-con"))
+                               rkclk_init_fracinfo(node, fracinfo, reg);
+
+                       else if (of_device_is_compatible(node, "rockchip,rk3188-inv-con"))
+                               clk_debug("INV clk\n");
+
+                       else
+                               clk_err("%s: unknown controler type, plz check dtsi "
+                                               "or add type support\n", __func__);
+
+               }
+       }
+       return 0;
+}
+
+static int __init rkclk_init_gatecon(struct device_node *np)
+{
+       struct clk_onecell_data *clk_data;
+       struct device_node *node;
+       const char *clk_parent;
+       const char *clk_name;
+       void __iomem *reg;
+       void __iomem *reg_idx;
+       int flags;
+       int cnt;
+       int reg_bit;
+       int clkflags = CLK_SET_RATE_PARENT;
+       int i;
+       struct rkclk_gateinfo *gateinfo;
+       u8 found = 0;
+       struct rkclk *rkclk;
+
+       for_each_available_child_of_node(np, node) {
+               cnt = of_property_count_strings(node, "clock-output-names");
+               if (cnt < 0) {
+                       clk_err("%s: error in clock-output-names %d\n",
+                                       __func__, cnt);
+                       continue;
+               }
+
+               if (cnt == 0) {
+                       pr_info("%s: nothing to do\n", __func__);
+                       continue;
+               }
+
+               reg = of_iomap(node, 0);
+
+               clk_data = kzalloc(sizeof(struct clk_onecell_data), GFP_KERNEL);
+               if (!clk_data)
+                       return -ENOMEM;
+
+               clk_data->clks = kzalloc(cnt * sizeof(struct clk *), GFP_KERNEL);
+               if (!clk_data->clks) {
+                       kfree(clk_data);
+                       return -ENOMEM;
+               }
+
+               flags = CLK_GATE_HIWORD_MASK | CLK_GATE_SET_TO_DISABLE;
+
+               for (i = 0; i < cnt; i++) {
+                       of_property_read_string_index(node, "clock-output-names",
+                                       i, &clk_name);
+
+                       /* ignore empty slots */
+                       if (!strcmp("reserved", clk_name))
+                               continue;
+
+                       clk_parent = of_clk_get_parent_name(node, i);
+
+                       clkflags |= CLK_IGNORE_UNUSED;
+
+                       reg_idx = reg + (4 * (i / 16));
+                       reg_bit = (i % 16);
+
+                       gateinfo = kzalloc(sizeof(struct rkclk_gateinfo), GFP_KERNEL);
+                       gateinfo->clk_name = clk_name;
+                       gateinfo->parent_name = clk_parent;
+                       gateinfo->addr = reg;
+                       gateinfo->shift = reg_bit;
+                       found = 0;
+                       list_for_each_entry(rkclk, &rk_clks, node) {
+                               if (strcmp(clk_name, rkclk->clk_name) == 0) {
+                                       if (rkclk->gate_info != NULL)
+                                               clk_err("%s(%d): This clk(%s) has been used\n",
+                                                               __func__, __LINE__, clk_name);
+                                       clk_debug("%s: find match %s\n", __func__, rkclk->clk_name);
+                                       found = 1;
+                                       rkclk->gate_info = gateinfo;
+                                       rkclk->clk_type |= RKCLK_GATE_TYPE;
+                                       break;
+                               }
+                       }
+                       if (!found) {
+                               rkclk = kzalloc(sizeof(struct rkclk), GFP_KERNEL);
+                               rkclk->clk_name = gateinfo->clk_name;
+                               rkclk->gate_info = gateinfo;
+                               rkclk->clk_type |= RKCLK_GATE_TYPE;
+                               rkclk->np = np;
+                               clk_debug("%s: creat %s\n", __func__, rkclk->clk_name);
+
+                               list_add_tail(&rkclk->node, &rk_clks);
+                       }
+               }
+
+       }
+       return 0;
+}
+static int __init rkclk_init_pllcon(struct device_node *np)
+{
+       struct rkclk_pllinfo *pllinfo;
+       struct device_node *node;
+       struct rkclk *rkclk;
+       void __iomem    *reg;
+       int i = 0;
+       int ret = 0, clknum = 0;
+       u8 found = 0;
+
+       for_each_available_child_of_node(np, node) {
+               clknum = of_property_count_strings(node, "clock-output-names");
+               if (clknum < 0) {
+                       clk_err("%s: error in get clock-output-names numbers = %d\n",
+                                       __func__, clknum);
+                       return -EINVAL;
+               }
+               reg = of_iomap(node, 0);
+               if (reg_start == 0)
+                       reg_start = reg;
+               for (i = 0; i < clknum; i++) {
+                       pllinfo = kzalloc(sizeof(struct rkclk_pllinfo), GFP_KERNEL);
+                       if (!pllinfo)
+                               return -ENOMEM;
+
+                       /*
+                        * Get pll parent name
+                        */
+                       pllinfo->parent_name = of_clk_get_parent_name(node, i);
+
+                       /*
+                        * Get pll output name
+                        */
+                       of_property_read_string_index(node, "clock-output-names",
+                                       i, &pllinfo->clk_name);
+
+                       pllinfo->addr = reg;
+
+                       ret = of_property_read_u32_index(node, "reg", 1, &pllinfo->width);
+                       if (ret != 0) {
+                               clk_err("%s: cat not get reg info\n", __func__);
+                       }
+
+                       clk_debug("%s: parent=%s, pllname=%s, reg =%08x, cnt=%d\n", __func__,
+                                       pllinfo->parent_name, pllinfo->clk_name,
+                                       (u32)pllinfo->addr, pllinfo->width);
+
+                       found = 0;
+                       list_for_each_entry(rkclk, &rk_clks, node) {
+                               if (strcmp(pllinfo->clk_name, rkclk->clk_name) == 0) {
+                                       if (rkclk->pll_info != NULL)
+                                               clk_err("%s(%d): This clk(%s) has been used\n",
+                                                               __func__, __LINE__, pllinfo->clk_name);
+                                       clk_debug("%s: find match %s\n", __func__, rkclk->clk_name);
+                                       found = 1;
+                                       rkclk->pll_info = pllinfo;
+                                       rkclk->clk_type |= RKCLK_PLL_TYPE;
+                                       break;
+                               }
+                       }
+                       if (!found) {
+                               rkclk = kzalloc(sizeof(struct rkclk), GFP_KERNEL);
+                               rkclk->clk_name = pllinfo->clk_name;
+                               rkclk->pll_info = pllinfo;
+                               rkclk->clk_type |= RKCLK_PLL_TYPE;
+                               rkclk->np = np;
+
+                               list_add_tail(&rkclk->node, &rk_clks);
+                       }
+               }
+       }
+
+       return 0;
+}
+
+#define MHZ (1000 * 1000)
+static unsigned long clk_pll_recalc_rate(struct clk_hw *hw,
+               unsigned long parent_rate)
+{
+       clk_debug("%s\n", __func__);
+       if (strncmp(hw->clk->name, "clk_apll", sizeof("clk_apll")) == 0) {
+               return 600 * MHZ;
+       } else if (strncmp(hw->clk->name, "clk_dpll", sizeof("clk_dpll")) == 0) {
+               return 300 * MHZ;
+       }else if (strncmp(hw->clk->name, "clk_cpll", sizeof("clk_cpll")) == 0) {
+               return 132 * MHZ;
+       }else if (strncmp(hw->clk->name, "clk_gpll", sizeof("clk_gpll")) == 0) {
+               return 891 * MHZ;
+       } else
+               clk_err("Unknown PLL\n");
+       return -EINVAL;
+}
+static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+               unsigned long *prate)
+{
+       clk_debug("%s\n", __func__);
+       return rate;
+}
+static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+               unsigned long parent_rate)
+{
+       clk_debug("%s\n", __func__);
+       return 0;
+}
+const struct clk_ops clk_pll_ops = {
+       .recalc_rate = clk_pll_recalc_rate,
+       .round_rate = clk_pll_round_rate,
+       .set_rate = clk_pll_set_rate,
+};
+static unsigned long clk_frac_recalc_rate(struct clk_hw *hw,
+               unsigned long parent_rate)
+{
+       return parent_rate;
+}
+static long clk_frac_round_rate(struct clk_hw *hw, unsigned long rate,
+               unsigned long *prate)
+{
+       return rate;
+}
+static int clk_frac_set_rate(struct clk_hw *hw, unsigned long rate,
+               unsigned long parent_rate)
+{
+       return 0;
+}
+const struct clk_ops clk_frac_ops = {
+       .recalc_rate = clk_frac_recalc_rate,
+       .round_rate = clk_frac_round_rate,
+       .set_rate = clk_frac_set_rate,
+};
+static unsigned long clk_div_special_recalc_rate(struct clk_hw *hw,
+               unsigned long parent_rate)
+{
+       return parent_rate;
+}
+static long clk_div_special_round_rate(struct clk_hw *hw, unsigned long rate,
+               unsigned long *prate)
+{
+       return rate;
+}
+static int clk_div_special_set_rate(struct clk_hw *hw, unsigned long rate,
+               unsigned long parent_rate)
+{
+       return 0;
+}
+// For fixed div clks and For user defined div clk
+const struct clk_ops clk_div_special_ops = {
+       .recalc_rate = clk_div_special_recalc_rate,
+       .round_rate = clk_div_special_round_rate,
+       .set_rate = clk_div_special_set_rate,
+};
+static int rkclk_register(struct rkclk *rkclk)
+{
+       struct clk_mux          *mux = NULL;
+       struct clk_divider      *div = NULL;
+       struct clk_gate         *gate = NULL;
+
+       const struct clk_ops    *rate_ops = NULL;
+       const struct clk_ops    *mux_ops = NULL;
+
+       struct clk              *clk = NULL;
+       const char              **parent_names = NULL;
+       struct clk_hw           *rate_hw;
+       int                     parent_num;
+       struct device_node      *node = rkclk->np;
+       /* Single clk */
+       clk_debug("%s: %s clk_type=%x\n", __func__,
+                       rkclk->clk_name, rkclk->clk_type);
+       if (rkclk->clk_type & RKCLK_PLL_TYPE) {
+               div = kzalloc(sizeof(struct clk_divider), GFP_KERNEL);
+               rate_ops = &clk_pll_ops;
+               div->reg = rkclk->pll_info->addr;
+               div->shift = 0;
+               div->width = rkclk->pll_info->width;
+               rate_hw = &div->hw;
+
+               parent_num = 1;
+               parent_names = &rkclk->pll_info->parent_name;
+
+       } else if (rkclk->clk_type & RKCLK_FRAC_TYPE) {
+               div = kzalloc(sizeof(struct clk_divider), GFP_KERNEL);
+               if (rkclk->frac_info->clkops_idx != CLKOPS_TABLE_END)
+                       rate_ops = rk_get_clkops(rkclk->frac_info->clkops_idx);
+               else
+                       rate_ops = &clk_frac_ops;
+               div->reg = rkclk->frac_info->addr;
+               div->shift = (u8)rkclk->frac_info->shift;
+               div->width = rkclk->frac_info->width;
+               rate_hw = &div->hw;
+
+               parent_num = 1;
+               parent_names = &rkclk->frac_info->parent_name;
+
+       } else if (rkclk->clk_type & RKCLK_DIV_TYPE) {
+               div = kzalloc(sizeof(struct clk_divider), GFP_KERNEL);
+               if (rkclk->div_info->clkops_idx != CLKOPS_TABLE_END)
+                       rate_ops = rk_get_clkops(rkclk->div_info->clkops_idx);
+               else
+                       rate_ops = &clk_divider_ops;
+               div->reg = rkclk->div_info->addr;
+               div->shift = (u8)rkclk->div_info->shift;
+               div->width = rkclk->div_info->width;
+               div->flags = rkclk->div_info->div_type;
+               rate_hw = &div->hw;
+               if (rkclk->div_info->div_table)
+                       div->table = rkclk->div_info->div_table;
+
+               parent_num = 1;
+               parent_names = &rkclk->div_info->parent_name;
+               if (rkclk->clk_type != (rkclk->clk_type & CLK_DIVIDER_MASK)) {
+                       // FIXME: fixed div add here
+                       clk_err("%s: %d, unknown clk_type=%x\n",
+                                       __func__, __LINE__, rkclk->clk_type);
+
+               }
+       }
+
+       if (rkclk->clk_type & RKCLK_MUX_TYPE) {
+               mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL);
+               mux->reg = rkclk->mux_info->addr;
+               mux->shift = (u8)rkclk->mux_info->shift;
+               mux->mask = (1 << rkclk->mux_info->width) - 1;
+               mux->flags = 0;
+               mux_ops = &clk_mux_ops;
+               if (rkclk->mux_info->clkops_idx != CLKOPS_TABLE_END) {
+                       rate_hw = kzalloc(sizeof(struct clk_hw), GFP_KERNEL);
+                       rate_ops = rk_get_clkops(rkclk->mux_info->clkops_idx);
+               }
+
+               parent_num = rkclk->mux_info->parent_num;
+               parent_names = rkclk->mux_info->parent_names;
+       }
+
+       if (rkclk->clk_type & RKCLK_GATE_TYPE) {
+               gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
+               gate->reg = rkclk->gate_info->addr;
+               gate->bit_idx = rkclk->gate_info->shift;
+               gate->flags = CLK_GATE_HIWORD_MASK | CLK_GATE_SET_TO_DISABLE;
+
+       }
+
+       // FIXME: flag(CLK_IGNORE_UNUSED) may need an input argument
+       if (rkclk->clk_type == RKCLK_MUX_TYPE
+                       && rkclk->mux_info->clkops_idx == CLKOPS_TABLE_END) {
+               clk = clk_register_mux(NULL, rkclk->clk_name,
+                               rkclk->mux_info->parent_names,
+                               (u8)rkclk->mux_info->parent_num,
+                               CLK_SET_RATE_PARENT,
+                               mux->reg, mux->shift, mux->mask,
+                               0, &clk_lock);
+       } else if (rkclk->clk_type == RKCLK_DIV_TYPE) {
+               clk = clk_register_divider(NULL, rkclk->clk_name,
+                               rkclk->div_info->parent_name,
+                               CLK_SET_RATE_PARENT, div->reg, div->shift,
+                               div->width, div->flags, &clk_lock);
+       } else if (rkclk->clk_type == RKCLK_GATE_TYPE) {
+               clk = clk_register_gate(NULL, rkclk->clk_name,
+                               rkclk->gate_info->parent_name,
+                               CLK_IGNORE_UNUSED, gate->reg,
+                               gate->bit_idx,
+                               gate->flags, &clk_lock);
+       } else {
+               int i = 0;
+               clk_debug("%s: composite clk(\"%s\") parents:\n",
+                               __func__, rkclk->clk_name);
+
+               for (i = 0; i < parent_num; i++) {
+                       clk_debug("\t\t%s: parent[%d]=%s\n", __func__,
+                                       i, parent_names[i]);
+               }
+               clk = clk_register_composite(NULL, rkclk->clk_name,
+                               parent_names, parent_num,
+                               mux ? &mux->hw : NULL, mux ? mux_ops : NULL,
+                               rate_hw, rate_ops,
+                               gate ? &gate->hw : NULL, gate ? &clk_gate_ops : NULL,
+                               CLK_IGNORE_UNUSED);
+       }
+       if (clk) {
+               of_clk_add_provider(node, of_clk_src_simple_get, clk);
+               clk_register_clkdev(clk, rkclk->clk_name, NULL);
+       } else {
+               clk_err("%s: clk(\"%s\") register clk error\n",
+                               __func__, rkclk->clk_name);
+       }
+       return 0;
+}
+
+struct test_table {
+       const char *name;
+       u32 rate;
+};
+struct test_table t_table[] = {
+       {.name = "clk_gpu", .rate = 297000000},
+       {.name = "dclk_lcdc0", .rate = 297000000},
+       {.name = "clk_i2s", .rate = 11289600},
+       {.name = "clk_spdif", .rate = 11289600},
+       {.name = "clk_sdmmc", .rate = 50000000},
+       {.name = "clk_emmc", .rate = 50000000},
+       {.name = "clk_sdio", .rate = 50000000},
+       {.name = "clk_uart0", .rate = 12288000},
+       {.name = "clk_hsadc", .rate = 12288000},
+       {.name = "clk_mac",   .rate = 50000000},
+       {.name = "clk_cif0",   .rate = 12000000},
+       {.name = "aclk_lcdc0",   .rate = 297000000},
+};
+
+#ifdef RKCLK_DEBUG
+void rk_clk_test(void)
+{
+       const char *clk_name;
+       struct clk *clk;
+       u32 rate = 0;
+
+       u32 i = 0, j = 0;
+       for (j = 0; j < ARRAY_SIZE(t_table); j++) {
+               clk_name = t_table[j].name;
+               rate = t_table[j].rate;
+
+               clk = clk_get(NULL, clk_name);
+               if (IS_ERR(clk)) {
+                       clk_err("%s: clk(\"%s\") \tclk_get error\n",
+                                       __func__, clk_name);
+               } else
+                       clk_debug("%s: clk(\"%s\") \tclk_get success\n",
+                                       __func__, __clk_get_name(clk));
+
+               /* TEST: clk_set_rate */
+               if (clk->ops->set_rate) {
+                       if (0 != clk_set_rate(clk, rate)) {
+                               clk_err("%s: clk(\"%s\") \tclk_set_rate error\n",
+                                               __func__, clk_name);
+                       } else {
+                               clk_debug("%s: clk(\"%s\") \tclk_set_rate success\n",
+                                               __func__, __clk_get_name(clk));
+                       }
+               } else {
+                       clk_debug("%s: clk(\"%s\") have no set ops\n",
+                                       __func__, clk->name);
+               }
+
+               for (i = 0; i * 4 <= 0xf4; i++) {
+                       if (i % 4 == 0)
+                               printk("\n%s: \t[0x%08x]: ",
+                                               __func__, 0x20000000 + i * 4);
+                       printk("%08x ", readl(reg_start + i * 4));
+               }
+               printk("\n\n");
+       }
+}
+EXPORT_SYMBOL_GPL(rk_clk_test);
+#else
+void rk_clk_test(void){};
+EXPORT_SYMBOL_GPL(rk_clk_test);
+#endif
+extern void clk_dump_tree(void);
+static void __init rkclk_init(struct device_node *np)
+{
+       struct device_node *node;
+       struct rkclk *rkclk;
+
+       for_each_available_child_of_node(np, node) {
+
+               if (!ERR_PTR(of_property_match_string(node,
+                                               "compatible",
+                                               "fixed-clock"))) {
+                       continue;
+
+               } else if (!ERR_PTR(of_property_match_string(node,
+                                               "compatible",
+                                               "rockchip,rk-pll-cons"))) {
+                       if (ERR_PTR(rkclk_init_pllcon(node))) {
+                               clk_err("%s: init pll clk err\n", __func__);
+                               return ;
+                       }
+
+               } else if (!ERR_PTR(of_property_match_string(node,
+                                               "compatible",
+                                               "rockchip,rk-sel-cons"))) {
+                       if (ERR_PTR(rkclk_init_selcon(node))) {
+                               clk_err("%s: init sel cons err\n", __func__);
+                               return ;
+                       }
+
+               } else if (!ERR_PTR(of_property_match_string(node,
+                                               "compatible",
+                                               "rockchip,rk-gate-cons"))) {
+                       if (ERR_PTR(rkclk_init_gatecon(node))) {
+                               clk_err("%s: init gate cons err\n", __func__);
+                               return ;
+                       }
+
+               } else {
+                       clk_err("%s: unknown\n", __func__);
+               }
+
+       };
+
+
+#if 0
+       list_for_each_entry(rkclk, &rk_clks, node) {
+               int i;
+               clk_debug("%s: clkname = %s; type=%d\n",
+                               __func__, rkclk->clk_name,
+                               rkclk->clk_type);
+               if (rkclk->pll_info) {
+                       clk_debug("\t\tpll: name=%s, parent=%s\n",
+                                       rkclk->pll_info->clk_name,
+                                       rkclk->pll_info->parent_name);
+               }
+               if (rkclk->mux_info) {
+                       for (i = 0; i < rkclk->mux_info->parent_num; i++)
+                               clk_debug("\t\tmux name=%s, parent: %s\n",
+                                               rkclk->mux_info->clk_name,
+                                               rkclk->mux_info->parent_names[i]);
+               }
+               if (rkclk->div_info) {
+                       clk_debug("\t\tdiv name=%s\n",
+                                       rkclk->div_info->clk_name);
+               }
+               if (rkclk->frac_info) {
+                       clk_debug("\t\tfrac name=%s\n",
+                                       rkclk->frac_info->clk_name);
+               }
+               if (rkclk->gate_info) {
+                       clk_debug("\t\tgate name=%s, \taddr=%08x, \tshift=%d\n",
+                                       rkclk->gate_info->clk_name,
+                                       (u32)rkclk->gate_info->addr,
+                                       rkclk->gate_info->shift);
+               }
+       }
+#endif
+       list_for_each_entry(rkclk, &rk_clks, node) {
+               rkclk_register(rkclk);
+       }
+       /* check clock parents init */
+       list_for_each_entry(rkclk, &rk_clks, node) {
+
+               struct clk *clk;
+               int i = 0;
+               const char *clk_name = rkclk->clk_name;
+               clk = clk_get(NULL, clk_name);
+               if (IS_ERR(clk)) {
+                       clk_err("%s: clk(\"%s\") \tclk_get error\n",
+                                       __func__, clk_name);
+                       continue;
+               } else {
+                       clk_debug("%s: clk(\"%s\") \tclk_get success\n",
+                                       __func__, __clk_get_name(clk));
+               }
+
+               if (clk->parents) {
+                       for (i = 0; i < clk->num_parents; i++) {
+                               if (clk->parents[i])
+                                       clk_debug("\t\tclk(\"%s\"): init parent:%s\n",
+                                                       clk->name,
+                                                       clk->parents[i]->name);
+                               else {
+                                       clk->parents[i] = clk_get(NULL, clk->parent_names[i]);
+                                       clk_debug("\t\tclk(\"%s\"): init parent:%s\n",
+                                                       clk->name,
+                                                       clk->parents[i]->name);
+                               }
+                       }
+
+               } else {
+                       clk_debug("\t\tNOT A MUX CLK, parent num=%d\n", clk->num_parents);
+               }
+       }
+}
+
+CLK_OF_DECLARE(rk_clocks,      "rockchip,rk-clock-regs",       rkclk_init);
diff --git a/drivers/clk/rockchip/clkops-dtsi.h b/drivers/clk/rockchip/clkops-dtsi.h
new file mode 100644 (file)
index 0000000..944d12c
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef __RK_CLKOPS_DTSI_H
+#define __RK_CLKOPS_DTSI_H
+
+/* rate_ops index */
+#define CLKOPS_RATE_MUX_DIV            0
+#define CLKOPS_RATE_EVENDIV            1
+#define CLKOPS_RATE_DCLK_LCDC          2
+#define CLKOPS_RATE_CIFOUT             3
+#define CLKOPS_RATE_I2S_FRAC           4
+#define CLKOPS_RATE_I2S                        5
+#define CLKOPS_RATE_HSADC_FRAC         6
+#define CLKOPS_RATE_UART_FRAC          7
+#define CLKOPS_RATE_UART               8
+#define CLKOPS_RATE_HSADC              9
+#define CLKOPS_RATE_MAC_REF            10
+#define CLKOPS_TABLE_END               ~0
+
+#ifndef BIT
+#define BIT(nr)                        (1 << (nr))
+#endif
+#define CLK_DIVIDER_PLUS_ONE           (0)
+#define CLK_DIVIDER_ONE_BASED          BIT(0)
+#define CLK_DIVIDER_POWER_OF_TWO       BIT(1)
+#define CLK_DIVIDER_ALLOW_ZERO         BIT(2)
+#define CLK_DIVIDER_FIXED              BIT(3)
+#define CLK_DIVIDER_USER_DEFINE                BIT(4)
+/* CLK_DIVIDER_MAX defined the bits been used above */
+#define CLK_DIVIDER_MASK               (0x1F)
+#endif /* __RK_CLKOPS_H */