--- /dev/null
+/*
+ * 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>;
+ };
+ };
+ };
+ };
+};
/dts-v1/;
#include "rk3188.dtsi"
+#include "rk3188-clocks.dtsi"
/ {
memory {
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
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)
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)
--- /dev/null
+obj-y += clk.o
+obj-y += clk-ops.o
--- /dev/null
+#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);
--- /dev/null
+#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 */
--- /dev/null
+/*
+ * 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);
--- /dev/null
+#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 */