diff options
35 files changed, 1802 insertions, 893 deletions
diff --git a/Documentation/clk.txt b/Documentation/clk.txt index c9c399af7c08..1fee72f4d331 100644 --- a/Documentation/clk.txt +++ b/Documentation/clk.txt @@ -68,21 +68,27 @@ the operations defined in clk.h: int (*is_enabled)(struct clk_hw *hw); unsigned long (*recalc_rate)(struct clk_hw *hw, unsigned long parent_rate); - long (*round_rate)(struct clk_hw *hw, unsigned long, - unsigned long *); + long (*round_rate)(struct clk_hw *hw, + unsigned long rate, + unsigned long *parent_rate); long (*determine_rate)(struct clk_hw *hw, unsigned long rate, unsigned long *best_parent_rate, struct clk **best_parent_clk); int (*set_parent)(struct clk_hw *hw, u8 index); u8 (*get_parent)(struct clk_hw *hw); - int (*set_rate)(struct clk_hw *hw, unsigned long); + int (*set_rate)(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate); int (*set_rate_and_parent)(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate, u8 index); + unsigned long parent_rate, + u8 index); unsigned long (*recalc_accuracy)(struct clk_hw *hw, - unsigned long parent_accuracy); + unsigned long parent_accuracy); void (*init)(struct clk_hw *hw); + int (*debug_init)(struct clk_hw *hw, + struct dentry *dentry); }; Part 3 - hardware clk implementations diff --git a/Documentation/devicetree/bindings/clock/bcm-kona-clock.txt b/Documentation/devicetree/bindings/clock/bcm-kona-clock.txt index 56d1f4961075..5286e260fcae 100644 --- a/Documentation/devicetree/bindings/clock/bcm-kona-clock.txt +++ b/Documentation/devicetree/bindings/clock/bcm-kona-clock.txt @@ -10,12 +10,12 @@ This binding uses the common clock binding: Required properties: - compatible - Shall have one of the following values: - - "brcm,bcm11351-root-ccu" - - "brcm,bcm11351-aon-ccu" - - "brcm,bcm11351-hub-ccu" - - "brcm,bcm11351-master-ccu" - - "brcm,bcm11351-slave-ccu" + Shall have a value of the form "brcm,<model>-<which>-ccu", + where <model> is a Broadcom SoC model number and <which> is + the name of a defined CCU. For example: + "brcm,bcm11351-root-ccu" + The compatible strings used for each supported SoC family + are defined below. - reg Shall define the base and range of the address space containing clock control registers @@ -26,12 +26,48 @@ Required properties: Shall be an ordered list of strings defining the names of the clocks provided by the CCU. +Device tree example: + + slave_ccu: slave_ccu { + compatible = "brcm,bcm11351-slave-ccu"; + reg = <0x3e011000 0x0f00>; + #clock-cells = <1>; + clock-output-names = "uartb", + "uartb2", + "uartb3", + "uartb4"; + }; + + ref_crystal_clk: ref_crystal { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <26000000>; + }; + + uart@3e002000 { + compatible = "brcm,bcm11351-dw-apb-uart", "snps,dw-apb-uart"; + status = "disabled"; + reg = <0x3e002000 0x1000>; + clocks = <&slave_ccu BCM281XX_SLAVE_CCU_UARTB3>; + interrupts = <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>; + reg-shift = <2>; + reg-io-width = <4>; + }; + +BCM281XX family +--------------- +CCU compatible string values for SoCs in the BCM281XX family are: + "brcm,bcm11351-root-ccu" + "brcm,bcm11351-aon-ccu" + "brcm,bcm11351-hub-ccu" + "brcm,bcm11351-master-ccu" + "brcm,bcm11351-slave-ccu" -BCM281XX family SoCs use Kona CCUs. The following table defines -the set of CCUs and clock specifiers for BCM281XX clocks. When -a clock consumer references a clocks, its symbolic specifier -(rather than its numeric index value) should be used. These -specifiers are defined in "include/dt-bindings/clock/bcm281xx.h". +The following table defines the set of CCUs and clock specifiers for +BCM281XX family clocks. When a clock consumer references a clocks, +its symbolic specifier (rather than its numeric index value) should +be used. These specifiers are defined in: + "include/dt-bindings/clock/bcm281xx.h" CCU Clock Type Index Specifier --- ----- ---- ----- --------- @@ -64,30 +100,40 @@ specifiers are defined in "include/dt-bindings/clock/bcm281xx.h". slave pwm peri 9 BCM281XX_SLAVE_CCU_PWM -Device tree example: +BCM21664 family +--------------- +CCU compatible string values for SoCs in the BCM21664 family are: + "brcm,bcm21664-root-ccu" + "brcm,bcm21664-aon-ccu" + "brcm,bcm21664-master-ccu" + "brcm,bcm21664-slave-ccu" - slave_ccu: slave_ccu { - compatible = "brcm,bcm11351-slave-ccu"; - reg = <0x3e011000 0x0f00>; - #clock-cells = <1>; - clock-output-names = "uartb", - "uartb2", - "uartb3", - "uartb4"; - }; +The following table defines the set of CCUs and clock specifiers for +BCM21664 family clocks. When a clock consumer references a clocks, +its symbolic specifier (rather than its numeric index value) should +be used. These specifiers are defined in: + "include/dt-bindings/clock/bcm21664.h" - ref_crystal_clk: ref_crystal { - #clock-cells = <0>; - compatible = "fixed-clock"; - clock-frequency = <26000000>; - }; + CCU Clock Type Index Specifier + --- ----- ---- ----- --------- + root frac_1m peri 0 BCM21664_ROOT_CCU_FRAC_1M - uart@3e002000 { - compatible = "brcm,bcm11351-dw-apb-uart", "snps,dw-apb-uart"; - status = "disabled"; - reg = <0x3e002000 0x1000>; - clocks = <&slave_ccu BCM281XX_SLAVE_CCU_UARTB3>; - interrupts = <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>; - reg-shift = <2>; - reg-io-width = <4>; - }; + aon hub_timer peri 0 BCM21664_AON_CCU_HUB_TIMER + + master sdio1 peri 0 BCM21664_MASTER_CCU_SDIO1 + master sdio2 peri 1 BCM21664_MASTER_CCU_SDIO2 + master sdio3 peri 2 BCM21664_MASTER_CCU_SDIO3 + master sdio4 peri 3 BCM21664_MASTER_CCU_SDIO4 + master sdio1_sleep peri 4 BCM21664_MASTER_CCU_SDIO1_SLEEP + master sdio2_sleep peri 5 BCM21664_MASTER_CCU_SDIO2_SLEEP + master sdio3_sleep peri 6 BCM21664_MASTER_CCU_SDIO3_SLEEP + master sdio4_sleep peri 7 BCM21664_MASTER_CCU_SDIO4_SLEEP + + slave uartb peri 0 BCM21664_SLAVE_CCU_UARTB + slave uartb2 peri 1 BCM21664_SLAVE_CCU_UARTB2 + slave uartb3 peri 2 BCM21664_SLAVE_CCU_UARTB3 + slave uartb4 peri 3 BCM21664_SLAVE_CCU_UARTB4 + slave bsc1 peri 4 BCM21664_SLAVE_CCU_BSC1 + slave bsc2 peri 5 BCM21664_SLAVE_CCU_BSC2 + slave bsc3 peri 6 BCM21664_SLAVE_CCU_BSC3 + slave bsc4 peri 7 BCM21664_SLAVE_CCU_BSC4 diff --git a/Documentation/devicetree/bindings/clock/clock-bindings.txt b/Documentation/devicetree/bindings/clock/clock-bindings.txt index 700e7aac3717..f15787817d6b 100644 --- a/Documentation/devicetree/bindings/clock/clock-bindings.txt +++ b/Documentation/devicetree/bindings/clock/clock-bindings.txt @@ -44,10 +44,9 @@ For example: clocks by index. The names should reflect the clock output signal names for the device. -clock-indices: If the identifyng number for the clocks in the node - is not linear from zero, then the this mapping allows - the mapping of identifiers into the clock-output-names - array. +clock-indices: If the identifying number for the clocks in the node + is not linear from zero, then this allows the mapping of + identifiers into the clock-output-names array. For example, if we have two clocks <&oscillator 1> and <&oscillator 3>: @@ -58,7 +57,7 @@ For example, if we have two clocks <&oscillator 1> and <&oscillator 3>: clock-output-names = "clka", "clkb"; } - This ensures we do not have any empty nodes in clock-output-names + This ensures we do not have any empty strings in clock-output-names ==Clock consumers== diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc.txt b/Documentation/devicetree/bindings/clock/qcom,gcc.txt index 767401f42871..7b7104e8cb1e 100644 --- a/Documentation/devicetree/bindings/clock/qcom,gcc.txt +++ b/Documentation/devicetree/bindings/clock/qcom,gcc.txt @@ -4,6 +4,7 @@ Qualcomm Global Clock & Reset Controller Binding Required properties : - compatible : shall contain only one of the following: + "qcom,gcc-apq8064" "qcom,gcc-msm8660" "qcom,gcc-msm8960" "qcom,gcc-msm8974" diff --git a/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt b/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt index 5992dceec7af..02a25d99ca61 100644 --- a/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt +++ b/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt @@ -43,7 +43,7 @@ Example clock-output-names = "tpu0", "mmcif1", "sdhi3", "sdhi2", "sdhi1", "sdhi0", "mmcif0"; - renesas,clock-indices = < + clock-indices = < R8A7790_CLK_TPU0 R8A7790_CLK_MMCIF1 R8A7790_CLK_SDHI3 R8A7790_CLK_SDHI2 R8A7790_CLK_SDHI1 R8A7790_CLK_SDHI0 R8A7790_CLK_MMCIF0 diff --git a/arch/arm/boot/dts/bcm21664.dtsi b/arch/arm/boot/dts/bcm21664.dtsi index 08a44d41b672..8b366822bb43 100644 --- a/arch/arm/boot/dts/bcm21664.dtsi +++ b/arch/arm/boot/dts/bcm21664.dtsi @@ -14,6 +14,8 @@ #include <dt-bindings/interrupt-controller/arm-gic.h> #include <dt-bindings/interrupt-controller/irq.h> +#include "dt-bindings/clock/bcm21664.h" + #include "skeleton.dtsi" / { @@ -43,7 +45,7 @@ compatible = "brcm,bcm21664-dw-apb-uart", "snps,dw-apb-uart"; status = "disabled"; reg = <0x3e000000 0x118>; - clocks = <&uartb_clk>; + clocks = <&slave_ccu BCM21664_SLAVE_CCU_UARTB>; interrupts = <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>; reg-shift = <2>; reg-io-width = <4>; @@ -53,7 +55,7 @@ compatible = "brcm,bcm21664-dw-apb-uart", "snps,dw-apb-uart"; status = "disabled"; reg = <0x3e001000 0x118>; - clocks = <&uartb2_clk>; + clocks = <&slave_ccu BCM21664_SLAVE_CCU_UARTB2>; interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>; reg-shift = <2>; reg-io-width = <4>; @@ -63,7 +65,7 @@ compatible = "brcm,bcm21664-dw-apb-uart", "snps,dw-apb-uart"; status = "disabled"; reg = <0x3e002000 0x118>; - clocks = <&uartb3_clk>; + clocks = <&slave_ccu BCM21664_SLAVE_CCU_UARTB3>; interrupts = <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>; reg-shift = <2>; reg-io-width = <4>; @@ -85,7 +87,7 @@ compatible = "brcm,kona-timer"; reg = <0x35006000 0x1c>; interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&hub_timer_clk>; + clocks = <&aon_ccu BCM21664_AON_CCU_HUB_TIMER>; }; gpio: gpio@35003000 { @@ -106,7 +108,7 @@ compatible = "brcm,kona-sdhci"; reg = <0x3f180000 0x801c>; interrupts = <GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&sdio1_clk>; + clocks = <&master_ccu BCM21664_MASTER_CCU_SDIO1>; status = "disabled"; }; @@ -114,7 +116,7 @@ compatible = "brcm,kona-sdhci"; reg = <0x3f190000 0x801c>; interrupts = <GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&sdio2_clk>; + clocks = <&master_ccu BCM21664_MASTER_CCU_SDIO2>; status = "disabled"; }; @@ -122,7 +124,7 @@ compatible = "brcm,kona-sdhci"; reg = <0x3f1a0000 0x801c>; interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&sdio3_clk>; + clocks = <&master_ccu BCM21664_MASTER_CCU_SDIO3>; status = "disabled"; }; @@ -130,7 +132,7 @@ compatible = "brcm,kona-sdhci"; reg = <0x3f1b0000 0x801c>; interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&sdio4_clk>; + clocks = <&master_ccu BCM21664_MASTER_CCU_SDIO4>; status = "disabled"; }; @@ -140,7 +142,7 @@ interrupts = <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>; #address-cells = <1>; #size-cells = <0>; - clocks = <&bsc1_clk>; + clocks = <&slave_ccu BCM21664_SLAVE_CCU_BSC1>; status = "disabled"; }; @@ -150,7 +152,7 @@ interrupts = <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>; #address-cells = <1>; #size-cells = <0>; - clocks = <&bsc2_clk>; + clocks = <&slave_ccu BCM21664_SLAVE_CCU_BSC2>; status = "disabled"; }; @@ -160,7 +162,7 @@ interrupts = <GIC_SPI 169 IRQ_TYPE_LEVEL_HIGH>; #address-cells = <1>; #size-cells = <0>; - clocks = <&bsc3_clk>; + clocks = <&slave_ccu BCM21664_SLAVE_CCU_BSC3>; status = "disabled"; }; @@ -170,105 +172,149 @@ interrupts = <GIC_SPI 170 IRQ_TYPE_LEVEL_HIGH>; #address-cells = <1>; #size-cells = <0>; - clocks = <&bsc4_clk>; + clocks = <&slave_ccu BCM21664_SLAVE_CCU_BSC4>; status = "disabled"; }; clocks { - bsc1_clk: bsc1 { - compatible = "fixed-clock"; - clock-frequency = <13000000>; - #clock-cells = <0>; - }; + #address-cells = <1>; + #size-cells = <1>; + ranges; - bsc2_clk: bsc2 { - compatible = "fixed-clock"; - clock-frequency = <13000000>; + /* + * Fixed clocks are defined before CCUs whose + * clocks may depend on them. + */ + + ref_32k_clk: ref_32k { #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <32768>; }; - bsc3_clk: bsc3 { - compatible = "fixed-clock"; - clock-frequency = <13000000>; + bbl_32k_clk: bbl_32k { #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <32768>; }; - bsc4_clk: bsc4 { + ref_13m_clk: ref_13m { + #clock-cells = <0>; compatible = "fixed-clock"; clock-frequency = <13000000>; - #clock-cells = <0>; }; - pmu_bsc_clk: pmu_bsc { + var_13m_clk: var_13m { + #clock-cells = <0>; compatible = "fixed-clock"; clock-frequency = <13000000>; - #clock-cells = <0>; }; - hub_timer_clk: hub_timer { - compatible = "fixed-clock"; - clock-frequency = <32768>; + dft_19_5m_clk: dft_19_5m { #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <19500000>; }; - pwm_clk: pwm { + ref_crystal_clk: ref_crystal { + #clock-cells = <0>; compatible = "fixed-clock"; clock-frequency = <26000000>; - #clock-cells = <0>; }; - sdio1_clk: sdio1 { - compatible = "fixed-clock"; - clock-frequency = <48000000>; + ref_52m_clk: ref_52m { #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <52000000>; }; - sdio2_clk: sdio2 { - compatible = "fixed-clock"; - clock-frequency = <48000000>; + var_52m_clk: var_52m { #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <52000000>; }; - sdio3_clk: sdio3 { - compatible = "fixed-clock"; - clock-frequency = <48000000>; + usb_otg_ahb_clk: usb_otg_ahb { #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <52000000>; }; - sdio4_clk: sdio4 { - compatible = "fixed-clock"; - clock-frequency = <48000000>; + ref_96m_clk: ref_96m { #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <96000000>; }; - tmon_1m_clk: tmon_1m { - compatible = "fixed-clock"; - clock-frequency = <1000000>; + var_96m_clk: var_96m { #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <96000000>; }; - uartb_clk: uartb { - compatible = "fixed-clock"; - clock-frequency = <13000000>; + ref_104m_clk: ref_104m { #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <104000000>; }; - uartb2_clk: uartb2 { - compatible = "fixed-clock"; - clock-frequency = <13000000>; + var_104m_clk: var_104m { #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <104000000>; }; - uartb3_clk: uartb3 { - compatible = "fixed-clock"; - clock-frequency = <13000000>; + ref_156m_clk: ref_156m { #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <156000000>; }; - usb_otg_ahb_clk: usb_otg_ahb { - compatible = "fixed-clock"; - clock-frequency = <52000000>; + var_156m_clk: var_156m { #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <156000000>; + }; + + root_ccu: root_ccu { + compatible = BCM21664_DT_ROOT_CCU_COMPAT; + reg = <0x35001000 0x0f00>; + #clock-cells = <1>; + clock-output-names = "frac_1m"; + }; + + aon_ccu: aon_ccu { + compatible = BCM21664_DT_AON_CCU_COMPAT; + reg = <0x35002000 0x0f00>; + #clock-cells = <1>; + clock-output-names = "hub_timer"; + }; + + master_ccu: master_ccu { + compatible = BCM21664_DT_MASTER_CCU_COMPAT; + reg = <0x3f001000 0x0f00>; + #clock-cells = <1>; + clock-output-names = "sdio1", + "sdio2", + "sdio3", + "sdio4", + "sdio1_sleep", + "sdio2_sleep", + "sdio3_sleep", + "sdio4_sleep"; + }; + + slave_ccu: slave_ccu { + compatible = BCM21664_DT_SLAVE_CCU_COMPAT; + reg = <0x3e011000 0x0f00>; + #clock-cells = <1>; + clock-output-names = "uartb", + "uartb2", + "uartb3", + "bsc1", + "bsc2", + "bsc3", + "bsc4"; }; }; diff --git a/drivers/clk/bcm/Kconfig b/drivers/clk/bcm/Kconfig index a7262fb8ce55..75506e53075b 100644 --- a/drivers/clk/bcm/Kconfig +++ b/drivers/clk/bcm/Kconfig @@ -6,4 +6,4 @@ config CLK_BCM_KONA help Enable common clock framework support for Broadcom SoCs using "Kona" style clock control units, including those - in the BCM281xx family. + in the BCM281xx and BCM21664 families. diff --git a/drivers/clk/bcm/Makefile b/drivers/clk/bcm/Makefile index cf93359aa862..6297d05a9a10 100644 --- a/drivers/clk/bcm/Makefile +++ b/drivers/clk/bcm/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_CLK_BCM_KONA) += clk-kona.o obj-$(CONFIG_CLK_BCM_KONA) += clk-kona-setup.o obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm281xx.o +obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm21664.o diff --git a/drivers/clk/bcm/clk-bcm21664.c b/drivers/clk/bcm/clk-bcm21664.c new file mode 100644 index 000000000000..eeae4cad2281 --- /dev/null +++ b/drivers/clk/bcm/clk-bcm21664.c @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2014 Broadcom Corporation + * Copyright 2014 Linaro Limited + * + * 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "clk-kona.h" +#include "dt-bindings/clock/bcm21664.h" + +#define BCM21664_CCU_COMMON(_name, _capname) \ + KONA_CCU_COMMON(BCM21664, _name, _capname) + +/* Root CCU */ + +static struct peri_clk_data frac_1m_data = { + .gate = HW_SW_GATE(0x214, 16, 0, 1), + .clocks = CLOCKS("ref_crystal"), +}; + +static struct ccu_data root_ccu_data = { + BCM21664_CCU_COMMON(root, ROOT), + /* no policy control */ + .kona_clks = { + [BCM21664_ROOT_CCU_FRAC_1M] = + KONA_CLK(root, frac_1m, peri), + [BCM21664_ROOT_CCU_CLOCK_COUNT] = LAST_KONA_CLK, + }, +}; + +/* AON CCU */ + +static struct peri_clk_data hub_timer_data = { + .gate = HW_SW_GATE(0x0414, 16, 0, 1), + .hyst = HYST(0x0414, 8, 9), + .clocks = CLOCKS("bbl_32k", + "frac_1m", + "dft_19_5m"), + .sel = SELECTOR(0x0a10, 0, 2), + .trig = TRIGGER(0x0a40, 4), +}; + +static struct ccu_data aon_ccu_data = { + BCM21664_CCU_COMMON(aon, AON), + .policy = { + .enable = CCU_LVM_EN(0x0034, 0), + .control = CCU_POLICY_CTL(0x000c, 0, 1, 2), + }, + .kona_clks = { + [BCM21664_AON_CCU_HUB_TIMER] = + KONA_CLK(aon, hub_timer, peri), + [BCM21664_AON_CCU_CLOCK_COUNT] = LAST_KONA_CLK, + }, +}; + +/* Master CCU */ + +static struct peri_clk_data sdio1_data = { + .gate = HW_SW_GATE(0x0358, 18, 2, 3), + .clocks = CLOCKS("ref_crystal", + "var_52m", + "ref_52m", + "var_96m", + "ref_96m"), + .sel = SELECTOR(0x0a28, 0, 3), + .div = DIVIDER(0x0a28, 4, 14), + .trig = TRIGGER(0x0afc, 9), +}; + +static struct peri_clk_data sdio2_data = { + .gate = HW_SW_GATE(0x035c, 18, 2, 3), + .clocks = CLOCKS("ref_crystal", + "var_52m", + "ref_52m", + "var_96m", + "ref_96m"), + .sel = SELECTOR(0x0a2c, 0, 3), + .div = DIVIDER(0x0a2c, 4, 14), + .trig = TRIGGER(0x0afc, 10), +}; + +static struct peri_clk_data sdio3_data = { + .gate = HW_SW_GATE(0x0364, 18, 2, 3), + .clocks = CLOCKS("ref_crystal", + "var_52m", + "ref_52m", + "var_96m", + "ref_96m"), + .sel = SELECTOR(0x0a34, 0, 3), + .div = DIVIDER(0x0a34, 4, 14), + .trig = TRIGGER(0x0afc, 12), +}; + +static struct peri_clk_data sdio4_data = { + .gate = HW_SW_GATE(0x0360, 18, 2, 3), + .clocks = CLOCKS("ref_crystal", + "var_52m", + "ref_52m", + "var_96m", + "ref_96m"), + .sel = SELECTOR(0x0a30, 0, 3), + .div = DIVIDER(0x0a30, 4, 14), + .trig = TRIGGER(0x0afc, 11), +}; + +static struct peri_clk_data sdio1_sleep_data = { + .clocks = CLOCKS("ref_32k"), /* Verify */ + .gate = HW_SW_GATE(0x0358, 18, 2, 3), +}; + +static struct peri_clk_data sdio2_sleep_data = { + .clocks = CLOCKS("ref_32k"), /* Verify */ + .gate = HW_SW_GATE(0x035c, 18, 2, 3), +}; + +static struct peri_clk_data sdio3_sleep_data = { + .clocks = CLOCKS("ref_32k"), /* Verify */ + .gate = HW_SW_GATE(0x0364, 18, 2, 3), +}; + +static struct peri_clk_data sdio4_sleep_data = { + .clocks = CLOCKS("ref_32k"), /* Verify */ + .gate = HW_SW_GATE(0x0360, 18, 2, 3), +}; + +static struct ccu_data master_ccu_data = { + BCM21664_CCU_COMMON(master, MASTER), + .policy = { + .enable = CCU_LVM_EN(0x0034, 0), + .control = CCU_POLICY_CTL(0x000c, 0, 1, 2), + }, + .kona_clks = { + [BCM21664_MASTER_CCU_SDIO1] = + KONA_CLK(master, sdio1, peri), + [BCM21664_MASTER_CCU_SDIO2] = + KONA_CLK(master, sdio2, peri), + [BCM21664_MASTER_CCU_SDIO3] = + KONA_CLK(master, sdio3, peri), + [BCM21664_MASTER_CCU_SDIO4] = + KONA_CLK(master, sdio4, peri), + [BCM21664_MASTER_CCU_SDIO1_SLEEP] = + KONA_CLK(master, sdio1_sleep, peri), + [BCM21664_MASTER_CCU_SDIO2_SLEEP] = + KONA_CLK(master, sdio2_sleep, peri), + [BCM21664_MASTER_CCU_SDIO3_SLEEP] = + KONA_CLK(master, sdio3_sleep, peri), + [BCM21664_MASTER_CCU_SDIO4_SLEEP] = + KONA_CLK(master, sdio4_sleep, peri), + [BCM21664_MASTER_CCU_CLOCK_COUNT] = LAST_KONA_CLK, + }, +}; + +/* Slave CCU */ + +static struct peri_clk_data uartb_data = { + .gate = HW_SW_GATE(0x0400, 18, 2, 3), + .clocks = CLOCKS("ref_crystal", + "var_156m", + "ref_156m"), + .sel = SELECTOR(0x0a10, 0, 2), + .div = FRAC_DIVIDER(0x0a10, 4, 12, 8), + .trig = TRIGGER(0x0afc, 2), +}; + +static struct peri_clk_data uartb2_data = { + .gate = HW_SW_GATE(0x0404, 18, 2, 3), + .clocks = CLOCKS("ref_crystal", + "var_156m", + "ref_156m"), + .sel = SELECTOR(0x0a14, 0, 2), + .div = FRAC_DIVIDER(0x0a14, 4, 12, 8), + .trig = TRIGGER(0x0afc, 3), +}; + +static struct peri_clk_data uartb3_data = { + .gate = HW_SW_GATE(0x0408, 18, 2, 3), + .clocks = CLOCKS("ref_crystal", + "var_156m", + "ref_156m"), + .sel = SELECTOR(0x0a18, 0, 2), + .div = FRAC_DIVIDER(0x0a18, 4, 12, 8), + .trig = TRIGGER(0x0afc, 4), +}; + +static struct peri_clk_data bsc1_data = { + .gate = HW_SW_GATE(0x0458, 18, 2, 3), + .clocks = CLOCKS("ref_crystal", + "var_104m", + "ref_104m", + "var_13m", + "ref_13m"), + .sel = SELECTOR(0x0a64, 0, 3), + .trig = TRIGGER(0x0afc, 23), +}; + +static struct peri_clk_data bsc2_data = { + .gate = HW_SW_GATE(0x045c, 18, 2, 3), + .clocks = CLOCKS("ref_crystal", + "var_104m", + "ref_104m", + "var_13m", + "ref_13m"), + .sel = SELECTOR(0x0a68, 0, 3), + .trig = TRIGGER(0x0afc, 24), +}; + +static struct peri_clk_data bsc3_data = { + .gate = HW_SW_GATE(0x0470, 18, 2, 3), + .clocks = CLOCKS("ref_crystal", + "var_104m", + "ref_104m", + "var_13m", + "ref_13m"), + .sel = SELECTOR(0x0a7c, 0, 3), + .trig = TRIGGER(0x0afc, 18), +}; + +static struct peri_clk_data bsc4_data = { + .gate = HW_SW_GATE(0x0474, 18, 2, 3), + .clocks = CLOCKS("ref_crystal", + "var_104m", + "ref_104m", + "var_13m", + "ref_13m"), + .sel = SELECTOR(0x0a80, 0, 3), + .trig = TRIGGER(0x0afc, 19), +}; + +static struct ccu_data slave_ccu_data = { + BCM21664_CCU_COMMON(slave, SLAVE), + .policy = { + .enable = CCU_LVM_EN(0x0034, 0), + .control = CCU_POLICY_CTL(0x000c, 0, 1, 2), + }, + .kona_clks = { + [BCM21664_SLAVE_CCU_UARTB] = + KONA_CLK(slave, uartb, peri), + [BCM21664_SLAVE_CCU_UARTB2] = + KONA_CLK(slave, uartb2, peri), + [BCM21664_SLAVE_CCU_UARTB3] = + KONA_CLK(slave, uartb3, peri), + [BCM21664_SLAVE_CCU_BSC1] = + KONA_CLK(slave, bsc1, peri), + [BCM21664_SLAVE_CCU_BSC2] = + KONA_CLK(slave, bsc2, peri), + [BCM21664_SLAVE_CCU_BSC3] = + KONA_CLK(slave, bsc3, peri), + [BCM21664_SLAVE_CCU_BSC4] = + KONA_CLK(slave, bsc4, peri), + [BCM21664_SLAVE_CCU_CLOCK_COUNT] = LAST_KONA_CLK, + }, +}; + +/* Device tree match table callback functions */ + +static void __init kona_dt_root_ccu_setup(struct device_node *node) +{ + kona_dt_ccu_setup(&root_ccu_data, node); +} + +static void __init kona_dt_aon_ccu_setup(struct device_node *node) +{ + kona_dt_ccu_setup(&aon_ccu_data, node); +} + +static void __init kona_dt_master_ccu_setup(struct device_node *node) +{ + kona_dt_ccu_setup(&master_ccu_data, node); +} + +static void __init kona_dt_slave_ccu_setup(struct device_node *node) +{ + kona_dt_ccu_setup(&slave_ccu_data, node); +} + +CLK_OF_DECLARE(bcm21664_root_ccu, BCM21664_DT_ROOT_CCU_COMPAT, + kona_dt_root_ccu_setup); +CLK_OF_DECLARE(bcm21664_aon_ccu, BCM21664_DT_AON_CCU_COMPAT, + kona_dt_aon_ccu_setup); +CLK_OF_DECLARE(bcm21664_master_ccu, BCM21664_DT_MASTER_CCU_COMPAT, + kona_dt_master_ccu_setup); +CLK_OF_DECLARE(bcm21664_slave_ccu, BCM21664_DT_SLAVE_CCU_COMPAT, + kona_dt_slave_ccu_setup); diff --git a/drivers/clk/bcm/clk-bcm281xx.c b/drivers/clk/bcm/clk-bcm281xx.c index 3c66de696aeb..502a487d62c5 100644 --- a/drivers/clk/bcm/clk-bcm281xx.c +++ b/drivers/clk/bcm/clk-bcm281xx.c @@ -15,14 +15,10 @@ #include "clk-kona.h" #include "dt-bindings/clock/bcm281xx.h" -/* bcm11351 CCU device tree "compatible" strings */ -#define BCM11351_DT_ROOT_CCU_COMPAT "brcm,bcm11351-root-ccu" -#define BCM11351_DT_AON_CCU_COMPAT "brcm,bcm11351-aon-ccu" -#define BCM11351_DT_HUB_CCU_COMPAT "brcm,bcm11351-hub-ccu" -#define BCM11351_DT_MASTER_CCU_COMPAT "brcm,bcm11351-master-ccu" -#define BCM11351_DT_SLAVE_CCU_COMPAT "brcm,bcm11351-slave-ccu" +#define BCM281XX_CCU_COMMON(_name, _ucase_name) \ + KONA_CCU_COMMON(BCM281XX, _name, _ucase_name) -/* Root CCU clocks */ +/* Root CCU */ static struct peri_clk_data frac_1m_data = { .gate = HW_SW_GATE(0x214, 16, 0, 1), @@ -31,7 +27,16 @@ static struct peri_clk_data frac_1m_data = { .clocks = CLOCKS("ref_crystal"), }; -/* AON CCU clocks */ +static struct ccu_data root_ccu_data = { + BCM281XX_CCU_COMMON(root, ROOT), + .kona_clks = { + [BCM281XX_ROOT_CCU_FRAC_1M] = + KONA_CLK(root, frac_1m, peri), + [BCM281XX_ROOT_CCU_CLOCK_COUNT] = LAST_KONA_CLK, + }, +}; + +/* AON CCU */ static struct peri_clk_data hub_timer_data = { .gate = HW_SW_GATE(0x0414, 16, 0, 1), @@ -60,7 +65,20 @@ static struct peri_clk_data pmu_bsc_var_data = { .trig = TRIGGER(0x0a40, 2), }; -/* Hub CCU clocks */ +static struct ccu_data aon_ccu_data = { + BCM281XX_CCU_COMMON(aon, AON), + .kona_clks = { + [BCM281XX_AON_CCU_HUB_TIMER] = + KONA_CLK(aon, hub_timer, peri), + [BCM281XX_AON_CCU_PMU_BSC] = + KONA_CLK(aon, pmu_bsc, peri), + [BCM281XX_AON_CCU_PMU_BSC_VAR] = + KONA_CLK(aon, pmu_bsc_var, peri), + [BCM281XX_AON_CCU_CLOCK_COUNT] = LAST_KONA_CLK, + }, +}; + +/* Hub CCU */ static struct peri_clk_data tmon_1m_data = { .gate = HW_SW_GATE(0x04a4, 18, 2, 3), @@ -70,7 +88,16 @@ static struct peri_clk_data tmon_1m_data = { .trig = TRIGGER(0x0e84, 1), }; -/* Master CCU clocks */ +static struct ccu_data hub_ccu_data = { + BCM281XX_CCU_COMMON(hub, HUB), + .kona_clks = { + [BCM281XX_HUB_CCU_TMON_1M] = + KONA_CLK(hub, tmon_1m, peri), + [BCM281XX_HUB_CCU_CLOCK_COUNT] = LAST_KONA_CLK, + }, +}; + +/* Master CCU */ static struct peri_clk_data sdio1_data = { .gate = HW_SW_GATE(0x0358, 18, 2, 3), @@ -153,7 +180,28 @@ static struct peri_clk_data hsic2_12m_data = { .trig = TRIGGER(0x0afc, 5), }; -/* Slave CCU clocks */ +static struct ccu_data master_ccu_data = { + BCM281XX_CCU_COMMON(master, MASTER), + .kona_clks = { + [BCM281XX_MASTER_CCU_SDIO1] = + KONA_CLK(master, sdio1, peri), + [BCM281XX_MASTER_CCU_SDIO2] = + KONA_CLK(master, sdio2, peri), + [BCM281XX_MASTER_CCU_SDIO3] = + KONA_CLK(master, sdio3, peri), + [BCM281XX_MASTER_CCU_SDIO4] = + KONA_CLK(master, sdio4, peri), + [BCM281XX_MASTER_CCU_USB_IC] = + KONA_CLK(master, usb_ic, peri), + [BCM281XX_MASTER_CCU_HSIC2_48M] = + KONA_CLK(master, hsic2_48m, peri), + [BCM281XX_MASTER_CCU_HSIC2_12M] = + KONA_CLK(master, hsic2_12m, peri), + [BCM281XX_MASTER_CCU_CLOCK_COUNT] = LAST_KONA_CLK, + }, +}; + +/* Slave CCU */ static struct peri_clk_data uartb_data = { .gate = HW_SW_GATE(0x0400, 18, 2, 3), @@ -261,156 +309,67 @@ static struct peri_clk_data pwm_data = { .trig = TRIGGER(0x0afc, 15), }; -/* - * CCU setup routines - * - * These are called from kona_dt_ccu_setup() to initialize the array - * of clocks provided by the CCU. Once allocated, the entries in - * the array are initialized by calling kona_clk_setup() with the - * initialization data for each clock. They return 0 if successful - * or an error code otherwise. - */ -static int __init bcm281xx_root_ccu_clks_setup(struct ccu_data *ccu) -{ - struct clk **clks; - size_t count = BCM281XX_ROOT_CCU_CLOCK_COUNT; - - clks = kzalloc(count * sizeof(*clks), GFP_KERNEL); - if (!clks) { - pr_err("%s: failed to allocate root clocks\n", __func__); - return -ENOMEM; - } - ccu->data.clks = clks; - ccu->data.clk_num = count; - - PERI_CLK_SETUP(clks, ccu, BCM281XX_ROOT_CCU_FRAC_1M, frac_1m); - - return 0; -} - -static int __init bcm281xx_aon_ccu_clks_setup(struct ccu_data *ccu) -{ - struct clk **clks; - size_t count = BCM281XX_AON_CCU_CLOCK_COUNT; - - clks = kzalloc(count * sizeof(*clks), GFP_KERNEL); - if (!clks) { - pr_err("%s: failed to allocate aon clocks\n", __func__); - return -ENOMEM; - } - ccu->data.clks = clks; - ccu->data.clk_num = count; - - PERI_CLK_SETUP(clks, ccu, BCM281XX_AON_CCU_HUB_TIMER, hub_timer); - PERI_CLK_SETUP(clks, ccu, BCM281XX_AON_CCU_PMU_BSC, pmu_bsc); - PERI_CLK_SETUP(clks, ccu, BCM281XX_AON_CCU_PMU_BSC_VAR, pmu_bsc_var); - - return 0; -} - -static int __init bcm281xx_hub_ccu_clks_setup(struct ccu_data *ccu) -{ - struct clk **clks; - size_t count = BCM281XX_HUB_CCU_CLOCK_COUNT; - - clks = kzalloc(count * sizeof(*clks), GFP_KERNEL); - if (!clks) { - pr_err("%s: failed to allocate hub clocks\n", __func__); - return -ENOMEM; - } - ccu->data.clks = clks; - ccu->data.clk_num = count; - - PERI_CLK_SETUP(clks, ccu, BCM281XX_HUB_CCU_TMON_1M, tmon_1m); - - return 0; -} - -static int __init bcm281xx_master_ccu_clks_setup(struct ccu_data *ccu) -{ - struct clk **clks; - size_t count = BCM281XX_MASTER_CCU_CLOCK_COUNT; - - clks = kzalloc(count * sizeof(*clks), GFP_KERNEL); - if (!clks) { - pr_err("%s: failed to allocate master clocks\n", __func__); - return -ENOMEM; - } - ccu->data.clks = clks; - ccu->data.clk_num = count; - - PERI_CLK_SETUP(clks, ccu, BCM281XX_MASTER_CCU_SDIO1, sdio1); - PERI_CLK_SETUP(clks, ccu, BCM281XX_MASTER_CCU_SDIO2, sdio2); - PERI_CLK_SETUP(clks, ccu, BCM281XX_MASTER_CCU_SDIO3, sdio3); - PERI_CLK_SETUP(clks, ccu, BCM281XX_MASTER_CCU_SDIO4, sdio4); - PERI_CLK_SETUP(clks, ccu, BCM281XX_MASTER_CCU_USB_IC, usb_ic); - PERI_CLK_SETUP(clks, ccu, BCM281XX_MASTER_CCU_HSIC2_48M, hsic2_48m); - PERI_CLK_SETUP(clks, ccu, BCM281XX_MASTER_CCU_HSIC2_12M, hsic2_12m); - - return 0; -} - -static int __init bcm281xx_slave_ccu_clks_setup(struct ccu_data *ccu) -{ - struct clk **clks; - size_t count = BCM281XX_SLAVE_CCU_CLOCK_COUNT; - - clks = kzalloc(count * sizeof(*clks), GFP_KERNEL); - if (!clks) { - pr_err("%s: failed to allocate slave clocks\n", __func__); - return -ENOMEM; - } - ccu->data.clks = clks; - ccu->data.clk_num = count; - - PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_UARTB, uartb); - PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_UARTB2, uartb2); - PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_UARTB3, uartb3); - PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_UARTB4, uartb4); - PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_SSP0, ssp0); - PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_SSP2, ssp2); - PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_BSC1, bsc1); - PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_BSC2, bsc2); - PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_BSC3, bsc3); - PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_PWM, pwm); - - return 0; -} +static struct ccu_data slave_ccu_data = { + BCM281XX_CCU_COMMON(slave, SLAVE), + .kona_clks = { + [BCM281XX_SLAVE_CCU_UARTB] = + KONA_CLK(slave, uartb, peri), + [BCM281XX_SLAVE_CCU_UARTB2] = + KONA_CLK(slave, uartb2, peri), + [BCM281XX_SLAVE_CCU_UARTB3] = + KONA_CLK(slave, uartb3, peri), + [BCM281XX_SLAVE_CCU_UARTB4] = + KONA_CLK(slave, uartb4, peri), + [BCM281XX_SLAVE_CCU_SSP0] = + KONA_CLK(slave, ssp0, peri), + [BCM281XX_SLAVE_CCU_SSP2] = + KONA_CLK(slave, ssp2, peri), + [BCM281XX_SLAVE_CCU_BSC1] = + KONA_CLK(slave, bsc1, peri), + [BCM281XX_SLAVE_CCU_BSC2] = + KONA_CLK(slave, bsc2, peri), + [BCM281XX_SLAVE_CCU_BSC3] = + KONA_CLK(slave, bsc3, peri), + [BCM281XX_SLAVE_CCU_PWM] = + KONA_CLK(slave, pwm, peri), + [BCM281XX_SLAVE_CCU_CLOCK_COUNT] = LAST_KONA_CLK, + }, +}; /* Device tree match table callback functions */ static void __init kona_dt_root_ccu_setup(struct device_node *node) { - kona_dt_ccu_setup(node, bcm281xx_root_ccu_clks_setup); + kona_dt_ccu_setup(&root_ccu_data, node); } static void __init kona_dt_aon_ccu_setup(struct device_node *node) { - kona_dt_ccu_setup(node, bcm281xx_aon_ccu_clks_setup); + kona_dt_ccu_setup(&aon_ccu_data, node); } static void __init kona_dt_hub_ccu_setup(struct device_node *node) { - kona_dt_ccu_setup(node, bcm281xx_hub_ccu_clks_setup); + kona_dt_ccu_setup(&hub_ccu_data, node); } static void __init kona_dt_master_ccu_setup(struct device_node *node) { - kona_dt_ccu_setup(node, bcm281xx_master_ccu_clks_setup); + kona_dt_ccu_setup(&master_ccu_data, node); } static void __init kona_dt_slave_ccu_setup(struct device_node *node) { - kona_dt_ccu_setup(node, bcm281xx_slave_ccu_clks_setup); + kona_dt_ccu_setup(&slave_ccu_data, node); } -CLK_OF_DECLARE(bcm11351_root_ccu, BCM11351_DT_ROOT_CCU_COMPAT, +CLK_OF_DECLARE(bcm281xx_root_ccu, BCM281XX_DT_ROOT_CCU_COMPAT, kona_dt_root_ccu_setup); -CLK_OF_DECLARE(bcm11351_aon_ccu, BCM11351_DT_AON_CCU_COMPAT, +CLK_OF_DECLARE(bcm281xx_aon_ccu, BCM281XX_DT_AON_CCU_COMPAT, kona_dt_aon_ccu_setup); -CLK_OF_DECLARE(bcm11351_hub_ccu, BCM11351_DT_HUB_CCU_COMPAT, +CLK_OF_DECLARE(bcm281xx_hub_ccu, BCM281XX_DT_HUB_CCU_COMPAT, kona_dt_hub_ccu_setup); -CLK_OF_DECLARE(bcm11351_master_ccu, BCM11351_DT_MASTER_CCU_COMPAT, +CLK_OF_DECLARE(bcm281xx_master_ccu, BCM281XX_DT_MASTER_CCU_COMPAT, kona_dt_master_ccu_setup); -CLK_OF_DECLARE(bcm11351_slave_ccu, BCM11351_DT_SLAVE_CCU_COMPAT, +CLK_OF_DECLARE(bcm281xx_slave_ccu, BCM281XX_DT_SLAVE_CCU_COMPAT, kona_dt_slave_ccu_setup); diff --git a/drivers/clk/bcm/clk-kona-setup.c b/drivers/clk/bcm/clk-kona-setup.c index c7607feb18dd..e5aededdd322 100644 --- a/drivers/clk/bcm/clk-kona-setup.c +++ b/drivers/clk/bcm/clk-kona-setup.c @@ -25,9 +25,34 @@ LIST_HEAD(ccu_list); /* The list of set up CCUs */ /* Validity checking */ +static bool ccu_data_offsets_valid(struct ccu_data *ccu) +{ + struct ccu_policy *ccu_policy = &ccu->policy; + u32 limit; + + limit = ccu->range - sizeof(u32); + limit = round_down(limit, sizeof(u32)); + if (ccu_policy_exists(ccu_policy)) { + if (ccu_policy->enable.offset > limit) { + pr_err("%s: bad policy enable offset for %s " + "(%u > %u)\n", __func__, + ccu->name, ccu_policy->enable.offset, limit); + return false; + } + if (ccu_policy->control.offset > limit) { + pr_err("%s: bad policy control offset for %s " + "(%u > %u)\n", __func__, + ccu->name, ccu_policy->control.offset, limit); + return false; + } + } + + return true; +} + static bool clk_requires_trigger(struct kona_clk *bcm_clk) { - struct peri_clk_data *peri = bcm_clk->peri; + struct peri_clk_data *peri = bcm_clk->u.peri; struct bcm_clk_sel *sel; struct bcm_clk_div *div; @@ -54,7 +79,9 @@ static bool clk_requires_trigger(struct kona_clk *bcm_clk) static bool peri_clk_data_offsets_valid(struct kona_clk *bcm_clk) { struct peri_clk_data *peri; + struct bcm_clk_policy *policy; struct bcm_clk_gate *gate; + struct bcm_clk_hyst *hyst; struct bcm_clk_div *div; struct bcm_clk_sel *sel; struct bcm_clk_trig *trig; @@ -63,37 +90,59 @@ static bool peri_clk_data_offsets_valid(struct kona_clk *bcm_clk) u32 limit; BUG_ON(bcm_clk->type != bcm_clk_peri); - peri = bcm_clk->peri; - name = bcm_clk->name; + peri = bcm_clk->u.peri; + name = bcm_clk->init_data.name; range = bcm_clk->ccu->range; limit = range - sizeof(u32); limit = round_down(limit, sizeof(u32)); + policy = &peri->policy; + if (policy_exists(policy)) { + if (policy->offset > limit) { + pr_err("%s: bad policy offset for %s (%u > %u)\n", + __func__, name, policy->offset, limit); + return false; + } + } + gate = &peri->gate; + hyst = &peri->hyst; if (gate_exists(gate)) { if (gate->offset > limit) { pr_err("%s: bad gate offset for %s (%u > %u)\n", __func__, name, gate->offset, limit); return false; } + + if (hyst_exists(hyst)) { + if (hyst->offset > limit) { + pr_err("%s: bad hysteresis offset for %s " + "(%u > %u)\n", __func__, + name, hyst->offset, limit); + return false; + } + } + } else if (hyst_exists(hyst)) { + pr_err("%s: hysteresis but no gate for %s\n", __func__, name); + return false; } div = &peri->div; if (divider_exists(div)) { - if (div->offset > limit) { + if (div->u.s.offset > limit) { pr_err("%s: bad divider offset for %s (%u > %u)\n", - __func__, name, div->offset, limit); + __func__, name, div->u.s.offset, limit); return false; } } div = &peri->pre_div; if (divider_exists(div)) { - if (div->offset > limit) { + if (div->u.s.offset > limit) { pr_err("%s: bad pre-divider offset for %s " "(%u > %u)\n", - __func__, name, div->offset, limit); + __func__, name, div->u.s.offset, limit); return false; } } @@ -167,6 +216,36 @@ static bool bitfield_valid(u32 shift, u32 width, const char *field_name, return true; } +static bool +ccu_policy_valid(struct ccu_policy *ccu_policy, const char *ccu_name) +{ + struct bcm_lvm_en *enable = &ccu_policy->enable; + struct bcm_policy_ctl *control; + + if (!bit_posn_valid(enable->bit, "policy enable", ccu_name)) + return false; + + control = &ccu_policy->control; + if (!bit_posn_valid(control->go_bit, "policy control GO", ccu_name)) + return false; + + if (!bit_posn_valid(control->atl_bit, "policy control ATL", ccu_name)) + return false; + + if (!bit_posn_valid(control->ac_bit, "policy control AC", ccu_name)) + return false; + + return true; +} + +static bool policy_valid(struct bcm_clk_policy *policy, const char *clock_name) +{ + if (!bit_posn_valid(policy->bit, "policy", clock_name)) + return false; + + return true; +} + /* * All gates, if defined, have a status bit, and for hardware-only * gates, that's it. Gates that can be software controlled also @@ -196,6 +275,17 @@ static bool gate_valid(struct bcm_clk_gate *gate, const char *field_name, return true; } +static bool hyst_valid(struct bcm_clk_hyst *hyst, const char *clock_name) +{ + if (!bit_posn_valid(hyst->en_bit, "hysteresis enable", clock_name)) + return false; + + if (!bit_posn_valid(hyst->val_bit, "hysteresis value", clock_name)) + return false; + + return true; +} + /* * A selector bitfield must be valid. Its parent_sel array must * also be reasonable for the field. @@ -249,21 +339,22 @@ static bool div_valid(struct bcm_clk_div *div, const char *field_name, { if (divider_is_fixed(div)) { /* Any fixed divider value but 0 is OK */ - if (div->fixed == 0) { + if (div->u.fixed == 0) { pr_err("%s: bad %s fixed value 0 for %s\n", __func__, field_name, clock_name); return false; } return true; } - if (!bitfield_valid(div->shift, div->width, field_name, clock_name)) + if (!bitfield_valid(div->u.s.shift, div->u.s.width, + field_name, clock_name)) return false; if (divider_has_fraction(div)) - if (div->frac_width > div->width) { + if (div->u.s.frac_width > div->u.s.width) { pr_warn("%s: bad %s fraction width for %s (%u > %u)\n", __func__, field_name, clock_name, - div->frac_width, div->width); + div->u.s.frac_width, div->u.s.width); return false; } @@ -278,7 +369,7 @@ static bool div_valid(struct bcm_clk_div *div, const char *field_name, */ static bool kona_dividers_valid(struct kona_clk *bcm_clk) { - struct peri_clk_data *peri = bcm_clk->peri; + struct peri_clk_data *peri = bcm_clk->u.peri; struct bcm_clk_div *div; struct bcm_clk_div *pre_div; u32 limit; @@ -295,7 +386,7 @@ static bool kona_dividers_valid(struct kona_clk *bcm_clk) limit = BITS_PER_BYTE * sizeof(u32); - return div->frac_width + pre_div->frac_width <= limit; + return div->u.s.frac_width + pre_div->u.s.frac_width <= limit; } @@ -311,7 +402,9 @@ static bool peri_clk_data_valid(struct kona_clk *bcm_clk) { struct peri_clk_data *peri; + struct bcm_clk_policy *policy; struct bcm_clk_gate *gate; + struct bcm_clk_hyst *hyst; struct bcm_clk_sel *sel; struct bcm_clk_div *div; struct bcm_clk_div *pre_div; @@ -328,12 +421,21 @@ peri_clk_data_valid(struct kona_clk *bcm_clk) if (!peri_clk_data_offsets_valid(bcm_clk)) return false; - peri = bcm_clk->peri; - name = bcm_clk->name; + peri = bcm_clk->u.peri; + name = bcm_clk->init_data.name; + + policy = &peri->policy; + if (policy_exists(policy) && !policy_valid(policy, name)) + return false; + gate = &peri->gate; if (gate_exists(gate) && !gate_valid(gate, "gate", name)) return false; + hyst = &peri->hyst; + if (hyst_exists(hyst) && !hyst_valid(hyst, name)) + return false; + sel = &peri->sel; if (selector_exists(sel)) { if (!sel_valid(sel, "selector", name)) @@ -566,7 +668,6 @@ static void peri_clk_teardown(struct peri_clk_data *data, struct clk_init_data *init_data) { clk_sel_teardown(&data->sel, init_data); - init_data->ops = NULL; } /* @@ -575,10 +676,9 @@ static void peri_clk_teardown(struct peri_clk_data *data, * that can be assigned if the clock has one or more parent clocks * associated with it. */ -static int peri_clk_setup(struct ccu_data *ccu, struct peri_clk_data *data, - struct clk_init_data *init_data) +static int +peri_clk_setup(struct peri_clk_data *data, struct clk_init_data *init_data) { - init_data->ops = &kona_peri_clk_ops; init_data->flags = CLK_IGNORE_UNUSED; return clk_sel_setup(data->clocks, &data->sel, init_data); @@ -588,12 +688,12 @@ static void bcm_clk_teardown(struct kona_clk *bcm_clk) { switch (bcm_clk->type) { case bcm_clk_peri: - peri_clk_teardown(bcm_clk->data, &bcm_clk->init_data); + peri_clk_teardown(bcm_clk->u.data, &bcm_clk->init_data); break; default: break; } - bcm_clk->data = NULL; + bcm_clk->u.data = NULL; bcm_clk->type = bcm_clk_none; } @@ -616,39 +716,26 @@ static void kona_clk_teardown(struct clk *clk) bcm_clk_teardown(bcm_clk); } -struct clk *kona_clk_setup(struct ccu_data *ccu, const char *name, - enum bcm_clk_type type, void *data) +struct clk *kona_clk_setup(struct kona_clk *bcm_clk) { - struct kona_clk *bcm_clk; - struct clk_init_data *init_data; + struct clk_init_data *init_data = &bcm_clk->init_data; struct clk *clk = NULL; - bcm_clk = kzalloc(sizeof(*bcm_clk), GFP_KERNEL); - if (!bcm_clk) { - pr_err("%s: failed to allocate bcm_clk for %s\n", __func__, - name); - return NULL; - } - bcm_clk->ccu = ccu; - bcm_clk->name = name; - - init_data = &bcm_clk->init_data; - init_data->name = name; - switch (type) { + switch (bcm_clk->type) { case bcm_clk_peri: - if (peri_clk_setup(ccu, data, init_data)) - goto out_free; + if (peri_clk_setup(bcm_clk->u.data, init_data)) + return NULL; break; default: - data = NULL; - break; + pr_err("%s: clock type %d invalid for %s\n", __func__, + (int)bcm_clk->type, init_data->name); + return NULL; } - bcm_clk->type = type; - bcm_clk->data = data; /* Make sure everything makes sense before we set it up */ if (!kona_clk_valid(bcm_clk)) { - pr_err("%s: clock data invalid for %s\n", __func__, name); + pr_err("%s: clock data invalid for %s\n", __func__, + init_data->name); goto out_teardown; } @@ -656,7 +743,7 @@ struct clk *kona_clk_setup(struct ccu_data *ccu, const char *name, clk = clk_register(NULL, &bcm_clk->hw); if (IS_ERR(clk)) { pr_err("%s: error registering clock %s (%ld)\n", __func__, - name, PTR_ERR(clk)); + init_data->name, PTR_ERR(clk)); goto out_teardown; } BUG_ON(!clk); @@ -664,8 +751,6 @@ struct clk *kona_clk_setup(struct ccu_data *ccu, const char *name, return clk; out_teardown: bcm_clk_teardown(bcm_clk); -out_free: - kfree(bcm_clk); return NULL; } @@ -674,50 +759,64 @@ static void ccu_clks_teardown(struct ccu_data *ccu) { u32 i; - for (i = 0; i < ccu->data.clk_num; i++) - kona_clk_teardown(ccu->data.clks[i]); - kfree(ccu->data.clks); + for (i = 0; i < ccu->clk_data.clk_num; i++) + kona_clk_teardown(ccu->clk_data.clks[i]); + kfree(ccu->clk_data.clks); } static void kona_ccu_teardown(struct ccu_data *ccu) { - if (!ccu) - return; - + kfree(ccu->clk_data.clks); + ccu->clk_data.clks = NULL; if (!ccu->base) - goto done; + return; of_clk_del_provider(ccu->node); /* safe if never added */ ccu_clks_teardown(ccu); list_del(&ccu->links); of_node_put(ccu->node); + ccu->node = NULL; iounmap(ccu->base); -done: - kfree(ccu->name); - kfree(ccu); + ccu->base = NULL; +} + +static bool ccu_data_valid(struct ccu_data *ccu) +{ + struct ccu_policy *ccu_policy; + + if (!ccu_data_offsets_valid(ccu)) + return false; + + ccu_policy = &ccu->policy; + if (ccu_policy_exists(ccu_policy)) + if (!ccu_policy_valid(ccu_policy, ccu->name)) + return false; + + return true; } /* * Set up a CCU. Call the provided ccu_clks_setup callback to * initialize the array of clocks provided by the CCU. */ -void __init kona_dt_ccu_setup(struct device_node *node, - int (*ccu_clks_setup)(struct ccu_data *)) +void __init kona_dt_ccu_setup(struct ccu_data *ccu, + struct device_node *node) { - struct ccu_data *ccu; struct resource res = { 0 }; resource_size_t range; + unsigned int i; int ret; - ccu = kzalloc(sizeof(*ccu), GFP_KERNEL); - if (ccu) - ccu->name = kstrdup(node->name, GFP_KERNEL); - if (!ccu || !ccu->name) { - pr_err("%s: unable to allocate CCU struct for %s\n", - __func__, node->name); - kfree(ccu); + if (ccu->clk_data.clk_num) { + size_t size; - return; + size = ccu->clk_data.clk_num * sizeof(*ccu->clk_data.clks); + ccu->clk_data.clks = kzalloc(size, GFP_KERNEL); + if (!ccu->clk_data.clks) { + pr_err("%s: unable to allocate %u clocks for %s\n", + __func__, ccu->clk_data.clk_num, node->name); + return; + } } ret = of_address_to_resource(node, 0, &res); @@ -735,24 +834,33 @@ void __init kona_dt_ccu_setup(struct device_node *node, } ccu->range = (u32)range; + + if (!ccu_data_valid(ccu)) { + pr_err("%s: ccu data not valid for %s\n", __func__, node->name); + goto out_err; + } + ccu->base = ioremap(res.start, ccu->range); if (!ccu->base) { pr_err("%s: unable to map CCU registers for %s\n", __func__, node->name); goto out_err; } - - spin_lock_init(&ccu->lock); - INIT_LIST_HEAD(&ccu->links); ccu->node = of_node_get(node); - list_add_tail(&ccu->links, &ccu_list); - /* Set up clocks array (in ccu->data) */ - if (ccu_clks_setup(ccu)) - goto out_err; + /* + * Set up each defined kona clock and save the result in + * the clock framework clock array (in ccu->data). Then + * register as a provider for these clocks. + */ + for (i = 0; i < ccu->clk_data.clk_num; i++) { + if (!ccu->kona_clks[i].ccu) + continue; + ccu->clk_data.clks[i] = kona_clk_setup(&ccu->kona_clks[i]); + } - ret = of_clk_add_provider(node, of_clk_src_onecell_get, &ccu->data); + ret = of_clk_add_provider(node, of_clk_src_onecell_get, &ccu->clk_data); if (ret) { pr_err("%s: error adding ccu %s as provider (%d)\n", __func__, node->name, ret); diff --git a/drivers/clk/bcm/clk-kona.c b/drivers/clk/bcm/clk-kona.c index e3d339e08309..d603c4e22fca 100644 --- a/drivers/clk/bcm/clk-kona.c +++ b/drivers/clk/bcm/clk-kona.c @@ -16,6 +16,14 @@ #include <linux/delay.h> +/* + * "Policies" affect the frequencies of bus clocks provided by a + * CCU. (I believe these polices are named "Deep Sleep", "Economy", + * "Normal", and "Turbo".) A lower policy number has lower power + * consumption, and policy 2 is the default. + */ +#define CCU_POLICY_COUNT 4 + #define CCU_ACCESS_PASSWORD 0xA5A500 #define CLK_GATE_DELAY_LOOP 2000 @@ -61,7 +69,7 @@ u64 do_div_round_closest(u64 dividend, unsigned long divisor) /* Convert a divider into the scaled divisor value it represents. */ static inline u64 scaled_div_value(struct bcm_clk_div *div, u32 reg_div) { - return (u64)reg_div + ((u64)1 << div->frac_width); + return (u64)reg_div + ((u64)1 << div->u.s.frac_width); } /* @@ -77,7 +85,7 @@ u64 scaled_div_build(struct bcm_clk_div *div, u32 div_value, u32 billionths) BUG_ON(billionths >= BILLION); combined = (u64)div_value * BILLION + billionths; - combined <<= div->frac_width; + combined <<= div->u.s.frac_width; return do_div_round_closest(combined, BILLION); } @@ -87,7 +95,7 @@ static inline u64 scaled_div_min(struct bcm_clk_div *div) { if (divider_is_fixed(div)) - return (u64)div->fixed; + return (u64)div->u.fixed; return scaled_div_value(div, 0); } @@ -98,9 +106,9 @@ u64 scaled_div_max(struct bcm_clk_div *div) u32 reg_div; if (divider_is_fixed(div)) - return (u64)div->fixed; + return (u64)div->u.fixed; - reg_div = ((u32)1 << div->width) - 1; + reg_div = ((u32)1 << div->u.s.width) - 1; return scaled_div_value(div, reg_div); } @@ -115,7 +123,7 @@ divider(struct bcm_clk_div *div, u64 scaled_div) BUG_ON(scaled_div < scaled_div_min(div)); BUG_ON(scaled_div > scaled_div_max(div)); - return (u32)(scaled_div - ((u64)1 << div->frac_width)); + return (u32)(scaled_div - ((u64)1 << div->u.s.frac_width)); } /* Return a rate scaled for use when dividing by a scaled divisor. */ @@ -125,7 +133,7 @@ scale_rate(struct bcm_clk_div *div, u32 rate) if (divider_is_fixed(div)) return (u64)rate; - return (u64)rate << div->frac_width; + return (u64)rate << div->u.s.frac_width; } /* CCU access */ @@ -207,9 +215,154 @@ __ccu_wait_bit(struct ccu_data *ccu, u32 reg_offset, u32 bit, bool want) return true; udelay(1); } + pr_warn("%s: %s/0x%04x bit %u was never %s\n", __func__, + ccu->name, reg_offset, bit, want ? "set" : "clear"); + return false; } +/* Policy operations */ + +static bool __ccu_policy_engine_start(struct ccu_data *ccu, bool sync) +{ + struct bcm_policy_ctl *control = &ccu->policy.control; + u32 offset; + u32 go_bit; + u32 mask; + bool ret; + + /* If we don't need to control policy for this CCU, we're done. */ + if (!policy_ctl_exists(control)) + return true; + + offset = control->offset; + go_bit = control->go_bit; + + /* Ensure we're not busy before we start */ + ret = __ccu_wait_bit(ccu, offset, go_bit, false); + if (!ret) { + pr_err("%s: ccu %s policy engine wouldn't go idle\n", + __func__, ccu->name); + return false; + } + + /* + * If it's a synchronous request, we'll wait for the voltage + * and frequency of the active load to stabilize before + * returning. To do this we select the active load by + * setting the ATL bit. + * + * An asynchronous request instead ramps the voltage in the + * background, and when that process stabilizes, the target + * load is copied to the active load and the CCU frequency + * is switched. We do this by selecting the target load + * (ATL bit clear) and setting the request auto-copy (AC bit + * set). + * + * Note, we do NOT read-modify-write this register. + */ + mask = (u32)1 << go_bit; + if (sync) + mask |= 1 << control->atl_bit; + else + mask |= 1 << control->ac_bit; + __ccu_write(ccu, offset, mask); + + /* Wait for indication that operation is complete. */ + ret = __ccu_wait_bit(ccu, offset, go_bit, false); + if (!ret) + pr_err("%s: ccu %s policy engine never started\n", + __func__, ccu->name); + + return ret; +} + +static bool __ccu_policy_engine_stop(struct ccu_data *ccu) +{ + struct bcm_lvm_en *enable = &ccu->policy.enable; + u32 offset; + u32 enable_bit; + bool ret; + + /* If we don't need to control policy for this CCU, we're done. */ + if (!policy_lvm_en_exists(enable)) + return true; + + /* Ensure we're not busy before we start */ + offset = enable->offset; + enable_bit = enable->bit; + ret = __ccu_wait_bit(ccu, offset, enable_bit, false); + if (!ret) { + pr_err("%s: ccu %s policy engine already stopped\n", + __func__, ccu->name); + return false; + } + + /* Now set the bit to stop the engine (NO read-modify-write) */ + __ccu_write(ccu, offset, (u32)1 << enable_bit); + + /* Wait for indication that it has stopped. */ + ret = __ccu_wait_bit(ccu, offset, enable_bit, false); + if (!ret) + pr_err("%s: ccu %s policy engine never stopped\n", + __func__, ccu->name); + + return ret; +} + +/* + * A CCU has four operating conditions ("policies"), and some clocks + * can be disabled or enabled based on which policy is currently in + * effect. Such clocks have a bit in a "policy mask" register for + * each policy indicating whether the clock is enabled for that + * policy or not. The bit position for a clock is the same for all + * four registers, and the 32-bit registers are at consecutive + * addresses. + */ +static bool policy_init(struct ccu_data *ccu, struct bcm_clk_policy *policy) +{ + u32 offset; + u32 mask; + int i; + bool ret; + + if (!policy_exists(policy)) + return true; + + /* + * We need to stop the CCU policy engine to allow update + * of our policy bits. + */ + if (!__ccu_policy_engine_stop(ccu)) { + pr_err("%s: unable to stop CCU %s policy engine\n", + __func__, ccu->name); + return false; + } + + /* + * For now, if a clock defines its policy bit we just mark + * it "enabled" for all four policies. + */ + offset = policy->offset; + mask = (u32)1 << policy->bit; + for (i = 0; i < CCU_POLICY_COUNT; i++) { + u32 reg_val; + + reg_val = __ccu_read(ccu, offset); + reg_val |= mask; + __ccu_write(ccu, offset, reg_val); + offset += sizeof(u32); + } + + /* We're done updating; fire up the policy engine again. */ + ret = __ccu_policy_engine_start(ccu, true); + if (!ret) + pr_err("%s: unable to restart CCU %s policy engine\n", + __func__, ccu->name); + + return ret; +} + /* Gate operations */ /* Determine whether a clock is gated. CCU lock must be held. */ @@ -374,6 +527,35 @@ static int clk_gate(struct ccu_data *ccu, const char *name, return -EIO; } +/* Hysteresis operations */ + +/* + * If a clock gate requires a turn-off delay it will have + * "hysteresis" register bits defined. The first, if set, enables + * the delay; and if enabled, the second bit determines whether the + * delay is "low" or "high" (1 means high). For now, if it's + * defined for a clock, we set it. + */ +static bool hyst_init(struct ccu_data *ccu, struct bcm_clk_hyst *hyst) +{ + u32 offset; + u32 reg_val; + u32 mask; + + if (!hyst_exists(hyst)) + return true; + + offset = hyst->offset; + mask = (u32)1 << hyst->en_bit; + mask |= (u32)1 << hyst->val_bit; + + reg_val = __ccu_read(ccu, offset); + reg_val |= mask; + __ccu_write(ccu, offset, reg_val); + + return true; +} + /* Trigger operations */ /* @@ -398,14 +580,14 @@ static u64 divider_read_scaled(struct ccu_data *ccu, struct bcm_clk_div *div) u32 reg_div; if (divider_is_fixed(div)) - return (u64)div->fixed; + return (u64)div->u.fixed; flags = ccu_lock(ccu); - reg_val = __ccu_read(ccu, div->offset); + reg_val = __ccu_read(ccu, div->u.s.offset); ccu_unlock(ccu, flags); /* Extract the full divider field from the register value */ - reg_div = bitfield_extract(reg_val, div->shift, div->width); + reg_div = bitfield_extract(reg_val, div->u.s.shift, div->u.s.width); /* Return the scaled divisor value it represents */ return scaled_div_value(div, reg_div); @@ -433,16 +615,17 @@ static int __div_commit(struct ccu_data *ccu, struct bcm_clk_gate *gate, * state was defined in the device tree, we just find out * what its current value is rather than updating it. */ - if (div->scaled_div == BAD_SCALED_DIV_VALUE) { - reg_val = __ccu_read(ccu, div->offset); - reg_div = bitfield_extract(reg_val, div->shift, div->width); - div->scaled_div = scaled_div_value(div, reg_div); + if (div->u.s.scaled_div == BAD_SCALED_DIV_VALUE) { + reg_val = __ccu_read(ccu, div->u.s.offset); + reg_div = bitfield_extract(reg_val, div->u.s.shift, + div->u.s.width); + div->u.s.scaled_div = scaled_div_value(div, reg_div); return 0; } /* Convert the scaled divisor to the value we need to record */ - reg_div = divider(div, div->scaled_div); + reg_div = divider(div, div->u.s.scaled_div); /* Clock needs to be enabled before changing the rate */ enabled = __is_clk_gate_enabled(ccu, gate); @@ -452,9 +635,10 @@ static int __div_commit(struct ccu_data *ccu, struct bcm_clk_gate *gate, } /* Replace the divider value and record the result */ - reg_val = __ccu_read(ccu, div->offset); - reg_val = bitfield_replace(reg_val, div->shift, div->width, reg_div); - __ccu_write(ccu, div->offset, reg_val); + reg_val = __ccu_read(ccu, div->u.s.offset); + reg_val = bitfield_replace(reg_val, div->u.s.shift, div->u.s.width, + reg_div); + __ccu_write(ccu, div->u.s.offset, reg_val); /* If the trigger fails we still want to disable the gate */ if (!__clk_trigger(ccu, trig)) @@ -490,11 +674,11 @@ static int divider_write(struct ccu_data *ccu, struct bcm_clk_gate *gate, BUG_ON(divider_is_fixed(div)); - previous = div->scaled_div; + previous = div->u.s.scaled_div; if (previous == scaled_div) return 0; /* No change */ - div->scaled_div = scaled_div; + div->u.s.scaled_div = scaled_div; flags = ccu_lock(ccu); __ccu_write_enable(ccu); @@ -505,7 +689,7 @@ static int divider_write(struct ccu_data *ccu, struct bcm_clk_gate *gate, ccu_unlock(ccu, flags); if (ret) - div->scaled_div = previous; /* Revert the change */ + div->u.s.scaled_div = previous; /* Revert the change */ return ret; @@ -802,23 +986,23 @@ static int selector_write(struct ccu_data *ccu, struct bcm_clk_gate *gate, static int kona_peri_clk_enable(struct clk_hw *hw) { struct kona_clk *bcm_clk = to_kona_clk(hw); - struct bcm_clk_gate *gate = &bcm_clk->peri->gate; + struct bcm_clk_gate *gate = &bcm_clk->u.peri->gate; - return clk_gate(bcm_clk->ccu, bcm_clk->name, gate, true); + return clk_gate(bcm_clk->ccu, bcm_clk->init_data.name, gate, true); } static void kona_peri_clk_disable(struct clk_hw *hw) { struct kona_clk *bcm_clk = to_kona_clk(hw); - struct bcm_clk_gate *gate = &bcm_clk->peri->gate; + struct bcm_clk_gate *gate = &bcm_clk->u.peri->gate; - (void)clk_gate(bcm_clk->ccu, bcm_clk->name, gate, false); + (void)clk_gate(bcm_clk->ccu, bcm_clk->init_data.name, gate, false); } static int kona_peri_clk_is_enabled(struct clk_hw *hw) { struct kona_clk *bcm_clk = to_kona_clk(hw); - struct bcm_clk_gate *gate = &bcm_clk->peri->gate; + struct bcm_clk_gate *gate = &bcm_clk->u.peri->gate; return is_clk_gate_enabled(bcm_clk->ccu, gate) ? 1 : 0; } @@ -827,7 +1011,7 @@ static unsigned long kona_peri_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct kona_clk *bcm_clk = to_kona_clk(hw); - struct peri_clk_data *data = bcm_clk->peri; + struct peri_clk_data *data = bcm_clk->u.peri; return clk_recalc_rate(bcm_clk->ccu, &data->div, &data->pre_div, parent_rate); @@ -837,20 +1021,20 @@ static long kona_peri_clk_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) { struct kona_clk *bcm_clk = to_kona_clk(hw); - struct bcm_clk_div *div = &bcm_clk->peri->div; + struct bcm_clk_div *div = &bcm_clk->u.peri->div; if (!divider_exists(div)) return __clk_get_rate(hw->clk); /* Quietly avoid a zero rate */ - return round_rate(bcm_clk->ccu, div, &bcm_clk->peri->pre_div, + return round_rate(bcm_clk->ccu, div, &bcm_clk->u.peri->pre_div, rate ? rate : 1, *parent_rate, NULL); } static int kona_peri_clk_set_parent(struct clk_hw *hw, u8 index) { struct kona_clk *bcm_clk = to_kona_clk(hw); - struct peri_clk_data *data = bcm_clk->peri; + struct peri_clk_data *data = bcm_clk->u.peri; struct bcm_clk_sel *sel = &data->sel; struct bcm_clk_trig *trig; int ret; @@ -870,12 +1054,13 @@ static int kona_peri_clk_set_parent(struct clk_hw *hw, u8 index) ret = selector_write(bcm_clk->ccu, &data->gate, sel, trig, index); if (ret == -ENXIO) { - pr_err("%s: gating failure for %s\n", __func__, bcm_clk->name); + pr_err("%s: gating failure for %s\n", __func__, + bcm_clk->init_data.name); ret = -EIO; /* Don't proliferate weird errors */ } else if (ret == -EIO) { pr_err("%s: %strigger failed for %s\n", __func__, trig == &data->pre_trig ? "pre-" : "", - bcm_clk->name); + bcm_clk->init_data.name); } return ret; @@ -884,7 +1069,7 @@ static int kona_peri_clk_set_parent(struct clk_hw *hw, u8 index) static u8 kona_peri_clk_get_parent(struct clk_hw *hw) { struct kona_clk *bcm_clk = to_kona_clk(hw); - struct peri_clk_data *data = bcm_clk->peri; + struct peri_clk_data *data = bcm_clk->u.peri; u8 index; index = selector_read_index(bcm_clk->ccu, &data->sel); @@ -897,7 +1082,7 @@ static int kona_peri_clk_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct kona_clk *bcm_clk = to_kona_clk(hw); - struct peri_clk_data *data = bcm_clk->peri; + struct peri_clk_data *data = bcm_clk->u.peri; struct bcm_clk_div *div = &data->div; u64 scaled_div = 0; int ret; @@ -934,10 +1119,12 @@ static int kona_peri_clk_set_rate(struct clk_hw *hw, unsigned long rate, ret = divider_write(bcm_clk->ccu, &data->gate, &data->div, &data->trig, scaled_div); if (ret == -ENXIO) { - pr_err("%s: gating failure for %s\n", __func__, bcm_clk->name); + pr_err("%s: gating failure for %s\n", __func__, + bcm_clk->init_data.name); ret = -EIO; /* Don't proliferate weird errors */ } else if (ret == -EIO) { - pr_err("%s: trigger failed for %s\n", __func__, bcm_clk->name); + pr_err("%s: trigger failed for %s\n", __func__, + bcm_clk->init_data.name); } return ret; @@ -958,16 +1145,25 @@ struct clk_ops kona_peri_clk_ops = { static bool __peri_clk_init(struct kona_clk *bcm_clk) { struct ccu_data *ccu = bcm_clk->ccu; - struct peri_clk_data *peri = bcm_clk->peri; - const char *name = bcm_clk->name; + struct peri_clk_data *peri = bcm_clk->u.peri; + const char *name = bcm_clk->init_data.name; struct bcm_clk_trig *trig; BUG_ON(bcm_clk->type != bcm_clk_peri); + if (!policy_init(ccu, &peri->policy)) { + pr_err("%s: error initializing policy for %s\n", + __func__, name); + return false; + } if (!gate_init(ccu, &peri->gate)) { pr_err("%s: error initializing gate for %s\n", __func__, name); return false; } + if (!hyst_init(ccu, &peri->hyst)) { + pr_err("%s: error initializing hyst for %s\n", __func__, name); + return false; + } if (!div_init(ccu, &peri->gate, &peri->div, &peri->trig)) { pr_err("%s: error initializing divider for %s\n", __func__, name); @@ -1012,13 +1208,13 @@ bool __init kona_ccu_init(struct ccu_data *ccu) { unsigned long flags; unsigned int which; - struct clk **clks = ccu->data.clks; + struct clk **clks = ccu->clk_data.clks; bool success = true; flags = ccu_lock(ccu); __ccu_write_enable(ccu); - for (which = 0; which < ccu->data.clk_num; which++) { + for (which = 0; which < ccu->clk_data.clk_num; which++) { struct kona_clk *bcm_clk; if (!clks[which]) diff --git a/drivers/clk/bcm/clk-kona.h b/drivers/clk/bcm/clk-kona.h index 5e139adc3dc5..2537b3072910 100644 --- a/drivers/clk/bcm/clk-kona.h +++ b/drivers/clk/bcm/clk-kona.h @@ -43,8 +43,14 @@ #define FLAG_FLIP(obj, type, flag) ((obj)->flags ^= FLAG(type, flag)) #define FLAG_TEST(obj, type, flag) (!!((obj)->flags & FLAG(type, flag))) +/* CCU field state tests */ + +#define ccu_policy_exists(ccu_policy) ((ccu_policy)->enable.offset != 0) + /* Clock field state tests */ +#define policy_exists(policy) ((policy)->offset != 0) + #define gate_exists(gate) FLAG_TEST(gate, GATE, EXISTS) #define gate_is_enabled(gate) FLAG_TEST(gate, GATE, ENABLED) #define gate_is_hw_controllable(gate) FLAG_TEST(gate, GATE, HW) @@ -54,14 +60,19 @@ #define gate_flip_enabled(gate) FLAG_FLIP(gate, GATE, ENABLED) +#define hyst_exists(hyst) ((hyst)->offset != 0) + #define divider_exists(div) FLAG_TEST(div, DIV, EXISTS) #define divider_is_fixed(div) FLAG_TEST(div, DIV, FIXED) #define divider_has_fraction(div) (!divider_is_fixed(div) && \ - (div)->frac_width > 0) + (div)->u.s.frac_width > 0) #define selector_exists(sel) ((sel)->width != 0) #define trigger_exists(trig) FLAG_TEST(trig, TRIG, EXISTS) +#define policy_lvm_en_exists(enable) ((enable)->offset != 0) +#define policy_ctl_exists(control) ((control)->offset != 0) + /* Clock type, used to tell common block what it's part of */ enum bcm_clk_type { bcm_clk_none, /* undefined clock type */ @@ -71,25 +82,26 @@ enum bcm_clk_type { }; /* - * Each CCU defines a mapped area of memory containing registers - * used to manage clocks implemented by the CCU. Access to memory - * within the CCU's space is serialized by a spinlock. Before any - * (other) address can be written, a special access "password" value - * must be written to its WR_ACCESS register (located at the base - * address of the range). We keep track of the name of each CCU as - * it is set up, and maintain them in a list. + * CCU policy control for clocks. Clocks can be enabled or disabled + * based on the CCU policy in effect. One bit in each policy mask + * register (one per CCU policy) represents whether the clock is + * enabled when that policy is effect or not. The CCU policy engine + * must be stopped to update these bits, and must be restarted again + * afterward. */ -struct ccu_data { - void __iomem *base; /* base of mapped address space */ - spinlock_t lock; /* serialization lock */ - bool write_enabled; /* write access is currently enabled */ - struct list_head links; /* for ccu_list */ - struct device_node *node; - struct clk_onecell_data data; - const char *name; - u32 range; /* byte range of address space */ +struct bcm_clk_policy { + u32 offset; /* first policy mask register offset */ + u32 bit; /* bit used in all mask registers */ }; +/* Policy initialization macro */ + +#define POLICY(_offset, _bit) \ + { \ + .offset = (_offset), \ + .bit = (_bit), \ + } + /* * Gating control and status is managed by a 32-bit gate register. * @@ -195,6 +207,22 @@ struct bcm_clk_gate { .flags = FLAG(GATE, HW)|FLAG(GATE, EXISTS), \ } +/* Gate hysteresis for clocks */ +struct bcm_clk_hyst { + u32 offset; /* hyst register offset (normally CLKGATE) */ + u32 en_bit; /* bit used to enable hysteresis */ + u32 val_bit; /* if enabled: 0 = low delay; 1 = high delay */ +}; + +/* Hysteresis initialization macro */ + +#define HYST(_offset, _en_bit, _val_bit) \ + { \ + .offset = (_offset), \ + .en_bit = (_en_bit), \ + .val_bit = (_val_bit), \ + } + /* * Each clock can have zero, one, or two dividers which change the * output rate of the clock. Each divider can be either fixed or @@ -244,9 +272,9 @@ struct bcm_clk_div { u32 frac_width; /* field fraction width */ u64 scaled_div; /* scaled divider value */ - }; + } s; u32 fixed; /* non-zero fixed divider value */ - }; + } u; u32 flags; /* BCM_CLK_DIV_FLAGS_* below */ }; @@ -263,28 +291,28 @@ struct bcm_clk_div { /* A fixed (non-zero) divider */ #define FIXED_DIVIDER(_value) \ { \ - .fixed = (_value), \ + .u.fixed = (_value), \ .flags = FLAG(DIV, EXISTS)|FLAG(DIV, FIXED), \ } /* A divider with an integral divisor */ #define DIVIDER(_offset, _shift, _width) \ { \ - .offset = (_offset), \ - .shift = (_shift), \ - .width = (_width), \ - .scaled_div = BAD_SCALED_DIV_VALUE, \ + .u.s.offset = (_offset), \ + .u.s.shift = (_shift), \ + .u.s.width = (_width), \ + .u.s.scaled_div = BAD_SCALED_DIV_VALUE, \ .flags = FLAG(DIV, EXISTS), \ } /* A divider whose divisor has an integer and fractional part */ #define FRAC_DIVIDER(_offset, _shift, _width, _frac_width) \ { \ - .offset = (_offset), \ - .shift = (_shift), \ - .width = (_width), \ - .frac_width = (_frac_width), \ - .scaled_div = BAD_SCALED_DIV_VALUE, \ + .u.s.offset = (_offset), \ + .u.s.shift = (_shift), \ + .u.s.width = (_width), \ + .u.s.frac_width = (_frac_width), \ + .u.s.scaled_div = BAD_SCALED_DIV_VALUE, \ .flags = FLAG(DIV, EXISTS), \ } @@ -360,7 +388,9 @@ struct bcm_clk_trig { } struct peri_clk_data { + struct bcm_clk_policy policy; struct bcm_clk_gate gate; + struct bcm_clk_hyst hyst; struct bcm_clk_trig pre_trig; struct bcm_clk_div pre_div; struct bcm_clk_trig trig; @@ -373,26 +403,103 @@ struct peri_clk_data { struct kona_clk { struct clk_hw hw; - struct clk_init_data init_data; - const char *name; /* name of this clock */ + struct clk_init_data init_data; /* includes name of this clock */ struct ccu_data *ccu; /* ccu this clock is associated with */ enum bcm_clk_type type; union { void *data; struct peri_clk_data *peri; - }; + } u; }; #define to_kona_clk(_hw) \ container_of(_hw, struct kona_clk, hw) -/* Exported globals */ +/* Initialization macro for an entry in a CCU's kona_clks[] array. */ +#define KONA_CLK(_ccu_name, _clk_name, _type) \ + { \ + .init_data = { \ + .name = #_clk_name, \ + .ops = &kona_ ## _type ## _clk_ops, \ + }, \ + .ccu = &_ccu_name ## _ccu_data, \ + .type = bcm_clk_ ## _type, \ + .u.data = &_clk_name ## _data, \ + } +#define LAST_KONA_CLK { .type = bcm_clk_none } -extern struct clk_ops kona_peri_clk_ops; +/* + * CCU policy control. To enable software update of the policy + * tables the CCU policy engine must be stopped by setting the + * software update enable bit (LVM_EN). After an update the engine + * is restarted using the GO bit and either the GO_ATL or GO_AC bit. + */ +struct bcm_lvm_en { + u32 offset; /* LVM_EN register offset */ + u32 bit; /* POLICY_CONFIG_EN bit in register */ +}; -/* Help functions */ +/* Policy enable initialization macro */ +#define CCU_LVM_EN(_offset, _bit) \ + { \ + .offset = (_offset), \ + .bit = (_bit), \ + } + +struct bcm_policy_ctl { + u32 offset; /* POLICY_CTL register offset */ + u32 go_bit; + u32 atl_bit; /* GO, GO_ATL, and GO_AC bits */ + u32 ac_bit; +}; -#define PERI_CLK_SETUP(clks, ccu, id, name) \ - clks[id] = kona_clk_setup(ccu, #name, bcm_clk_peri, &name ## _data) +/* Policy control initialization macro */ +#define CCU_POLICY_CTL(_offset, _go_bit, _ac_bit, _atl_bit) \ + { \ + .offset = (_offset), \ + .go_bit = (_go_bit), \ + .ac_bit = (_ac_bit), \ + .atl_bit = (_atl_bit), \ + } + +struct ccu_policy { + struct bcm_lvm_en enable; + struct bcm_policy_ctl control; +}; + +/* + * Each CCU defines a mapped area of memory containing registers + * used to manage clocks implemented by the CCU. Access to memory + * within the CCU's space is serialized by a spinlock. Before any + * (other) address can be written, a special access "password" value + * must be written to its WR_ACCESS register (located at the base + * address of the range). We keep track of the name of each CCU as + * it is set up, and maintain them in a list. + */ +struct ccu_data { + void __iomem *base; /* base of mapped address space */ + spinlock_t lock; /* serialization lock */ + bool write_enabled; /* write access is currently enabled */ + struct ccu_policy policy; + struct list_head links; /* for ccu_list */ + struct device_node *node; + struct clk_onecell_data clk_data; + const char *name; + u32 range; /* byte range of address space */ + struct kona_clk kona_clks[]; /* must be last */ +}; + +/* Initialization for common fields in a Kona ccu_data structure */ +#define KONA_CCU_COMMON(_prefix, _name, _ccuname) \ + .name = #_name "_ccu", \ + .lock = __SPIN_LOCK_UNLOCKED(_name ## _ccu_data.lock), \ + .links = LIST_HEAD_INIT(_name ## _ccu_data.links), \ + .clk_data = { \ + .clk_num = _prefix ## _ ## _ccuname ## _CCU_CLOCK_COUNT, \ + } + +/* Exported globals */ + +extern struct clk_ops kona_peri_clk_ops; /* Externally visible functions */ @@ -401,10 +508,9 @@ extern u64 scaled_div_max(struct bcm_clk_div *div); extern u64 scaled_div_build(struct bcm_clk_div *div, u32 div_value, u32 billionths); -extern struct clk *kona_clk_setup(struct ccu_data *ccu, const char *name, - enum bcm_clk_type type, void *data); -extern void __init kona_dt_ccu_setup(struct device_node *node, - int (*ccu_clks_setup)(struct ccu_data *)); +extern struct clk *kona_clk_setup(struct kona_clk *bcm_clk); +extern void __init kona_dt_ccu_setup(struct ccu_data *ccu, + struct device_node *node); extern bool __init kona_ccu_init(struct ccu_data *ccu); #endif /* _CLK_KONA_H */ diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index ec22112e569f..b3c83966be18 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -43,6 +43,17 @@ static unsigned int _get_table_maxdiv(const struct clk_div_table *table) return maxdiv; } +static unsigned int _get_table_mindiv(const struct clk_div_table *table) +{ + unsigned int mindiv = UINT_MAX; + const struct clk_div_table *clkt; + + for (clkt = table; clkt->div; clkt++) + if (clkt->div < mindiv) + mindiv = clkt->div; + return mindiv; +} + static unsigned int _get_maxdiv(struct clk_divider *divider) { if (divider->flags & CLK_DIVIDER_ONE_BASED) @@ -144,6 +155,103 @@ static bool _is_valid_div(struct clk_divider *divider, unsigned int div) return true; } +static int _round_up_table(const struct clk_div_table *table, int div) +{ + const struct clk_div_table *clkt; + int up = _get_table_maxdiv(table); + + for (clkt = table; clkt->div; clkt++) { + if (clkt->div == div) + return clkt->div; + else if (clkt->div < div) + continue; + + if ((clkt->div - div) < (up - div)) + up = clkt->div; + } + + return up; +} + +static int _round_down_table(const struct clk_div_table *table, int div) +{ + const struct clk_div_table *clkt; + int down = _get_table_mindiv(table); + + for (clkt = table; clkt->div; clkt++) { + if (clkt->div == div) + return clkt->div; + else if (clkt->div > div) + continue; + + if ((div - clkt->div) < (div - down)) + down = clkt->div; + } + + return down; +} + +static int _div_round_up(struct clk_divider *divider, + unsigned long parent_rate, unsigned long rate) +{ + int div = DIV_ROUND_UP(parent_rate, rate); + + if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) + div = __roundup_pow_of_two(div); + if (divider->table) + div = _round_up_table(divider->table, div); + + return div; +} + +static int _div_round_closest(struct clk_divider *divider, + unsigned long parent_rate, unsigned long rate) +{ + int up, down, div; + + up = down = div = DIV_ROUND_CLOSEST(parent_rate, rate); + + if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) { + up = __roundup_pow_of_two(div); + down = __rounddown_pow_of_two(div); + } else if (divider->table) { + up = _round_up_table(divider->table, div); + down = _round_down_table(divider->table, div); + } + + return (up - div) <= (div - down) ? up : down; +} + +static int _div_round(struct clk_divider *divider, unsigned long parent_rate, + unsigned long rate) +{ + if (divider->flags & CLK_DIVIDER_ROUND_CLOSEST) + return _div_round_closest(divider, parent_rate, rate); + + return _div_round_up(divider, parent_rate, rate); +} + +static bool _is_best_div(struct clk_divider *divider, + int rate, int now, int best) +{ + if (divider->flags & CLK_DIVIDER_ROUND_CLOSEST) + return abs(rate - now) < abs(rate - best); + + return now <= rate && now > best; +} + +static int _next_div(struct clk_divider *divider, int div) +{ + div++; + + if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) + return __roundup_pow_of_two(div); + if (divider->table) + return _round_up_table(divider->table, div); + + return div; +} + static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, unsigned long *best_parent_rate) { @@ -159,7 +267,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) { parent_rate = *best_parent_rate; - bestdiv = DIV_ROUND_UP(parent_rate, rate); + bestdiv = _div_round(divider, parent_rate, rate); bestdiv = bestdiv == 0 ? 1 : bestdiv; bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv; return bestdiv; @@ -171,7 +279,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, */ maxdiv = min(ULONG_MAX / rate, maxdiv); - for (i = 1; i <= maxdiv; i++) { + for (i = 1; i <= maxdiv; i = _next_div(divider, i)) { if (!_is_valid_div(divider, i)) continue; if (rate * i == parent_rate_saved) { @@ -186,7 +294,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), MULT_ROUND_UP(rate, i)); now = DIV_ROUND_UP(parent_rate, i); - if (now <= rate && now > best) { + if (_is_best_div(divider, rate, now, best)) { bestdiv = i; best = now; *best_parent_rate = parent_rate; @@ -219,6 +327,10 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate, u32 val; div = DIV_ROUND_UP(parent_rate, rate); + + if (!_is_valid_div(divider, div)) + return -EINVAL; + value = _get_val(divider, div); if (value > div_mask(divider)) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index dff0373f53c1..4d562206d62f 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -822,6 +822,9 @@ void __clk_unprepare(struct clk *clk) */ void clk_unprepare(struct clk *clk) { + if (IS_ERR_OR_NULL(clk)) + return; + clk_prepare_lock(); __clk_unprepare(clk); clk_prepare_unlock(); @@ -883,9 +886,6 @@ static void __clk_disable(struct clk *clk) if (!clk) return; - if (WARN_ON(IS_ERR(clk))) - return; - if (WARN_ON(clk->enable_count == 0)) return; @@ -914,6 +914,9 @@ void clk_disable(struct clk *clk) { unsigned long flags; + if (IS_ERR_OR_NULL(clk)) + return; + flags = clk_enable_lock(); __clk_disable(clk); clk_enable_unlock(flags); @@ -1115,6 +1118,13 @@ long clk_get_accuracy(struct clk *clk) } EXPORT_SYMBOL_GPL(clk_get_accuracy); +static unsigned long clk_recalc(struct clk *clk, unsigned long parent_rate) +{ + if (clk->ops->recalc_rate) + return clk->ops->recalc_rate(clk->hw, parent_rate); + return parent_rate; +} + /** * __clk_recalc_rates * @clk: first clk in the subtree @@ -1140,10 +1150,7 @@ static void __clk_recalc_rates(struct clk *clk, unsigned long msg) if (clk->parent) parent_rate = clk->parent->rate; - if (clk->ops->recalc_rate) - clk->rate = clk->ops->recalc_rate(clk->hw, parent_rate); - else - clk->rate = parent_rate; + clk->rate = clk_recalc(clk, parent_rate); /* * ignore NOTIFY_STOP and NOTIFY_BAD return values for POST_RATE_CHANGE @@ -1334,10 +1341,7 @@ static int __clk_speculate_rates(struct clk *clk, unsigned long parent_rate) unsigned long new_rate; int ret = NOTIFY_DONE; - if (clk->ops->recalc_rate) - new_rate = clk->ops->recalc_rate(clk->hw, parent_rate); - else - new_rate = parent_rate; + new_rate = clk_recalc(clk, parent_rate); /* abort rate change if a driver returns NOTIFY_BAD or NOTIFY_STOP */ if (clk->notifier_count) @@ -1373,10 +1377,7 @@ static void clk_calc_subtree(struct clk *clk, unsigned long new_rate, new_parent->new_child = clk; hlist_for_each_entry(child, &clk->children, child_node) { - if (child->ops->recalc_rate) - child->new_rate = child->ops->recalc_rate(child->hw, new_rate); - else - child->new_rate = new_rate; + child->new_rate = clk_recalc(child, new_rate); clk_calc_subtree(child, child->new_rate, NULL, 0); } } @@ -1524,10 +1525,7 @@ static void clk_change_rate(struct clk *clk) if (!skip_set_rate && clk->ops->set_rate) clk->ops->set_rate(clk->hw, clk->new_rate, best_parent_rate); - if (clk->ops->recalc_rate) - clk->rate = clk->ops->recalc_rate(clk->hw, best_parent_rate); - else - clk->rate = best_parent_rate; + clk->rate = clk_recalc(clk, best_parent_rate); if (clk->notifier_count && old_rate != clk->rate) __clk_notify(clk, POST_RATE_CHANGE, old_rate, clk->rate); @@ -1716,9 +1714,6 @@ int clk_set_parent(struct clk *clk, struct clk *parent) if (!clk) return 0; - if (!clk->ops) - return -EINVAL; - /* verify ops for for multi-parent clks */ if ((clk->num_parents > 1) && (!clk->ops->set_parent)) return -ENOSYS; @@ -1984,9 +1979,28 @@ struct clk *__clk_register(struct device *dev, struct clk_hw *hw) } EXPORT_SYMBOL_GPL(__clk_register); -static int _clk_register(struct device *dev, struct clk_hw *hw, struct clk *clk) +/** + * clk_register - allocate a new clock, register it and return an opaque cookie + * @dev: device that is registering this clock + * @hw: link to hardware-specific clock data + * + * clk_register is the primary interface for populating the clock tree with new + * clock nodes. It returns a pointer to the newly allocated struct clk which + * cannot be dereferenced by driver code but may be used in conjuction with the + * rest of the clock API. In the event of an error clk_register will return an + * error code; drivers must test for an error code after calling clk_register. + */ +struct clk *clk_register(struct device *dev, struct clk_hw *hw) { int i, ret; + struct clk *clk; + + clk = kzalloc(sizeof(*clk), GFP_KERNEL); + if (!clk) { + pr_err("%s: could not allocate clk\n", __func__); + ret = -ENOMEM; + goto fail_out; + } clk->name = kstrdup(hw->init->name, GFP_KERNEL); if (!clk->name) { @@ -2026,7 +2040,7 @@ static int _clk_register(struct device *dev, struct clk_hw *hw, struct clk *clk) ret = __clk_init(dev, clk); if (!ret) - return 0; + return clk; fail_parent_names_copy: while (--i >= 0) @@ -2035,36 +2049,6 @@ fail_parent_names_copy: fail_parent_names: kfree(clk->name); fail_name: - return ret; -} - -/** - * clk_register - allocate a new clock, register it and return an opaque cookie - * @dev: device that is registering this clock - * @hw: link to hardware-specific clock data - * - * clk_register is the primary interface for populating the clock tree with new - * clock nodes. It returns a pointer to the newly allocated struct clk which - * cannot be dereferenced by driver code but may be used in conjuction with the - * rest of the clock API. In the event of an error clk_register will return an - * error code; drivers must test for an error code after calling clk_register. - */ -struct clk *clk_register(struct device *dev, struct clk_hw *hw) -{ - int ret; - struct clk *clk; - - clk = kzalloc(sizeof(*clk), GFP_KERNEL); - if (!clk) { - pr_err("%s: could not allocate clk\n", __func__); - ret = -ENOMEM; - goto fail_out; - } - - ret = _clk_register(dev, hw, clk); - if (!ret) - return clk; - kfree(clk); fail_out: return ERR_PTR(ret); @@ -2151,9 +2135,10 @@ void clk_unregister(struct clk *clk) if (!hlist_empty(&clk->children)) { struct clk *child; + struct hlist_node *t; /* Reparent all children to the orphan list. */ - hlist_for_each_entry(child, &clk->children, child_node) + hlist_for_each_entry_safe(child, t, &clk->children, child_node) clk_set_parent(child, NULL); } @@ -2173,7 +2158,7 @@ EXPORT_SYMBOL_GPL(clk_unregister); static void devm_clk_release(struct device *dev, void *res) { - clk_unregister(res); + clk_unregister(*(struct clk **)res); } /** @@ -2188,18 +2173,18 @@ static void devm_clk_release(struct device *dev, void *res) struct clk *devm_clk_register(struct device *dev, struct clk_hw *hw) { struct clk *clk; - int ret; + struct clk **clkp; - clk = devres_alloc(devm_clk_release, sizeof(*clk), GFP_KERNEL); - if (!clk) + clkp = devres_alloc(devm_clk_release, sizeof(*clkp), GFP_KERNEL); + if (!clkp) return ERR_PTR(-ENOMEM); - ret = _clk_register(dev, hw, clk); - if (!ret) { - devres_add(dev, clk); + clk = clk_register(dev, hw); + if (!IS_ERR(clk)) { + *clkp = clk; + devres_add(dev, clkp); } else { - devres_free(clk); - clk = ERR_PTR(ret); + devres_free(clkp); } return clk; diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index 995bcfa021a4..7f696b7d4422 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -13,10 +13,10 @@ config MSM_GCC_8660 i2c, USB, SD/eMMC, etc. config MSM_GCC_8960 - tristate "MSM8960 Global Clock Controller" + tristate "APQ8064/MSM8960 Global Clock Controller" depends on COMMON_CLK_QCOM help - Support for the global clock controller on msm8960 devices. + Support for the global clock controller on apq8064/msm8960 devices. Say Y if you want to use peripheral devices such as UART, SPI, i2c, USB, SD/eMMC, SATA, PCIe, etc. diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index f60db2ef1aee..689e05bf4f95 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_COMMON_CLK_QCOM) += clk-qcom.o +clk-qcom-y += common.o clk-qcom-y += clk-regmap.o clk-qcom-y += clk-pll.o clk-qcom-y += clk-rcg.o diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c new file mode 100644 index 000000000000..86b45fba5f90 --- /dev/null +++ b/drivers/clk/qcom/common.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * + * 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/export.h> +#include <linux/regmap.h> +#include <linux/platform_device.h> +#include <linux/clk-provider.h> +#include <linux/reset-controller.h> + +#include "common.h" +#include "clk-regmap.h" +#include "reset.h" + +struct qcom_cc { + struct qcom_reset_controller reset; + struct clk_onecell_data data; + struct clk *clks[]; +}; + +int qcom_cc_probe(struct platform_device *pdev, const struct qcom_cc_desc *desc) +{ + void __iomem *base; + struct resource *res; + int i, ret; + struct device *dev = &pdev->dev; + struct clk *clk; + struct clk_onecell_data *data; + struct clk **clks; + struct regmap *regmap; + struct qcom_reset_controller *reset; + struct qcom_cc *cc; + size_t num_clks = desc->num_clks; + struct clk_regmap **rclks = desc->clks; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + regmap = devm_regmap_init_mmio(dev, base, desc->config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks, + GFP_KERNEL); + if (!cc) + return -ENOMEM; + + clks = cc->clks; + data = &cc->data; + data->clks = clks; + data->clk_num = num_clks; + + for (i = 0; i < num_clks; i++) { + if (!rclks[i]) + continue; + clk = devm_clk_register_regmap(dev, rclks[i]); + if (IS_ERR(clk)) + return PTR_ERR(clk); + clks[i] = clk; + } + + ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data); + if (ret) + return ret; + + reset = &cc->reset; + reset->rcdev.of_node = dev->of_node; + reset->rcdev.ops = &qcom_reset_ops; + reset->rcdev.owner = dev->driver->owner; + reset->rcdev.nr_resets = desc->num_resets; + reset->regmap = regmap; + reset->reset_map = desc->resets; + platform_set_drvdata(pdev, &reset->rcdev); + + ret = reset_controller_register(&reset->rcdev); + if (ret) + of_clk_del_provider(dev->of_node); + + return ret; +} +EXPORT_SYMBOL_GPL(qcom_cc_probe); + +void qcom_cc_remove(struct platform_device *pdev) +{ + of_clk_del_provider(pdev->dev.of_node); + reset_controller_unregister(platform_get_drvdata(pdev)); +} +EXPORT_SYMBOL_GPL(qcom_cc_remove); diff --git a/drivers/clk/qcom/common.h b/drivers/clk/qcom/common.h new file mode 100644 index 000000000000..2c3cfc860348 --- /dev/null +++ b/drivers/clk/qcom/common.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * 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. + */ +#ifndef __QCOM_CLK_COMMON_H__ +#define __QCOM_CLK_COMMON_H__ + +struct platform_device; +struct regmap_config; +struct clk_regmap; +struct qcom_reset_map; + +struct qcom_cc_desc { + const struct regmap_config *config; + struct clk_regmap **clks; + size_t num_clks; + const struct qcom_reset_map *resets; + size_t num_resets; +}; + +extern int qcom_cc_probe(struct platform_device *pdev, + const struct qcom_cc_desc *desc); + +extern void qcom_cc_remove(struct platform_device *pdev); + +#endif diff --git a/drivers/clk/qcom/gcc-msm8660.c b/drivers/clk/qcom/gcc-msm8660.c index bc0b7f1fcfbe..44bc6fa64d78 100644 --- a/drivers/clk/qcom/gcc-msm8660.c +++ b/drivers/clk/qcom/gcc-msm8660.c @@ -25,6 +25,7 @@ #include <dt-bindings/clock/qcom,gcc-msm8660.h> #include <dt-bindings/reset/qcom,gcc-msm8660.h> +#include "common.h" #include "clk-regmap.h" #include "clk-pll.h" #include "clk-rcg.h" @@ -2701,94 +2702,28 @@ static const struct regmap_config gcc_msm8660_regmap_config = { .fast_io = true, }; +static const struct qcom_cc_desc gcc_msm8660_desc = { + .config = &gcc_msm8660_regmap_config, + .clks = gcc_msm8660_clks, + .num_clks = ARRAY_SIZE(gcc_msm8660_clks), + .resets = gcc_msm8660_resets, + .num_resets = ARRAY_SIZE(gcc_msm8660_resets), +}; + static const struct of_device_id gcc_msm8660_match_table[] = { { .compatible = "qcom,gcc-msm8660" }, { } }; MODULE_DEVICE_TABLE(of, gcc_msm8660_match_table); -struct qcom_cc { - struct qcom_reset_controller reset; - struct clk_onecell_data data; - struct clk *clks[]; -}; - static int gcc_msm8660_probe(struct platform_device *pdev) { - void __iomem *base; - struct resource *res; - int i, ret; - struct device *dev = &pdev->dev; - struct clk *clk; - struct clk_onecell_data *data; - struct clk **clks; - struct regmap *regmap; - size_t num_clks; - struct qcom_reset_controller *reset; - struct qcom_cc *cc; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); - - regmap = devm_regmap_init_mmio(dev, base, &gcc_msm8660_regmap_config); - if (IS_ERR(regmap)) - return PTR_ERR(regmap); - - num_clks = ARRAY_SIZE(gcc_msm8660_clks); - cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks, - GFP_KERNEL); - if (!cc) - return -ENOMEM; - - clks = cc->clks; - data = &cc->data; - data->clks = clks; - data->clk_num = num_clks; - - /* Temporary until RPM clocks supported */ - clk = clk_register_fixed_rate(dev, "cxo", NULL, CLK_IS_ROOT, 19200000); - if (IS_ERR(clk)) - return PTR_ERR(clk); - - clk = clk_register_fixed_rate(dev, "pxo", NULL, CLK_IS_ROOT, 27000000); - if (IS_ERR(clk)) - return PTR_ERR(clk); - - for (i = 0; i < num_clks; i++) { - if (!gcc_msm8660_clks[i]) - continue; - clk = devm_clk_register_regmap(dev, gcc_msm8660_clks[i]); - if (IS_ERR(clk)) - return PTR_ERR(clk); - clks[i] = clk; - } - - ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data); - if (ret) - return ret; - - reset = &cc->reset; - reset->rcdev.of_node = dev->of_node; - reset->rcdev.ops = &qcom_reset_ops, - reset->rcdev.owner = THIS_MODULE, - reset->rcdev.nr_resets = ARRAY_SIZE(gcc_msm8660_resets), - reset->regmap = regmap; - reset->reset_map = gcc_msm8660_resets, - platform_set_drvdata(pdev, &reset->rcdev); - - ret = reset_controller_register(&reset->rcdev); - if (ret) - of_clk_del_provider(dev->of_node); - - return ret; + return qcom_cc_probe(pdev, &gcc_msm8660_desc); } static int gcc_msm8660_remove(struct platform_device *pdev) { - of_clk_del_provider(pdev->dev.of_node); - reset_controller_unregister(platform_get_drvdata(pdev)); + qcom_cc_remove(pdev); return 0; } diff --git a/drivers/clk/qcom/gcc-msm8960.c b/drivers/clk/qcom/gcc-msm8960.c index fd446ab2fd98..f4ffd91901f8 100644 --- a/drivers/clk/qcom/gcc-msm8960.c +++ b/drivers/clk/qcom/gcc-msm8960.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -25,6 +25,7 @@ #include <dt-bindings/clock/qcom,gcc-msm8960.h> #include <dt-bindings/reset/qcom,gcc-msm8960.h> +#include "common.h" #include "clk-regmap.h" #include "clk-pll.h" #include "clk-rcg.h" @@ -2809,7 +2810,7 @@ static const struct qcom_reset_map gcc_msm8960_resets[] = { [PPSS_PROC_RESET] = { 0x2594, 1 }, [PPSS_RESET] = { 0x2594}, [DMA_BAM_RESET] = { 0x25c0, 7 }, - [SIC_TIC_RESET] = { 0x2600, 7 }, + [SPS_TIC_H_RESET] = { 0x2600, 7 }, [SLIMBUS_H_RESET] = { 0x2620, 7 }, [SFAB_CFPB_M_RESET] = { 0x2680, 7 }, [SFAB_CFPB_S_RESET] = { 0x26c0, 7 }, @@ -2822,7 +2823,7 @@ static const struct qcom_reset_map gcc_msm8960_resets[] = { [SFAB_SFPB_M_RESET] = { 0x2780, 7 }, [SFAB_SFPB_S_RESET] = { 0x27a0, 7 }, [RPM_PROC_RESET] = { 0x27c0, 7 }, - [PMIC_SSBI2_RESET] = { 0x270c, 12 }, + [PMIC_SSBI2_RESET] = { 0x280c, 12 }, [SDC1_RESET] = { 0x2830 }, [SDC2_RESET] = { 0x2850 }, [SDC3_RESET] = { 0x2870 }, @@ -2867,6 +2868,16 @@ static const struct qcom_reset_map gcc_msm8960_resets[] = { [RIVA_RESET] = { 0x35e0 }, }; +static struct clk_regmap *gcc_apq8064_clks[] = { + [PLL8] = &pll8.clkr, + [PLL8_VOTE] = &pll8_vote, + [GSBI7_UART_SRC] = &gsbi7_uart_src.clkr, + [GSBI7_UART_CLK] = &gsbi7_uart_clk.clkr, + [GSBI7_QUP_SRC] = &gsbi7_qup_src.clkr, + [GSBI7_QUP_CLK] = &gsbi7_qup_clk.clkr, + [GSBI7_H_CLK] = &gsbi7_h_clk.clkr, +}; + static const struct regmap_config gcc_msm8960_regmap_config = { .reg_bits = 32, .reg_stride = 4, @@ -2875,51 +2886,38 @@ static const struct regmap_config gcc_msm8960_regmap_config = { .fast_io = true, }; +static const struct qcom_cc_desc gcc_msm8960_desc = { + .config = &gcc_msm8960_regmap_config, + .clks = gcc_msm8960_clks, + .num_clks = ARRAY_SIZE(gcc_msm8960_clks), + .resets = gcc_msm8960_resets, + .num_resets = ARRAY_SIZE(gcc_msm8960_resets), +}; + +static const struct qcom_cc_desc gcc_apq8064_desc = { + .config = &gcc_msm8960_regmap_config, + .clks = gcc_apq8064_clks, + .num_clks = ARRAY_SIZE(gcc_apq8064_clks), + .resets = gcc_msm8960_resets, + .num_resets = ARRAY_SIZE(gcc_msm8960_resets), +}; + static const struct of_device_id gcc_msm8960_match_table[] = { - { .compatible = "qcom,gcc-msm8960" }, + { .compatible = "qcom,gcc-msm8960", .data = &gcc_msm8960_desc }, + { .compatible = "qcom,gcc-apq8064", .data = &gcc_apq8064_desc }, { } }; MODULE_DEVICE_TABLE(of, gcc_msm8960_match_table); -struct qcom_cc { - struct qcom_reset_controller reset; - struct clk_onecell_data data; - struct clk *clks[]; -}; - static int gcc_msm8960_probe(struct platform_device *pdev) { - void __iomem *base; - struct resource *res; - int i, ret; - struct device *dev = &pdev->dev; struct clk *clk; - struct clk_onecell_data *data; - struct clk **clks; - struct regmap *regmap; - size_t num_clks; - struct qcom_reset_controller *reset; - struct qcom_cc *cc; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); - - regmap = devm_regmap_init_mmio(dev, base, &gcc_msm8960_regmap_config); - if (IS_ERR(regmap)) - return PTR_ERR(regmap); - - num_clks = ARRAY_SIZE(gcc_msm8960_clks); - cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks, - GFP_KERNEL); - if (!cc) - return -ENOMEM; - - clks = cc->clks; - data = &cc->data; - data->clks = clks; - data->clk_num = num_clks; + struct device *dev = &pdev->dev; + const struct of_device_id *match; + + match = of_match_device(gcc_msm8960_match_table, &pdev->dev); + if (!match) + return -EINVAL; /* Temporary until RPM clocks supported */ clk = clk_register_fixed_rate(dev, "cxo", NULL, CLK_IS_ROOT, 19200000); @@ -2930,39 +2928,12 @@ static int gcc_msm8960_probe(struct platform_device *pdev) if (IS_ERR(clk)) return PTR_ERR(clk); - for (i = 0; i < num_clks; i++) { - if (!gcc_msm8960_clks[i]) - continue; - clk = devm_clk_register_regmap(dev, gcc_msm8960_clks[i]); - if (IS_ERR(clk)) - return PTR_ERR(clk); - clks[i] = clk; - } - - ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data); - if (ret) - return ret; - - reset = &cc->reset; - reset->rcdev.of_node = dev->of_node; - reset->rcdev.ops = &qcom_reset_ops, - reset->rcdev.owner = THIS_MODULE, - reset->rcdev.nr_resets = ARRAY_SIZE(gcc_msm8960_resets), - reset->regmap = regmap; - reset->reset_map = gcc_msm8960_resets, - platform_set_drvdata(pdev, &reset->rcdev); - - ret = reset_controller_register(&reset->rcdev); - if (ret) - of_clk_del_provider(dev->of_node); - - return ret; + return qcom_cc_probe(pdev, match->data); } static int gcc_msm8960_remove(struct platform_device *pdev) { - of_clk_del_provider(pdev->dev.of_node); - reset_controller_unregister(platform_get_drvdata(pdev)); + qcom_cc_remove(pdev); return 0; } diff --git a/drivers/clk/qcom/gcc-msm8974.c b/drivers/clk/qcom/gcc-msm8974.c index 51d457e2b959..0d1edc1e9b31 100644 --- a/drivers/clk/qcom/gcc-msm8974.c +++ b/drivers/clk/qcom/gcc-msm8974.c @@ -25,6 +25,7 @@ #include <dt-bindings/clock/qcom,gcc-msm8974.h> #include <dt-bindings/reset/qcom,gcc-msm8974.h> +#include "common.h" #include "clk-regmap.h" #include "clk-pll.h" #include "clk-rcg.h" @@ -2574,51 +2575,24 @@ static const struct regmap_config gcc_msm8974_regmap_config = { .fast_io = true, }; +static const struct qcom_cc_desc gcc_msm8974_desc = { + .config = &gcc_msm8974_regmap_config, + .clks = gcc_msm8974_clocks, + .num_clks = ARRAY_SIZE(gcc_msm8974_clocks), + .resets = gcc_msm8974_resets, + .num_resets = ARRAY_SIZE(gcc_msm8974_resets), +}; + static const struct of_device_id gcc_msm8974_match_table[] = { { .compatible = "qcom,gcc-msm8974" }, { } }; MODULE_DEVICE_TABLE(of, gcc_msm8974_match_table); -struct qcom_cc { - struct qcom_reset_controller reset; - struct clk_onecell_data data; - struct clk *clks[]; -}; - static int gcc_msm8974_probe(struct platform_device *pdev) { - void __iomem *base; - struct resource *res; - int i, ret; - struct device *dev = &pdev->dev; struct clk *clk; - struct clk_onecell_data *data; - struct clk **clks; - struct regmap *regmap; - size_t num_clks; - struct qcom_reset_controller *reset; - struct qcom_cc *cc; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); - - regmap = devm_regmap_init_mmio(dev, base, &gcc_msm8974_regmap_config); - if (IS_ERR(regmap)) - return PTR_ERR(regmap); - - num_clks = ARRAY_SIZE(gcc_msm8974_clocks); - cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks, - GFP_KERNEL); - if (!cc) - return -ENOMEM; - - clks = cc->clks; - data = &cc->data; - data->clks = clks; - data->clk_num = num_clks; + struct device *dev = &pdev->dev; /* Temporary until RPM clocks supported */ clk = clk_register_fixed_rate(dev, "xo", NULL, CLK_IS_ROOT, 19200000); @@ -2631,39 +2605,12 @@ static int gcc_msm8974_probe(struct platform_device *pdev) if (IS_ERR(clk)) return PTR_ERR(clk); - for (i = 0; i < num_clks; i++) { - if (!gcc_msm8974_clocks[i]) - continue; - clk = devm_clk_register_regmap(dev, gcc_msm8974_clocks[i]); - if (IS_ERR(clk)) - return PTR_ERR(clk); - clks[i] = clk; - } - - ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data); - if (ret) - return ret; - - reset = &cc->reset; - reset->rcdev.of_node = dev->of_node; - reset->rcdev.ops = &qcom_reset_ops, - reset->rcdev.owner = THIS_MODULE, - reset->rcdev.nr_resets = ARRAY_SIZE(gcc_msm8974_resets), - reset->regmap = regmap; - reset->reset_map = gcc_msm8974_resets, - platform_set_drvdata(pdev, &reset->rcdev); - - ret = reset_controller_register(&reset->rcdev); - if (ret) - of_clk_del_provider(dev->of_node); - - return ret; + return qcom_cc_probe(pdev, &gcc_msm8974_desc); } static int gcc_msm8974_remove(struct platform_device *pdev) { - of_clk_del_provider(pdev->dev.of_node); - reset_controller_unregister(platform_get_drvdata(pdev)); + qcom_cc_remove(pdev); return 0; } diff --git a/drivers/clk/qcom/mmcc-msm8960.c b/drivers/clk/qcom/mmcc-msm8960.c index f9b59c7e48e9..12f3c0b64fcd 100644 --- a/drivers/clk/qcom/mmcc-msm8960.c +++ b/drivers/clk/qcom/mmcc-msm8960.c @@ -26,6 +26,7 @@ #include <dt-bindings/clock/qcom,mmcc-msm8960.h> #include <dt-bindings/reset/qcom,mmcc-msm8960.h> +#include "common.h" #include "clk-regmap.h" #include "clk-pll.h" #include "clk-rcg.h" @@ -2222,85 +2223,28 @@ static const struct regmap_config mmcc_msm8960_regmap_config = { .fast_io = true, }; +static const struct qcom_cc_desc mmcc_msm8960_desc = { + .config = &mmcc_msm8960_regmap_config, + .clks = mmcc_msm8960_clks, + .num_clks = ARRAY_SIZE(mmcc_msm8960_clks), + .resets = mmcc_msm8960_resets, + .num_resets = ARRAY_SIZE(mmcc_msm8960_resets), +}; + static const struct of_device_id mmcc_msm8960_match_table[] = { { .compatible = "qcom,mmcc-msm8960" }, { } }; MODULE_DEVICE_TABLE(of, mmcc_msm8960_match_table); -struct qcom_cc { - struct qcom_reset_controller reset; - struct clk_onecell_data data; - struct clk *clks[]; -}; - static int mmcc_msm8960_probe(struct platform_device *pdev) { - void __iomem *base; - struct resource *res; - int i, ret; - struct device *dev = &pdev->dev; - struct clk *clk; - struct clk_onecell_data *data; - struct clk **clks; - struct regmap *regmap; - size_t num_clks; - struct qcom_reset_controller *reset; - struct qcom_cc *cc; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); - - regmap = devm_regmap_init_mmio(dev, base, &mmcc_msm8960_regmap_config); - if (IS_ERR(regmap)) - return PTR_ERR(regmap); - - num_clks = ARRAY_SIZE(mmcc_msm8960_clks); - cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks, - GFP_KERNEL); - if (!cc) - return -ENOMEM; - - clks = cc->clks; - data = &cc->data; - data->clks = clks; - data->clk_num = num_clks; - - for (i = 0; i < num_clks; i++) { - if (!mmcc_msm8960_clks[i]) - continue; - clk = devm_clk_register_regmap(dev, mmcc_msm8960_clks[i]); - if (IS_ERR(clk)) - return PTR_ERR(clk); - clks[i] = clk; - } - - ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data); - if (ret) - return ret; - - reset = &cc->reset; - reset->rcdev.of_node = dev->of_node; - reset->rcdev.ops = &qcom_reset_ops, - reset->rcdev.owner = THIS_MODULE, - reset->rcdev.nr_resets = ARRAY_SIZE(mmcc_msm8960_resets), - reset->regmap = regmap; - reset->reset_map = mmcc_msm8960_resets, - platform_set_drvdata(pdev, &reset->rcdev); - - ret = reset_controller_register(&reset->rcdev); - if (ret) - of_clk_del_provider(dev->of_node); - - return ret; + return qcom_cc_probe(pdev, &mmcc_msm8960_desc); } static int mmcc_msm8960_remove(struct platform_device *pdev) { - of_clk_del_provider(pdev->dev.of_node); - reset_controller_unregister(platform_get_drvdata(pdev)); + qcom_cc_remove(pdev); return 0; } diff --git a/drivers/clk/qcom/mmcc-msm8974.c b/drivers/clk/qcom/mmcc-msm8974.c index c95774514b81..60b7c24a5cd6 100644 --- a/drivers/clk/qcom/mmcc-msm8974.c +++ b/drivers/clk/qcom/mmcc-msm8974.c @@ -25,6 +25,7 @@ #include <dt-bindings/clock/qcom,mmcc-msm8974.h> #include <dt-bindings/reset/qcom,mmcc-msm8974.h> +#include "common.h" #include "clk-regmap.h" #include "clk-pll.h" #include "clk-rcg.h" @@ -2524,88 +2525,39 @@ static const struct regmap_config mmcc_msm8974_regmap_config = { .fast_io = true, }; +static const struct qcom_cc_desc mmcc_msm8974_desc = { + .config = &mmcc_msm8974_regmap_config, + .clks = mmcc_msm8974_clocks, + .num_clks = ARRAY_SIZE(mmcc_msm8974_clocks), + .resets = mmcc_msm8974_resets, + .num_resets = ARRAY_SIZE(mmcc_msm8974_resets), +}; + static const struct of_device_id mmcc_msm8974_match_table[] = { { .compatible = "qcom,mmcc-msm8974" }, { } }; MODULE_DEVICE_TABLE(of, mmcc_msm8974_match_table); -struct qcom_cc { - struct qcom_reset_controller reset; - struct clk_onecell_data data; - struct clk *clks[]; -}; - static int mmcc_msm8974_probe(struct platform_device *pdev) { - void __iomem *base; - struct resource *res; - int i, ret; - struct device *dev = &pdev->dev; - struct clk *clk; - struct clk_onecell_data *data; - struct clk **clks; + int ret; struct regmap *regmap; - size_t num_clks; - struct qcom_reset_controller *reset; - struct qcom_cc *cc; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); - - regmap = devm_regmap_init_mmio(dev, base, &mmcc_msm8974_regmap_config); - if (IS_ERR(regmap)) - return PTR_ERR(regmap); - - num_clks = ARRAY_SIZE(mmcc_msm8974_clocks); - cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks, - GFP_KERNEL); - if (!cc) - return -ENOMEM; - - clks = cc->clks; - data = &cc->data; - data->clks = clks; - data->clk_num = num_clks; - - clk_pll_configure_sr_hpm_lp(&mmpll1, regmap, &mmpll1_config, true); - clk_pll_configure_sr_hpm_lp(&mmpll3, regmap, &mmpll3_config, false); - for (i = 0; i < num_clks; i++) { - if (!mmcc_msm8974_clocks[i]) - continue; - clk = devm_clk_register_regmap(dev, mmcc_msm8974_clocks[i]); - if (IS_ERR(clk)) - return PTR_ERR(clk); - clks[i] = clk; - } - - ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data); + ret = qcom_cc_probe(pdev, &mmcc_msm8974_desc); if (ret) return ret; - reset = &cc->reset; - reset->rcdev.of_node = dev->of_node; - reset->rcdev.ops = &qcom_reset_ops, - reset->rcdev.owner = THIS_MODULE, - reset->rcdev.nr_resets = ARRAY_SIZE(mmcc_msm8974_resets), - reset->regmap = regmap; - reset->reset_map = mmcc_msm8974_resets, - platform_set_drvdata(pdev, &reset->rcdev); - - ret = reset_controller_register(&reset->rcdev); - if (ret) - of_clk_del_provider(dev->of_node); + regmap = dev_get_regmap(&pdev->dev, NULL); + clk_pll_configure_sr_hpm_lp(&mmpll1, regmap, &mmpll1_config, true); + clk_pll_configure_sr_hpm_lp(&mmpll3, regmap, &mmpll3_config, false); - return ret; + return 0; } static int mmcc_msm8974_remove(struct platform_device *pdev) { - of_clk_del_provider(pdev->dev.of_node); - reset_controller_unregister(platform_get_drvdata(pdev)); + qcom_cc_remove(pdev); return 0; } diff --git a/drivers/clk/shmobile/clk-mstp.c b/drivers/clk/shmobile/clk-mstp.c index 2e5810c88d11..1f6324e29a80 100644 --- a/drivers/clk/shmobile/clk-mstp.c +++ b/drivers/clk/shmobile/clk-mstp.c @@ -156,6 +156,7 @@ cpg_mstp_clock_register(const char *name, const char *parent_name, static void __init cpg_mstp_clocks_init(struct device_node *np) { struct mstp_clock_group *group; + const char *idxname; struct clk **clks; unsigned int i; @@ -184,6 +185,11 @@ static void __init cpg_mstp_clocks_init(struct device_node *np) for (i = 0; i < MSTP_MAX_CLOCKS; ++i) clks[i] = ERR_PTR(-ENOENT); + if (of_find_property(np, "clock-indices", &i)) + idxname = "clock-indices"; + else + idxname = "renesas,clock-indices"; + for (i = 0; i < MSTP_MAX_CLOCKS; ++i) { const char *parent_name; const char *name; @@ -197,8 +203,7 @@ static void __init cpg_mstp_clocks_init(struct device_node *np) continue; parent_name = of_clk_get_parent_name(np, i); - ret = of_property_read_u32_index(np, "renesas,clock-indices", i, - &clkidx); + ret = of_property_read_u32_index(np, idxname, i, &clkidx); if (parent_name == NULL || ret < 0) break; diff --git a/drivers/clk/socfpga/clk-pll.c b/drivers/clk/socfpga/clk-pll.c index 88dafb5e9627..de6da957a09d 100644 --- a/drivers/clk/socfpga/clk-pll.c +++ b/drivers/clk/socfpga/clk-pll.c @@ -20,6 +20,7 @@ #include <linux/clk-provider.h> #include <linux/io.h> #include <linux/of.h> +#include <linux/of_address.h> #include "clk.h" @@ -43,6 +44,8 @@ #define to_socfpga_clk(p) container_of(p, struct socfpga_pll, hw.hw) +void __iomem *clk_mgr_base_addr; + static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk, unsigned long parent_rate) { @@ -87,6 +90,7 @@ static __init struct clk *__socfpga_pll_init(struct device_node *node, const char *clk_name = node->name; const char *parent_name[SOCFPGA_MAX_PARENTS]; struct clk_init_data init; + struct device_node *clkmgr_np; int rc; int i = 0; @@ -96,6 +100,9 @@ static __init struct clk *__socfpga_pll_init(struct device_node *node, if (WARN_ON(!pll_clk)) return NULL; + clkmgr_np = of_find_compatible_node(NULL, NULL, "altr,clk-mgr"); + clk_mgr_base_addr = of_iomap(clkmgr_np, 0); + BUG_ON(!clk_mgr_base_addr); pll_clk->hw.reg = clk_mgr_base_addr + reg; of_property_read_string(node, "clock-output-names", &clk_name); diff --git a/drivers/clk/socfpga/clk.c b/drivers/clk/socfpga/clk.c index 35a960a993f9..43db947e5f0e 100644 --- a/drivers/clk/socfpga/clk.c +++ b/drivers/clk/socfpga/clk.c @@ -17,28 +17,11 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <linux/clk.h> -#include <linux/clkdev.h> -#include <linux/clk-provider.h> -#include <linux/io.h> #include <linux/of.h> -#include <linux/of_address.h> #include "clk.h" -void __iomem *clk_mgr_base_addr; - -static const struct of_device_id socfpga_child_clocks[] __initconst = { - { .compatible = "altr,socfpga-pll-clock", socfpga_pll_init, }, - { .compatible = "altr,socfpga-perip-clk", socfpga_periph_init, }, - { .compatible = "altr,socfpga-gate-clk", socfpga_gate_init, }, - {}, -}; - -static void __init socfpga_clkmgr_init(struct device_node *node) -{ - clk_mgr_base_addr = of_iomap(node, 0); - of_clk_init(socfpga_child_clocks); -} -CLK_OF_DECLARE(socfpga_mgr, "altr,clk-mgr", socfpga_clkmgr_init); +CLK_OF_DECLARE(socfpga_pll_clk, "altr,socfpga-pll-clock", socfpga_pll_init); +CLK_OF_DECLARE(socfpga_perip_clk, "altr,socfpga-perip-clk", socfpga_periph_init); +CLK_OF_DECLARE(socfpga_gate_clk, "altr,socfpga-gate-clk", socfpga_gate_init); diff --git a/drivers/clk/sunxi/clk-factors.c b/drivers/clk/sunxi/clk-factors.c index 9e232644f07e..3806d97e529b 100644 --- a/drivers/clk/sunxi/clk-factors.c +++ b/drivers/clk/sunxi/clk-factors.c @@ -77,6 +77,41 @@ static long clk_factors_round_rate(struct clk_hw *hw, unsigned long rate, return rate; } +static long clk_factors_determine_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *best_parent_rate, + struct clk **best_parent_p) +{ + struct clk *clk = hw->clk, *parent, *best_parent = NULL; + int i, num_parents; + unsigned long parent_rate, best = 0, child_rate, best_child_rate = 0; + + /* find the parent that can help provide the fastest rate <= rate */ + num_parents = __clk_get_num_parents(clk); + for (i = 0; i < num_parents; i++) { + parent = clk_get_parent_by_index(clk, i); + if (!parent) + continue; + if (__clk_get_flags(clk) & CLK_SET_RATE_PARENT) + parent_rate = __clk_round_rate(parent, rate); + else + parent_rate = __clk_get_rate(parent); + + child_rate = clk_factors_round_rate(hw, rate, &parent_rate); + + if (child_rate <= rate && child_rate > best_child_rate) { + best_parent = parent; + best = parent_rate; + best_child_rate = child_rate; + } + } + + if (best_parent) + *best_parent_p = best_parent; + *best_parent_rate = best; + + return best_child_rate; +} + static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { @@ -113,6 +148,7 @@ static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate, } const struct clk_ops clk_factors_ops = { + .determine_rate = clk_factors_determine_rate, .recalc_rate = clk_factors_recalc_rate, .round_rate = clk_factors_round_rate, .set_rate = clk_factors_set_rate, diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index bd7dc733c1ca..59f90401b900 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -507,6 +507,42 @@ CLK_OF_DECLARE(sun7i_a20_gmac, "allwinner,sun7i-a20-gmac-clk", /** + * clk_sunxi_mmc_phase_control() - configures MMC clock phase control + */ + +void clk_sunxi_mmc_phase_control(struct clk_hw *hw, u8 sample, u8 output) +{ + #define to_clk_composite(_hw) container_of(_hw, struct clk_composite, hw) + #define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw) + + struct clk_composite *composite = to_clk_composite(hw); + struct clk_hw *rate_hw = composite->rate_hw; + struct clk_factors *factors = to_clk_factors(rate_hw); + unsigned long flags = 0; + u32 reg; + + if (factors->lock) + spin_lock_irqsave(factors->lock, flags); + + reg = readl(factors->reg); + + /* set sample clock phase control */ + reg &= ~(0x7 << 20); + reg |= ((sample & 0x7) << 20); + + /* set output clock phase control */ + reg &= ~(0x7 << 8); + reg |= ((output & 0x7) << 8); + + writel(reg, factors->reg); + + if (factors->lock) + spin_unlock_irqrestore(factors->lock, flags); +} +EXPORT_SYMBOL(clk_sunxi_mmc_phase_control); + + +/** * sunxi_factors_clk_setup() - Setup function for factor clocks */ diff --git a/include/dt-bindings/clock/bcm21664.h b/include/dt-bindings/clock/bcm21664.h new file mode 100644 index 000000000000..5a7f0e4750a8 --- /dev/null +++ b/include/dt-bindings/clock/bcm21664.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2013 Broadcom Corporation + * Copyright 2013 Linaro Limited + * + * 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _CLOCK_BCM21664_H +#define _CLOCK_BCM21664_H + +/* + * This file defines the values used to specify clocks provided by + * the clock control units (CCUs) on Broadcom BCM21664 family SoCs. + */ + +/* bcm21664 CCU device tree "compatible" strings */ +#define BCM21664_DT_ROOT_CCU_COMPAT "brcm,bcm21664-root-ccu" +#define BCM21664_DT_AON_CCU_COMPAT "brcm,bcm21664-aon-ccu" +#define BCM21664_DT_MASTER_CCU_COMPAT "brcm,bcm21664-master-ccu" +#define BCM21664_DT_SLAVE_CCU_COMPAT "brcm,bcm21664-slave-ccu" + +/* root CCU clock ids */ + +#define BCM21664_ROOT_CCU_FRAC_1M 0 +#define BCM21664_ROOT_CCU_CLOCK_COUNT 1 + +/* aon CCU clock ids */ + +#define BCM21664_AON_CCU_HUB_TIMER 0 +#define BCM21664_AON_CCU_CLOCK_COUNT 1 + +/* master CCU clock ids */ + +#define BCM21664_MASTER_CCU_SDIO1 0 +#define BCM21664_MASTER_CCU_SDIO2 1 +#define BCM21664_MASTER_CCU_SDIO3 2 +#define BCM21664_MASTER_CCU_SDIO4 3 +#define BCM21664_MASTER_CCU_SDIO1_SLEEP 4 +#define BCM21664_MASTER_CCU_SDIO2_SLEEP 5 +#define BCM21664_MASTER_CCU_SDIO3_SLEEP 6 +#define BCM21664_MASTER_CCU_SDIO4_SLEEP 7 +#define BCM21664_MASTER_CCU_CLOCK_COUNT 8 + +/* slave CCU clock ids */ + +#define BCM21664_SLAVE_CCU_UARTB 0 +#define BCM21664_SLAVE_CCU_UARTB2 1 +#define BCM21664_SLAVE_CCU_UARTB3 2 +#define BCM21664_SLAVE_CCU_BSC1 3 +#define BCM21664_SLAVE_CCU_BSC2 4 +#define BCM21664_SLAVE_CCU_BSC3 5 +#define BCM21664_SLAVE_CCU_BSC4 6 +#define BCM21664_SLAVE_CCU_CLOCK_COUNT 7 + +#endif /* _CLOCK_BCM21664_H */ diff --git a/include/dt-bindings/clock/bcm281xx.h b/include/dt-bindings/clock/bcm281xx.h index e0096940886d..a763460cf1af 100644 --- a/include/dt-bindings/clock/bcm281xx.h +++ b/include/dt-bindings/clock/bcm281xx.h @@ -20,6 +20,18 @@ * the clock control units (CCUs) on Broadcom BCM281XX family SoCs. */ +/* + * These are the bcm281xx CCU device tree "compatible" strings. + * We're stuck with using "bcm11351" in the string because wild + * cards aren't allowed, and that name was the first one defined + * in this family of devices. + */ +#define BCM281XX_DT_ROOT_CCU_COMPAT "brcm,bcm11351-root-ccu" +#define BCM281XX_DT_AON_CCU_COMPAT "brcm,bcm11351-aon-ccu" +#define BCM281XX_DT_HUB_CCU_COMPAT "brcm,bcm11351-hub-ccu" +#define BCM281XX_DT_MASTER_CCU_COMPAT "brcm,bcm11351-master-ccu" +#define BCM281XX_DT_SLAVE_CCU_COMPAT "brcm,bcm11351-slave-ccu" + /* root CCU clock ids */ #define BCM281XX_ROOT_CCU_FRAC_1M 0 diff --git a/include/dt-bindings/clock/qcom,gcc-msm8960.h b/include/dt-bindings/clock/qcom,gcc-msm8960.h index 03bbf49d43b7..f9f547146a15 100644 --- a/include/dt-bindings/clock/qcom,gcc-msm8960.h +++ b/include/dt-bindings/clock/qcom,gcc-msm8960.h @@ -51,7 +51,7 @@ #define QDSS_TSCTR_CLK 34 #define SFAB_ADM0_M0_A_CLK 35 #define SFAB_ADM0_M1_A_CLK 36 -#define SFAB_ADM0_M2_A_CLK 37 +#define SFAB_ADM0_M2_H_CLK 37 #define ADM0_CLK 38 #define ADM0_PBUS_CLK 39 #define MSS_XPU_CLK 40 @@ -99,7 +99,7 @@ #define CFPB2_H_CLK 82 #define SFAB_CFPB_M_H_CLK 83 #define CFPB_MASTER_H_CLK 84 -#define SFAB_CFPB_S_HCLK 85 +#define SFAB_CFPB_S_H_CLK 85 #define CFPB_SPLITTER_H_CLK 86 #define TSIF_H_CLK 87 #define TSIF_INACTIVITY_TIMERS_CLK 88 @@ -110,7 +110,6 @@ #define CE1_SLEEP_CLK 93 #define CE2_H_CLK 94 #define CE2_CORE_CLK 95 -#define CE2_SLEEP_CLK 96 #define SFPB_H_CLK_SRC 97 #define SFPB_H_CLK 98 #define SFAB_SFPB_M_H_CLK 99 @@ -252,7 +251,7 @@ #define MSS_S_H_CLK 235 #define MSS_CXO_SRC_CLK 236 #define SATA_H_CLK 237 -#define SATA_SRC_CLK 238 +#define SATA_CLK_SRC 238 #define SATA_RXOOB_CLK 239 #define SATA_PMALIVE_CLK 240 #define SATA_PHY_REF_CLK 241 diff --git a/include/dt-bindings/reset/qcom,gcc-msm8960.h b/include/dt-bindings/reset/qcom,gcc-msm8960.h index a840e680323c..07edd0e65eed 100644 --- a/include/dt-bindings/reset/qcom,gcc-msm8960.h +++ b/include/dt-bindings/reset/qcom,gcc-msm8960.h @@ -58,7 +58,7 @@ #define PPSS_PROC_RESET 41 #define PPSS_RESET 42 #define DMA_BAM_RESET 43 -#define SIC_TIC_RESET 44 +#define SPS_TIC_H_RESET 44 #define SLIMBUS_H_RESET 45 #define SFAB_CFPB_M_RESET 46 #define SFAB_CFPB_S_RESET 47 diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 511917416fb0..40809431641e 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -40,14 +40,14 @@ struct dentry; * through the clk_* api. * * @prepare: Prepare the clock for enabling. This must not return until - * the clock is fully prepared, and it's safe to call clk_enable. - * This callback is intended to allow clock implementations to - * do any initialisation that may sleep. Called with - * prepare_lock held. + * the clock is fully prepared, and it's safe to call clk_enable. + * This callback is intended to allow clock implementations to + * do any initialisation that may sleep. Called with + * prepare_lock held. * * @unprepare: Release the clock from its prepared state. This will typically - * undo any work done in the @prepare callback. Called with - * prepare_lock held. + * undo any work done in the @prepare callback. Called with + * prepare_lock held. * * @is_prepared: Queries the hardware to determine if the clock is prepared. * This function is allowed to sleep. Optional, if this op is not @@ -58,16 +58,16 @@ struct dentry; * Called with prepare mutex held. This function may sleep. * * @enable: Enable the clock atomically. This must not return until the - * clock is generating a valid clock signal, usable by consumer - * devices. Called with enable_lock held. This function must not - * sleep. + * clock is generating a valid clock signal, usable by consumer + * devices. Called with enable_lock held. This function must not + * sleep. * * @disable: Disable the clock atomically. Called with enable_lock held. - * This function must not sleep. + * This function must not sleep. * * @is_enabled: Queries the hardware to determine if the clock is enabled. - * This function must not sleep. Optional, if this op is not - * set then the enable count will be used. + * This function must not sleep. Optional, if this op is not + * set then the enable count will be used. * * @disable_unused: Disable the clock atomically. Only called from * clk_disable_unused for gate clocks with special needs. @@ -75,34 +75,35 @@ struct dentry; * sleep. * * @recalc_rate Recalculate the rate of this clock, by querying hardware. The - * parent rate is an input parameter. It is up to the caller to - * ensure that the prepare_mutex is held across this call. - * Returns the calculated rate. Optional, but recommended - if - * this op is not set then clock rate will be initialized to 0. + * parent rate is an input parameter. It is up to the caller to + * ensure that the prepare_mutex is held across this call. + * Returns the calculated rate. Optional, but recommended - if + * this op is not set then clock rate will be initialized to 0. * * @round_rate: Given a target rate as input, returns the closest rate actually - * supported by the clock. + * supported by the clock. The parent rate is an input/output + * parameter. * * @determine_rate: Given a target rate as input, returns the closest rate * actually supported by the clock, and optionally the parent clock * that should be used to provide the clock rate. * - * @get_parent: Queries the hardware to determine the parent of a clock. The - * return value is a u8 which specifies the index corresponding to - * the parent clock. This index can be applied to either the - * .parent_names or .parents arrays. In short, this function - * translates the parent value read from hardware into an array - * index. Currently only called when the clock is initialized by - * __clk_init. This callback is mandatory for clocks with - * multiple parents. It is optional (and unnecessary) for clocks - * with 0 or 1 parents. - * * @set_parent: Change the input source of this clock; for clocks with multiple - * possible parents specify a new parent by passing in the index - * as a u8 corresponding to the parent in either the .parent_names - * or .parents arrays. This function in affect translates an - * array index into the value programmed into the hardware. - * Returns 0 on success, -EERROR otherwise. + * possible parents specify a new parent by passing in the index + * as a u8 corresponding to the parent in either the .parent_names + * or .parents arrays. This function in affect translates an + * array index into the value programmed into the hardware. + * Returns 0 on success, -EERROR otherwise. + * + * @get_parent: Queries the hardware to determine the parent of a clock. The + * return value is a u8 which specifies the index corresponding to + * the parent clock. This index can be applied to either the + * .parent_names or .parents arrays. In short, this function + * translates the parent value read from hardware into an array + * index. Currently only called when the clock is initialized by + * __clk_init. This callback is mandatory for clocks with + * multiple parents. It is optional (and unnecessary) for clocks + * with 0 or 1 parents. * * @set_rate: Change the rate of this clock. The requested rate is specified * by the second argument, which should typically be the return @@ -110,13 +111,6 @@ struct dentry; * which is likely helpful for most .set_rate implementation. * Returns 0 on success, -EERROR otherwise. * - * @recalc_accuracy: Recalculate the accuracy of this clock. The clock accuracy - * is expressed in ppb (parts per billion). The parent accuracy is - * an input parameter. - * Returns the calculated accuracy. Optional - if this op is not - * set then clock accuracy will be initialized to parent accuracy - * or 0 (perfect clock) if clock has no parent. - * * @set_rate_and_parent: Change the rate and the parent of this clock. The * requested rate is specified by the second argument, which * should typically be the return of .round_rate call. The @@ -128,6 +122,18 @@ struct dentry; * separately via calls to .set_parent and .set_rate. * Returns 0 on success, -EERROR otherwise. * + * @recalc_accuracy: Recalculate the accuracy of this clock. The clock accuracy + * is expressed in ppb (parts per billion). The parent accuracy is + * an input parameter. + * Returns the calculated accuracy. Optional - if this op is not + * set then clock accuracy will be initialized to parent accuracy + * or 0 (perfect clock) if clock has no parent. + * + * @init: Perform platform-specific initialization magic. + * This is not not used by any of the basic clock types. + * Please consider other ways of solving initialization problems + * before using this callback, as its use is discouraged. + * * @debug_init: Set up type-specific debugfs entries for this clock. This * is called once, after the debugfs directory entry for this * clock has been created. The dentry pointer representing that @@ -157,15 +163,15 @@ struct clk_ops { void (*disable_unused)(struct clk_hw *hw); unsigned long (*recalc_rate)(struct clk_hw *hw, unsigned long parent_rate); - long (*round_rate)(struct clk_hw *hw, unsigned long, - unsigned long *); + long (*round_rate)(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate); long (*determine_rate)(struct clk_hw *hw, unsigned long rate, unsigned long *best_parent_rate, struct clk **best_parent_clk); int (*set_parent)(struct clk_hw *hw, u8 index); u8 (*get_parent)(struct clk_hw *hw); - int (*set_rate)(struct clk_hw *hw, unsigned long, - unsigned long); + int (*set_rate)(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate); int (*set_rate_and_parent)(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate, u8 index); @@ -254,12 +260,12 @@ void of_fixed_clk_setup(struct device_node *np); * * Flags: * CLK_GATE_SET_TO_DISABLE - by default this clock sets the bit at bit_idx to - * enable the clock. Setting this flag does the opposite: setting the bit - * disable the clock and clearing it enables the clock + * enable the clock. Setting this flag does the opposite: setting the bit + * disable the clock and clearing it enables the clock * CLK_GATE_HIWORD_MASK - The gate settings are only in lower 16-bit - * of this register, and mask of gate bits are in higher 16-bit of this - * register. While setting the gate bits, higher 16-bit should also be - * updated to indicate changing gate bits. + * of this register, and mask of gate bits are in higher 16-bit of this + * register. While setting the gate bits, higher 16-bit should also be + * updated to indicate changing gate bits. */ struct clk_gate { struct clk_hw hw; @@ -298,20 +304,22 @@ struct clk_div_table { * * Flags: * CLK_DIVIDER_ONE_BASED - by default the divisor is the value read from the - * register plus one. If CLK_DIVIDER_ONE_BASED is set then the divider is - * the raw value read from the register, with the value of zero considered + * register plus one. If CLK_DIVIDER_ONE_BASED is set then the divider is + * the raw value read from the register, with the value of zero considered * invalid, unless CLK_DIVIDER_ALLOW_ZERO is set. * CLK_DIVIDER_POWER_OF_TWO - clock divisor is 2 raised to the value read from - * the hardware register + * the hardware register * CLK_DIVIDER_ALLOW_ZERO - Allow zero divisors. For dividers which have * CLK_DIVIDER_ONE_BASED set, it is possible to end up with a zero divisor. * Some hardware implementations gracefully handle this case and allow a * zero divisor by not modifying their input clock * (divide by one / bypass). * CLK_DIVIDER_HIWORD_MASK - The divider settings are only in lower 16-bit - * of this register, and mask of divider bits are in higher 16-bit of this - * register. While setting the divider bits, higher 16-bit should also be - * updated to indicate changing divider bits. + * of this register, and mask of divider bits are in higher 16-bit of this + * register. While setting the divider bits, higher 16-bit should also be + * updated to indicate changing divider bits. + * CLK_DIVIDER_ROUND_CLOSEST - Makes the best calculated divider to be rounded + * to the closest integer instead of the up one. */ struct clk_divider { struct clk_hw hw; @@ -327,6 +335,7 @@ struct clk_divider { #define CLK_DIVIDER_POWER_OF_TWO BIT(1) #define CLK_DIVIDER_ALLOW_ZERO BIT(2) #define CLK_DIVIDER_HIWORD_MASK BIT(3) +#define CLK_DIVIDER_ROUND_CLOSEST BIT(4) extern const struct clk_ops clk_divider_ops; struct clk *clk_register_divider(struct device *dev, const char *name, @@ -356,9 +365,9 @@ struct clk *clk_register_divider_table(struct device *dev, const char *name, * CLK_MUX_INDEX_ONE - register index starts at 1, not 0 * CLK_MUX_INDEX_BIT - register index is a single bit (power of two) * CLK_MUX_HIWORD_MASK - The mux settings are only in lower 16-bit of this - * register, and mask of mux bits are in higher 16-bit of this register. - * While setting the mux bits, higher 16-bit should also be updated to - * indicate changing mux bits. + * register, and mask of mux bits are in higher 16-bit of this register. + * While setting the mux bits, higher 16-bit should also be updated to + * indicate changing mux bits. */ struct clk_mux { struct clk_hw hw; diff --git a/include/linux/clk/sunxi.h b/include/linux/clk/sunxi.h new file mode 100644 index 000000000000..1ef5c899e458 --- /dev/null +++ b/include/linux/clk/sunxi.h @@ -0,0 +1,22 @@ +/* + * Copyright 2013 - Hans de Goede <hdegoede@redhat.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. + */ + +#ifndef __LINUX_CLK_SUNXI_H_ +#define __LINUX_CLK_SUNXI_H_ + +#include <linux/clk.h> + +void clk_sunxi_mmc_phase_control(struct clk_hw *hw, u8 sample, u8 output); + +#endif |