From: chenxing Date: Fri, 20 Dec 2013 11:07:23 +0000 (+0800) Subject: rk3188: add clock support X-Git-Tag: firefly_0821_release~6433 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=a456b372ec77b829a5fca24df55627391bc2378f;p=firefly-linux-kernel-4.4.55.git rk3188: add clock support --- diff --git a/arch/arm/boot/dts/rk3188-clocks.dtsi b/arch/arm/boot/dts/rk3188-clocks.dtsi new file mode 100644 index 000000000000..342e2f4dbc14 --- /dev/null +++ b/arch/arm/boot/dts/rk3188-clocks.dtsi @@ -0,0 +1,1369 @@ +/* + * Copyright (C) 2013 ROCKCHIP, Inc. + * Author: chenxing + * + * 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 = ; + }; + + 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 = ; + #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 = ; + }; + + /* 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 = ; + 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 = ; + 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 = ; + 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 = ; + 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 = ; + 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 = ; + #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 = ; + 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 = ; + #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 = + ; + }; + }; + + /* 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 = + ; + }; + }; + + 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 = ; + #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 = ; + #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 = ; + #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 = ; + rockchip,clkops-idx = + ; + #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 = ; + }; + + /* 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 = ; + rockchip,clkops-idx = + ; + #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 = ; + rockchip,clkops-idx = + ; + #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 = ; + #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 = + ; + 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 = ; + #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 = + ; + 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 = ; + #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 = + ; + 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 = ; + #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 = + ; + 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 = + ; + #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 = + ; + #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 = + ; + #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 = + ; + #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 = + ; + 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 = ; + + }; + + /* 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 = + ; + 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 = ; + 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_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 = + ; + #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 = ; + #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 = ; + #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 = ; + #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 = ; + }; + + /* 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 = ; + rockchip,clkops-idx = + ; + }; + }; + + + 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 = ; + rockchip,clkops-idx = + ; + }; + }; + + 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 = ; + }; + + /* 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 = + ; + 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 = ; + rockchip,clkops-idx = + ; + }; + + /* 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 = ; + rockchip,clkops-idx = + ; + }; + + /* 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 = ; + rockchip,clkops-idx = + ; + }; + + /* 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 = ; + rockchip,clkops-idx = + ; + }; + + /* 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 = + ; + }; + + /* 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>; + }; + }; + }; + }; +}; diff --git a/arch/arm/boot/dts/rk3188-tb.dts b/arch/arm/boot/dts/rk3188-tb.dts index 390a413d41f1..845505349275 100644 --- a/arch/arm/boot/dts/rk3188-tb.dts +++ b/arch/arm/boot/dts/rk3188-tb.dts @@ -1,6 +1,7 @@ /dts-v1/; #include "rk3188.dtsi" +#include "rk3188-clocks.dtsi" / { memory { diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 137d3e730f86..5eca625228fd 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -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 diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 6d9674160430..2c066abb2452 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -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) diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c index 25b1734560d0..09f9e1e9f2de 100644 --- a/drivers/clk/clk-mux.c +++ b/drivers/clk/clk-mux.c @@ -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 index 000000000000..ae86b42576d7 --- /dev/null +++ b/drivers/clk/rockchip/Makefile @@ -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 index 000000000000..bf9bc8f99b25 --- /dev/null +++ b/drivers/clk/rockchip/clk-ops.c @@ -0,0 +1,814 @@ +#include +#include +#include +#include +#include +#include +#include +#include "clk-ops.h" +#include + +/* 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; inum_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; inum_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 index 000000000000..e3bfb85ef79a --- /dev/null +++ b/drivers/clk/rockchip/clk-ops.h @@ -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 index 000000000000..c058683fabbd --- /dev/null +++ b/drivers/clk/rockchip/clk.c @@ -0,0 +1,924 @@ +/* + * Copyright (C) 2013 ROCKCHIP, Inc. + * Author: chenxing + * + * 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 +#include +#include +#include +#include "clk-ops.h" +#include +#include + +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 index 000000000000..944d12cdf33d --- /dev/null +++ b/drivers/clk/rockchip/clkops-dtsi.h @@ -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 */