diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-10-15 07:05:03 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-10-15 07:05:03 +0200 |
commit | c0fa2373f8cfed90437d8d7b17e0b1a84009a10a (patch) | |
tree | 43fb2edd0c11874d0b2e56714e53894d10321e19 | |
parent | fcc3a5d277571bc6048e7b4ef8cd391b935de629 (diff) | |
parent | 98d147f50eb0ce4328e013f5f2c076896003c761 (diff) | |
download | blackbird-op-linux-c0fa2373f8cfed90437d8d7b17e0b1a84009a10a.tar.gz blackbird-op-linux-c0fa2373f8cfed90437d8d7b17e0b1a84009a10a.zip |
Merge tag 'clk-for-linus-3.18' of git://git.linaro.org/people/mike.turquette/linux
Pull clock tree updates from Mike Turquette:
"The clk tree changes for 3.18 are dominated by clock drivers. Mostly
fixes and enhancements to existing drivers as well as new drivers.
This tag contains a bit more arch code than I usually take due to some
OMAP2+ changes. Additionally it contains the restart notifier
handlers which are merged as a dependency into several trees.
The PXA changes are the only messy part. Due to having a stable tree
I had to revert one patch and follow up with one more fix near the tip
of this tag. Some dead code is introduced but it will soon become
live code after 3.18-rc1 is released as the rest of the PXA family is
converted over to the common clock framework.
Another trend in this tag is that multiple vendors have started to
push the complexity of changing their CPU frequency into the clock
driver, whereas this used to be done in CPUfreq drivers.
Changes to the clk core include a generic gpio-clock type and a
clk_set_phase() function added to the top-level clk.h api. Due to
some confusion on the fbdev mailing list the kernel boot parameters
documentation was updated to further explain the clk_ignore_unused
parameter, which is often required by users of the simplefb driver.
Finally some fixes to the locking around the clock debugfs stuff was
done to prevent deadlocks when interacting with other subsystems."
* tag 'clk-for-linus-3.18' of git://git.linaro.org/people/mike.turquette/linux: (99 commits)
clk: pxa clocks build system fix
Revert "arm: pxa: Transition pxa27x to clk framework"
clk: samsung: register restart handlers for s3c2412 and s3c2443
clk: rockchip: add restart handler
clk: rockchip: rk3288: i2s_frac adds flag to set parent's rate
doc/kernel-parameters.txt: clarify clk_ignore_unused
arm: pxa: Transition pxa27x to clk framework
dts: add devicetree bindings for pxa27x clocks
clk: add pxa27x clock drivers
arm: pxa: add clock pll selection bits
clk: dts: document pxa clock binding
clk: add pxa clocks infrastructure
clk: gpio-gate: Ensure gpiod_ APIs are prototyped
clk: ti: dra7-atl-clock: Mark the device as pm_runtime_irq_safe
clk: ti: LLVMLinux: Move __init outside of type definition
clk: ti: consider the fact that of_clk_get() might return an error
clk: ti: dra7-atl-clock: fix a memory leak
clk: ti: change clock init to use generic of_clk_init
clk: hix5hd2: add I2C clocks
clk: hix5hd2: add watchdog0 clocks
...
101 files changed, 4134 insertions, 798 deletions
diff --git a/Documentation/devicetree/bindings/clock/exynos3250-clock.txt b/Documentation/devicetree/bindings/clock/exynos3250-clock.txt index aadc9c59e2d1..f57d9dd9ea85 100644 --- a/Documentation/devicetree/bindings/clock/exynos3250-clock.txt +++ b/Documentation/devicetree/bindings/clock/exynos3250-clock.txt @@ -7,6 +7,8 @@ Required Properties: - compatible: should be one of the following. - "samsung,exynos3250-cmu" - controller compatible with Exynos3250 SoC. + - "samsung,exynos3250-cmu-dmc" - controller compatible with + Exynos3250 SoC for Dynamic Memory Controller domain. - reg: physical base address of the controller and length of memory mapped region. @@ -20,7 +22,7 @@ All available clocks are defined as preprocessor macros in dt-bindings/clock/exynos3250.h header and can be used in device tree sources. -Example 1: An example of a clock controller node is listed below. +Example 1: Examples of clock controller nodes are listed below. cmu: clock-controller@10030000 { compatible = "samsung,exynos3250-cmu"; @@ -28,6 +30,12 @@ Example 1: An example of a clock controller node is listed below. #clock-cells = <1>; }; + cmu_dmc: clock-controller@105C0000 { + compatible = "samsung,exynos3250-cmu-dmc"; + reg = <0x105C0000 0x2000>; + #clock-cells = <1>; + }; + Example 2: UART controller node that consumes the clock generated by the clock controller. Refer to the standard clock bindings for information about 'clocks' and 'clock-names' property. diff --git a/Documentation/devicetree/bindings/clock/gpio-gate-clock.txt b/Documentation/devicetree/bindings/clock/gpio-gate-clock.txt new file mode 100644 index 000000000000..d3379ff9b84b --- /dev/null +++ b/Documentation/devicetree/bindings/clock/gpio-gate-clock.txt @@ -0,0 +1,21 @@ +Binding for simple gpio gated clock. + +This binding uses the common clock binding[1]. + +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt + +Required properties: +- compatible : shall be "gpio-gate-clock". +- #clock-cells : from common clock binding; shall be set to 0. +- enable-gpios : GPIO reference for enabling and disabling the clock. + +Optional properties: +- clocks: Maximum of one parent clock is supported. + +Example: + clock { + compatible = "gpio-gate-clock"; + clocks = <&parentclk>; + #clock-cells = <0>; + enable-gpios = <&gpio 1 GPIO_ACTIVE_HIGH>; + }; diff --git a/Documentation/devicetree/bindings/clock/maxim,max77686.txt b/Documentation/devicetree/bindings/clock/maxim,max77686.txt index 96ce71bbd745..9c40739a661a 100644 --- a/Documentation/devicetree/bindings/clock/maxim,max77686.txt +++ b/Documentation/devicetree/bindings/clock/maxim,max77686.txt @@ -9,13 +9,21 @@ The MAX77686 contains three 32.768khz clock outputs that can be controlled Following properties should be presend in main device node of the MFD chip. Required properties: -- #clock-cells: simple one-cell clock specifier format is used, where the - only cell is used as an index of the clock inside the provider. Following - indices are allowed: + +- #clock-cells: from common clock binding; shall be set to 1. + +Optional properties: +- clock-output-names: From common clock binding. + +Each clock is assigned an identifier and client nodes can use this identifier +to specify the clock which they consume. Following indices are allowed: - 0: 32khz_ap clock, - 1: 32khz_cp clock, - 2: 32khz_pmic clock. +Clocks are defined as preprocessor macros in dt-bindings/clock/maxim,max77686.h +header and can be used in device tree sources. + Example: Node of the MFD chip max77686: max77686@09 { @@ -34,5 +42,5 @@ Example: Clock consumer node compatible = "bar,foo"; /* ... */ clock-names = "my-clock"; - clocks = <&max77686 2>; + clocks = <&max77686 MAX77686_CLK_PMIC>; }; diff --git a/Documentation/devicetree/bindings/clock/maxim,max77802.txt b/Documentation/devicetree/bindings/clock/maxim,max77802.txt new file mode 100644 index 000000000000..c6dc7835f06c --- /dev/null +++ b/Documentation/devicetree/bindings/clock/maxim,max77802.txt @@ -0,0 +1,44 @@ +Binding for Maxim MAX77802 32k clock generator block + +This is a part of device tree bindings of MAX77802 multi-function device. +More information can be found in bindings/mfd/max77802.txt file. + +The MAX77802 contains two 32.768khz clock outputs that can be controlled +(gated/ungated) over I2C. + +Following properties should be present in main device node of the MFD chip. + +Required properties: +- #clock-cells: From common clock binding; shall be set to 1. + +Optional properties: +- clock-output-names: From common clock binding. + +Each clock is assigned an identifier and client nodes can use this identifier +to specify the clock which they consume. Following indices are allowed: + - 0: 32khz_ap clock, + - 1: 32khz_cp clock. + +Clocks are defined as preprocessor macros in dt-bindings/clock/maxim,max77802.h +header and can be used in device tree sources. + +Example: Node of the MFD chip + + max77802: max77802@09 { + compatible = "maxim,max77802"; + interrupt-parent = <&wakeup_eint>; + interrupts = <26 0>; + reg = <0x09>; + #clock-cells = <1>; + + /* ... */ + }; + +Example: Clock consumer node + + foo@0 { + compatible = "bar,foo"; + /* ... */ + clock-names = "my-clock"; + clocks = <&max77802 MAX77802_CLK_32K_AP>; + }; diff --git a/Documentation/devicetree/bindings/clock/pxa-clock.txt b/Documentation/devicetree/bindings/clock/pxa-clock.txt new file mode 100644 index 000000000000..4b4a9024bd99 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/pxa-clock.txt @@ -0,0 +1,16 @@ +* Clock bindings for Marvell PXA chips + +Required properties: +- compatible: Should be "marvell,pxa-clocks" +- #clock-cells: Should be <1> + +The clock consumer should specify the desired clock by having the clock +ID in its "clocks" phandle cell (see include/.../pxa-clock.h). + +Examples: + +pxa2xx_clks: pxa2xx_clks@41300004 { + compatible = "marvell,pxa-clocks"; + #clock-cells = <1>; + status = "okay"; +}; diff --git a/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt b/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt index 8f1424f0fa43..a5f52238c80d 100644 --- a/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt +++ b/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt @@ -15,6 +15,7 @@ Required Properties: - "renesas,r8a7779-mstp-clocks" for R8A7779 (R-Car H1) MSTP gate clocks - "renesas,r8a7790-mstp-clocks" for R8A7790 (R-Car H2) MSTP gate clocks - "renesas,r8a7791-mstp-clocks" for R8A7791 (R-Car M2) MSTP gate clocks + - "renesas,r8a7794-mstp-clocks" for R8A7794 (R-Car E2) MSTP gate clocks - "renesas,sh73a0-mstp-clocks" for SH73A0 (SH-MobileAG5) MSTP gate clocks - "renesas,cpg-mstp-clock" for generic MSTP gate clocks - reg: Base address and length of the I/O mapped registers used by the MSTP diff --git a/Documentation/devicetree/bindings/clock/renesas,rcar-gen2-cpg-clocks.txt b/Documentation/devicetree/bindings/clock/renesas,rcar-gen2-cpg-clocks.txt index 7b41c2fe54db..e6ad35b894f9 100644 --- a/Documentation/devicetree/bindings/clock/renesas,rcar-gen2-cpg-clocks.txt +++ b/Documentation/devicetree/bindings/clock/renesas,rcar-gen2-cpg-clocks.txt @@ -8,6 +8,7 @@ Required Properties: - compatible: Must be one of - "renesas,r8a7790-cpg-clocks" for the r8a7790 CPG - "renesas,r8a7791-cpg-clocks" for the r8a7791 CPG + - "renesas,r8a7794-cpg-clocks" for the r8a7794 CPG - "renesas,rcar-gen2-cpg-clocks" for the generic R-Car Gen2 CPG - reg: Base address and length of the memory resource used by the CPG diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt index d3a5c3c6d677..ed116df9c3e7 100644 --- a/Documentation/devicetree/bindings/clock/sunxi.txt +++ b/Documentation/devicetree/bindings/clock/sunxi.txt @@ -46,7 +46,11 @@ Required properties: "allwinner,sun6i-a31-apb2-div-clk" - for the APB2 gates on A31 "allwinner,sun6i-a31-apb2-gates-clk" - for the APB2 gates on A31 "allwinner,sun8i-a23-apb2-gates-clk" - for the APB2 gates on A23 + "allwinner,sun5i-a13-mbus-clk" - for the MBUS clock on A13 + "allwinner,sun4i-a10-mmc-output-clk" - for the MMC output clock on A10 + "allwinner,sun4i-a10-mmc-sample-clk" - for the MMC sample clock on A10 "allwinner,sun4i-a10-mod0-clk" - for the module 0 family of clocks + "allwinner,sun8i-a23-mbus-clk" - for the MBUS clock on A23 "allwinner,sun7i-a20-out-clk" - for the external output clocks "allwinner,sun7i-a20-gmac-clk" - for the GMAC clock module on A20/A31 "allwinner,sun4i-a10-usb-clk" - for usb gates + resets on A10 / A20 diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 04e9f5505faa..b62bdcb1eb39 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -605,11 +605,15 @@ bytes respectively. Such letter suffixes can also be entirely omitted. See Documentation/s390/CommonIO for details. clk_ignore_unused [CLK] - Keep all clocks already enabled by bootloader on, - even if no driver has claimed them. This is useful - for debug and development, but should not be - needed on a platform with proper driver support. - For more information, see Documentation/clk.txt. + Prevents the clock framework from automatically gating + clocks that have not been explicitly enabled by a Linux + device driver but are enabled in hardware at reset or + by the bootloader/firmware. Note that this does not + force such clocks to be always-on nor does it reserve + those clocks in any way. This parameter is useful for + debug and development, but should not be needed on a + platform with proper driver support. For more + information, see Documentation/clk.txt. clock= [BUGS=X86-32, HW] gettimeofday clocksource override. [Deprecated] diff --git a/arch/arm/boot/dts/exynos3250.dtsi b/arch/arm/boot/dts/exynos3250.dtsi index 8831c48c2bc9..693a3275606f 100644 --- a/arch/arm/boot/dts/exynos3250.dtsi +++ b/arch/arm/boot/dts/exynos3250.dtsi @@ -169,6 +169,12 @@ #clock-cells = <1>; }; + cmu_dmc: clock-controller@105C0000 { + compatible = "samsung,exynos3250-cmu-dmc"; + reg = <0x105C0000 0x2000>; + #clock-cells = <1>; + }; + rtc: rtc@10070000 { compatible = "samsung,exynos3250-rtc"; reg = <0x10070000 0x100>; diff --git a/arch/arm/boot/dts/pxa27x.dtsi b/arch/arm/boot/dts/pxa27x.dtsi index a70546945985..80fc5d7e9ef9 100644 --- a/arch/arm/boot/dts/pxa27x.dtsi +++ b/arch/arm/boot/dts/pxa27x.dtsi @@ -1,5 +1,6 @@ /* The pxa3xx skeleton simply augments the 2xx version */ -/include/ "pxa2xx.dtsi" +#include "pxa2xx.dtsi" +#include "dt-bindings/clock/pxa2xx-clock.h" / { model = "Marvell PXA27x familiy SoC"; @@ -35,4 +36,21 @@ #pwm-cells = <1>; }; }; + + clocks { + /* + * The muxing of external clocks/internal dividers for osc* clock + * sources has been hidden under the carpet by now. + */ + #address-cells = <1>; + #size-cells = <1>; + ranges; + + pxa2xx_clks: pxa2xx_clks@41300004 { + compatible = "marvell,pxa-clocks"; + #clock-cells = <1>; + status = "okay"; + }; + }; + }; diff --git a/arch/arm/boot/dts/sun5i-a10s.dtsi b/arch/arm/boot/dts/sun5i-a10s.dtsi index d73a2287b37a..531272c0e526 100644 --- a/arch/arm/boot/dts/sun5i-a10s.dtsi +++ b/arch/arm/boot/dts/sun5i-a10s.dtsi @@ -287,7 +287,7 @@ mbus_clk: clk@01c2015c { #clock-cells = <0>; - compatible = "allwinner,sun4i-a10-mod0-clk"; + compatible = "allwinner,sun5i-a13-mbus-clk"; reg = <0x01c2015c 0x4>; clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; clock-output-names = "mbus"; diff --git a/arch/arm/boot/dts/sun5i-a13.dtsi b/arch/arm/boot/dts/sun5i-a13.dtsi index c4b5d7825b9f..b131068f4f35 100644 --- a/arch/arm/boot/dts/sun5i-a13.dtsi +++ b/arch/arm/boot/dts/sun5i-a13.dtsi @@ -285,7 +285,7 @@ mbus_clk: clk@01c2015c { #clock-cells = <0>; - compatible = "allwinner,sun4i-a10-mod0-clk"; + compatible = "allwinner,sun5i-a13-mbus-clk"; reg = <0x01c2015c 0x4>; clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; clock-output-names = "mbus"; diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi index a96b99465069..82097c905c48 100644 --- a/arch/arm/boot/dts/sun7i-a20.dtsi +++ b/arch/arm/boot/dts/sun7i-a20.dtsi @@ -382,7 +382,7 @@ mbus_clk: clk@01c2015c { #clock-cells = <0>; - compatible = "allwinner,sun4i-a10-mod0-clk"; + compatible = "allwinner,sun5i-a13-mbus-clk"; reg = <0x01c2015c 0x4>; clocks = <&osc24M>, <&pll6 2>, <&pll5 1>; clock-output-names = "mbus"; diff --git a/arch/arm/mach-omap2/io.c b/arch/arm/mach-omap2/io.c index b8ad045bcb8d..03cbb16898a3 100644 --- a/arch/arm/mach-omap2/io.c +++ b/arch/arm/mach-omap2/io.c @@ -723,8 +723,16 @@ int __init omap_clk_init(void) ti_clk_init_features(); ret = of_prcm_init(); - if (!ret) - ret = omap_clk_soc_init(); + if (ret) + return ret; + + of_clk_init(NULL); + + ti_dt_clk_init_retry_clks(); + + ti_dt_clockdomains_setup(); + + ret = omap_clk_soc_init(); return ret; } diff --git a/arch/arm/mach-omap2/prm_common.c b/arch/arm/mach-omap2/prm_common.c index 74054b813600..ee2b5222eac0 100644 --- a/arch/arm/mach-omap2/prm_common.c +++ b/arch/arm/mach-omap2/prm_common.c @@ -525,8 +525,6 @@ int __init of_prcm_init(void) memmap_index++; } - ti_dt_clockdomains_setup(); - return 0; } diff --git a/arch/arm/mach-pxa/include/mach/pxa2xx-regs.h b/arch/arm/mach-pxa/include/mach/pxa2xx-regs.h index ee6ced1cea7f..f1dd62946b36 100644 --- a/arch/arm/mach-pxa/include/mach/pxa2xx-regs.h +++ b/arch/arm/mach-pxa/include/mach/pxa2xx-regs.h @@ -143,6 +143,16 @@ #define CCCR_M_MASK 0x0060 /* Memory Frequency to Run Mode Frequency Multiplier */ #define CCCR_L_MASK 0x001f /* Crystal Frequency to Memory Frequency Multiplier */ +#define CCCR_CPDIS_BIT (31) +#define CCCR_PPDIS_BIT (30) +#define CCCR_LCD_26_BIT (27) +#define CCCR_A_BIT (25) + +#define CCSR_N2_MASK CCCR_N_MASK +#define CCSR_M_MASK CCCR_M_MASK +#define CCSR_L_MASK CCCR_L_MASK +#define CCSR_N2_SHIFT 7 + #define CKEN_AC97CONF (31) /* AC97 Controller Configuration */ #define CKEN_CAMERA (24) /* Camera Interface Clock Enable */ #define CKEN_SSP1 (23) /* SSP1 Unit Clock Enable */ diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 84e0590e31dc..455fd17d938e 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -32,12 +32,23 @@ config COMMON_CLK_WM831X source "drivers/clk/versatile/Kconfig" +config COMMON_CLK_MAX_GEN + bool + config COMMON_CLK_MAX77686 tristate "Clock driver for Maxim 77686 MFD" depends on MFD_MAX77686 + select COMMON_CLK_MAX_GEN ---help--- This driver supports Maxim 77686 crystal oscillator clock. +config COMMON_CLK_MAX77802 + tristate "Clock driver for Maxim 77802 PMIC" + depends on MFD_MAX77686 + select COMMON_CLK_MAX_GEN + ---help--- + This driver supports Maxim 77802 crystal oscillator clock. + config COMMON_CLK_RK808 tristate "Clock driver for RK808" depends on MFD_RK808 @@ -118,6 +129,11 @@ config COMMON_CLK_PALMAS This driver supports TI Palmas devices 32KHz output KG and KG_AUDIO using common clock framework. +config COMMON_CLK_PXA + def_bool COMMON_CLK && ARCH_PXA + ---help--- + Sypport for the Marvell PXA SoC. + source "drivers/clk/qcom/Kconfig" endmenu diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 99f53d5f8766..d5fba5bc6e1b 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_COMMON_CLK) += clk-gate.o obj-$(CONFIG_COMMON_CLK) += clk-mux.o obj-$(CONFIG_COMMON_CLK) += clk-composite.o obj-$(CONFIG_COMMON_CLK) += clk-fractional-divider.o +obj-$(CONFIG_COMMON_CLK) += clk-gpio-gate.o ifeq ($(CONFIG_OF), y) obj-$(CONFIG_COMMON_CLK) += clk-conf.o endif @@ -22,7 +23,9 @@ obj-$(CONFIG_ARCH_CLPS711X) += clk-clps711x.o obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o +obj-$(CONFIG_COMMON_CLK_MAX_GEN) += clk-max-gen.o obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o +obj-$(CONFIG_COMMON_CLK_MAX77802) += clk-max77802.o obj-$(CONFIG_ARCH_MOXART) += clk-moxart.o obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o obj-$(CONFIG_ARCH_NSPIRE) += clk-nspire.o @@ -49,6 +52,7 @@ obj-$(CONFIG_ARCH_MMP) += mmp/ endif obj-$(CONFIG_PLAT_ORION) += mvebu/ obj-$(CONFIG_ARCH_MXS) += mxs/ +obj-$(CONFIG_COMMON_CLK_PXA) += pxa/ obj-$(CONFIG_COMMON_CLK_QCOM) += qcom/ obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ obj-$(CONFIG_COMMON_CLK_SAMSUNG) += samsung/ diff --git a/drivers/clk/at91/clk-pll.c b/drivers/clk/at91/clk-pll.c index cf6ed023504c..6ec79dbc0840 100644 --- a/drivers/clk/at91/clk-pll.c +++ b/drivers/clk/at91/clk-pll.c @@ -15,6 +15,7 @@ #include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/io.h> +#include <linux/kernel.h> #include <linux/wait.h> #include <linux/sched.h> #include <linux/interrupt.h> @@ -29,9 +30,12 @@ #define PLL_DIV(reg) ((reg) & PLL_DIV_MASK) #define PLL_MUL(reg, layout) (((reg) >> (layout)->mul_shift) & \ (layout)->mul_mask) +#define PLL_MUL_MIN 2 +#define PLL_MUL_MASK(layout) ((layout)->mul_mask) +#define PLL_MUL_MAX(layout) (PLL_MUL_MASK(layout) + 1) #define PLL_ICPR_SHIFT(id) ((id) * 16) #define PLL_ICPR_MASK(id) (0xffff << PLL_ICPR_SHIFT(id)) -#define PLL_MAX_COUNT 0x3ff +#define PLL_MAX_COUNT 0x3f #define PLL_COUNT_SHIFT 8 #define PLL_OUT_SHIFT 14 #define PLL_MAX_ID 1 @@ -147,115 +151,113 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct clk_pll *pll = to_clk_pll(hw); - const struct clk_pll_layout *layout = pll->layout; - struct at91_pmc *pmc = pll->pmc; - int offset = PLL_REG(pll->id); - u32 tmp = pmc_read(pmc, offset) & layout->pllr_mask; - u8 div = PLL_DIV(tmp); - u16 mul = PLL_MUL(tmp, layout); - if (!div || !mul) + + if (!pll->div || !pll->mul) return 0; - return (parent_rate * (mul + 1)) / div; + return (parent_rate / pll->div) * (pll->mul + 1); } static long clk_pll_get_best_div_mul(struct clk_pll *pll, unsigned long rate, unsigned long parent_rate, u32 *div, u32 *mul, u32 *index) { - unsigned long maxrate; - unsigned long minrate; - unsigned long divrate; - unsigned long bestdiv = 1; - unsigned long bestmul; - unsigned long tmpdiv; - unsigned long roundup; - unsigned long rounddown; - unsigned long remainder; - unsigned long bestremainder; - unsigned long maxmul; - unsigned long maxdiv; - unsigned long mindiv; - int i = 0; const struct clk_pll_layout *layout = pll->layout; const struct clk_pll_characteristics *characteristics = pll->characteristics; + unsigned long bestremainder = ULONG_MAX; + unsigned long maxdiv, mindiv, tmpdiv; + long bestrate = -ERANGE; + unsigned long bestdiv; + unsigned long bestmul; + int i = 0; - /* Minimum divider = 1 */ - /* Maximum multiplier = max_mul */ - maxmul = layout->mul_mask + 1; - maxrate = (parent_rate * maxmul) / 1; - - /* Maximum divider = max_div */ - /* Minimum multiplier = 2 */ - maxdiv = PLL_DIV_MAX; - minrate = (parent_rate * 2) / maxdiv; - + /* Check if parent_rate is a valid input rate */ if (parent_rate < characteristics->input.min || - parent_rate < characteristics->input.max) - return -ERANGE; - - if (parent_rate < minrate || parent_rate > maxrate) + parent_rate > characteristics->input.max) return -ERANGE; - for (i = 0; i < characteristics->num_output; i++) { - if (parent_rate >= characteristics->output[i].min && - parent_rate <= characteristics->output[i].max) - break; - } - - if (i >= characteristics->num_output) - return -ERANGE; - - bestmul = rate / parent_rate; - rounddown = parent_rate % rate; - roundup = rate - rounddown; - bestremainder = roundup < rounddown ? roundup : rounddown; - - if (!bestremainder) { - if (div) - *div = bestdiv; - if (mul) - *mul = bestmul; - if (index) - *index = i; - return rate; - } - - maxdiv = 255 / (bestmul + 1); - if (parent_rate / maxdiv < characteristics->input.min) - maxdiv = parent_rate / characteristics->input.min; - mindiv = parent_rate / characteristics->input.max; - if (parent_rate % characteristics->input.max) - mindiv++; - - for (tmpdiv = mindiv; tmpdiv < maxdiv; tmpdiv++) { - divrate = parent_rate / tmpdiv; - - rounddown = rate % divrate; - roundup = divrate - rounddown; - remainder = roundup < rounddown ? roundup : rounddown; - + /* + * Calculate minimum divider based on the minimum multiplier, the + * parent_rate and the requested rate. + * Should always be 2 according to the input and output characteristics + * of the PLL blocks. + */ + mindiv = (parent_rate * PLL_MUL_MIN) / rate; + if (!mindiv) + mindiv = 1; + + /* + * Calculate the maximum divider which is limited by PLL register + * layout (limited by the MUL or DIV field size). + */ + maxdiv = DIV_ROUND_UP(parent_rate * PLL_MUL_MAX(layout), rate); + if (maxdiv > PLL_DIV_MAX) + maxdiv = PLL_DIV_MAX; + + /* + * Iterate over the acceptable divider values to find the best + * divider/multiplier pair (the one that generates the closest + * rate to the requested one). + */ + for (tmpdiv = mindiv; tmpdiv <= maxdiv; tmpdiv++) { + unsigned long remainder; + unsigned long tmprate; + unsigned long tmpmul; + + /* + * Calculate the multiplier associated with the current + * divider that provide the closest rate to the requested one. + */ + tmpmul = DIV_ROUND_CLOSEST(rate, parent_rate / tmpdiv); + tmprate = (parent_rate / tmpdiv) * tmpmul; + if (tmprate > rate) + remainder = tmprate - rate; + else + remainder = rate - tmprate; + + /* + * Compare the remainder with the best remainder found until + * now and elect a new best multiplier/divider pair if the + * current remainder is smaller than the best one. + */ if (remainder < bestremainder) { bestremainder = remainder; - bestmul = rate / divrate; bestdiv = tmpdiv; + bestmul = tmpmul; + bestrate = tmprate; } + /* + * We've found a perfect match! + * Stop searching now and use this multiplier/divider pair. + */ if (!remainder) break; } - rate = (parent_rate / bestdiv) * bestmul; + /* We haven't found any multiplier/divider pair => return -ERANGE */ + if (bestrate < 0) + return bestrate; + + /* Check if bestrate is a valid output rate */ + for (i = 0; i < characteristics->num_output; i++) { + if (bestrate >= characteristics->output[i].min && + bestrate <= characteristics->output[i].max) + break; + } + + if (i >= characteristics->num_output) + return -ERANGE; if (div) *div = bestdiv; if (mul) - *mul = bestmul; + *mul = bestmul - 1; if (index) *index = i; - return rate; + return bestrate; } static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, diff --git a/drivers/clk/at91/clk-usb.c b/drivers/clk/at91/clk-usb.c index 7d1d26a4bd04..24b5b020753a 100644 --- a/drivers/clk/at91/clk-usb.c +++ b/drivers/clk/at91/clk-usb.c @@ -238,16 +238,22 @@ static long at91rm9200_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) { struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw); + struct clk *parent = __clk_get_parent(hw->clk); unsigned long bestrate = 0; int bestdiff = -1; unsigned long tmprate; int tmpdiff; int i = 0; - for (i = 0; i < 4; i++) { + for (i = 0; i < RM9200_USB_DIV_TAB_SIZE; i++) { + unsigned long tmp_parent_rate; + if (!usb->divisors[i]) continue; - tmprate = *parent_rate / usb->divisors[i]; + + tmp_parent_rate = rate * usb->divisors[i]; + tmp_parent_rate = __clk_round_rate(parent, tmp_parent_rate); + tmprate = tmp_parent_rate / usb->divisors[i]; if (tmprate < rate) tmpdiff = rate - tmprate; else @@ -256,6 +262,7 @@ static long at91rm9200_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate, if (bestdiff < 0 || bestdiff > tmpdiff) { bestrate = tmprate; bestdiff = tmpdiff; + *parent_rate = tmp_parent_rate; } if (!bestdiff) @@ -272,10 +279,13 @@ static int at91rm9200_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate, int i; struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw); struct at91_pmc *pmc = usb->pmc; - unsigned long div = parent_rate / rate; + unsigned long div; - if (parent_rate % rate) + if (!rate || parent_rate % rate) return -EINVAL; + + div = parent_rate / rate; + for (i = 0; i < RM9200_USB_DIV_TAB_SIZE; i++) { if (usb->divisors[i] == div) { tmp = pmc_read(pmc, AT91_CKGR_PLLBR) & @@ -311,7 +321,7 @@ at91rm9200_clk_register_usb(struct at91_pmc *pmc, const char *name, init.ops = &at91rm9200_usb_ops; init.parent_names = &parent_name; init.num_parents = 1; - init.flags = 0; + init.flags = CLK_SET_RATE_PARENT; usb->hw.init = &init; usb->pmc = pmc; diff --git a/drivers/clk/clk-axi-clkgen.c b/drivers/clk/clk-axi-clkgen.c index 1127ee46b802..e619285c6def 100644 --- a/drivers/clk/clk-axi-clkgen.c +++ b/drivers/clk/clk-axi-clkgen.c @@ -544,7 +544,6 @@ static int axi_clkgen_remove(struct platform_device *pdev) static struct platform_driver axi_clkgen_driver = { .driver = { .name = "adi-axi-clkgen", - .owner = THIS_MODULE, .of_match_table = axi_clkgen_ids, }, .probe = axi_clkgen_probe, diff --git a/drivers/clk/clk-fractional-divider.c b/drivers/clk/clk-fractional-divider.c index ede685ca0d20..82a59d0086cc 100644 --- a/drivers/clk/clk-fractional-divider.c +++ b/drivers/clk/clk-fractional-divider.c @@ -36,7 +36,7 @@ static unsigned long clk_fd_recalc_rate(struct clk_hw *hw, m = (val & fd->mmask) >> fd->mshift; n = (val & fd->nmask) >> fd->nshift; - ret = parent_rate * m; + ret = (u64)parent_rate * m; do_div(ret, n); return ret; diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c index 4a58c55255bd..51fd87fb7ba6 100644 --- a/drivers/clk/clk-gate.c +++ b/drivers/clk/clk-gate.c @@ -45,7 +45,7 @@ static void clk_gate_endisable(struct clk_hw *hw, int enable) { struct clk_gate *gate = to_clk_gate(hw); int set = gate->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0; - unsigned long flags = 0; + unsigned long uninitialized_var(flags); u32 reg; set ^= enable; diff --git a/drivers/clk/clk-gpio-gate.c b/drivers/clk/clk-gpio-gate.c new file mode 100644 index 000000000000..08e43224fd52 --- /dev/null +++ b/drivers/clk/clk-gpio-gate.c @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2013 - 2014 Texas Instruments Incorporated - http://www.ti.com + * Author: Jyri Sarha <jsarha@ti.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Gpio gated clock implementation + */ + +#include <linux/clk-provider.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/gpio.h> +#include <linux/gpio/consumer.h> +#include <linux/of_gpio.h> +#include <linux/err.h> +#include <linux/device.h> + +/** + * DOC: basic gpio gated clock which can be enabled and disabled + * with gpio output + * Traits of this clock: + * prepare - clk_(un)prepare only ensures parent is (un)prepared + * enable - clk_enable and clk_disable are functional & control gpio + * rate - inherits rate from parent. No clk_set_rate support + * parent - fixed parent. No clk_set_parent support + */ + +#define to_clk_gpio(_hw) container_of(_hw, struct clk_gpio, hw) + +static int clk_gpio_gate_enable(struct clk_hw *hw) +{ + struct clk_gpio *clk = to_clk_gpio(hw); + + gpiod_set_value(clk->gpiod, 1); + + return 0; +} + +static void clk_gpio_gate_disable(struct clk_hw *hw) +{ + struct clk_gpio *clk = to_clk_gpio(hw); + + gpiod_set_value(clk->gpiod, 0); +} + +static int clk_gpio_gate_is_enabled(struct clk_hw *hw) +{ + struct clk_gpio *clk = to_clk_gpio(hw); + + return gpiod_get_value(clk->gpiod); +} + +const struct clk_ops clk_gpio_gate_ops = { + .enable = clk_gpio_gate_enable, + .disable = clk_gpio_gate_disable, + .is_enabled = clk_gpio_gate_is_enabled, +}; +EXPORT_SYMBOL_GPL(clk_gpio_gate_ops); + +/** + * clk_register_gpio - register a gpip clock with the clock framework + * @dev: device that is registering this clock + * @name: name of this clock + * @parent_name: name of this clock's parent + * @gpiod: gpio descriptor to gate this clock + */ +struct clk *clk_register_gpio_gate(struct device *dev, const char *name, + const char *parent_name, struct gpio_desc *gpiod, + unsigned long flags) +{ + struct clk_gpio *clk_gpio = NULL; + struct clk *clk = ERR_PTR(-EINVAL); + struct clk_init_data init = { NULL }; + unsigned long gpio_flags; + int err; + + if (gpiod_is_active_low(gpiod)) + gpio_flags = GPIOF_OUT_INIT_HIGH; + else + gpio_flags = GPIOF_OUT_INIT_LOW; + + if (dev) + err = devm_gpio_request_one(dev, desc_to_gpio(gpiod), + gpio_flags, name); + else + err = gpio_request_one(desc_to_gpio(gpiod), gpio_flags, name); + + if (err) { + pr_err("%s: %s: Error requesting clock control gpio %u\n", + __func__, name, desc_to_gpio(gpiod)); + return ERR_PTR(err); + } + + if (dev) + clk_gpio = devm_kzalloc(dev, sizeof(struct clk_gpio), + GFP_KERNEL); + else + clk_gpio = kzalloc(sizeof(struct clk_gpio), GFP_KERNEL); + + if (!clk_gpio) { + clk = ERR_PTR(-ENOMEM); + goto clk_register_gpio_gate_err; + } + + init.name = name; + init.ops = &clk_gpio_gate_ops; + init.flags = flags | CLK_IS_BASIC; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + + clk_gpio->gpiod = gpiod; + clk_gpio->hw.init = &init; + + clk = clk_register(dev, &clk_gpio->hw); + + if (!IS_ERR(clk)) + return clk; + + if (!dev) + kfree(clk_gpio); + +clk_register_gpio_gate_err: + gpiod_put(gpiod); + + return clk; +} +EXPORT_SYMBOL_GPL(clk_register_gpio_gate); + +#ifdef CONFIG_OF +/** + * The clk_register_gpio_gate has to be delayed, because the EPROBE_DEFER + * can not be handled properly at of_clk_init() call time. + */ + +struct clk_gpio_gate_delayed_register_data { + struct device_node *node; + struct mutex lock; + struct clk *clk; +}; + +static struct clk *of_clk_gpio_gate_delayed_register_get( + struct of_phandle_args *clkspec, + void *_data) +{ + struct clk_gpio_gate_delayed_register_data *data = _data; + struct clk *clk; + const char *clk_name = data->node->name; + const char *parent_name; + struct gpio_desc *gpiod; + int gpio; + + mutex_lock(&data->lock); + + if (data->clk) { + mutex_unlock(&data->lock); + return data->clk; + } + + gpio = of_get_named_gpio_flags(data->node, "enable-gpios", 0, NULL); + if (gpio < 0) { + mutex_unlock(&data->lock); + if (gpio != -EPROBE_DEFER) + pr_err("%s: %s: Can't get 'enable-gpios' DT property\n", + __func__, clk_name); + return ERR_PTR(gpio); + } + gpiod = gpio_to_desc(gpio); + + parent_name = of_clk_get_parent_name(data->node, 0); + + clk = clk_register_gpio_gate(NULL, clk_name, parent_name, gpiod, 0); + if (IS_ERR(clk)) { + mutex_unlock(&data->lock); + return clk; + } + + data->clk = clk; + mutex_unlock(&data->lock); + + return clk; +} + +/** + * of_gpio_gate_clk_setup() - Setup function for gpio controlled clock + */ +void __init of_gpio_gate_clk_setup(struct device_node *node) +{ + struct clk_gpio_gate_delayed_register_data *data; + + data = kzalloc(sizeof(struct clk_gpio_gate_delayed_register_data), + GFP_KERNEL); + if (!data) + return; + + data->node = node; + mutex_init(&data->lock); + + of_clk_add_provider(node, of_clk_gpio_gate_delayed_register_get, data); +} +EXPORT_SYMBOL_GPL(of_gpio_gate_clk_setup); +CLK_OF_DECLARE(gpio_gate_clk, "gpio-gate-clock", of_gpio_gate_clk_setup); +#endif diff --git a/drivers/clk/clk-max-gen.c b/drivers/clk/clk-max-gen.c new file mode 100644 index 000000000000..6505049d50f1 --- /dev/null +++ b/drivers/clk/clk-max-gen.c @@ -0,0 +1,192 @@ +/* + * clk-max-gen.c - Generic clock driver for Maxim PMICs clocks + * + * Copyright (C) 2014 Google, Inc + * + * Copyright (C) 2012 Samsung Electornics + * Jonghwa Lee <jonghwa3.lee@samsung.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. + * + * This driver is based on clk-max77686.c + * + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/regmap.h> +#include <linux/platform_device.h> +#include <linux/clk-provider.h> +#include <linux/mutex.h> +#include <linux/clkdev.h> +#include <linux/of.h> +#include <linux/export.h> + +struct max_gen_clk { + struct regmap *regmap; + u32 mask; + u32 reg; + struct clk_hw hw; +}; + +static struct max_gen_clk *to_max_gen_clk(struct clk_hw *hw) +{ + return container_of(hw, struct max_gen_clk, hw); +} + +static int max_gen_clk_prepare(struct clk_hw *hw) +{ + struct max_gen_clk *max_gen = to_max_gen_clk(hw); + + return regmap_update_bits(max_gen->regmap, max_gen->reg, + max_gen->mask, max_gen->mask); +} + +static void max_gen_clk_unprepare(struct clk_hw *hw) +{ + struct max_gen_clk *max_gen = to_max_gen_clk(hw); + + regmap_update_bits(max_gen->regmap, max_gen->reg, + max_gen->mask, ~max_gen->mask); +} + +static int max_gen_clk_is_prepared(struct clk_hw *hw) +{ + struct max_gen_clk *max_gen = to_max_gen_clk(hw); + int ret; + u32 val; + + ret = regmap_read(max_gen->regmap, max_gen->reg, &val); + + if (ret < 0) + return -EINVAL; + + return val & max_gen->mask; +} + +static unsigned long max_gen_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return 32768; +} + +struct clk_ops max_gen_clk_ops = { + .prepare = max_gen_clk_prepare, + .unprepare = max_gen_clk_unprepare, + .is_prepared = max_gen_clk_is_prepared, + .recalc_rate = max_gen_recalc_rate, +}; +EXPORT_SYMBOL_GPL(max_gen_clk_ops); + +static struct clk *max_gen_clk_register(struct device *dev, + struct max_gen_clk *max_gen) +{ + struct clk *clk; + struct clk_hw *hw = &max_gen->hw; + int ret; + + clk = devm_clk_register(dev, hw); + if (IS_ERR(clk)) + return clk; + + ret = clk_register_clkdev(clk, hw->init->name, NULL); + + if (ret) + return ERR_PTR(ret); + + return clk; +} + +int max_gen_clk_probe(struct platform_device *pdev, struct regmap *regmap, + u32 reg, struct clk_init_data *clks_init, int num_init) +{ + int i, ret; + struct max_gen_clk *max_gen_clks; + struct clk **clocks; + struct device *dev = pdev->dev.parent; + const char *clk_name; + struct clk_init_data *init; + + clocks = devm_kzalloc(dev, sizeof(struct clk *) * num_init, GFP_KERNEL); + if (!clocks) + return -ENOMEM; + + max_gen_clks = devm_kzalloc(dev, sizeof(struct max_gen_clk) + * num_init, GFP_KERNEL); + if (!max_gen_clks) + return -ENOMEM; + + for (i = 0; i < num_init; i++) { + max_gen_clks[i].regmap = regmap; + max_gen_clks[i].mask = 1 << i; + max_gen_clks[i].reg = reg; + + init = devm_kzalloc(dev, sizeof(*init), GFP_KERNEL); + if (!init) + return -ENOMEM; + + if (dev->of_node && + !of_property_read_string_index(dev->of_node, + "clock-output-names", + i, &clk_name)) + init->name = clk_name; + else + init->name = clks_init[i].name; + + init->ops = clks_init[i].ops; + init->flags = clks_init[i].flags; + + max_gen_clks[i].hw.init = init; + + clocks[i] = max_gen_clk_register(dev, &max_gen_clks[i]); + if (IS_ERR(clocks[i])) { + ret = PTR_ERR(clocks[i]); + dev_err(dev, "failed to register %s\n", + max_gen_clks[i].hw.init->name); + return ret; + } + } + + platform_set_drvdata(pdev, clocks); + + if (dev->of_node) { + struct clk_onecell_data *of_data; + + of_data = devm_kzalloc(dev, sizeof(*of_data), GFP_KERNEL); + if (!of_data) + return -ENOMEM; + + of_data->clks = clocks; + of_data->clk_num = num_init; + ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, + of_data); + + if (ret) { + dev_err(dev, "failed to register OF clock provider\n"); + return ret; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(max_gen_clk_probe); + +int max_gen_clk_remove(struct platform_device *pdev, int num_init) +{ + struct device *dev = pdev->dev.parent; + + if (dev->of_node) + of_clk_del_provider(dev->of_node); + + return 0; +} +EXPORT_SYMBOL_GPL(max_gen_clk_remove); diff --git a/drivers/clk/clk-max-gen.h b/drivers/clk/clk-max-gen.h new file mode 100644 index 000000000000..997e86fc3f4d --- /dev/null +++ b/drivers/clk/clk-max-gen.h @@ -0,0 +1,32 @@ +/* + * clk-max-gen.h - Generic clock driver for Maxim PMICs clocks + * + * Copyright (C) 2014 Google, Inc + * + * 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 __CLK_MAX_GEN_H__ +#define __CLK_MAX_GEN_H__ + +#include <linux/types.h> +#include <linux/device.h> +#include <linux/clkdev.h> +#include <linux/regmap.h> +#include <linux/platform_device.h> + +int max_gen_clk_probe(struct platform_device *pdev, struct regmap *regmap, + u32 reg, struct clk_init_data *clks_init, int num_init); +int max_gen_clk_remove(struct platform_device *pdev, int num_init); +extern struct clk_ops max_gen_clk_ops; + +#endif /* __CLK_MAX_GEN_H__ */ diff --git a/drivers/clk/clk-max77686.c b/drivers/clk/clk-max77686.c index 3d7e8dd8fd58..86cdb3a28629 100644 --- a/drivers/clk/clk-max77686.c +++ b/drivers/clk/clk-max77686.c @@ -30,193 +30,38 @@ #include <linux/mutex.h> #include <linux/clkdev.h> -enum { - MAX77686_CLK_AP = 0, - MAX77686_CLK_CP, - MAX77686_CLK_PMIC, - MAX77686_CLKS_NUM, -}; - -struct max77686_clk { - struct max77686_dev *iodev; - u32 mask; - struct clk_hw hw; - struct clk_lookup *lookup; -}; - -static struct max77686_clk *to_max77686_clk(struct clk_hw *hw) -{ - return container_of(hw, struct max77686_clk, hw); -} - -static int max77686_clk_prepare(struct clk_hw *hw) -{ - struct max77686_clk *max77686 = to_max77686_clk(hw); - - return regmap_update_bits(max77686->iodev->regmap, - MAX77686_REG_32KHZ, max77686->mask, - max77686->mask); -} - -static void max77686_clk_unprepare(struct clk_hw *hw) -{ - struct max77686_clk *max77686 = to_max77686_clk(hw); - - regmap_update_bits(max77686->iodev->regmap, - MAX77686_REG_32KHZ, max77686->mask, ~max77686->mask); -} - -static int max77686_clk_is_prepared(struct clk_hw *hw) -{ - struct max77686_clk *max77686 = to_max77686_clk(hw); - int ret; - u32 val; - - ret = regmap_read(max77686->iodev->regmap, - MAX77686_REG_32KHZ, &val); - - if (ret < 0) - return -EINVAL; - - return val & max77686->mask; -} - -static unsigned long max77686_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) -{ - return 32768; -} - -static struct clk_ops max77686_clk_ops = { - .prepare = max77686_clk_prepare, - .unprepare = max77686_clk_unprepare, - .is_prepared = max77686_clk_is_prepared, - .recalc_rate = max77686_recalc_rate, -}; +#include <dt-bindings/clock/maxim,max77686.h> +#include "clk-max-gen.h" static struct clk_init_data max77686_clks_init[MAX77686_CLKS_NUM] = { [MAX77686_CLK_AP] = { .name = "32khz_ap", - .ops = &max77686_clk_ops, + .ops = &max_gen_clk_ops, .flags = CLK_IS_ROOT, }, [MAX77686_CLK_CP] = { .name = "32khz_cp", - .ops = &max77686_clk_ops, + .ops = &max_gen_clk_ops, .flags = CLK_IS_ROOT, }, [MAX77686_CLK_PMIC] = { .name = "32khz_pmic", - .ops = &max77686_clk_ops, + .ops = &max_gen_clk_ops, .flags = CLK_IS_ROOT, }, }; -static struct clk *max77686_clk_register(struct device *dev, - struct max77686_clk *max77686) -{ - struct clk *clk; - struct clk_hw *hw = &max77686->hw; - - clk = clk_register(dev, hw); - if (IS_ERR(clk)) - return clk; - - max77686->lookup = kzalloc(sizeof(struct clk_lookup), GFP_KERNEL); - if (!max77686->lookup) - return ERR_PTR(-ENOMEM); - - max77686->lookup->con_id = hw->init->name; - max77686->lookup->clk = clk; - - clkdev_add(max77686->lookup); - - return clk; -} - static int max77686_clk_probe(struct platform_device *pdev) { struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent); - struct max77686_clk *max77686_clks[MAX77686_CLKS_NUM]; - struct clk **clocks; - int i, ret; - - clocks = devm_kzalloc(&pdev->dev, sizeof(struct clk *) - * MAX77686_CLKS_NUM, GFP_KERNEL); - if (!clocks) - return -ENOMEM; - - for (i = 0; i < MAX77686_CLKS_NUM; i++) { - max77686_clks[i] = devm_kzalloc(&pdev->dev, - sizeof(struct max77686_clk), GFP_KERNEL); - if (!max77686_clks[i]) - return -ENOMEM; - } - - for (i = 0; i < MAX77686_CLKS_NUM; i++) { - max77686_clks[i]->iodev = iodev; - max77686_clks[i]->mask = 1 << i; - max77686_clks[i]->hw.init = &max77686_clks_init[i]; - - clocks[i] = max77686_clk_register(&pdev->dev, max77686_clks[i]); - if (IS_ERR(clocks[i])) { - ret = PTR_ERR(clocks[i]); - dev_err(&pdev->dev, "failed to register %s\n", - max77686_clks[i]->hw.init->name); - goto err_clocks; - } - } - - platform_set_drvdata(pdev, clocks); - - if (iodev->dev->of_node) { - struct clk_onecell_data *of_data; - of_data = devm_kzalloc(&pdev->dev, - sizeof(*of_data), GFP_KERNEL); - if (!of_data) { - ret = -ENOMEM; - goto err_clocks; - } - - of_data->clks = clocks; - of_data->clk_num = MAX77686_CLKS_NUM; - ret = of_clk_add_provider(iodev->dev->of_node, - of_clk_src_onecell_get, of_data); - if (ret) { - dev_err(&pdev->dev, "failed to register OF clock provider\n"); - goto err_clocks; - } - } - - return 0; - -err_clocks: - for (--i; i >= 0; --i) { - clkdev_drop(max77686_clks[i]->lookup); - clk_unregister(max77686_clks[i]->hw.clk); - } - - return ret; + return max_gen_clk_probe(pdev, iodev->regmap, MAX77686_REG_32KHZ, + max77686_clks_init, MAX77686_CLKS_NUM); } static int max77686_clk_remove(struct platform_device *pdev) { - struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent); - struct clk **clocks = platform_get_drvdata(pdev); - int i; - - if (iodev->dev->of_node) - of_clk_del_provider(iodev->dev->of_node); - - for (i = 0; i < MAX77686_CLKS_NUM; i++) { - struct clk_hw *hw = __clk_get_hw(clocks[i]); - struct max77686_clk *max77686 = to_max77686_clk(hw); - - clkdev_drop(max77686->lookup); - clk_unregister(clocks[i]); - } - return 0; + return max_gen_clk_remove(pdev, MAX77686_CLKS_NUM); } static const struct platform_device_id max77686_clk_id[] = { @@ -228,24 +73,13 @@ MODULE_DEVICE_TABLE(platform, max77686_clk_id); static struct platform_driver max77686_clk_driver = { .driver = { .name = "max77686-clk", - .owner = THIS_MODULE, }, .probe = max77686_clk_probe, .remove = max77686_clk_remove, .id_table = max77686_clk_id, }; -static int __init max77686_clk_init(void) -{ - return platform_driver_register(&max77686_clk_driver); -} -subsys_initcall(max77686_clk_init); - -static void __init max77686_clk_cleanup(void) -{ - platform_driver_unregister(&max77686_clk_driver); -} -module_exit(max77686_clk_cleanup); +module_platform_driver(max77686_clk_driver); MODULE_DESCRIPTION("MAXIM 77686 Clock Driver"); MODULE_AUTHOR("Jonghwa Lee <jonghwa3.lee@samsung.com>"); diff --git a/drivers/clk/clk-max77802.c b/drivers/clk/clk-max77802.c new file mode 100644 index 000000000000..0729dc723a8f --- /dev/null +++ b/drivers/clk/clk-max77802.c @@ -0,0 +1,97 @@ +/* + * clk-max77802.c - Clock driver for Maxim 77802 + * + * Copyright (C) 2014 Google, Inc + * + * Copyright (C) 2012 Samsung Electornics + * Jonghwa Lee <jonghwa3.lee@samsung.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. + * + * This driver is based on clk-max77686.c + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/mfd/max77686-private.h> +#include <linux/clk-provider.h> +#include <linux/mutex.h> +#include <linux/clkdev.h> + +#include <dt-bindings/clock/maxim,max77802.h> +#include "clk-max-gen.h" + +#define MAX77802_CLOCK_OPMODE_MASK 0x1 +#define MAX77802_CLOCK_LOW_JITTER_SHIFT 0x3 + +static struct clk_init_data max77802_clks_init[MAX77802_CLKS_NUM] = { + [MAX77802_CLK_32K_AP] = { + .name = "32khz_ap", + .ops = &max_gen_clk_ops, + .flags = CLK_IS_ROOT, + }, + [MAX77802_CLK_32K_CP] = { + .name = "32khz_cp", + .ops = &max_gen_clk_ops, + .flags = CLK_IS_ROOT, + }, +}; + +static int max77802_clk_probe(struct platform_device *pdev) +{ + struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent); + int ret; + + ret = max_gen_clk_probe(pdev, iodev->regmap, MAX77802_REG_32KHZ, + max77802_clks_init, MAX77802_CLKS_NUM); + + if (ret) { + dev_err(&pdev->dev, "generic probe failed %d\n", ret); + return ret; + } + + /* Enable low-jitter mode on the 32khz clocks. */ + ret = regmap_update_bits(iodev->regmap, MAX77802_REG_32KHZ, + 1 << MAX77802_CLOCK_LOW_JITTER_SHIFT, + 1 << MAX77802_CLOCK_LOW_JITTER_SHIFT); + if (ret < 0) + dev_err(&pdev->dev, "failed to enable low-jitter mode\n"); + + return ret; +} + +static int max77802_clk_remove(struct platform_device *pdev) +{ + return max_gen_clk_remove(pdev, MAX77802_CLKS_NUM); +} + +static const struct platform_device_id max77802_clk_id[] = { + { "max77802-clk", 0}, + { }, +}; +MODULE_DEVICE_TABLE(platform, max77802_clk_id); + +static struct platform_driver max77802_clk_driver = { + .driver = { + .name = "max77802-clk", + }, + .probe = max77802_clk_probe, + .remove = max77802_clk_remove, + .id_table = max77802_clk_id, +}; + +module_platform_driver(max77802_clk_driver); + +MODULE_DESCRIPTION("MAXIM 77802 Clock Driver"); +MODULE_AUTHOR("Javier Martinez Canillas <javier.martinez@collabora.co.uk>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/clk-palmas.c b/drivers/clk/clk-palmas.c index 781630e1372b..8d459923a15f 100644 --- a/drivers/clk/clk-palmas.c +++ b/drivers/clk/clk-palmas.c @@ -292,7 +292,6 @@ static int palmas_clks_remove(struct platform_device *pdev) static struct platform_driver palmas_clks_driver = { .driver = { .name = "palmas-clk", - .owner = THIS_MODULE, .of_match_table = palmas_clks_of_match, }, .probe = palmas_clks_probe, diff --git a/drivers/clk/clk-twl6040.c b/drivers/clk/clk-twl6040.c index 1ada79a28052..4a755135bcd3 100644 --- a/drivers/clk/clk-twl6040.c +++ b/drivers/clk/clk-twl6040.c @@ -112,7 +112,6 @@ static int twl6040_clk_remove(struct platform_device *pdev) static struct platform_driver twl6040_clk_driver = { .driver = { .name = "twl6040-clk", - .owner = THIS_MODULE, }, .probe = twl6040_clk_probe, .remove = twl6040_clk_remove, diff --git a/drivers/clk/clk-wm831x.c b/drivers/clk/clk-wm831x.c index b131041c8f48..ef67719f4e52 100644 --- a/drivers/clk/clk-wm831x.c +++ b/drivers/clk/clk-wm831x.c @@ -395,7 +395,6 @@ static struct platform_driver wm831x_clk_driver = { .probe = wm831x_clk_probe, .driver = { .name = "wm831x-clk", - .owner = THIS_MODULE, }, }; diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index bacc06ff939b..4896ae9e23da 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -100,6 +100,8 @@ static void clk_enable_unlock(unsigned long flags) static struct dentry *rootdir; static int inited = 0; +static DEFINE_MUTEX(clk_debug_lock); +static HLIST_HEAD(clk_debug_list); static struct hlist_head *all_lists[] = { &clk_root_list, @@ -117,11 +119,11 @@ static void clk_summary_show_one(struct seq_file *s, struct clk *c, int level) if (!c) return; - seq_printf(s, "%*s%-*s %11d %12d %11lu %10lu\n", + seq_printf(s, "%*s%-*s %11d %12d %11lu %10lu %-3d\n", level * 3 + 1, "", 30 - level * 3, c->name, c->enable_count, c->prepare_count, clk_get_rate(c), - clk_get_accuracy(c)); + clk_get_accuracy(c), clk_get_phase(c)); } static void clk_summary_show_subtree(struct seq_file *s, struct clk *c, @@ -143,8 +145,8 @@ static int clk_summary_show(struct seq_file *s, void *data) struct clk *c; struct hlist_head **lists = (struct hlist_head **)s->private; - seq_puts(s, " clock enable_cnt prepare_cnt rate accuracy\n"); - seq_puts(s, "--------------------------------------------------------------------------------\n"); + seq_puts(s, " clock enable_cnt prepare_cnt rate accuracy phase\n"); + seq_puts(s, "----------------------------------------------------------------------------------------\n"); clk_prepare_lock(); @@ -180,6 +182,7 @@ static void clk_dump_one(struct seq_file *s, struct clk *c, int level) seq_printf(s, "\"prepare_count\": %d,", c->prepare_count); seq_printf(s, "\"rate\": %lu", clk_get_rate(c)); seq_printf(s, "\"accuracy\": %lu", clk_get_accuracy(c)); + seq_printf(s, "\"phase\": %d", clk_get_phase(c)); } static void clk_dump_subtree(struct seq_file *s, struct clk *c, int level) @@ -264,6 +267,11 @@ static int clk_debug_create_one(struct clk *clk, struct dentry *pdentry) if (!d) goto err_out; + d = debugfs_create_u32("clk_phase", S_IRUGO, clk->dentry, + (u32 *)&clk->phase); + if (!d) + goto err_out; + d = debugfs_create_x32("clk_flags", S_IRUGO, clk->dentry, (u32 *)&clk->flags); if (!d) @@ -300,28 +308,6 @@ out: return ret; } -/* caller must hold prepare_lock */ -static int clk_debug_create_subtree(struct clk *clk, struct dentry *pdentry) -{ - struct clk *child; - int ret = -EINVAL;; - - if (!clk || !pdentry) - goto out; - - ret = clk_debug_create_one(clk, pdentry); - - if (ret) - goto out; - - hlist_for_each_entry(child, &clk->children, child_node) - clk_debug_create_subtree(child, pdentry); - - ret = 0; -out: - return ret; -} - /** * clk_debug_register - add a clk node to the debugfs clk tree * @clk: the clk being added to the debugfs clk tree @@ -329,20 +315,21 @@ out: * Dynamically adds a clk to the debugfs clk tree if debugfs has been * initialized. Otherwise it bails out early since the debugfs clk tree * will be created lazily by clk_debug_init as part of a late_initcall. - * - * Caller must hold prepare_lock. Only clk_init calls this function (so - * far) so this is taken care. */ static int clk_debug_register(struct clk *clk) { int ret = 0; + mutex_lock(&clk_debug_lock); + hlist_add_head(&clk->debug_node, &clk_debug_list); + if (!inited) - goto out; + goto unlock; - ret = clk_debug_create_subtree(clk, rootdir); + ret = clk_debug_create_one(clk, rootdir); +unlock: + mutex_unlock(&clk_debug_lock); -out: return ret; } @@ -353,12 +340,18 @@ out: * Dynamically removes a clk and all it's children clk nodes from the * debugfs clk tree if clk->dentry points to debugfs created by * clk_debug_register in __clk_init. - * - * Caller must hold prepare_lock. */ static void clk_debug_unregister(struct clk *clk) { + mutex_lock(&clk_debug_lock); + if (!clk->dentry) + goto out; + + hlist_del_init(&clk->debug_node); debugfs_remove_recursive(clk->dentry); + clk->dentry = NULL; +out: + mutex_unlock(&clk_debug_lock); } struct dentry *clk_debugfs_add_file(struct clk *clk, char *name, umode_t mode, @@ -415,17 +408,12 @@ static int __init clk_debug_init(void) if (!d) return -ENOMEM; - clk_prepare_lock(); - - hlist_for_each_entry(clk, &clk_root_list, child_node) - clk_debug_create_subtree(clk, rootdir); - - hlist_for_each_entry(clk, &clk_orphan_list, child_node) - clk_debug_create_subtree(clk, rootdir); + mutex_lock(&clk_debug_lock); + hlist_for_each_entry(clk, &clk_debug_list, debug_node) + clk_debug_create_one(clk, rootdir); inited = 1; - - clk_prepare_unlock(); + mutex_unlock(&clk_debug_lock); return 0; } @@ -1744,6 +1732,77 @@ out: EXPORT_SYMBOL_GPL(clk_set_parent); /** + * clk_set_phase - adjust the phase shift of a clock signal + * @clk: clock signal source + * @degrees: number of degrees the signal is shifted + * + * Shifts the phase of a clock signal by the specified + * degrees. Returns 0 on success, -EERROR otherwise. + * + * This function makes no distinction about the input or reference + * signal that we adjust the clock signal phase against. For example + * phase locked-loop clock signal generators we may shift phase with + * respect to feedback clock signal input, but for other cases the + * clock phase may be shifted with respect to some other, unspecified + * signal. + * + * Additionally the concept of phase shift does not propagate through + * the clock tree hierarchy, which sets it apart from clock rates and + * clock accuracy. A parent clock phase attribute does not have an + * impact on the phase attribute of a child clock. + */ +int clk_set_phase(struct clk *clk, int degrees) +{ + int ret = 0; + + if (!clk) + goto out; + + /* sanity check degrees */ + degrees %= 360; + if (degrees < 0) + degrees += 360; + + clk_prepare_lock(); + + if (!clk->ops->set_phase) + goto out_unlock; + + ret = clk->ops->set_phase(clk->hw, degrees); + + if (!ret) + clk->phase = degrees; + +out_unlock: + clk_prepare_unlock(); + +out: + return ret; +} + +/** + * clk_get_phase - return the phase shift of a clock signal + * @clk: clock signal source + * + * Returns the phase shift of a clock node in degrees, otherwise returns + * -EERROR. + */ +int clk_get_phase(struct clk *clk) +{ + int ret = 0; + + if (!clk) + goto out; + + clk_prepare_lock(); + ret = clk->phase; + clk_prepare_unlock(); + +out: + return ret; +} + +/** * __clk_init - initialize the data structures in a struct clk * @dev: device initializing this clk, placeholder for now * @clk: clk being initialized @@ -1862,6 +1921,16 @@ int __clk_init(struct device *dev, struct clk *clk) clk->accuracy = 0; /* + * Set clk's phase. + * Since a phase is by definition relative to its parent, just + * query the current clock phase, or just assume it's in phase. + */ + if (clk->ops->get_phase) + clk->phase = clk->ops->get_phase(clk->hw); + else + clk->phase = 0; + + /* * Set clk's rate. The preferred method is to use .recalc_rate. For * simple clocks and lazy developers the default fallback is to use the * parent's rate. If a clock doesn't have a parent (or is orphaned) @@ -2092,14 +2161,16 @@ void clk_unregister(struct clk *clk) { unsigned long flags; - if (!clk || WARN_ON_ONCE(IS_ERR(clk))) - return; + if (!clk || WARN_ON_ONCE(IS_ERR(clk))) + return; + + clk_debug_unregister(clk); clk_prepare_lock(); if (clk->ops == &clk_nodrv_ops) { pr_err("%s: unregistered clock: %s\n", __func__, clk->name); - goto out; + return; } /* * Assign empty clock ops for consumers that might still hold @@ -2118,16 +2189,13 @@ void clk_unregister(struct clk *clk) clk_set_parent(child, NULL); } - clk_debug_unregister(clk); - hlist_del_init(&clk->child_node); if (clk->prepare_count) pr_warn("%s: unregistering prepared clock: %s\n", __func__, clk->name); - kref_put(&clk->ref, __clk_release); -out: + clk_prepare_unlock(); } EXPORT_SYMBOL_GPL(clk_unregister); diff --git a/drivers/clk/hisilicon/clk-hix5hd2.c b/drivers/clk/hisilicon/clk-hix5hd2.c index e5fcfb4e32ef..3f369c60fe56 100644 --- a/drivers/clk/hisilicon/clk-hix5hd2.c +++ b/drivers/clk/hisilicon/clk-hix5hd2.c @@ -9,6 +9,8 @@ #include <linux/of_address.h> #include <dt-bindings/clock/hix5hd2-clock.h> +#include <linux/slab.h> +#include <linux/delay.h> #include "clk.h" static struct hisi_fixed_rate_clock hix5hd2_fixed_rate_clks[] __initdata = { @@ -48,9 +50,9 @@ static const char *sfc_mux_p[] __initconst = { "24m", "150m", "200m", "100m", "75m", }; static u32 sfc_mux_table[] = {0, 4, 5, 6, 7}; -static const char *sdio1_mux_p[] __initconst = { +static const char *sdio_mux_p[] __initconst = { "75m", "100m", "50m", "15m", }; -static u32 sdio1_mux_table[] = {0, 1, 2, 3}; +static u32 sdio_mux_table[] = {0, 1, 2, 3}; static const char *fephy_mux_p[] __initconst = { "25m", "125m"}; static u32 fephy_mux_table[] = {0, 1}; @@ -59,28 +61,243 @@ static u32 fephy_mux_table[] = {0, 1}; static struct hisi_mux_clock hix5hd2_mux_clks[] __initdata = { { HIX5HD2_SFC_MUX, "sfc_mux", sfc_mux_p, ARRAY_SIZE(sfc_mux_p), CLK_SET_RATE_PARENT, 0x5c, 8, 3, 0, sfc_mux_table, }, - { HIX5HD2_MMC_MUX, "mmc_mux", sdio1_mux_p, ARRAY_SIZE(sdio1_mux_p), - CLK_SET_RATE_PARENT, 0xa0, 8, 2, 0, sdio1_mux_table, }, + { HIX5HD2_MMC_MUX, "mmc_mux", sdio_mux_p, ARRAY_SIZE(sdio_mux_p), + CLK_SET_RATE_PARENT, 0xa0, 8, 2, 0, sdio_mux_table, }, + { HIX5HD2_SD_MUX, "sd_mux", sdio_mux_p, ARRAY_SIZE(sdio_mux_p), + CLK_SET_RATE_PARENT, 0x9c, 8, 2, 0, sdio_mux_table, }, { HIX5HD2_FEPHY_MUX, "fephy_mux", fephy_mux_p, ARRAY_SIZE(fephy_mux_p), CLK_SET_RATE_PARENT, 0x120, 8, 2, 0, fephy_mux_table, }, }; static struct hisi_gate_clock hix5hd2_gate_clks[] __initdata = { - /*sfc*/ + /* sfc */ { HIX5HD2_SFC_CLK, "clk_sfc", "sfc_mux", CLK_SET_RATE_PARENT, 0x5c, 0, 0, }, { HIX5HD2_SFC_RST, "rst_sfc", "clk_sfc", CLK_SET_RATE_PARENT, 0x5c, 4, CLK_GATE_SET_TO_DISABLE, }, - /*sdio1*/ + /* sdio0 */ + { HIX5HD2_SD_BIU_CLK, "clk_sd_biu", "200m", + CLK_SET_RATE_PARENT, 0x9c, 0, 0, }, + { HIX5HD2_SD_CIU_CLK, "clk_sd_ciu", "sd_mux", + CLK_SET_RATE_PARENT, 0x9c, 1, 0, }, + { HIX5HD2_SD_CIU_RST, "rst_sd_ciu", "clk_sd_ciu", + CLK_SET_RATE_PARENT, 0x9c, 4, CLK_GATE_SET_TO_DISABLE, }, + /* sdio1 */ { HIX5HD2_MMC_BIU_CLK, "clk_mmc_biu", "200m", CLK_SET_RATE_PARENT, 0xa0, 0, 0, }, { HIX5HD2_MMC_CIU_CLK, "clk_mmc_ciu", "mmc_mux", CLK_SET_RATE_PARENT, 0xa0, 1, 0, }, { HIX5HD2_MMC_CIU_RST, "rst_mmc_ciu", "clk_mmc_ciu", CLK_SET_RATE_PARENT, 0xa0, 4, CLK_GATE_SET_TO_DISABLE, }, + /* gsf */ + { HIX5HD2_FWD_BUS_CLK, "clk_fwd_bus", NULL, 0, 0xcc, 0, 0, }, + { HIX5HD2_FWD_SYS_CLK, "clk_fwd_sys", "clk_fwd_bus", 0, 0xcc, 5, 0, }, + { HIX5HD2_MAC0_PHY_CLK, "clk_fephy", "clk_fwd_sys", + CLK_SET_RATE_PARENT, 0x120, 0, 0, }, + /* wdg0 */ + { HIX5HD2_WDG0_CLK, "clk_wdg0", "24m", + CLK_SET_RATE_PARENT, 0x178, 0, 0, }, + { HIX5HD2_WDG0_RST, "rst_wdg0", "clk_wdg0", + CLK_SET_RATE_PARENT, 0x178, 4, CLK_GATE_SET_TO_DISABLE, }, + /* I2C */ + {HIX5HD2_I2C0_CLK, "clk_i2c0", "100m", + CLK_SET_RATE_PARENT, 0x06c, 4, 0, }, + {HIX5HD2_I2C0_RST, "rst_i2c0", "clk_i2c0", + CLK_SET_RATE_PARENT, 0x06c, 5, CLK_GATE_SET_TO_DISABLE, }, + {HIX5HD2_I2C1_CLK, "clk_i2c1", "100m", + CLK_SET_RATE_PARENT, 0x06c, 8, 0, }, + {HIX5HD2_I2C1_RST, "rst_i2c1", "clk_i2c1", + CLK_SET_RATE_PARENT, 0x06c, 9, CLK_GATE_SET_TO_DISABLE, }, + {HIX5HD2_I2C2_CLK, "clk_i2c2", "100m", + CLK_SET_RATE_PARENT, 0x06c, 12, 0, }, + {HIX5HD2_I2C2_RST, "rst_i2c2", "clk_i2c2", + CLK_SET_RATE_PARENT, 0x06c, 13, CLK_GATE_SET_TO_DISABLE, }, + {HIX5HD2_I2C3_CLK, "clk_i2c3", "100m", + CLK_SET_RATE_PARENT, 0x06c, 16, 0, }, + {HIX5HD2_I2C3_RST, "rst_i2c3", "clk_i2c3", + CLK_SET_RATE_PARENT, 0x06c, 17, CLK_GATE_SET_TO_DISABLE, }, + {HIX5HD2_I2C4_CLK, "clk_i2c4", "100m", + CLK_SET_RATE_PARENT, 0x06c, 20, 0, }, + {HIX5HD2_I2C4_RST, "rst_i2c4", "clk_i2c4", + CLK_SET_RATE_PARENT, 0x06c, 21, CLK_GATE_SET_TO_DISABLE, }, + {HIX5HD2_I2C5_CLK, "clk_i2c5", "100m", + CLK_SET_RATE_PARENT, 0x06c, 0, 0, }, + {HIX5HD2_I2C5_RST, "rst_i2c5", "clk_i2c5", + CLK_SET_RATE_PARENT, 0x06c, 1, CLK_GATE_SET_TO_DISABLE, }, }; +enum hix5hd2_clk_type { + TYPE_COMPLEX, + TYPE_ETHER, +}; + +struct hix5hd2_complex_clock { + const char *name; + const char *parent_name; + u32 id; + u32 ctrl_reg; + u32 ctrl_clk_mask; + u32 ctrl_rst_mask; + u32 phy_reg; + u32 phy_clk_mask; + u32 phy_rst_mask; + enum hix5hd2_clk_type type; +}; + +struct hix5hd2_clk_complex { + struct clk_hw hw; + u32 id; + void __iomem *ctrl_reg; + u32 ctrl_clk_mask; + u32 ctrl_rst_mask; + void __iomem *phy_reg; + u32 phy_clk_mask; + u32 phy_rst_mask; +}; + +static struct hix5hd2_complex_clock hix5hd2_complex_clks[] __initdata = { + {"clk_mac0", "clk_fephy", HIX5HD2_MAC0_CLK, + 0xcc, 0xa, 0x500, 0x120, 0, 0x10, TYPE_ETHER}, + {"clk_mac1", "clk_fwd_sys", HIX5HD2_MAC1_CLK, + 0xcc, 0x14, 0xa00, 0x168, 0x2, 0, TYPE_ETHER}, + {"clk_sata", NULL, HIX5HD2_SATA_CLK, + 0xa8, 0x1f, 0x300, 0xac, 0x1, 0x0, TYPE_COMPLEX}, + {"clk_usb", NULL, HIX5HD2_USB_CLK, + 0xb8, 0xff, 0x3f000, 0xbc, 0x7, 0x3f00, TYPE_COMPLEX}, +}; + +#define to_complex_clk(_hw) container_of(_hw, struct hix5hd2_clk_complex, hw) + +static int clk_ether_prepare(struct clk_hw *hw) +{ + struct hix5hd2_clk_complex *clk = to_complex_clk(hw); + u32 val; + + val = readl_relaxed(clk->ctrl_reg); + val |= clk->ctrl_clk_mask | clk->ctrl_rst_mask; + writel_relaxed(val, clk->ctrl_reg); + val &= ~(clk->ctrl_rst_mask); + writel_relaxed(val, clk->ctrl_reg); + + val = readl_relaxed(clk->phy_reg); + val |= clk->phy_clk_mask; + val &= ~(clk->phy_rst_mask); + writel_relaxed(val, clk->phy_reg); + mdelay(10); + + val &= ~(clk->phy_clk_mask); + val |= clk->phy_rst_mask; + writel_relaxed(val, clk->phy_reg); + mdelay(10); + + val |= clk->phy_clk_mask; + val &= ~(clk->phy_rst_mask); + writel_relaxed(val, clk->phy_reg); + mdelay(30); + return 0; +} + +static void clk_ether_unprepare(struct clk_hw *hw) +{ + struct hix5hd2_clk_complex *clk = to_complex_clk(hw); + u32 val; + + val = readl_relaxed(clk->ctrl_reg); + val &= ~(clk->ctrl_clk_mask); + writel_relaxed(val, clk->ctrl_reg); +} + +static struct clk_ops clk_ether_ops = { + .prepare = clk_ether_prepare, + .unprepare = clk_ether_unprepare, +}; + +static int clk_complex_enable(struct clk_hw *hw) +{ + struct hix5hd2_clk_complex *clk = to_complex_clk(hw); + u32 val; + + val = readl_relaxed(clk->ctrl_reg); + val |= clk->ctrl_clk_mask; + val &= ~(clk->ctrl_rst_mask); + writel_relaxed(val, clk->ctrl_reg); + + val = readl_relaxed(clk->phy_reg); + val |= clk->phy_clk_mask; + val &= ~(clk->phy_rst_mask); + writel_relaxed(val, clk->phy_reg); + + return 0; +} + +static void clk_complex_disable(struct clk_hw *hw) +{ + struct hix5hd2_clk_complex *clk = to_complex_clk(hw); + u32 val; + + val = readl_relaxed(clk->ctrl_reg); + val |= clk->ctrl_rst_mask; + val &= ~(clk->ctrl_clk_mask); + writel_relaxed(val, clk->ctrl_reg); + + val = readl_relaxed(clk->phy_reg); + val |= clk->phy_rst_mask; + val &= ~(clk->phy_clk_mask); + writel_relaxed(val, clk->phy_reg); +} + +static struct clk_ops clk_complex_ops = { + .enable = clk_complex_enable, + .disable = clk_complex_disable, +}; + +void __init hix5hd2_clk_register_complex(struct hix5hd2_complex_clock *clks, + int nums, struct hisi_clock_data *data) +{ + void __iomem *base = data->base; + int i; + + for (i = 0; i < nums; i++) { + struct hix5hd2_clk_complex *p_clk; + struct clk *clk; + struct clk_init_data init; + + p_clk = kzalloc(sizeof(*p_clk), GFP_KERNEL); + if (!p_clk) + return; + + init.name = clks[i].name; + if (clks[i].type == TYPE_ETHER) + init.ops = &clk_ether_ops; + else + init.ops = &clk_complex_ops; + + init.flags = CLK_IS_BASIC; + init.parent_names = + (clks[i].parent_name ? &clks[i].parent_name : NULL); + init.num_parents = (clks[i].parent_name ? 1 : 0); + + p_clk->ctrl_reg = base + clks[i].ctrl_reg; + p_clk->ctrl_clk_mask = clks[i].ctrl_clk_mask; + p_clk->ctrl_rst_mask = clks[i].ctrl_rst_mask; + p_clk->phy_reg = base + clks[i].phy_reg; + p_clk->phy_clk_mask = clks[i].phy_clk_mask; + p_clk->phy_rst_mask = clks[i].phy_rst_mask; + p_clk->hw.init = &init; + + clk = clk_register(NULL, &p_clk->hw); + if (IS_ERR(clk)) { + kfree(p_clk); + pr_err("%s: failed to register clock %s\n", + __func__, clks[i].name); + continue; + } + + data->clk_data.clks[clks[i].id] = clk; + } +} + static void __init hix5hd2_clk_init(struct device_node *np) { struct hisi_clock_data *clk_data; @@ -96,6 +313,9 @@ static void __init hix5hd2_clk_init(struct device_node *np) clk_data); hisi_clk_register_gate(hix5hd2_gate_clks, ARRAY_SIZE(hix5hd2_gate_clks), clk_data); + hix5hd2_clk_register_complex(hix5hd2_complex_clks, + ARRAY_SIZE(hix5hd2_complex_clks), + clk_data); } CLK_OF_DECLARE(hix5hd2_clk, "hisilicon,hix5hd2-clock", hix5hd2_clk_init); diff --git a/drivers/clk/mvebu/armada-370.c b/drivers/clk/mvebu/armada-370.c index bef198a83863..756f0f39d6a3 100644 --- a/drivers/clk/mvebu/armada-370.c +++ b/drivers/clk/mvebu/armada-370.c @@ -23,6 +23,7 @@ */ #define SARL 0 /* Low part [0:31] */ +#define SARL_A370_SSCG_ENABLE BIT(10) #define SARL_A370_PCLK_FREQ_OPT 11 #define SARL_A370_PCLK_FREQ_OPT_MASK 0xF #define SARL_A370_FAB_FREQ_OPT 15 @@ -133,10 +134,17 @@ static void __init a370_get_clk_ratio( } } +static bool a370_is_sscg_enabled(void __iomem *sar) +{ + return !(readl(sar) & SARL_A370_SSCG_ENABLE); +} + static const struct coreclk_soc_desc a370_coreclks = { .get_tclk_freq = a370_get_tclk_freq, .get_cpu_freq = a370_get_cpu_freq, .get_clk_ratio = a370_get_clk_ratio, + .is_sscg_enabled = a370_is_sscg_enabled, + .fix_sscg_deviation = kirkwood_fix_sscg_deviation, .ratios = a370_coreclk_ratios, .num_ratios = ARRAY_SIZE(a370_coreclk_ratios), }; diff --git a/drivers/clk/mvebu/armada-375.c b/drivers/clk/mvebu/armada-375.c index c991a4d95e10..c7af2242b796 100644 --- a/drivers/clk/mvebu/armada-375.c +++ b/drivers/clk/mvebu/armada-375.c @@ -27,14 +27,14 @@ * all modified at the same time, and not separately as for the Armada * 370 or the Armada XP SoCs. * - * SAR0[21:17] : CPU frequency DDR frequency L2 frequency + * SAR1[21:17] : CPU frequency DDR frequency L2 frequency * 6 = 400 MHz 400 MHz 200 MHz * 15 = 600 MHz 600 MHz 300 MHz * 21 = 800 MHz 534 MHz 400 MHz * 25 = 1000 MHz 500 MHz 500 MHz * others reserved. * - * SAR0[22] : TCLK frequency + * SAR1[22] : TCLK frequency * 0 = 166 MHz * 1 = 200 MHz */ diff --git a/drivers/clk/mvebu/common.c b/drivers/clk/mvebu/common.c index 25ceccf939ad..b7fcb469c87a 100644 --- a/drivers/clk/mvebu/common.c +++ b/drivers/clk/mvebu/common.c @@ -26,8 +26,85 @@ * Core Clocks */ +#define SSCG_CONF_MODE(reg) (((reg) >> 16) & 0x3) +#define SSCG_SPREAD_DOWN 0x0 +#define SSCG_SPREAD_UP 0x1 +#define SSCG_SPREAD_CENTRAL 0x2 +#define SSCG_CONF_LOW(reg) (((reg) >> 8) & 0xFF) +#define SSCG_CONF_HIGH(reg) ((reg) & 0xFF) + static struct clk_onecell_data clk_data; +/* + * This function can be used by the Kirkwood, the Armada 370, the + * Armada XP and the Armada 375 SoC. The name of the function was + * chosen following the dt convention: using the first known SoC + * compatible with it. + */ +u32 kirkwood_fix_sscg_deviation(u32 system_clk) +{ + struct device_node *sscg_np = NULL; + void __iomem *sscg_map; + u32 sscg_reg; + s32 low_bound, high_bound; + u64 freq_swing_half; + + sscg_np = of_find_node_by_name(NULL, "sscg"); + if (sscg_np == NULL) { + pr_err("cannot get SSCG register node\n"); + return system_clk; + } + + sscg_map = of_iomap(sscg_np, 0); + if (sscg_map == NULL) { + pr_err("cannot map SSCG register\n"); + goto out; + } + + sscg_reg = readl(sscg_map); + high_bound = SSCG_CONF_HIGH(sscg_reg); + low_bound = SSCG_CONF_LOW(sscg_reg); + + if ((high_bound - low_bound) <= 0) + goto out; + /* + * From Marvell engineer we got the following formula (when + * this code was written, the datasheet was erroneous) + * Spread percentage = 1/96 * (H - L) / H + * H = SSCG_High_Boundary + * L = SSCG_Low_Boundary + * + * As the deviation is half of spread then it lead to the + * following formula in the code. + * + * To avoid an overflow and not lose any significant digit in + * the same time we have to use a 64 bit integer. + */ + + freq_swing_half = (((u64)high_bound - (u64)low_bound) + * (u64)system_clk); + do_div(freq_swing_half, (2 * 96 * high_bound)); + + switch (SSCG_CONF_MODE(sscg_reg)) { + case SSCG_SPREAD_DOWN: + system_clk -= freq_swing_half; + break; + case SSCG_SPREAD_UP: + system_clk += freq_swing_half; + break; + case SSCG_SPREAD_CENTRAL: + default: + break; + } + + iounmap(sscg_map); + +out: + of_node_put(sscg_np); + + return system_clk; +} + void __init mvebu_coreclk_setup(struct device_node *np, const struct coreclk_soc_desc *desc) { @@ -62,6 +139,11 @@ void __init mvebu_coreclk_setup(struct device_node *np, of_property_read_string_index(np, "clock-output-names", 1, &cpuclk_name); rate = desc->get_cpu_freq(base); + + if (desc->is_sscg_enabled && desc->fix_sscg_deviation + && desc->is_sscg_enabled(base)) + rate = desc->fix_sscg_deviation(rate); + clk_data.clks[1] = clk_register_fixed_rate(NULL, cpuclk_name, NULL, CLK_IS_ROOT, rate); WARN_ON(IS_ERR(clk_data.clks[1])); @@ -89,8 +171,10 @@ void __init mvebu_coreclk_setup(struct device_node *np, * Clock Gating Control */ +DEFINE_SPINLOCK(ctrl_gating_lock); + struct clk_gating_ctrl { - spinlock_t lock; + spinlock_t *lock; struct clk **gates; int num_gates; }; @@ -138,7 +222,8 @@ void __init mvebu_clk_gating_setup(struct device_node *np, if (WARN_ON(!ctrl)) goto ctrl_out; - spin_lock_init(&ctrl->lock); + /* lock must already be initialized */ + ctrl->lock = &ctrl_gating_lock; /* Count, allocate, and register clock gates */ for (n = 0; desc[n].name;) @@ -155,7 +240,7 @@ void __init mvebu_clk_gating_setup(struct device_node *np, (desc[n].parent) ? desc[n].parent : default_parent; ctrl->gates[n] = clk_register_gate(NULL, desc[n].name, parent, desc[n].flags, base, desc[n].bit_idx, - 0, &ctrl->lock); + 0, ctrl->lock); WARN_ON(IS_ERR(ctrl->gates[n])); } diff --git a/drivers/clk/mvebu/common.h b/drivers/clk/mvebu/common.h index f968b4d9df92..783b5631a453 100644 --- a/drivers/clk/mvebu/common.h +++ b/drivers/clk/mvebu/common.h @@ -17,6 +17,8 @@ #include <linux/kernel.h> +extern spinlock_t ctrl_gating_lock; + struct device_node; struct coreclk_ratio { @@ -28,6 +30,8 @@ struct coreclk_soc_desc { u32 (*get_tclk_freq)(void __iomem *sar); u32 (*get_cpu_freq)(void __iomem *sar); void (*get_clk_ratio)(void __iomem *sar, int id, int *mult, int *div); + bool (*is_sscg_enabled)(void __iomem *sar); + u32 (*fix_sscg_deviation)(u32 system_clk); const struct coreclk_ratio *ratios; int num_ratios; }; @@ -45,4 +49,9 @@ void __init mvebu_coreclk_setup(struct device_node *np, void __init mvebu_clk_gating_setup(struct device_node *np, const struct clk_gating_soc_desc *desc); +/* + * This function is shared among the Kirkwood, Armada 370, Armada XP + * and Armada 375 SoC + */ +u32 kirkwood_fix_sscg_deviation(u32 system_clk); #endif diff --git a/drivers/clk/mvebu/kirkwood.c b/drivers/clk/mvebu/kirkwood.c index ddb666a86500..99550f25975e 100644 --- a/drivers/clk/mvebu/kirkwood.c +++ b/drivers/clk/mvebu/kirkwood.c @@ -13,9 +13,11 @@ */ #include <linux/kernel.h> +#include <linux/slab.h> #include <linux/clk-provider.h> #include <linux/io.h> #include <linux/of.h> +#include <linux/of_address.h> #include "common.h" /* @@ -214,7 +216,6 @@ static const struct clk_gating_soc_desc kirkwood_gating_desc[] __initconst = { { "runit", NULL, 7, 0 }, { "xor0", NULL, 8, 0 }, { "audio", NULL, 9, 0 }, - { "powersave", "cpuclk", 11, 0 }, { "sata0", NULL, 14, 0 }, { "sata1", NULL, 15, 0 }, { "xor1", NULL, 16, 0 }, @@ -225,6 +226,101 @@ static const struct clk_gating_soc_desc kirkwood_gating_desc[] __initconst = { { } }; + +/* + * Clock Muxing Control + */ + +struct clk_muxing_soc_desc { + const char *name; + const char **parents; + int num_parents; + int shift; + int width; + unsigned long flags; +}; + +struct clk_muxing_ctrl { + spinlock_t *lock; + struct clk **muxes; + int num_muxes; +}; + +static const char *powersave_parents[] = { + "cpuclk", + "ddrclk", +}; + +static const struct clk_muxing_soc_desc kirkwood_mux_desc[] __initconst = { + { "powersave", powersave_parents, ARRAY_SIZE(powersave_parents), + 11, 1, 0 }, +}; + +#define to_clk_mux(_hw) container_of(_hw, struct clk_mux, hw) + +static struct clk *clk_muxing_get_src( + struct of_phandle_args *clkspec, void *data) +{ + struct clk_muxing_ctrl *ctrl = (struct clk_muxing_ctrl *)data; + int n; + + if (clkspec->args_count < 1) + return ERR_PTR(-EINVAL); + + for (n = 0; n < ctrl->num_muxes; n++) { + struct clk_mux *mux = + to_clk_mux(__clk_get_hw(ctrl->muxes[n])); + if (clkspec->args[0] == mux->shift) + return ctrl->muxes[n]; + } + return ERR_PTR(-ENODEV); +} + +static void __init kirkwood_clk_muxing_setup(struct device_node *np, + const struct clk_muxing_soc_desc *desc) +{ + struct clk_muxing_ctrl *ctrl; + void __iomem *base; + int n; + + base = of_iomap(np, 0); + if (WARN_ON(!base)) + return; + + ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); + if (WARN_ON(!ctrl)) + goto ctrl_out; + + /* lock must already be initialized */ + ctrl->lock = &ctrl_gating_lock; + + /* Count, allocate, and register clock muxes */ + for (n = 0; desc[n].name;) + n++; + + ctrl->num_muxes = n; + ctrl->muxes = kcalloc(ctrl->num_muxes, sizeof(struct clk *), + GFP_KERNEL); + if (WARN_ON(!ctrl->muxes)) + goto muxes_out; + + for (n = 0; n < ctrl->num_muxes; n++) { + ctrl->muxes[n] = clk_register_mux(NULL, desc[n].name, + desc[n].parents, desc[n].num_parents, + desc[n].flags, base, desc[n].shift, + desc[n].width, desc[n].flags, ctrl->lock); + WARN_ON(IS_ERR(ctrl->muxes[n])); + } + + of_clk_add_provider(np, clk_muxing_get_src, ctrl); + + return; +muxes_out: + kfree(ctrl); +ctrl_out: + iounmap(base); +} + static void __init kirkwood_clk_init(struct device_node *np) { struct device_node *cgnp = @@ -236,8 +332,10 @@ static void __init kirkwood_clk_init(struct device_node *np) else mvebu_coreclk_setup(np, &kirkwood_coreclks); - if (cgnp) + if (cgnp) { mvebu_clk_gating_setup(cgnp, kirkwood_gating_desc); + kirkwood_clk_muxing_setup(cgnp, kirkwood_mux_desc); + } } CLK_OF_DECLARE(kirkwood_clk, "marvell,kirkwood-core-clock", kirkwood_clk_init); diff --git a/drivers/clk/pxa/Makefile b/drivers/clk/pxa/Makefile new file mode 100644 index 000000000000..4ff2abcd500b --- /dev/null +++ b/drivers/clk/pxa/Makefile @@ -0,0 +1,2 @@ +obj-y += clk-pxa.o +obj-$(CONFIG_PXA27x) += clk-pxa27x.o diff --git a/drivers/clk/pxa/clk-pxa.c b/drivers/clk/pxa/clk-pxa.c new file mode 100644 index 000000000000..ef3c05389c0a --- /dev/null +++ b/drivers/clk/pxa/clk-pxa.c @@ -0,0 +1,97 @@ +/* + * Marvell PXA family clocks + * + * Copyright (C) 2014 Robert Jarzmik + * + * Common clock code for PXA clocks ("CKEN" type clocks + DT) + * + * 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 of the License. + * + */ +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/clkdev.h> +#include <linux/of.h> + +#include <dt-bindings/clock/pxa-clock.h> +#include "clk-pxa.h" + +DEFINE_SPINLOCK(lock); + +static struct clk *pxa_clocks[CLK_MAX]; +static struct clk_onecell_data onecell_data = { + .clks = pxa_clocks, + .clk_num = CLK_MAX, +}; + +#define to_pxa_clk(_hw) container_of(_hw, struct pxa_clk_cken, hw) + +static unsigned long cken_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct pxa_clk_cken *pclk = to_pxa_clk(hw); + struct clk_fixed_factor *fix; + + if (!pclk->is_in_low_power || pclk->is_in_low_power()) + fix = &pclk->lp; + else + fix = &pclk->hp; + fix->hw.clk = hw->clk; + return clk_fixed_factor_ops.recalc_rate(&fix->hw, parent_rate); +} + +static struct clk_ops cken_rate_ops = { + .recalc_rate = cken_recalc_rate, +}; + +static u8 cken_get_parent(struct clk_hw *hw) +{ + struct pxa_clk_cken *pclk = to_pxa_clk(hw); + + if (!pclk->is_in_low_power) + return 0; + return pclk->is_in_low_power() ? 0 : 1; +} + +static struct clk_ops cken_mux_ops = { + .get_parent = cken_get_parent, + .set_parent = dummy_clk_set_parent, +}; + +void __init clkdev_pxa_register(int ckid, const char *con_id, + const char *dev_id, struct clk *clk) +{ + if (!IS_ERR(clk) && (ckid != CLK_NONE)) + pxa_clocks[ckid] = clk; + if (!IS_ERR(clk)) + clk_register_clkdev(clk, con_id, dev_id); +} + +int __init clk_pxa_cken_init(struct pxa_clk_cken *clks, int nb_clks) +{ + int i; + struct pxa_clk_cken *pclk; + struct clk *clk; + + for (i = 0; i < nb_clks; i++) { + pclk = clks + i; + pclk->gate.lock = &lock; + clk = clk_register_composite(NULL, pclk->name, + pclk->parent_names, 2, + &pclk->hw, &cken_mux_ops, + &pclk->hw, &cken_rate_ops, + &pclk->gate.hw, &clk_gate_ops, + pclk->flags); + clkdev_pxa_register(pclk->ckid, pclk->con_id, pclk->dev_id, + clk); + } + return 0; +} + +static void __init pxa_dt_clocks_init(struct device_node *np) +{ + of_clk_add_provider(np, of_clk_src_onecell_get, &onecell_data); +} +CLK_OF_DECLARE(pxa_clks, "marvell,pxa-clocks", pxa_dt_clocks_init); diff --git a/drivers/clk/pxa/clk-pxa.h b/drivers/clk/pxa/clk-pxa.h new file mode 100644 index 000000000000..5fe219d06b49 --- /dev/null +++ b/drivers/clk/pxa/clk-pxa.h @@ -0,0 +1,107 @@ +/* + * Marvell PXA family clocks + * + * Copyright (C) 2014 Robert Jarzmik + * + * Common clock code for PXA clocks ("CKEN" type clocks + DT) + * + * 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 of the License. + * + */ +#ifndef _CLK_PXA_ +#define _CLK_PXA_ + +#define PARENTS(name) \ + static const char *name ## _parents[] __initconst +#define MUX_RO_RATE_RO_OPS(name, clk_name) \ + static struct clk_hw name ## _mux_hw; \ + static struct clk_hw name ## _rate_hw; \ + static struct clk_ops name ## _mux_ops = { \ + .get_parent = name ## _get_parent, \ + .set_parent = dummy_clk_set_parent, \ + }; \ + static struct clk_ops name ## _rate_ops = { \ + .recalc_rate = name ## _get_rate, \ + }; \ + static struct clk *clk_register_ ## name(void) \ + { \ + return clk_register_composite(NULL, clk_name, \ + name ## _parents, \ + ARRAY_SIZE(name ## _parents), \ + &name ## _mux_hw, &name ## _mux_ops, \ + &name ## _rate_hw, &name ## _rate_ops, \ + NULL, NULL, CLK_GET_RATE_NOCACHE); \ + } + +#define RATE_RO_OPS(name, clk_name) \ + static struct clk_hw name ## _rate_hw; \ + static struct clk_ops name ## _rate_ops = { \ + .recalc_rate = name ## _get_rate, \ + }; \ + static struct clk *clk_register_ ## name(void) \ + { \ + return clk_register_composite(NULL, clk_name, \ + name ## _parents, \ + ARRAY_SIZE(name ## _parents), \ + NULL, NULL, \ + &name ## _rate_hw, &name ## _rate_ops, \ + NULL, NULL, CLK_GET_RATE_NOCACHE); \ + } + +/* + * CKEN clock type + * This clock takes it source from 2 possible parents : + * - a low power parent + * - a normal parent + * + * +------------+ +-----------+ + * | Low Power | --- | x mult_lp | + * | Clock | | / div_lp |\ + * +------------+ +-----------+ \+-----+ +-----------+ + * | Mux |---| CKEN gate | + * +------------+ +-----------+ /+-----+ +-----------+ + * | High Power | | x mult_hp |/ + * | Clock | --- | / div_hp | + * +------------+ +-----------+ + */ +struct pxa_clk_cken { + struct clk_hw hw; + int ckid; + const char *name; + const char *dev_id; + const char *con_id; + const char **parent_names; + struct clk_fixed_factor lp; + struct clk_fixed_factor hp; + struct clk_gate gate; + bool (*is_in_low_power)(void); + const unsigned long flags; +}; + +#define PXA_CKEN(_dev_id, _con_id, _name, parents, _mult_lp, _div_lp, \ + _mult_hp, _div_hp, is_lp, _cken_reg, _cken_bit, flag) \ + { .ckid = CLK_ ## _name, .name = #_name, \ + .dev_id = _dev_id, .con_id = _con_id, .parent_names = parents,\ + .lp = { .mult = _mult_lp, .div = _div_lp }, \ + .hp = { .mult = _mult_hp, .div = _div_hp }, \ + .is_in_low_power = is_lp, \ + .gate = { .reg = (void __iomem *)_cken_reg, .bit_idx = _cken_bit }, \ + .flags = flag, \ + } +#define PXA_CKEN_1RATE(dev_id, con_id, name, parents, cken_reg, \ + cken_bit, flag) \ + PXA_CKEN(dev_id, con_id, name, parents, 1, 1, 1, 1, \ + NULL, cken_reg, cken_bit, flag) + +static int dummy_clk_set_parent(struct clk_hw *hw, u8 index) +{ + return 0; +} + +extern void clkdev_pxa_register(int ckid, const char *con_id, + const char *dev_id, struct clk *clk); +extern int clk_pxa_cken_init(struct pxa_clk_cken *clks, int nb_clks); + +#endif diff --git a/drivers/clk/pxa/clk-pxa27x.c b/drivers/clk/pxa/clk-pxa27x.c new file mode 100644 index 000000000000..b345cc791e5d --- /dev/null +++ b/drivers/clk/pxa/clk-pxa27x.c @@ -0,0 +1,370 @@ +/* + * Marvell PXA27x family clocks + * + * Copyright (C) 2014 Robert Jarzmik + * + * Heavily inspired from former arch/arm/mach-pxa/clock.c. + * + * 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 of the License. + * + */ +#include <linux/clk-provider.h> +#include <mach/pxa2xx-regs.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/of.h> + +#include <dt-bindings/clock/pxa-clock.h> +#include "clk-pxa.h" + +#define KHz 1000 +#define MHz (1000 * 1000) + +enum { + PXA_CORE_13Mhz = 0, + PXA_CORE_RUN, + PXA_CORE_TURBO, +}; + +enum { + PXA_BUS_13Mhz = 0, + PXA_BUS_RUN, +}; + +enum { + PXA_LCD_13Mhz = 0, + PXA_LCD_RUN, +}; + +enum { + PXA_MEM_13Mhz = 0, + PXA_MEM_SYSTEM_BUS, + PXA_MEM_RUN, +}; + +static const char * const get_freq_khz[] = { + "core", "run", "cpll", "memory", + "system_bus" +}; + +/* + * Get the clock frequency as reflected by CCSR and the turbo flag. + * We assume these values have been applied via a fcs. + * If info is not 0 we also display the current settings. + */ +unsigned int pxa27x_get_clk_frequency_khz(int info) +{ + struct clk *clk; + unsigned long clks[5]; + int i; + + for (i = 0; i < 5; i++) { + clk = clk_get(NULL, get_freq_khz[i]); + if (IS_ERR(clk)) { + clks[i] = 0; + } else { + clks[i] = clk_get_rate(clk); + clk_put(clk); + } + } + if (info) { + pr_info("Run Mode clock: %ld.%02ldMHz\n", + clks[1] / 1000000, (clks[1] % 1000000) / 10000); + pr_info("Turbo Mode clock: %ld.%02ldMHz\n", + clks[2] / 1000000, (clks[2] % 1000000) / 10000); + pr_info("Memory clock: %ld.%02ldMHz\n", + clks[3] / 1000000, (clks[3] % 1000000) / 10000); + pr_info("System bus clock: %ld.%02ldMHz\n", + clks[4] / 1000000, (clks[4] % 1000000) / 10000); + } + return (unsigned int)clks[0]; +} + +bool pxa27x_is_ppll_disabled(void) +{ + unsigned long ccsr = CCSR; + + return ccsr & (1 << CCCR_PPDIS_BIT); +} + +#define PXA27X_CKEN(dev_id, con_id, parents, mult_hp, div_hp, \ + bit, is_lp, flags) \ + PXA_CKEN(dev_id, con_id, bit, parents, 1, 1, mult_hp, div_hp, \ + is_lp, &CKEN, CKEN_ ## bit, flags) +#define PXA27X_PBUS_CKEN(dev_id, con_id, bit, mult_hp, div_hp, delay) \ + PXA27X_CKEN(dev_id, con_id, pxa27x_pbus_parents, mult_hp, \ + div_hp, bit, pxa27x_is_ppll_disabled, 0) + +PARENTS(pxa27x_pbus) = { "osc_13mhz", "ppll_312mhz" }; +PARENTS(pxa27x_sbus) = { "system_bus", "system_bus" }; +PARENTS(pxa27x_32Mhz_bus) = { "osc_32_768khz", "osc_32_768khz" }; +PARENTS(pxa27x_lcd_bus) = { "lcd_base", "lcd_base" }; +PARENTS(pxa27x_membus) = { "lcd_base", "lcd_base" }; + +#define PXA27X_CKEN_1RATE(dev_id, con_id, bit, parents, delay) \ + PXA_CKEN_1RATE(dev_id, con_id, bit, parents, \ + &CKEN, CKEN_ ## bit, 0) +#define PXA27X_CKEN_1RATE_AO(dev_id, con_id, bit, parents, delay) \ + PXA_CKEN_1RATE(dev_id, con_id, bit, parents, \ + &CKEN, CKEN_ ## bit, CLK_IGNORE_UNUSED) + +static struct pxa_clk_cken pxa27x_clocks[] = { + PXA27X_PBUS_CKEN("pxa2xx-uart.0", NULL, FFUART, 2, 42, 1), + PXA27X_PBUS_CKEN("pxa2xx-uart.1", NULL, BTUART, 2, 42, 1), + PXA27X_PBUS_CKEN("pxa2xx-uart.2", NULL, STUART, 2, 42, 1), + PXA27X_PBUS_CKEN("pxa2xx-i2s", NULL, I2S, 2, 51, 0), + PXA27X_PBUS_CKEN("pxa2xx-i2c.0", NULL, I2C, 2, 19, 0), + PXA27X_PBUS_CKEN("pxa27x-udc", NULL, USB, 2, 13, 5), + PXA27X_PBUS_CKEN("pxa2xx-mci.0", NULL, MMC, 2, 32, 0), + PXA27X_PBUS_CKEN("pxa2xx-ir", "FICPCLK", FICP, 2, 13, 0), + PXA27X_PBUS_CKEN("pxa27x-ohci", NULL, USBHOST, 2, 13, 0), + PXA27X_PBUS_CKEN("pxa2xx-i2c.1", NULL, PWRI2C, 1, 24, 0), + PXA27X_PBUS_CKEN("pxa27x-ssp.0", NULL, SSP1, 1, 24, 0), + PXA27X_PBUS_CKEN("pxa27x-ssp.1", NULL, SSP2, 1, 24, 0), + PXA27X_PBUS_CKEN("pxa27x-ssp.2", NULL, SSP3, 1, 24, 0), + PXA27X_PBUS_CKEN("pxa27x-pwm.0", NULL, PWM0, 1, 24, 0), + PXA27X_PBUS_CKEN("pxa27x-pwm.1", NULL, PWM1, 1, 24, 0), + PXA27X_PBUS_CKEN(NULL, "MSLCLK", MSL, 2, 13, 0), + PXA27X_PBUS_CKEN(NULL, "USIMCLK", USIM, 2, 13, 0), + PXA27X_PBUS_CKEN(NULL, "MSTKCLK", MEMSTK, 2, 32, 0), + PXA27X_PBUS_CKEN(NULL, "AC97CLK", AC97, 1, 1, 0), + PXA27X_PBUS_CKEN(NULL, "AC97CONFCLK", AC97CONF, 1, 1, 0), + PXA27X_PBUS_CKEN(NULL, "OSTIMER0", OSTIMER, 1, 96, 0), + + PXA27X_CKEN_1RATE("pxa27x-keypad", NULL, KEYPAD, + pxa27x_32Mhz_bus_parents, 0), + PXA27X_CKEN_1RATE(NULL, "IMCLK", IM, pxa27x_sbus_parents, 0), + PXA27X_CKEN_1RATE("pxa2xx-fb", NULL, LCD, pxa27x_lcd_bus_parents, 0), + PXA27X_CKEN_1RATE("pxa27x-camera.0", NULL, CAMERA, + pxa27x_lcd_bus_parents, 0), + PXA27X_CKEN_1RATE_AO("pxa2xx-pcmcia", NULL, MEMC, + pxa27x_membus_parents, 0), + +}; + +static unsigned long clk_pxa27x_cpll_get_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + unsigned long clkcfg; + unsigned int t, ht; + unsigned int l, L, n2, N; + unsigned long ccsr = CCSR; + + asm("mrc\tp14, 0, %0, c6, c0, 0" : "=r" (clkcfg)); + t = clkcfg & (1 << 0); + ht = clkcfg & (1 << 2); + + l = ccsr & CCSR_L_MASK; + n2 = (ccsr & CCSR_N2_MASK) >> CCSR_N2_SHIFT; + L = l * parent_rate; + N = (L * n2) / 2; + + return t ? N : L; +} +PARENTS(clk_pxa27x_cpll) = { "osc_13mhz" }; +RATE_RO_OPS(clk_pxa27x_cpll, "cpll"); + +static unsigned long clk_pxa27x_lcd_base_get_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + unsigned int l, osc_forced; + unsigned long ccsr = CCSR; + unsigned long cccr = CCCR; + + l = ccsr & CCSR_L_MASK; + osc_forced = ccsr & (1 << CCCR_CPDIS_BIT); + if (osc_forced) { + if (cccr & (1 << CCCR_LCD_26_BIT)) + return parent_rate * 2; + else + return parent_rate; + } + + if (l <= 7) + return parent_rate; + if (l <= 16) + return parent_rate / 2; + return parent_rate / 4; +} + +static u8 clk_pxa27x_lcd_base_get_parent(struct clk_hw *hw) +{ + unsigned int osc_forced; + unsigned long ccsr = CCSR; + + osc_forced = ccsr & (1 << CCCR_CPDIS_BIT); + if (osc_forced) + return PXA_LCD_13Mhz; + else + return PXA_LCD_RUN; +} + +PARENTS(clk_pxa27x_lcd_base) = { "osc_13mhz", "run" }; +MUX_RO_RATE_RO_OPS(clk_pxa27x_lcd_base, "lcd_base"); + +static void __init pxa27x_register_plls(void) +{ + clk_register_fixed_rate(NULL, "osc_13mhz", NULL, + CLK_GET_RATE_NOCACHE | CLK_IS_ROOT, + 13 * MHz); + clk_register_fixed_rate(NULL, "osc_32_768khz", NULL, + CLK_GET_RATE_NOCACHE | CLK_IS_ROOT, + 32768 * KHz); + clk_register_fixed_rate(NULL, "clk_dummy", NULL, CLK_IS_ROOT, 0); + clk_register_fixed_factor(NULL, "ppll_312mhz", "osc_13mhz", 0, 24, 1); +} + +static unsigned long clk_pxa27x_core_get_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + unsigned long clkcfg; + unsigned int t, ht, b, osc_forced; + unsigned long ccsr = CCSR; + + osc_forced = ccsr & (1 << CCCR_CPDIS_BIT); + asm("mrc\tp14, 0, %0, c6, c0, 0" : "=r" (clkcfg)); + t = clkcfg & (1 << 0); + ht = clkcfg & (1 << 2); + b = clkcfg & (1 << 3); + + if (osc_forced) + return parent_rate; + if (ht) + return parent_rate / 2; + else + return parent_rate; +} + +static u8 clk_pxa27x_core_get_parent(struct clk_hw *hw) +{ + unsigned long clkcfg; + unsigned int t, ht, b, osc_forced; + unsigned long ccsr = CCSR; + + osc_forced = ccsr & (1 << CCCR_CPDIS_BIT); + if (osc_forced) + return PXA_CORE_13Mhz; + + asm("mrc\tp14, 0, %0, c6, c0, 0" : "=r" (clkcfg)); + t = clkcfg & (1 << 0); + ht = clkcfg & (1 << 2); + b = clkcfg & (1 << 3); + + if (ht || t) + return PXA_CORE_TURBO; + return PXA_CORE_RUN; +} +PARENTS(clk_pxa27x_core) = { "osc_13mhz", "run", "cpll" }; +MUX_RO_RATE_RO_OPS(clk_pxa27x_core, "core"); + +static unsigned long clk_pxa27x_run_get_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + unsigned long ccsr = CCSR; + unsigned int n2 = (ccsr & CCSR_N2_MASK) >> CCSR_N2_SHIFT; + + return (parent_rate / n2) * 2; +} +PARENTS(clk_pxa27x_run) = { "cpll" }; +RATE_RO_OPS(clk_pxa27x_run, "run"); + +static void __init pxa27x_register_core(void) +{ + clk_register_clk_pxa27x_cpll(); + clk_register_clk_pxa27x_run(); + + clkdev_pxa_register(CLK_CORE, "core", NULL, + clk_register_clk_pxa27x_core()); +} + +static unsigned long clk_pxa27x_system_bus_get_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + unsigned long clkcfg; + unsigned int b, osc_forced; + unsigned long ccsr = CCSR; + + osc_forced = ccsr & (1 << CCCR_CPDIS_BIT); + asm("mrc\tp14, 0, %0, c6, c0, 0" : "=r" (clkcfg)); + b = clkcfg & (1 << 3); + + if (osc_forced) + return parent_rate; + if (b) + return parent_rate / 2; + else + return parent_rate; +} + +static u8 clk_pxa27x_system_bus_get_parent(struct clk_hw *hw) +{ + unsigned int osc_forced; + unsigned long ccsr = CCSR; + + osc_forced = ccsr & (1 << CCCR_CPDIS_BIT); + if (osc_forced) + return PXA_BUS_13Mhz; + else + return PXA_BUS_RUN; +} + +PARENTS(clk_pxa27x_system_bus) = { "osc_13mhz", "run" }; +MUX_RO_RATE_RO_OPS(clk_pxa27x_system_bus, "system_bus"); + +static unsigned long clk_pxa27x_memory_get_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + unsigned int a, l, osc_forced; + unsigned long cccr = CCCR; + unsigned long ccsr = CCSR; + + osc_forced = ccsr & (1 << CCCR_CPDIS_BIT); + a = cccr & CCCR_A_BIT; + l = ccsr & CCSR_L_MASK; + + if (osc_forced || a) + return parent_rate; + if (l <= 10) + return parent_rate; + if (l <= 20) + return parent_rate / 2; + return parent_rate / 4; +} + +static u8 clk_pxa27x_memory_get_parent(struct clk_hw *hw) +{ + unsigned int osc_forced, a; + unsigned long cccr = CCCR; + unsigned long ccsr = CCSR; + + osc_forced = ccsr & (1 << CCCR_CPDIS_BIT); + a = cccr & CCCR_A_BIT; + if (osc_forced) + return PXA_MEM_13Mhz; + if (a) + return PXA_MEM_SYSTEM_BUS; + else + return PXA_MEM_RUN; +} + +PARENTS(clk_pxa27x_memory) = { "osc_13mhz", "system_bus", "run" }; +MUX_RO_RATE_RO_OPS(clk_pxa27x_memory, "memory"); + +static void __init pxa27x_base_clocks_init(void) +{ + pxa27x_register_plls(); + pxa27x_register_core(); + clk_register_clk_pxa27x_system_bus(); + clk_register_clk_pxa27x_memory(); + clk_register_clk_pxa27x_lcd_base(); +} + +static int __init pxa27x_clocks_init(void) +{ + pxa27x_base_clocks_init(); + return clk_pxa_cken_init(pxa27x_clocks, ARRAY_SIZE(pxa27x_clocks)); +} +postcore_initcall(pxa27x_clocks_init); diff --git a/drivers/clk/qcom/clk-pll.c b/drivers/clk/qcom/clk-pll.c index 9db03d3b1657..b823bc3b6250 100644 --- a/drivers/clk/qcom/clk-pll.c +++ b/drivers/clk/qcom/clk-pll.c @@ -97,7 +97,7 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct clk_pll *pll = to_clk_pll(hw); - u32 l, m, n; + u32 l, m, n, config; unsigned long rate; u64 tmp; @@ -116,13 +116,79 @@ clk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) do_div(tmp, n); rate += tmp; } + if (pll->post_div_width) { + regmap_read(pll->clkr.regmap, pll->config_reg, &config); + config >>= pll->post_div_shift; + config &= BIT(pll->post_div_width) - 1; + rate /= config + 1; + } + return rate; } +static const +struct pll_freq_tbl *find_freq(const struct pll_freq_tbl *f, unsigned long rate) +{ + if (!f) + return NULL; + + for (; f->freq; f++) + if (rate <= f->freq) + return f; + + return NULL; +} + +static long +clk_pll_determine_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *p_rate, struct clk **p) +{ + struct clk_pll *pll = to_clk_pll(hw); + const struct pll_freq_tbl *f; + + f = find_freq(pll->freq_tbl, rate); + if (!f) + return clk_pll_recalc_rate(hw, *p_rate); + + return f->freq; +} + +static int +clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long p_rate) +{ + struct clk_pll *pll = to_clk_pll(hw); + const struct pll_freq_tbl *f; + bool enabled; + u32 mode; + u32 enable_mask = PLL_OUTCTRL | PLL_BYPASSNL | PLL_RESET_N; + + f = find_freq(pll->freq_tbl, rate); + if (!f) + return -EINVAL; + + regmap_read(pll->clkr.regmap, pll->mode_reg, &mode); + enabled = (mode & enable_mask) == enable_mask; + + if (enabled) + clk_pll_disable(hw); + + regmap_update_bits(pll->clkr.regmap, pll->l_reg, 0x3ff, f->l); + regmap_update_bits(pll->clkr.regmap, pll->m_reg, 0x7ffff, f->m); + regmap_update_bits(pll->clkr.regmap, pll->n_reg, 0x7ffff, f->n); + regmap_write(pll->clkr.regmap, pll->config_reg, f->ibits); + + if (enabled) + clk_pll_enable(hw); + + return 0; +} + const struct clk_ops clk_pll_ops = { .enable = clk_pll_enable, .disable = clk_pll_disable, .recalc_rate = clk_pll_recalc_rate, + .determine_rate = clk_pll_determine_rate, + .set_rate = clk_pll_set_rate, }; EXPORT_SYMBOL_GPL(clk_pll_ops); diff --git a/drivers/clk/qcom/clk-pll.h b/drivers/clk/qcom/clk-pll.h index 3003e9962472..c9c0cda306d0 100644 --- a/drivers/clk/qcom/clk-pll.h +++ b/drivers/clk/qcom/clk-pll.h @@ -18,6 +18,21 @@ #include "clk-regmap.h" /** + * struct pll_freq_tbl - PLL frequency table + * @l: L value + * @m: M value + * @n: N value + * @ibits: internal values + */ +struct pll_freq_tbl { + unsigned long freq; + u16 l; + u16 m; + u16 n; + u32 ibits; +}; + +/** * struct clk_pll - phase locked loop (PLL) * @l_reg: L register * @m_reg: M register @@ -26,6 +41,7 @@ * @mode_reg: mode register * @status_reg: status register * @status_bit: ANDed with @status_reg to determine if PLL is enabled + * @freq_tbl: PLL frequency table * @hw: handle between common and hardware-specific interfaces */ struct clk_pll { @@ -36,6 +52,10 @@ struct clk_pll { u32 mode_reg; u32 status_reg; u8 status_bit; + u8 post_div_width; + u8 post_div_shift; + + const struct pll_freq_tbl *freq_tbl; struct clk_regmap clkr; }; diff --git a/drivers/clk/qcom/clk-rcg.c b/drivers/clk/qcom/clk-rcg.c index b638c5846dbf..b6e6959e89aa 100644 --- a/drivers/clk/qcom/clk-rcg.c +++ b/drivers/clk/qcom/clk-rcg.c @@ -21,6 +21,7 @@ #include <asm/div64.h> #include "clk-rcg.h" +#include "common.h" static u32 ns_to_src(struct src_sel *s, u32 ns) { @@ -67,16 +68,16 @@ static u8 clk_dyn_rcg_get_parent(struct clk_hw *hw) { struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw); int num_parents = __clk_get_num_parents(hw->clk); - u32 ns, ctl; + u32 ns, reg; int bank; int i; struct src_sel *s; - regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, &ctl); - bank = reg_to_bank(rcg, ctl); + regmap_read(rcg->clkr.regmap, rcg->bank_reg, ®); + bank = reg_to_bank(rcg, reg); s = &rcg->s[bank]; - regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns); + regmap_read(rcg->clkr.regmap, rcg->ns_reg[bank], &ns); ns = ns_to_src(s, ns); for (i = 0; i < num_parents; i++) @@ -192,90 +193,93 @@ static u32 mn_to_reg(struct mn *mn, u32 m, u32 n, u32 val) static void configure_bank(struct clk_dyn_rcg *rcg, const struct freq_tbl *f) { - u32 ns, md, ctl, *regp; + u32 ns, md, reg; int bank, new_bank; struct mn *mn; struct pre_div *p; struct src_sel *s; bool enabled; - u32 md_reg; - u32 bank_reg; + u32 md_reg, ns_reg; bool banked_mn = !!rcg->mn[1].width; + bool banked_p = !!rcg->p[1].pre_div_width; struct clk_hw *hw = &rcg->clkr.hw; enabled = __clk_is_enabled(hw->clk); - regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns); - regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, &ctl); - - if (banked_mn) { - regp = &ctl; - bank_reg = rcg->clkr.enable_reg; - } else { - regp = &ns; - bank_reg = rcg->ns_reg; - } - - bank = reg_to_bank(rcg, *regp); + regmap_read(rcg->clkr.regmap, rcg->bank_reg, ®); + bank = reg_to_bank(rcg, reg); new_bank = enabled ? !bank : bank; + ns_reg = rcg->ns_reg[new_bank]; + regmap_read(rcg->clkr.regmap, ns_reg, &ns); + if (banked_mn) { mn = &rcg->mn[new_bank]; md_reg = rcg->md_reg[new_bank]; ns |= BIT(mn->mnctr_reset_bit); - regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns); + regmap_write(rcg->clkr.regmap, ns_reg, ns); regmap_read(rcg->clkr.regmap, md_reg, &md); md = mn_to_md(mn, f->m, f->n, md); regmap_write(rcg->clkr.regmap, md_reg, md); ns = mn_to_ns(mn, f->m, f->n, ns); - regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns); + regmap_write(rcg->clkr.regmap, ns_reg, ns); - ctl = mn_to_reg(mn, f->m, f->n, ctl); - regmap_write(rcg->clkr.regmap, rcg->clkr.enable_reg, ctl); + /* Two NS registers means mode control is in NS register */ + if (rcg->ns_reg[0] != rcg->ns_reg[1]) { + ns = mn_to_reg(mn, f->m, f->n, ns); + regmap_write(rcg->clkr.regmap, ns_reg, ns); + } else { + reg = mn_to_reg(mn, f->m, f->n, reg); + regmap_write(rcg->clkr.regmap, rcg->bank_reg, reg); + } ns &= ~BIT(mn->mnctr_reset_bit); - regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns); - } else { + regmap_write(rcg->clkr.regmap, ns_reg, ns); + } + + if (banked_p) { p = &rcg->p[new_bank]; ns = pre_div_to_ns(p, f->pre_div - 1, ns); } s = &rcg->s[new_bank]; ns = src_to_ns(s, s->parent_map[f->src], ns); - regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns); + regmap_write(rcg->clkr.regmap, ns_reg, ns); if (enabled) { - *regp ^= BIT(rcg->mux_sel_bit); - regmap_write(rcg->clkr.regmap, bank_reg, *regp); + regmap_read(rcg->clkr.regmap, rcg->bank_reg, ®); + reg ^= BIT(rcg->mux_sel_bit); + regmap_write(rcg->clkr.regmap, rcg->bank_reg, reg); } } static int clk_dyn_rcg_set_parent(struct clk_hw *hw, u8 index) { struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw); - u32 ns, ctl, md, reg; + u32 ns, md, reg; int bank; struct freq_tbl f = { 0 }; bool banked_mn = !!rcg->mn[1].width; + bool banked_p = !!rcg->p[1].pre_div_width; - regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns); - regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, &ctl); - reg = banked_mn ? ctl : ns; - + regmap_read(rcg->clkr.regmap, rcg->bank_reg, ®); bank = reg_to_bank(rcg, reg); + regmap_read(rcg->clkr.regmap, rcg->ns_reg[bank], &ns); + if (banked_mn) { regmap_read(rcg->clkr.regmap, rcg->md_reg[bank], &md); f.m = md_to_m(&rcg->mn[bank], md); f.n = ns_m_to_n(&rcg->mn[bank], ns, f.m); - } else { - f.pre_div = ns_to_pre_div(&rcg->p[bank], ns) + 1; } - f.src = index; + if (banked_p) + f.pre_div = ns_to_pre_div(&rcg->p[bank], ns) + 1; + + f.src = index; configure_bank(rcg, &f); return 0; @@ -336,41 +340,30 @@ clk_dyn_rcg_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) u32 m, n, pre_div, ns, md, mode, reg; int bank; struct mn *mn; + bool banked_p = !!rcg->p[1].pre_div_width; bool banked_mn = !!rcg->mn[1].width; - regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns); - - if (banked_mn) - regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, ®); - else - reg = ns; - + regmap_read(rcg->clkr.regmap, rcg->bank_reg, ®); bank = reg_to_bank(rcg, reg); + regmap_read(rcg->clkr.regmap, rcg->ns_reg[bank], &ns); + m = n = pre_div = mode = 0; + if (banked_mn) { mn = &rcg->mn[bank]; regmap_read(rcg->clkr.regmap, rcg->md_reg[bank], &md); m = md_to_m(mn, md); n = ns_m_to_n(mn, ns, m); + /* Two NS registers means mode control is in NS register */ + if (rcg->ns_reg[0] != rcg->ns_reg[1]) + reg = ns; mode = reg_to_mnctr_mode(mn, reg); - return calc_rate(parent_rate, m, n, mode, 0); - } else { - pre_div = ns_to_pre_div(&rcg->p[bank], ns); - return calc_rate(parent_rate, 0, 0, 0, pre_div); } -} -static const -struct freq_tbl *find_freq(const struct freq_tbl *f, unsigned long rate) -{ - if (!f) - return NULL; - - for (; f->freq; f++) - if (rate <= f->freq) - return f; + if (banked_p) + pre_div = ns_to_pre_div(&rcg->p[bank], ns); - return NULL; + return calc_rate(parent_rate, m, n, mode, pre_div); } static long _freq_tbl_determine_rate(struct clk_hw *hw, @@ -379,7 +372,7 @@ static long _freq_tbl_determine_rate(struct clk_hw *hw, { unsigned long clk_flags; - f = find_freq(f, rate); + f = qcom_find_freq(f, rate); if (!f) return -EINVAL; @@ -477,7 +470,7 @@ static int clk_rcg_set_rate(struct clk_hw *hw, unsigned long rate, struct clk_rcg *rcg = to_clk_rcg(hw); const struct freq_tbl *f; - f = find_freq(rcg->freq_tbl, rate); + f = qcom_find_freq(rcg->freq_tbl, rate); if (!f) return -EINVAL; @@ -497,7 +490,7 @@ static int __clk_dyn_rcg_set_rate(struct clk_hw *hw, unsigned long rate) struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw); const struct freq_tbl *f; - f = find_freq(rcg->freq_tbl, rate); + f = qcom_find_freq(rcg->freq_tbl, rate); if (!f) return -EINVAL; diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h index ba0523cefd2e..687e41f91d7c 100644 --- a/drivers/clk/qcom/clk-rcg.h +++ b/drivers/clk/qcom/clk-rcg.h @@ -103,8 +103,9 @@ extern const struct clk_ops clk_rcg_bypass_ops; * struct clk_dyn_rcg - root clock generator with glitch free mux * * @mux_sel_bit: bit to switch glitch free mux - * @ns_reg: NS register + * @ns_reg: NS0 and NS1 register * @md_reg: MD0 and MD1 register + * @bank_reg: register to XOR @mux_sel_bit into to switch glitch free mux * @mn: mn counter (banked) * @s: source selector (banked) * @freq_tbl: frequency table @@ -113,8 +114,9 @@ extern const struct clk_ops clk_rcg_bypass_ops; * */ struct clk_dyn_rcg { - u32 ns_reg; + u32 ns_reg[2]; u32 md_reg[2]; + u32 bank_reg; u8 mux_sel_bit; diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c index cd185d5cc67a..cfa9eb4fe9ca 100644 --- a/drivers/clk/qcom/clk-rcg2.c +++ b/drivers/clk/qcom/clk-rcg2.c @@ -24,6 +24,7 @@ #include <asm/div64.h> #include "clk-rcg.h" +#include "common.h" #define CMD_REG 0x0 #define CMD_UPDATE BIT(0) @@ -172,27 +173,13 @@ clk_rcg2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) return calc_rate(parent_rate, m, n, mode, hid_div); } -static const -struct freq_tbl *find_freq(const struct freq_tbl *f, unsigned long rate) -{ - if (!f) - return NULL; - - for (; f->freq; f++) - if (rate <= f->freq) - return f; - - /* Default to our fastest rate */ - return f - 1; -} - static long _freq_tbl_determine_rate(struct clk_hw *hw, const struct freq_tbl *f, unsigned long rate, unsigned long *p_rate, struct clk **p) { unsigned long clk_flags; - f = find_freq(f, rate); + f = qcom_find_freq(f, rate); if (!f) return -EINVAL; @@ -268,7 +255,7 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate) struct clk_rcg2 *rcg = to_clk_rcg2(hw); const struct freq_tbl *f; - f = find_freq(rcg->freq_tbl, rate); + f = qcom_find_freq(rcg->freq_tbl, rate); if (!f) return -EINVAL; diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c index eeb3eea01f4c..e20d947db3e5 100644 --- a/drivers/clk/qcom/common.c +++ b/drivers/clk/qcom/common.c @@ -18,6 +18,7 @@ #include <linux/reset-controller.h> #include "common.h" +#include "clk-rcg.h" #include "clk-regmap.h" #include "reset.h" @@ -27,6 +28,21 @@ struct qcom_cc { struct clk *clks[]; }; +const +struct freq_tbl *qcom_find_freq(const struct freq_tbl *f, unsigned long rate) +{ + if (!f) + return NULL; + + for (; f->freq; f++) + if (rate <= f->freq) + return f; + + /* Default to our fastest rate */ + return f - 1; +} +EXPORT_SYMBOL_GPL(qcom_find_freq); + struct regmap * qcom_cc_map(struct platform_device *pdev, const struct qcom_cc_desc *desc) { diff --git a/drivers/clk/qcom/common.h b/drivers/clk/qcom/common.h index 2765e9d3da97..f519322acdf3 100644 --- a/drivers/clk/qcom/common.h +++ b/drivers/clk/qcom/common.h @@ -18,6 +18,7 @@ struct regmap_config; struct clk_regmap; struct qcom_reset_map; struct regmap; +struct freq_tbl; struct qcom_cc_desc { const struct regmap_config *config; @@ -27,6 +28,9 @@ struct qcom_cc_desc { size_t num_resets; }; +extern const struct freq_tbl *qcom_find_freq(const struct freq_tbl *f, + unsigned long rate); + extern struct regmap *qcom_cc_map(struct platform_device *pdev, const struct qcom_cc_desc *desc); extern int qcom_cc_really_probe(struct platform_device *pdev, diff --git a/drivers/clk/qcom/gcc-ipq806x.c b/drivers/clk/qcom/gcc-ipq806x.c index 3b83b7dd78c7..5cd62a709ac7 100644 --- a/drivers/clk/qcom/gcc-ipq806x.c +++ b/drivers/clk/qcom/gcc-ipq806x.c @@ -32,6 +32,33 @@ #include "clk-branch.h" #include "reset.h" +static struct clk_pll pll0 = { + .l_reg = 0x30c4, + .m_reg = 0x30c8, + .n_reg = 0x30cc, + .config_reg = 0x30d4, + .mode_reg = 0x30c0, + .status_reg = 0x30d8, + .status_bit = 16, + .clkr.hw.init = &(struct clk_init_data){ + .name = "pll0", + .parent_names = (const char *[]){ "pxo" }, + .num_parents = 1, + .ops = &clk_pll_ops, + }, +}; + +static struct clk_regmap pll0_vote = { + .enable_reg = 0x34c0, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "pll0_vote", + .parent_names = (const char *[]){ "pll0" }, + .num_parents = 1, + .ops = &clk_pll_vote_ops, + }, +}; + static struct clk_pll pll3 = { .l_reg = 0x3164, .m_reg = 0x3168, @@ -154,7 +181,7 @@ static const u8 gcc_pxo_pll8_pll0[] = { static const char *gcc_pxo_pll8_pll0_map[] = { "pxo", "pll8_vote", - "pll0", + "pll0_vote", }; static struct freq_tbl clk_tbl_gsbi_uart[] = { @@ -2133,6 +2160,8 @@ static struct clk_branch usb_fs1_h_clk = { }; static struct clk_regmap *gcc_ipq806x_clks[] = { + [PLL0] = &pll0.clkr, + [PLL0_VOTE] = &pll0_vote, [PLL3] = &pll3.clkr, [PLL8] = &pll8.clkr, [PLL8_VOTE] = &pll8_vote, diff --git a/drivers/clk/qcom/mmcc-apq8084.c b/drivers/clk/qcom/mmcc-apq8084.c index 751eea376a2b..dab988ab8cf1 100644 --- a/drivers/clk/qcom/mmcc-apq8084.c +++ b/drivers/clk/qcom/mmcc-apq8084.c @@ -3341,7 +3341,6 @@ static struct platform_driver mmcc_apq8084_driver = { .remove = mmcc_apq8084_remove, .driver = { .name = "mmcc-apq8084", - .owner = THIS_MODULE, .of_match_table = mmcc_apq8084_match_table, }, }; diff --git a/drivers/clk/qcom/mmcc-msm8960.c b/drivers/clk/qcom/mmcc-msm8960.c index 2e80a219b8ea..e8b33bbc362f 100644 --- a/drivers/clk/qcom/mmcc-msm8960.c +++ b/drivers/clk/qcom/mmcc-msm8960.c @@ -773,9 +773,11 @@ static struct freq_tbl clk_tbl_gfx2d[] = { }; static struct clk_dyn_rcg gfx2d0_src = { - .ns_reg = 0x0070, + .ns_reg[0] = 0x0070, + .ns_reg[1] = 0x0070, .md_reg[0] = 0x0064, .md_reg[1] = 0x0068, + .bank_reg = 0x0060, .mn[0] = { .mnctr_en_bit = 8, .mnctr_reset_bit = 25, @@ -831,9 +833,11 @@ static struct clk_branch gfx2d0_clk = { }; static struct clk_dyn_rcg gfx2d1_src = { - .ns_reg = 0x007c, + .ns_reg[0] = 0x007c, + .ns_reg[1] = 0x007c, .md_reg[0] = 0x0078, .md_reg[1] = 0x006c, + .bank_reg = 0x0074, .mn[0] = { .mnctr_en_bit = 8, .mnctr_reset_bit = 25, @@ -930,9 +934,11 @@ static struct freq_tbl clk_tbl_gfx3d_8064[] = { }; static struct clk_dyn_rcg gfx3d_src = { - .ns_reg = 0x008c, + .ns_reg[0] = 0x008c, + .ns_reg[1] = 0x008c, .md_reg[0] = 0x0084, .md_reg[1] = 0x0088, + .bank_reg = 0x0080, .mn[0] = { .mnctr_en_bit = 8, .mnctr_reset_bit = 25, @@ -1006,9 +1012,11 @@ static struct freq_tbl clk_tbl_vcap[] = { }; static struct clk_dyn_rcg vcap_src = { - .ns_reg = 0x021c, + .ns_reg[0] = 0x021c, + .ns_reg[1] = 0x021c, .md_reg[0] = 0x01ec, .md_reg[1] = 0x0218, + .bank_reg = 0x0178, .mn[0] = { .mnctr_en_bit = 8, .mnctr_reset_bit = 23, @@ -1211,9 +1219,11 @@ static struct freq_tbl clk_tbl_mdp[] = { }; static struct clk_dyn_rcg mdp_src = { - .ns_reg = 0x00d0, + .ns_reg[0] = 0x00d0, + .ns_reg[1] = 0x00d0, .md_reg[0] = 0x00c4, .md_reg[1] = 0x00c8, + .bank_reg = 0x00c0, .mn[0] = { .mnctr_en_bit = 8, .mnctr_reset_bit = 31, @@ -1318,7 +1328,9 @@ static struct freq_tbl clk_tbl_rot[] = { }; static struct clk_dyn_rcg rot_src = { - .ns_reg = 0x00e8, + .ns_reg[0] = 0x00e8, + .ns_reg[1] = 0x00e8, + .bank_reg = 0x00e8, .p[0] = { .pre_div_shift = 22, .pre_div_width = 4, @@ -1542,9 +1554,11 @@ static struct freq_tbl clk_tbl_vcodec[] = { }; static struct clk_dyn_rcg vcodec_src = { - .ns_reg = 0x0100, + .ns_reg[0] = 0x0100, + .ns_reg[1] = 0x0100, .md_reg[0] = 0x00fc, .md_reg[1] = 0x0128, + .bank_reg = 0x00f8, .mn[0] = { .mnctr_en_bit = 5, .mnctr_reset_bit = 31, @@ -2679,7 +2693,6 @@ static struct platform_driver mmcc_msm8960_driver = { .remove = mmcc_msm8960_remove, .driver = { .name = "mmcc-msm8960", - .owner = THIS_MODULE, .of_match_table = mmcc_msm8960_match_table, }, }; diff --git a/drivers/clk/qcom/mmcc-msm8974.c b/drivers/clk/qcom/mmcc-msm8974.c index bc8f519c47aa..be94c54a9a4f 100644 --- a/drivers/clk/qcom/mmcc-msm8974.c +++ b/drivers/clk/qcom/mmcc-msm8974.c @@ -2570,7 +2570,6 @@ static struct platform_driver mmcc_msm8974_driver = { .remove = mmcc_msm8974_remove, .driver = { .name = "mmcc-msm8974", - .owner = THIS_MODULE, .of_match_table = mmcc_msm8974_match_table, }, }; diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile index ee6b077381e1..bd8514d63634 100644 --- a/drivers/clk/rockchip/Makefile +++ b/drivers/clk/rockchip/Makefile @@ -5,6 +5,7 @@ obj-y += clk-rockchip.o obj-y += clk.o obj-y += clk-pll.o +obj-y += clk-cpu.o obj-$(CONFIG_RESET_CONTROLLER) += softrst.o obj-y += clk-rk3188.o diff --git a/drivers/clk/rockchip/clk-cpu.c b/drivers/clk/rockchip/clk-cpu.c new file mode 100644 index 000000000000..75c8c45ef728 --- /dev/null +++ b/drivers/clk/rockchip/clk-cpu.c @@ -0,0 +1,329 @@ +/* + * Copyright (c) 2014 MundoReader S.L. + * Author: Heiko Stuebner <heiko@sntech.de> + * + * based on clk/samsung/clk-cpu.c + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * Author: Thomas Abraham <thomas.ab@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * A CPU clock is defined as a clock supplied to a CPU or a group of CPUs. + * The CPU clock is typically derived from a hierarchy of clock + * blocks which includes mux and divider blocks. There are a number of other + * auxiliary clocks supplied to the CPU domain such as the debug blocks and AXI + * clock for CPU domain. The rates of these auxiliary clocks are related to the + * CPU clock rate and this relation is usually specified in the hardware manual + * of the SoC or supplied after the SoC characterization. + * + * The below implementation of the CPU clock allows the rate changes of the CPU + * clock and the corresponding rate changes of the auxillary clocks of the CPU + * domain. The platform clock driver provides a clock register configuration + * for each configurable rate which is then used to program the clock hardware + * registers to acheive a fast co-oridinated rate change for all the CPU domain + * clocks. + * + * On a rate change request for the CPU clock, the rate change is propagated + * upto the PLL supplying the clock to the CPU domain clock blocks. While the + * CPU domain PLL is reconfigured, the CPU domain clocks are driven using an + * alternate clock source. If required, the alternate clock source is divided + * down in order to keep the output clock rate within the previous OPP limits. + */ + +#include <linux/of.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/clk-provider.h> +#include "clk.h" + +/** + * struct rockchip_cpuclk: information about clock supplied to a CPU core. + * @hw: handle between ccf and cpu clock. + * @alt_parent: alternate parent clock to use when switching the speed + * of the primary parent clock. + * @reg_base: base register for cpu-clock values. + * @clk_nb: clock notifier registered for changes in clock speed of the + * primary parent clock. + * @rate_count: number of rates in the rate_table + * @rate_table: pll-rates and their associated dividers + * @reg_data: cpu-specific register settings + * @lock: clock lock + */ +struct rockchip_cpuclk { + struct clk_hw hw; + + struct clk_mux cpu_mux; + const struct clk_ops *cpu_mux_ops; + + struct clk *alt_parent; + void __iomem *reg_base; + struct notifier_block clk_nb; + unsigned int rate_count; + struct rockchip_cpuclk_rate_table *rate_table; + const struct rockchip_cpuclk_reg_data *reg_data; + spinlock_t *lock; +}; + +#define to_rockchip_cpuclk_hw(hw) container_of(hw, struct rockchip_cpuclk, hw) +#define to_rockchip_cpuclk_nb(nb) \ + container_of(nb, struct rockchip_cpuclk, clk_nb) + +static const struct rockchip_cpuclk_rate_table *rockchip_get_cpuclk_settings( + struct rockchip_cpuclk *cpuclk, unsigned long rate) +{ + const struct rockchip_cpuclk_rate_table *rate_table = + cpuclk->rate_table; + int i; + + for (i = 0; i < cpuclk->rate_count; i++) { + if (rate == rate_table[i].prate) + return &rate_table[i]; + } + + return NULL; +} + +static unsigned long rockchip_cpuclk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct rockchip_cpuclk *cpuclk = to_rockchip_cpuclk_hw(hw); + const struct rockchip_cpuclk_reg_data *reg_data = cpuclk->reg_data; + u32 clksel0 = readl_relaxed(cpuclk->reg_base + reg_data->core_reg); + + clksel0 >>= reg_data->div_core_shift; + clksel0 &= reg_data->div_core_mask; + return parent_rate / (clksel0 + 1); +} + +static const struct clk_ops rockchip_cpuclk_ops = { + .recalc_rate = rockchip_cpuclk_recalc_rate, +}; + +static void rockchip_cpuclk_set_dividers(struct rockchip_cpuclk *cpuclk, + const struct rockchip_cpuclk_rate_table *rate) +{ + int i; + + /* alternate parent is active now. set the dividers */ + for (i = 0; i < ARRAY_SIZE(rate->divs); i++) { + const struct rockchip_cpuclk_clksel *clksel = &rate->divs[i]; + + if (!clksel->reg) + continue; + + pr_debug("%s: setting reg 0x%x to 0x%x\n", + __func__, clksel->reg, clksel->val); + writel(clksel->val , cpuclk->reg_base + clksel->reg); + } +} + +static int rockchip_cpuclk_pre_rate_change(struct rockchip_cpuclk *cpuclk, + struct clk_notifier_data *ndata) +{ + const struct rockchip_cpuclk_reg_data *reg_data = cpuclk->reg_data; + unsigned long alt_prate, alt_div; + + alt_prate = clk_get_rate(cpuclk->alt_parent); + + spin_lock(cpuclk->lock); + + /* + * If the old parent clock speed is less than the clock speed + * of the alternate parent, then it should be ensured that at no point + * the armclk speed is more than the old_rate until the dividers are + * set. + */ + if (alt_prate > ndata->old_rate) { + /* calculate dividers */ + alt_div = DIV_ROUND_UP(alt_prate, ndata->old_rate) - 1; + if (alt_div > reg_data->div_core_mask) { + pr_warn("%s: limiting alt-divider %lu to %d\n", + __func__, alt_div, reg_data->div_core_mask); + alt_div = reg_data->div_core_mask; + } + + /* + * Change parents and add dividers in a single transaction. + * + * NOTE: we do this in a single transaction so we're never + * dividing the primary parent by the extra dividers that were + * needed for the alt. + */ + pr_debug("%s: setting div %lu as alt-rate %lu > old-rate %lu\n", + __func__, alt_div, alt_prate, ndata->old_rate); + + writel(HIWORD_UPDATE(alt_div, reg_data->div_core_mask, + reg_data->div_core_shift) | + HIWORD_UPDATE(1, 1, reg_data->mux_core_shift), + cpuclk->reg_base + reg_data->core_reg); + } else { + /* select alternate parent */ + writel(HIWORD_UPDATE(1, 1, reg_data->mux_core_shift), + cpuclk->reg_base + reg_data->core_reg); + } + + spin_unlock(cpuclk->lock); + return 0; +} + +static int rockchip_cpuclk_post_rate_change(struct rockchip_cpuclk *cpuclk, + struct clk_notifier_data *ndata) +{ + const struct rockchip_cpuclk_reg_data *reg_data = cpuclk->reg_data; + const struct rockchip_cpuclk_rate_table *rate; + + rate = rockchip_get_cpuclk_settings(cpuclk, ndata->new_rate); + if (!rate) { + pr_err("%s: Invalid rate : %lu for cpuclk\n", + __func__, ndata->new_rate); + return -EINVAL; + } + + spin_lock(cpuclk->lock); + + if (ndata->old_rate < ndata->new_rate) + rockchip_cpuclk_set_dividers(cpuclk, rate); + + /* + * post-rate change event, re-mux to primary parent and remove dividers. + * + * NOTE: we do this in a single transaction so we're never dividing the + * primary parent by the extra dividers that were needed for the alt. + */ + + writel(HIWORD_UPDATE(0, reg_data->div_core_mask, + reg_data->div_core_shift) | + HIWORD_UPDATE(0, 1, reg_data->mux_core_shift), + cpuclk->reg_base + reg_data->core_reg); + + if (ndata->old_rate > ndata->new_rate) + rockchip_cpuclk_set_dividers(cpuclk, rate); + + spin_unlock(cpuclk->lock); + return 0; +} + +/* + * This clock notifier is called when the frequency of the parent clock + * of cpuclk is to be changed. This notifier handles the setting up all + * the divider clocks, remux to temporary parent and handling the safe + * frequency levels when using temporary parent. + */ +static int rockchip_cpuclk_notifier_cb(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct clk_notifier_data *ndata = data; + struct rockchip_cpuclk *cpuclk = to_rockchip_cpuclk_nb(nb); + int ret = 0; + + pr_debug("%s: event %lu, old_rate %lu, new_rate: %lu\n", + __func__, event, ndata->old_rate, ndata->new_rate); + if (event == PRE_RATE_CHANGE) + ret = rockchip_cpuclk_pre_rate_change(cpuclk, ndata); + else if (event == POST_RATE_CHANGE) + ret = rockchip_cpuclk_post_rate_change(cpuclk, ndata); + + return notifier_from_errno(ret); +} + +struct clk *rockchip_clk_register_cpuclk(const char *name, + const char **parent_names, u8 num_parents, + const struct rockchip_cpuclk_reg_data *reg_data, + const struct rockchip_cpuclk_rate_table *rates, + int nrates, void __iomem *reg_base, spinlock_t *lock) +{ + struct rockchip_cpuclk *cpuclk; + struct clk_init_data init; + struct clk *clk, *cclk; + int ret; + + if (num_parents != 2) { + pr_err("%s: needs two parent clocks\n", __func__); + return ERR_PTR(-EINVAL); + } + + cpuclk = kzalloc(sizeof(*cpuclk), GFP_KERNEL); + if (!cpuclk) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.parent_names = &parent_names[0]; + init.num_parents = 1; + init.ops = &rockchip_cpuclk_ops; + + /* only allow rate changes when we have a rate table */ + init.flags = (nrates > 0) ? CLK_SET_RATE_PARENT : 0; + + /* disallow automatic parent changes by ccf */ + init.flags |= CLK_SET_RATE_NO_REPARENT; + + init.flags |= CLK_GET_RATE_NOCACHE; + + cpuclk->reg_base = reg_base; + cpuclk->lock = lock; + cpuclk->reg_data = reg_data; + cpuclk->clk_nb.notifier_call = rockchip_cpuclk_notifier_cb; + cpuclk->hw.init = &init; + + cpuclk->alt_parent = __clk_lookup(parent_names[1]); + if (!cpuclk->alt_parent) { + pr_err("%s: could not lookup alternate parent\n", + __func__); + ret = -EINVAL; + goto free_cpuclk; + } + + ret = clk_prepare_enable(cpuclk->alt_parent); + if (ret) { + pr_err("%s: could not enable alternate parent\n", + __func__); + goto free_cpuclk; + } + + clk = __clk_lookup(parent_names[0]); + if (!clk) { + pr_err("%s: could not lookup parent clock %s\n", + __func__, parent_names[0]); + ret = -EINVAL; + goto free_cpuclk; + } + + ret = clk_notifier_register(clk, &cpuclk->clk_nb); + if (ret) { + pr_err("%s: failed to register clock notifier for %s\n", + __func__, name); + goto free_cpuclk; + } + + if (nrates > 0) { + cpuclk->rate_count = nrates; + cpuclk->rate_table = kmemdup(rates, + sizeof(*rates) * nrates, + GFP_KERNEL); + if (!cpuclk->rate_table) { + pr_err("%s: could not allocate memory for cpuclk rates\n", + __func__); + ret = -ENOMEM; + goto unregister_notifier; + } + } + + cclk = clk_register(NULL, &cpuclk->hw); + if (IS_ERR(clk)) { + pr_err("%s: could not register cpuclk %s\n", __func__, name); + ret = PTR_ERR(clk); + goto free_rate_table; + } + + return cclk; + +free_rate_table: + kfree(cpuclk->rate_table); +unregister_notifier: + clk_notifier_unregister(clk, &cpuclk->clk_nb); +free_cpuclk: + kfree(cpuclk); + return ERR_PTR(ret); +} diff --git a/drivers/clk/rockchip/clk-pll.c b/drivers/clk/rockchip/clk-pll.c index f2a1c7abf4d9..a3e886a38480 100644 --- a/drivers/clk/rockchip/clk-pll.c +++ b/drivers/clk/rockchip/clk-pll.c @@ -34,7 +34,6 @@ struct rockchip_clk_pll { const struct clk_ops *pll_mux_ops; struct notifier_block clk_nb; - bool rate_change_remuxed; void __iomem *reg_base; int lock_offset; @@ -109,38 +108,6 @@ static int rockchip_pll_wait_lock(struct rockchip_clk_pll *pll) } /** - * Set pll mux when changing the pll rate. - * This makes sure to move the pll mux away from the actual pll before - * changing its rate and back to the original parent after the change. - */ -static int rockchip_pll_notifier_cb(struct notifier_block *nb, - unsigned long event, void *data) -{ - struct rockchip_clk_pll *pll = to_rockchip_clk_pll_nb(nb); - struct clk_mux *pll_mux = &pll->pll_mux; - const struct clk_ops *pll_mux_ops = pll->pll_mux_ops; - int cur_parent; - - switch (event) { - case PRE_RATE_CHANGE: - cur_parent = pll_mux_ops->get_parent(&pll_mux->hw); - if (cur_parent == PLL_MODE_NORM) { - pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_SLOW); - pll->rate_change_remuxed = 1; - } - break; - case POST_RATE_CHANGE: - if (pll->rate_change_remuxed) { - pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_NORM); - pll->rate_change_remuxed = 0; - } - break; - } - - return NOTIFY_OK; -} - -/** * PLL used in RK3066, RK3188 and RK3288 */ @@ -194,6 +161,10 @@ static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate, const struct rockchip_pll_rate_table *rate; unsigned long old_rate = rockchip_rk3066_pll_recalc_rate(hw, prate); struct regmap *grf = rockchip_clk_get_grf(); + struct clk_mux *pll_mux = &pll->pll_mux; + const struct clk_ops *pll_mux_ops = pll->pll_mux_ops; + int rate_change_remuxed = 0; + int cur_parent; int ret; if (IS_ERR(grf)) { @@ -216,6 +187,12 @@ static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate, pr_debug("%s: rate settings for %lu (nr, no, nf): (%d, %d, %d)\n", __func__, rate->rate, rate->nr, rate->no, rate->nf); + cur_parent = pll_mux_ops->get_parent(&pll_mux->hw); + if (cur_parent == PLL_MODE_NORM) { + pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_SLOW); + rate_change_remuxed = 1; + } + /* enter reset mode */ writel(HIWORD_UPDATE(RK3066_PLLCON3_RESET, RK3066_PLLCON3_RESET, 0), pll->reg_base + RK3066_PLLCON(3)); @@ -247,6 +224,9 @@ static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate, rockchip_rk3066_pll_set_rate(hw, old_rate, prate); } + if (rate_change_remuxed) + pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_NORM); + return ret; } @@ -310,7 +290,6 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type, struct clk_mux *pll_mux; struct clk *pll_clk, *mux_clk; char pll_name[20]; - int ret; if (num_parents != 2) { pr_err("%s: needs two parent clocks\n", __func__); @@ -367,7 +346,6 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type, pll->lock_offset = grf_lock_offset; pll->lock_shift = lock_shift; pll->lock = lock; - pll->clk_nb.notifier_call = rockchip_pll_notifier_cb; pll_clk = clk_register(NULL, &pll->hw); if (IS_ERR(pll_clk)) { @@ -377,14 +355,6 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type, goto err_pll; } - ret = clk_notifier_register(pll_clk, &pll->clk_nb); - if (ret) { - pr_err("%s: failed to register clock notifier for %s : %d\n", - __func__, name, ret); - mux_clk = ERR_PTR(ret); - goto err_pll_notifier; - } - /* create the mux on top of the real pll */ pll->pll_mux_ops = &clk_mux_ops; pll_mux = &pll->pll_mux; @@ -417,13 +387,6 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type, return mux_clk; err_mux: - ret = clk_notifier_unregister(pll_clk, &pll->clk_nb); - if (ret) { - pr_err("%s: could not unregister clock notifier in error path : %d\n", - __func__, ret); - return mux_clk; - } -err_pll_notifier: clk_unregister(pll_clk); err_pll: kfree(pll); diff --git a/drivers/clk/rockchip/clk-rk3188.c b/drivers/clk/rockchip/clk-rk3188.c index a83a6d8d0fb6..beed49c79126 100644 --- a/drivers/clk/rockchip/clk-rk3188.c +++ b/drivers/clk/rockchip/clk-rk3188.c @@ -19,6 +19,7 @@ #include <dt-bindings/clock/rk3188-cru-common.h> #include "clk.h" +#define RK3066_GRF_SOC_STATUS 0x15c #define RK3188_GRF_SOC_STATUS 0xac enum rk3188_plls { @@ -100,6 +101,98 @@ struct rockchip_pll_rate_table rk3188_pll_rates[] = { { /* sentinel */ }, }; +#define RK3066_DIV_CORE_PERIPH_MASK 0x3 +#define RK3066_DIV_CORE_PERIPH_SHIFT 6 +#define RK3066_DIV_ACLK_CORE_MASK 0x7 +#define RK3066_DIV_ACLK_CORE_SHIFT 0 +#define RK3066_DIV_ACLK_HCLK_MASK 0x3 +#define RK3066_DIV_ACLK_HCLK_SHIFT 8 +#define RK3066_DIV_ACLK_PCLK_MASK 0x3 +#define RK3066_DIV_ACLK_PCLK_SHIFT 12 +#define RK3066_DIV_AHB2APB_MASK 0x3 +#define RK3066_DIV_AHB2APB_SHIFT 14 + +#define RK3066_CLKSEL0(_core_peri) \ + { \ + .reg = RK2928_CLKSEL_CON(0), \ + .val = HIWORD_UPDATE(_core_peri, RK3066_DIV_CORE_PERIPH_MASK, \ + RK3066_DIV_CORE_PERIPH_SHIFT) \ + } +#define RK3066_CLKSEL1(_aclk_core, _aclk_hclk, _aclk_pclk, _ahb2apb) \ + { \ + .reg = RK2928_CLKSEL_CON(1), \ + .val = HIWORD_UPDATE(_aclk_core, RK3066_DIV_ACLK_CORE_MASK, \ + RK3066_DIV_ACLK_CORE_SHIFT) | \ + HIWORD_UPDATE(_aclk_hclk, RK3066_DIV_ACLK_HCLK_MASK, \ + RK3066_DIV_ACLK_HCLK_SHIFT) | \ + HIWORD_UPDATE(_aclk_pclk, RK3066_DIV_ACLK_PCLK_MASK, \ + RK3066_DIV_ACLK_PCLK_SHIFT) | \ + HIWORD_UPDATE(_ahb2apb, RK3066_DIV_AHB2APB_MASK, \ + RK3066_DIV_AHB2APB_SHIFT), \ + } + +#define RK3066_CPUCLK_RATE(_prate, _core_peri, _acore, _ahclk, _apclk, _h2p) \ + { \ + .prate = _prate, \ + .divs = { \ + RK3066_CLKSEL0(_core_peri), \ + RK3066_CLKSEL1(_acore, _ahclk, _apclk, _h2p), \ + }, \ + } + +static struct rockchip_cpuclk_rate_table rk3066_cpuclk_rates[] __initdata = { + RK3066_CPUCLK_RATE(1416000000, 2, 3, 1, 2, 1), + RK3066_CPUCLK_RATE(1200000000, 2, 3, 1, 2, 1), + RK3066_CPUCLK_RATE(1008000000, 2, 2, 1, 2, 1), + RK3066_CPUCLK_RATE( 816000000, 2, 2, 1, 2, 1), + RK3066_CPUCLK_RATE( 600000000, 1, 2, 1, 2, 1), + RK3066_CPUCLK_RATE( 504000000, 1, 1, 1, 2, 1), + RK3066_CPUCLK_RATE( 312000000, 0, 1, 1, 1, 0), +}; + +static const struct rockchip_cpuclk_reg_data rk3066_cpuclk_data = { + .core_reg = RK2928_CLKSEL_CON(0), + .div_core_shift = 0, + .div_core_mask = 0x1f, + .mux_core_shift = 8, +}; + +#define RK3188_DIV_ACLK_CORE_MASK 0x7 +#define RK3188_DIV_ACLK_CORE_SHIFT 3 + +#define RK3188_CLKSEL1(_aclk_core) \ + { \ + .reg = RK2928_CLKSEL_CON(1), \ + .val = HIWORD_UPDATE(_aclk_core, RK3188_DIV_ACLK_CORE_MASK,\ + RK3188_DIV_ACLK_CORE_SHIFT) \ + } +#define RK3188_CPUCLK_RATE(_prate, _core_peri, _aclk_core) \ + { \ + .prate = _prate, \ + .divs = { \ + RK3066_CLKSEL0(_core_peri), \ + RK3188_CLKSEL1(_aclk_core), \ + }, \ + } + +static struct rockchip_cpuclk_rate_table rk3188_cpuclk_rates[] __initdata = { + RK3188_CPUCLK_RATE(1608000000, 2, 3), + RK3188_CPUCLK_RATE(1416000000, 2, 3), + RK3188_CPUCLK_RATE(1200000000, 2, 3), + RK3188_CPUCLK_RATE(1008000000, 2, 3), + RK3188_CPUCLK_RATE( 816000000, 2, 3), + RK3188_CPUCLK_RATE( 600000000, 1, 3), + RK3188_CPUCLK_RATE( 504000000, 1, 3), + RK3188_CPUCLK_RATE( 312000000, 0, 1), +}; + +static const struct rockchip_cpuclk_reg_data rk3188_cpuclk_data = { + .core_reg = RK2928_CLKSEL_CON(0), + .div_core_shift = 9, + .div_core_mask = 0x1f, + .mux_core_shift = 8, +}; + PNAME(mux_pll_p) = { "xin24m", "xin32k" }; PNAME(mux_armclk_p) = { "apll", "gpll_armclk" }; PNAME(mux_ddrphy_p) = { "dpll", "gpll_ddr" }; @@ -173,17 +266,10 @@ static struct rockchip_clk_branch common_clk_branches[] __initdata = { GATE(0, "aclk_cpu", "aclk_cpu_pre", 0, RK2928_CLKGATE_CON(0), 3, GFLAGS), - DIV(0, "pclk_cpu_pre", "aclk_cpu_pre", 0, - RK2928_CLKSEL_CON(1), 12, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO), GATE(0, "atclk_cpu", "pclk_cpu_pre", 0, RK2928_CLKGATE_CON(0), 6, GFLAGS), GATE(0, "pclk_cpu", "pclk_cpu_pre", 0, RK2928_CLKGATE_CON(0), 5, GFLAGS), - DIV(0, "hclk_cpu_pre", "aclk_cpu_pre", 0, - RK2928_CLKSEL_CON(1), 8, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO), - COMPOSITE_NOMUX(0, "hclk_ahb2apb", "hclk_cpu_pre", 0, - RK2928_CLKSEL_CON(1), 14, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO, - RK2928_CLKGATE_CON(4), 9, GFLAGS), GATE(0, "hclk_cpu", "hclk_cpu_pre", 0, RK2928_CLKGATE_CON(0), 4, GFLAGS), @@ -412,10 +498,18 @@ static struct clk_div_table div_aclk_cpu_t[] = { }; static struct rockchip_clk_branch rk3066a_clk_branches[] __initdata = { - COMPOSITE_NOGATE(0, "armclk", mux_armclk_p, 0, - RK2928_CLKSEL_CON(0), 8, 1, MFLAGS, 0, 5, DFLAGS), DIVTBL(0, "aclk_cpu_pre", "armclk", 0, - RK2928_CLKSEL_CON(1), 0, 3, DFLAGS, div_aclk_cpu_t), + RK2928_CLKSEL_CON(1), 0, 3, DFLAGS | CLK_DIVIDER_READ_ONLY, div_aclk_cpu_t), + DIV(0, "pclk_cpu_pre", "aclk_cpu_pre", 0, + RK2928_CLKSEL_CON(1), 12, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO + | CLK_DIVIDER_READ_ONLY), + DIV(0, "hclk_cpu_pre", "aclk_cpu_pre", 0, + RK2928_CLKSEL_CON(1), 8, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO + | CLK_DIVIDER_READ_ONLY), + COMPOSITE_NOMUX(0, "hclk_ahb2apb", "hclk_cpu_pre", 0, + RK2928_CLKSEL_CON(1), 14, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO + | CLK_DIVIDER_READ_ONLY, + RK2928_CLKGATE_CON(4), 9, GFLAGS), GATE(CORE_L2C, "core_l2c", "aclk_cpu", 0, RK2928_CLKGATE_CON(9), 4, GFLAGS), @@ -524,8 +618,6 @@ PNAME(mux_hsicphy_p) = { "sclk_otgphy0", "sclk_otgphy1", "gpll", "cpll" }; static struct rockchip_clk_branch rk3188_clk_branches[] __initdata = { - COMPOSITE_NOGATE(0, "armclk", mux_armclk_p, 0, - RK2928_CLKSEL_CON(0), 8, 1, MFLAGS, 9, 5, DFLAGS), COMPOSITE_NOMUX_DIVTBL(0, "aclk_core", "armclk", 0, RK2928_CLKSEL_CON(1), 3, 3, DFLAGS | CLK_DIVIDER_READ_ONLY, div_rk3188_aclk_core_t, RK2928_CLKGATE_CON(0), 7, GFLAGS), @@ -533,6 +625,13 @@ static struct rockchip_clk_branch rk3188_clk_branches[] __initdata = { /* do not source aclk_cpu_pre from the apll, to keep complexity down */ COMPOSITE_NOGATE(0, "aclk_cpu_pre", mux_aclk_cpu_p, CLK_SET_RATE_NO_REPARENT, RK2928_CLKSEL_CON(0), 5, 1, MFLAGS, 0, 5, DFLAGS), + DIV(0, "pclk_cpu_pre", "aclk_cpu_pre", 0, + RK2928_CLKSEL_CON(1), 12, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO), + DIV(0, "hclk_cpu_pre", "aclk_cpu_pre", 0, + RK2928_CLKSEL_CON(1), 8, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO), + COMPOSITE_NOMUX(0, "hclk_ahb2apb", "hclk_cpu_pre", 0, + RK2928_CLKSEL_CON(1), 14, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO, + RK2928_CLKGATE_CON(4), 9, GFLAGS), GATE(CORE_L2C, "core_l2c", "armclk", 0, RK2928_CLKGATE_CON(9), 4, GFLAGS), @@ -599,6 +698,12 @@ static struct rockchip_clk_branch rk3188_clk_branches[] __initdata = { GATE(ACLK_GPS, "aclk_gps", "aclk_peri", 0, RK2928_CLKGATE_CON(8), 13, GFLAGS), }; +static const char *rk3188_critical_clocks[] __initconst = { + "aclk_cpu", + "aclk_peri", + "hclk_peri", +}; + static void __init rk3188_common_clk_init(struct device_node *np) { void __iomem *reg_base; @@ -623,29 +728,65 @@ static void __init rk3188_common_clk_init(struct device_node *np) pr_warn("%s: could not register clock usb480m: %ld\n", __func__, PTR_ERR(clk)); - rockchip_clk_register_plls(rk3188_pll_clks, - ARRAY_SIZE(rk3188_pll_clks), - RK3188_GRF_SOC_STATUS); rockchip_clk_register_branches(common_clk_branches, ARRAY_SIZE(common_clk_branches)); + rockchip_clk_protect_critical(rk3188_critical_clocks, + ARRAY_SIZE(rk3188_critical_clocks)); rockchip_register_softrst(np, 9, reg_base + RK2928_SOFTRST_CON(0), ROCKCHIP_SOFTRST_HIWORD_MASK); + + rockchip_register_restart_notifier(RK2928_GLB_SRST_FST); } static void __init rk3066a_clk_init(struct device_node *np) { rk3188_common_clk_init(np); + rockchip_clk_register_plls(rk3188_pll_clks, + ARRAY_SIZE(rk3188_pll_clks), + RK3066_GRF_SOC_STATUS); rockchip_clk_register_branches(rk3066a_clk_branches, ARRAY_SIZE(rk3066a_clk_branches)); + rockchip_clk_register_armclk(ARMCLK, "armclk", + mux_armclk_p, ARRAY_SIZE(mux_armclk_p), + &rk3066_cpuclk_data, rk3066_cpuclk_rates, + ARRAY_SIZE(rk3066_cpuclk_rates)); } CLK_OF_DECLARE(rk3066a_cru, "rockchip,rk3066a-cru", rk3066a_clk_init); static void __init rk3188a_clk_init(struct device_node *np) { + struct clk *clk1, *clk2; + unsigned long rate; + int ret; + rk3188_common_clk_init(np); + rockchip_clk_register_plls(rk3188_pll_clks, + ARRAY_SIZE(rk3188_pll_clks), + RK3188_GRF_SOC_STATUS); rockchip_clk_register_branches(rk3188_clk_branches, ARRAY_SIZE(rk3188_clk_branches)); + rockchip_clk_register_armclk(ARMCLK, "armclk", + mux_armclk_p, ARRAY_SIZE(mux_armclk_p), + &rk3188_cpuclk_data, rk3188_cpuclk_rates, + ARRAY_SIZE(rk3188_cpuclk_rates)); + + /* reparent aclk_cpu_pre from apll */ + clk1 = __clk_lookup("aclk_cpu_pre"); + clk2 = __clk_lookup("gpll"); + if (clk1 && clk2) { + rate = clk_get_rate(clk1); + + ret = clk_set_parent(clk1, clk2); + if (ret < 0) + pr_warn("%s: could not reparent aclk_cpu_pre to gpll\n", + __func__); + + clk_set_rate(clk1, rate); + } else { + pr_warn("%s: missing clocks to reparent aclk_cpu_pre to gpll\n", + __func__); + } } CLK_OF_DECLARE(rk3188a_cru, "rockchip,rk3188a-cru", rk3188a_clk_init); diff --git a/drivers/clk/rockchip/clk-rk3288.c b/drivers/clk/rockchip/clk-rk3288.c index b22a2d2f21e9..23278291da44 100644 --- a/drivers/clk/rockchip/clk-rk3288.c +++ b/drivers/clk/rockchip/clk-rk3288.c @@ -20,7 +20,7 @@ #include "clk.h" #define RK3288_GRF_SOC_CON(x) (0x244 + x * 4) -#define RK3288_GRF_SOC_STATUS 0x280 +#define RK3288_GRF_SOC_STATUS1 0x284 enum rk3288_plls { apll, dpll, cpll, gpll, npll, @@ -101,6 +101,70 @@ struct rockchip_pll_rate_table rk3288_pll_rates[] = { { /* sentinel */ }, }; +#define RK3288_DIV_ACLK_CORE_M0_MASK 0xf +#define RK3288_DIV_ACLK_CORE_M0_SHIFT 0 +#define RK3288_DIV_ACLK_CORE_MP_MASK 0xf +#define RK3288_DIV_ACLK_CORE_MP_SHIFT 4 +#define RK3288_DIV_L2RAM_MASK 0x7 +#define RK3288_DIV_L2RAM_SHIFT 0 +#define RK3288_DIV_ATCLK_MASK 0x1f +#define RK3288_DIV_ATCLK_SHIFT 4 +#define RK3288_DIV_PCLK_DBGPRE_MASK 0x1f +#define RK3288_DIV_PCLK_DBGPRE_SHIFT 9 + +#define RK3288_CLKSEL0(_core_m0, _core_mp) \ + { \ + .reg = RK3288_CLKSEL_CON(0), \ + .val = HIWORD_UPDATE(_core_m0, RK3288_DIV_ACLK_CORE_M0_MASK, \ + RK3288_DIV_ACLK_CORE_M0_SHIFT) | \ + HIWORD_UPDATE(_core_mp, RK3288_DIV_ACLK_CORE_MP_MASK, \ + RK3288_DIV_ACLK_CORE_MP_SHIFT), \ + } +#define RK3288_CLKSEL37(_l2ram, _atclk, _pclk_dbg_pre) \ + { \ + .reg = RK3288_CLKSEL_CON(37), \ + .val = HIWORD_UPDATE(_l2ram, RK3288_DIV_L2RAM_MASK, \ + RK3288_DIV_L2RAM_SHIFT) | \ + HIWORD_UPDATE(_atclk, RK3288_DIV_ATCLK_MASK, \ + RK3288_DIV_ATCLK_SHIFT) | \ + HIWORD_UPDATE(_pclk_dbg_pre, \ + RK3288_DIV_PCLK_DBGPRE_MASK, \ + RK3288_DIV_PCLK_DBGPRE_SHIFT), \ + } + +#define RK3288_CPUCLK_RATE(_prate, _core_m0, _core_mp, _l2ram, _atclk, _pdbg) \ + { \ + .prate = _prate, \ + .divs = { \ + RK3288_CLKSEL0(_core_m0, _core_mp), \ + RK3288_CLKSEL37(_l2ram, _atclk, _pdbg), \ + }, \ + } + +static struct rockchip_cpuclk_rate_table rk3288_cpuclk_rates[] __initdata = { + RK3288_CPUCLK_RATE(1800000000, 2, 4, 2, 4, 4), + RK3288_CPUCLK_RATE(1704000000, 2, 4, 2, 4, 4), + RK3288_CPUCLK_RATE(1608000000, 2, 4, 2, 4, 4), + RK3288_CPUCLK_RATE(1512000000, 2, 4, 2, 4, 4), + RK3288_CPUCLK_RATE(1416000000, 2, 4, 2, 4, 4), + RK3288_CPUCLK_RATE(1200000000, 2, 4, 2, 4, 4), + RK3288_CPUCLK_RATE(1008000000, 2, 4, 2, 4, 4), + RK3288_CPUCLK_RATE( 816000000, 2, 4, 2, 4, 4), + RK3288_CPUCLK_RATE( 696000000, 2, 4, 2, 4, 4), + RK3288_CPUCLK_RATE( 600000000, 2, 4, 2, 4, 4), + RK3288_CPUCLK_RATE( 408000000, 2, 4, 2, 4, 4), + RK3288_CPUCLK_RATE( 312000000, 2, 4, 2, 4, 4), + RK3288_CPUCLK_RATE( 216000000, 2, 4, 2, 4, 4), + RK3288_CPUCLK_RATE( 126000000, 2, 4, 2, 4, 4), +}; + +static const struct rockchip_cpuclk_reg_data rk3288_cpuclk_data = { + .core_reg = RK3288_CLKSEL_CON(0), + .div_core_shift = 8, + .div_core_mask = 0x1f, + .mux_core_shift = 15, +}; + PNAME(mux_pll_p) = { "xin24m", "xin32k" }; PNAME(mux_armclk_p) = { "apll_core", "gpll_core" }; PNAME(mux_ddrphy_p) = { "dpll_ddr", "gpll_ddr" }; @@ -143,7 +207,7 @@ static struct rockchip_pll_clock rk3288_pll_clks[] __initdata = { [gpll] = PLL(pll_rk3066, PLL_GPLL, "gpll", mux_pll_p, 0, RK3288_PLL_CON(12), RK3288_MODE_CON, 12, 8, rk3288_pll_rates), [npll] = PLL(pll_rk3066, PLL_NPLL, "npll", mux_pll_p, 0, RK3288_PLL_CON(16), - RK3288_MODE_CON, 14, 9, NULL), + RK3288_MODE_CON, 14, 9, rk3288_pll_rates), }; static struct clk_div_table div_hclk_cpu_t[] = { @@ -166,35 +230,33 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = { RK3288_CLKGATE_CON(0), 1, GFLAGS), GATE(0, "gpll_core", "gpll", 0, RK3288_CLKGATE_CON(0), 2, GFLAGS), - COMPOSITE_NOGATE(0, "armclk", mux_armclk_p, 0, - RK3288_CLKSEL_CON(0), 15, 1, MFLAGS, 8, 5, DFLAGS), COMPOSITE_NOMUX(0, "armcore0", "armclk", 0, - RK3288_CLKSEL_CON(36), 0, 3, DFLAGS, + RK3288_CLKSEL_CON(36), 0, 3, DFLAGS | CLK_DIVIDER_READ_ONLY, RK3288_CLKGATE_CON(12), 0, GFLAGS), COMPOSITE_NOMUX(0, "armcore1", "armclk", 0, - RK3288_CLKSEL_CON(36), 4, 3, DFLAGS, + RK3288_CLKSEL_CON(36), 4, 3, DFLAGS | CLK_DIVIDER_READ_ONLY, RK3288_CLKGATE_CON(12), 1, GFLAGS), COMPOSITE_NOMUX(0, "armcore2", "armclk", 0, - RK3288_CLKSEL_CON(36), 8, 3, DFLAGS, + RK3288_CLKSEL_CON(36), 8, 3, DFLAGS | CLK_DIVIDER_READ_ONLY, RK3288_CLKGATE_CON(12), 2, GFLAGS), COMPOSITE_NOMUX(0, "armcore3", "armclk", 0, - RK3288_CLKSEL_CON(36), 12, 3, DFLAGS, + RK3288_CLKSEL_CON(36), 12, 3, DFLAGS | CLK_DIVIDER_READ_ONLY, RK3288_CLKGATE_CON(12), 3, GFLAGS), COMPOSITE_NOMUX(0, "l2ram", "armclk", 0, - RK3288_CLKSEL_CON(37), 0, 3, DFLAGS, + RK3288_CLKSEL_CON(37), 0, 3, DFLAGS | CLK_DIVIDER_READ_ONLY, RK3288_CLKGATE_CON(12), 4, GFLAGS), COMPOSITE_NOMUX(0, "aclk_core_m0", "armclk", 0, - RK3288_CLKSEL_CON(0), 0, 4, DFLAGS, + RK3288_CLKSEL_CON(0), 0, 4, DFLAGS | CLK_DIVIDER_READ_ONLY, RK3288_CLKGATE_CON(12), 5, GFLAGS), COMPOSITE_NOMUX(0, "aclk_core_mp", "armclk", 0, - RK3288_CLKSEL_CON(0), 4, 4, DFLAGS, + RK3288_CLKSEL_CON(0), 4, 4, DFLAGS | CLK_DIVIDER_READ_ONLY, RK3288_CLKGATE_CON(12), 6, GFLAGS), COMPOSITE_NOMUX(0, "atclk", "armclk", 0, - RK3288_CLKSEL_CON(37), 4, 5, DFLAGS, + RK3288_CLKSEL_CON(37), 4, 5, DFLAGS | CLK_DIVIDER_READ_ONLY, RK3288_CLKGATE_CON(12), 7, GFLAGS), COMPOSITE_NOMUX(0, "pclk_dbg_pre", "armclk", 0, - RK3288_CLKSEL_CON(37), 9, 5, DFLAGS, + RK3288_CLKSEL_CON(37), 9, 5, DFLAGS | CLK_DIVIDER_READ_ONLY, RK3288_CLKGATE_CON(12), 8, GFLAGS), GATE(0, "pclk_dbg", "pclk_dbg_pre", 0, RK3288_CLKGATE_CON(12), 9, GFLAGS), @@ -219,12 +281,12 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = { RK3288_CLKSEL_CON(1), 15, 1, MFLAGS, 3, 5, DFLAGS), DIV(0, "aclk_cpu_pre", "aclk_cpu_src", 0, RK3288_CLKSEL_CON(1), 0, 3, DFLAGS), - GATE(0, "aclk_cpu", "aclk_cpu_pre", 0, + GATE(ACLK_CPU, "aclk_cpu", "aclk_cpu_pre", 0, RK3288_CLKGATE_CON(0), 3, GFLAGS), - COMPOSITE_NOMUX(0, "pclk_cpu", "aclk_cpu_pre", 0, + COMPOSITE_NOMUX(PCLK_CPU, "pclk_cpu", "aclk_cpu_pre", 0, RK3288_CLKSEL_CON(1), 12, 3, DFLAGS, RK3288_CLKGATE_CON(0), 5, GFLAGS), - COMPOSITE_NOMUX_DIVTBL(0, "hclk_cpu", "aclk_cpu_pre", 0, + COMPOSITE_NOMUX_DIVTBL(HCLK_CPU, "hclk_cpu", "aclk_cpu_pre", 0, RK3288_CLKSEL_CON(1), 8, 2, DFLAGS, div_hclk_cpu_t, RK3288_CLKGATE_CON(0), 4, GFLAGS), GATE(0, "c2c_host", "aclk_cpu_src", 0, @@ -238,15 +300,15 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = { COMPOSITE(0, "i2s_src", mux_pll_src_cpll_gpll_p, 0, RK3288_CLKSEL_CON(4), 15, 1, MFLAGS, 0, 7, DFLAGS, RK3288_CLKGATE_CON(4), 1, GFLAGS), - COMPOSITE_FRAC(0, "i2s_frac", "i2s_src", 0, + COMPOSITE_FRAC(0, "i2s_frac", "i2s_src", CLK_SET_RATE_PARENT, RK3288_CLKSEL_CON(8), 0, RK3288_CLKGATE_CON(4), 2, GFLAGS), - MUX(0, "i2s_pre", mux_i2s_pre_p, 0, + MUX(0, "i2s_pre", mux_i2s_pre_p, CLK_SET_RATE_PARENT, RK3288_CLKSEL_CON(4), 8, 2, MFLAGS), - COMPOSITE_NODIV(0, "i2s0_clkout", mux_i2s_clkout_p, 0, + COMPOSITE_NODIV(0, "i2s0_clkout", mux_i2s_clkout_p, CLK_SET_RATE_PARENT, RK3288_CLKSEL_CON(4), 12, 1, MFLAGS, RK3288_CLKGATE_CON(4), 0, GFLAGS), - GATE(SCLK_I2S0, "sclk_i2s0", "i2s_pre", 0, + GATE(SCLK_I2S0, "sclk_i2s0", "i2s_pre", CLK_SET_RATE_PARENT, RK3288_CLKGATE_CON(4), 3, GFLAGS), MUX(0, "spdif_src", mux_pll_src_cpll_gpll_p, 0, @@ -296,6 +358,20 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = { COMPOSITE(0, "aclk_vdpu", mux_pll_src_cpll_gpll_usb480m_p, 0, RK3288_CLKSEL_CON(32), 14, 2, MFLAGS, 8, 5, DFLAGS, RK3288_CLKGATE_CON(3), 11, GFLAGS), + /* + * We use aclk_vdpu by default GRF_SOC_CON0[7] setting in system, + * so we ignore the mux and make clocks nodes as following, + */ + GATE(ACLK_VCODEC, "aclk_vcodec", "aclk_vdpu", 0, + RK3288_CLKGATE_CON(9), 0, GFLAGS), + /* + * We introduce a virtul node of hclk_vodec_pre_v to split one clock + * struct with a gate and a fix divider into two node in software. + */ + GATE(0, "hclk_vcodec_pre_v", "aclk_vdpu", 0, + RK3288_CLKGATE_CON(3), 10, GFLAGS), + GATE(HCLK_VCODEC, "hclk_vcodec", "hclk_vcodec_pre", 0, + RK3288_CLKGATE_CON(9), 1, GFLAGS), COMPOSITE(0, "aclk_vio0", mux_pll_src_cpll_gpll_usb480m_p, 0, RK3288_CLKSEL_CON(31), 6, 2, MFLAGS, 0, 5, DFLAGS, @@ -309,7 +385,7 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = { COMPOSITE(0, "aclk_rga_pre", mux_pll_src_cpll_gpll_usb480m_p, 0, RK3288_CLKSEL_CON(30), 6, 2, MFLAGS, 0, 5, DFLAGS, RK3288_CLKGATE_CON(3), 5, GFLAGS), - COMPOSITE(0, "sclk_rga", mux_pll_src_cpll_gpll_usb480m_p, 0, + COMPOSITE(SCLK_RGA, "sclk_rga", mux_pll_src_cpll_gpll_usb480m_p, 0, RK3288_CLKSEL_CON(30), 14, 2, MFLAGS, 8, 5, DFLAGS, RK3288_CLKGATE_CON(3), 4, GFLAGS), @@ -320,35 +396,35 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = { RK3288_CLKSEL_CON(29), 6, 2, MFLAGS, 8, 8, DFLAGS, RK3288_CLKGATE_CON(3), 3, GFLAGS), - COMPOSITE_NODIV(0, "sclk_edp_24m", mux_edp_24m_p, 0, + COMPOSITE_NODIV(SCLK_EDP_24M, "sclk_edp_24m", mux_edp_24m_p, 0, RK3288_CLKSEL_CON(28), 15, 1, MFLAGS, RK3288_CLKGATE_CON(3), 12, GFLAGS), - COMPOSITE(0, "sclk_edp", mux_pll_src_cpll_gpll_npll_p, 0, + COMPOSITE(SCLK_EDP, "sclk_edp", mux_pll_src_cpll_gpll_npll_p, 0, RK3288_CLKSEL_CON(28), 6, 2, MFLAGS, 0, 6, DFLAGS, RK3288_CLKGATE_CON(3), 13, GFLAGS), - COMPOSITE(0, "sclk_isp", mux_pll_src_cpll_gpll_npll_p, 0, + COMPOSITE(SCLK_ISP, "sclk_isp", mux_pll_src_cpll_gpll_npll_p, 0, RK3288_CLKSEL_CON(6), 6, 2, MFLAGS, 0, 6, DFLAGS, RK3288_CLKGATE_CON(3), 14, GFLAGS), - COMPOSITE(0, "sclk_isp_jpe", mux_pll_src_cpll_gpll_npll_p, 0, + COMPOSITE(SCLK_ISP_JPE, "sclk_isp_jpe", mux_pll_src_cpll_gpll_npll_p, 0, RK3288_CLKSEL_CON(6), 14, 2, MFLAGS, 8, 6, DFLAGS, RK3288_CLKGATE_CON(3), 15, GFLAGS), - GATE(0, "sclk_hdmi_hdcp", "xin24m", 0, + GATE(SCLK_HDMI_HDCP, "sclk_hdmi_hdcp", "xin24m", 0, RK3288_CLKGATE_CON(5), 12, GFLAGS), - GATE(0, "sclk_hdmi_cec", "xin32k", 0, + GATE(SCLK_HDMI_CEC, "sclk_hdmi_cec", "xin32k", 0, RK3288_CLKGATE_CON(5), 11, GFLAGS), - COMPOSITE(0, "aclk_hevc", mux_pll_src_cpll_gpll_npll_p, 0, + COMPOSITE(ACLK_HEVC, "aclk_hevc", mux_pll_src_cpll_gpll_npll_p, 0, RK3288_CLKSEL_CON(39), 14, 2, MFLAGS, 8, 5, DFLAGS, RK3288_CLKGATE_CON(13), 13, GFLAGS), - DIV(0, "hclk_hevc", "aclk_hevc", 0, + DIV(HCLK_HEVC, "hclk_hevc", "aclk_hevc", 0, RK3288_CLKSEL_CON(40), 12, 2, DFLAGS), - COMPOSITE(0, "sclk_hevc_cabac", mux_pll_src_cpll_gpll_npll_p, 0, + COMPOSITE(SCLK_HEVC_CABAC, "sclk_hevc_cabac", mux_pll_src_cpll_gpll_npll_p, 0, RK3288_CLKSEL_CON(42), 6, 2, MFLAGS, 0, 5, DFLAGS, RK3288_CLKGATE_CON(13), 14, GFLAGS), - COMPOSITE(0, "sclk_hevc_core", mux_pll_src_cpll_gpll_npll_p, 0, + COMPOSITE(SCLK_HEVC_CORE, "sclk_hevc_core", mux_pll_src_cpll_gpll_npll_p, 0, RK3288_CLKSEL_CON(42), 14, 2, MFLAGS, 8, 5, DFLAGS, RK3288_CLKGATE_CON(13), 15, GFLAGS), @@ -371,13 +447,13 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = { COMPOSITE(0, "aclk_peri_src", mux_pll_src_cpll_gpll_p, 0, RK3288_CLKSEL_CON(10), 15, 1, MFLAGS, 0, 5, DFLAGS, RK3288_CLKGATE_CON(2), 0, GFLAGS), - COMPOSITE_NOMUX(0, "pclk_peri", "aclk_peri_src", 0, + COMPOSITE_NOMUX(PCLK_PERI, "pclk_peri", "aclk_peri_src", 0, RK3288_CLKSEL_CON(10), 12, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO, RK3288_CLKGATE_CON(2), 3, GFLAGS), - COMPOSITE_NOMUX(0, "hclk_peri", "aclk_peri_src", 0, + COMPOSITE_NOMUX(HCLK_PERI, "hclk_peri", "aclk_peri_src", 0, RK3288_CLKSEL_CON(10), 8, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO, RK3288_CLKGATE_CON(2), 2, GFLAGS), - GATE(0, "aclk_peri", "aclk_peri_src", 0, + GATE(ACLK_PERI, "aclk_peri", "aclk_peri_src", 0, RK3288_CLKGATE_CON(2), 1, GFLAGS), /* @@ -643,34 +719,34 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = { GATE(HCLK_RGA, "hclk_rga", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 1, GFLAGS), GATE(HCLK_VOP0, "hclk_vop0", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 6, GFLAGS), GATE(HCLK_VOP1, "hclk_vop1", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 8, GFLAGS), - GATE(0, "hclk_vio_ahb_arbi", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 9, GFLAGS), - GATE(0, "hclk_vio_niu", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 10, GFLAGS), - GATE(0, "hclk_vip", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 15, GFLAGS), + GATE(HCLK_VIO_AHB_ARBI, "hclk_vio_ahb_arbi", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 9, GFLAGS), + GATE(HCLK_VIO_NIU, "hclk_vio_niu", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 10, GFLAGS), + GATE(HCLK_VIP, "hclk_vip", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 15, GFLAGS), GATE(HCLK_IEP, "hclk_iep", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 3, GFLAGS), GATE(HCLK_ISP, "hclk_isp", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 1, GFLAGS), - GATE(0, "hclk_vio2_h2p", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 10, GFLAGS), - GATE(0, "pclk_mipi_dsi0", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 4, GFLAGS), - GATE(0, "pclk_mipi_dsi1", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 5, GFLAGS), - GATE(0, "pclk_mipi_csi", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 6, GFLAGS), - GATE(0, "pclk_lvds_phy", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 7, GFLAGS), - GATE(0, "pclk_edp_ctrl", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 8, GFLAGS), - GATE(0, "pclk_hdmi_ctrl", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 9, GFLAGS), - GATE(0, "pclk_vio2_h2p", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 11, GFLAGS), + GATE(HCLK_VIO2_H2P, "hclk_vio2_h2p", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 10, GFLAGS), + GATE(PCLK_MIPI_DSI0, "pclk_mipi_dsi0", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 4, GFLAGS), + GATE(PCLK_MIPI_DSI1, "pclk_mipi_dsi1", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 5, GFLAGS), + GATE(PCLK_MIPI_CSI, "pclk_mipi_csi", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 6, GFLAGS), + GATE(PCLK_LVDS_PHY, "pclk_lvds_phy", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 7, GFLAGS), + GATE(PCLK_EDP_CTRL, "pclk_edp_ctrl", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 8, GFLAGS), + GATE(PCLK_HDMI_CTRL, "pclk_hdmi_ctrl", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 9, GFLAGS), + GATE(PCLK_VIO2_H2P, "pclk_vio2_h2p", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 11, GFLAGS), /* aclk_vio0 gates */ GATE(ACLK_VOP0, "aclk_vop0", "aclk_vio0", 0, RK3288_CLKGATE_CON(15), 5, GFLAGS), - GATE(0, "aclk_iep", "aclk_vio0", 0, RK3288_CLKGATE_CON(15), 2, GFLAGS), - GATE(0, "aclk_vio0_niu", "aclk_vio0", 0, RK3288_CLKGATE_CON(15), 11, GFLAGS), - GATE(0, "aclk_vip", "aclk_vio0", 0, RK3288_CLKGATE_CON(15), 14, GFLAGS), + GATE(ACLK_IEP, "aclk_iep", "aclk_vio0", 0, RK3288_CLKGATE_CON(15), 2, GFLAGS), + GATE(ACLK_VIO0_NIU, "aclk_vio0_niu", "aclk_vio0", 0, RK3288_CLKGATE_CON(15), 11, GFLAGS), + GATE(ACLK_VIP, "aclk_vip", "aclk_vio0", 0, RK3288_CLKGATE_CON(15), 14, GFLAGS), /* aclk_vio1 gates */ GATE(ACLK_VOP1, "aclk_vop1", "aclk_vio1", 0, RK3288_CLKGATE_CON(15), 7, GFLAGS), - GATE(0, "aclk_isp", "aclk_vio1", 0, RK3288_CLKGATE_CON(16), 2, GFLAGS), - GATE(0, "aclk_vio1_niu", "aclk_vio1", 0, RK3288_CLKGATE_CON(15), 12, GFLAGS), + GATE(ACLK_ISP, "aclk_isp", "aclk_vio1", 0, RK3288_CLKGATE_CON(16), 2, GFLAGS), + GATE(ACLK_VIO1_NIU, "aclk_vio1_niu", "aclk_vio1", 0, RK3288_CLKGATE_CON(15), 12, GFLAGS), /* aclk_rga_pre gates */ GATE(ACLK_RGA, "aclk_rga", "aclk_rga_pre", 0, RK3288_CLKGATE_CON(15), 0, GFLAGS), - GATE(0, "aclk_rga_niu", "aclk_rga_pre", 0, RK3288_CLKGATE_CON(15), 13, GFLAGS), + GATE(ACLK_RGA_NIU, "aclk_rga_niu", "aclk_rga_pre", 0, RK3288_CLKGATE_CON(15), 13, GFLAGS), /* * Other ungrouped clocks. @@ -680,6 +756,12 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = { GATE(0, "pclk_isp_in", "ext_isp", 0, RK3288_CLKGATE_CON(16), 3, GFLAGS), }; +static const char *rk3288_critical_clocks[] __initconst = { + "aclk_cpu", + "aclk_peri", + "hclk_peri", +}; + static void __init rk3288_clk_init(struct device_node *np) { void __iomem *reg_base; @@ -705,13 +787,28 @@ static void __init rk3288_clk_init(struct device_node *np) pr_warn("%s: could not register clock usb480m: %ld\n", __func__, PTR_ERR(clk)); + clk = clk_register_fixed_factor(NULL, "hclk_vcodec_pre", + "hclk_vcodec_pre_v", 0, 1, 4); + if (IS_ERR(clk)) + pr_warn("%s: could not register clock hclk_vcodec_pre: %ld\n", + __func__, PTR_ERR(clk)); + rockchip_clk_register_plls(rk3288_pll_clks, ARRAY_SIZE(rk3288_pll_clks), - RK3288_GRF_SOC_STATUS); + RK3288_GRF_SOC_STATUS1); rockchip_clk_register_branches(rk3288_clk_branches, ARRAY_SIZE(rk3288_clk_branches)); + rockchip_clk_protect_critical(rk3288_critical_clocks, + ARRAY_SIZE(rk3288_critical_clocks)); - rockchip_register_softrst(np, 9, reg_base + RK3288_SOFTRST_CON(0), + rockchip_clk_register_armclk(ARMCLK, "armclk", + mux_armclk_p, ARRAY_SIZE(mux_armclk_p), + &rk3288_cpuclk_data, rk3288_cpuclk_rates, + ARRAY_SIZE(rk3288_cpuclk_rates)); + + rockchip_register_softrst(np, 12, reg_base + RK3288_SOFTRST_CON(0), ROCKCHIP_SOFTRST_HIWORD_MASK); + + rockchip_register_restart_notifier(RK3288_GLB_SRST_FST); } CLK_OF_DECLARE(rk3288_cru, "rockchip,rk3288-cru", rk3288_clk_init); diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c index 278cf9dd1e23..1e68bff481b8 100644 --- a/drivers/clk/rockchip/clk.c +++ b/drivers/clk/rockchip/clk.c @@ -25,6 +25,7 @@ #include <linux/clk-provider.h> #include <linux/mfd/syscon.h> #include <linux/regmap.h> +#include <linux/reboot.h> #include "clk.h" /** @@ -37,7 +38,7 @@ * * sometimes without one of those components. */ -struct clk *rockchip_clk_register_branch(const char *name, +static struct clk *rockchip_clk_register_branch(const char *name, const char **parent_names, u8 num_parents, void __iomem *base, int muxdiv_offset, u8 mux_shift, u8 mux_width, u8 mux_flags, u8 div_shift, u8 div_width, u8 div_flags, @@ -103,6 +104,54 @@ struct clk *rockchip_clk_register_branch(const char *name, return clk; } +static struct clk *rockchip_clk_register_frac_branch(const char *name, + const char **parent_names, u8 num_parents, void __iomem *base, + int muxdiv_offset, u8 div_flags, + int gate_offset, u8 gate_shift, u8 gate_flags, + unsigned long flags, spinlock_t *lock) +{ + struct clk *clk; + struct clk_gate *gate = NULL; + struct clk_fractional_divider *div = NULL; + const struct clk_ops *div_ops = NULL, *gate_ops = NULL; + + if (gate_offset >= 0) { + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) + return ERR_PTR(-ENOMEM); + + gate->flags = gate_flags; + gate->reg = base + gate_offset; + gate->bit_idx = gate_shift; + gate->lock = lock; + gate_ops = &clk_gate_ops; + } + + if (muxdiv_offset < 0) + return ERR_PTR(-EINVAL); + + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) + return ERR_PTR(-ENOMEM); + + div->flags = div_flags; + div->reg = base + muxdiv_offset; + div->mshift = 16; + div->mmask = 0xffff0000; + div->nshift = 0; + div->nmask = 0xffff; + div->lock = lock; + div_ops = &clk_fractional_divider_ops; + + clk = clk_register_composite(NULL, name, parent_names, num_parents, + NULL, NULL, + &div->hw, div_ops, + gate ? &gate->hw : NULL, gate_ops, + flags); + + return clk; +} + static DEFINE_SPINLOCK(clk_lock); static struct clk **clk_table; static void __iomem *reg_base; @@ -197,8 +246,14 @@ void __init rockchip_clk_register_branches( list->div_flags, &clk_lock); break; case branch_fraction_divider: - /* unimplemented */ - continue; + /* keep all gates untouched for now */ + flags |= CLK_IGNORE_UNUSED; + + clk = rockchip_clk_register_frac_branch(list->name, + list->parent_names, list->num_parents, + reg_base, list->muxdiv_offset, list->div_flags, + list->gate_offset, list->gate_shift, + list->gate_flags, flags, &clk_lock); break; case branch_gate: flags |= CLK_SET_RATE_PARENT; @@ -242,3 +297,61 @@ void __init rockchip_clk_register_branches( rockchip_clk_add_lookup(clk, list->id); } } + +void __init rockchip_clk_register_armclk(unsigned int lookup_id, + const char *name, const char **parent_names, + u8 num_parents, + const struct rockchip_cpuclk_reg_data *reg_data, + const struct rockchip_cpuclk_rate_table *rates, + int nrates) +{ + struct clk *clk; + + clk = rockchip_clk_register_cpuclk(name, parent_names, num_parents, + reg_data, rates, nrates, reg_base, + &clk_lock); + if (IS_ERR(clk)) { + pr_err("%s: failed to register clock %s: %ld\n", + __func__, name, PTR_ERR(clk)); + return; + } + + rockchip_clk_add_lookup(clk, lookup_id); +} + +void __init rockchip_clk_protect_critical(const char *clocks[], int nclocks) +{ + int i; + + /* Protect the clocks that needs to stay on */ + for (i = 0; i < nclocks; i++) { + struct clk *clk = __clk_lookup(clocks[i]); + + if (clk) + clk_prepare_enable(clk); + } +} + +static unsigned int reg_restart; +static int rockchip_restart_notify(struct notifier_block *this, + unsigned long mode, void *cmd) +{ + writel(0xfdb9, reg_base + reg_restart); + return NOTIFY_DONE; +} + +static struct notifier_block rockchip_restart_handler = { + .notifier_call = rockchip_restart_notify, + .priority = 128, +}; + +void __init rockchip_register_restart_notifier(unsigned int reg) +{ + int ret; + + reg_restart = reg; + ret = register_restart_handler(&rockchip_restart_handler); + if (ret) + pr_err("%s: cannot register restart handler, %d\n", + __func__, ret); +} diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h index 887cbdeca2aa..ca009ab0a33a 100644 --- a/drivers/clk/rockchip/clk.h +++ b/drivers/clk/rockchip/clk.h @@ -120,6 +120,38 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type, struct rockchip_pll_rate_table *rate_table, spinlock_t *lock); +struct rockchip_cpuclk_clksel { + int reg; + u32 val; +}; + +#define ROCKCHIP_CPUCLK_NUM_DIVIDERS 2 +struct rockchip_cpuclk_rate_table { + unsigned long prate; + struct rockchip_cpuclk_clksel divs[ROCKCHIP_CPUCLK_NUM_DIVIDERS]; +}; + +/** + * struct rockchip_cpuclk_reg_data: describes register offsets and masks of the cpuclock + * @core_reg: register offset of the core settings register + * @div_core_shift: core divider offset used to divide the pll value + * @div_core_mask: core divider mask + * @mux_core_shift: offset of the core multiplexer + */ +struct rockchip_cpuclk_reg_data { + int core_reg; + u8 div_core_shift; + u32 div_core_mask; + int mux_core_reg; + u8 mux_core_shift; +}; + +struct clk *rockchip_clk_register_cpuclk(const char *name, + const char **parent_names, u8 num_parents, + const struct rockchip_cpuclk_reg_data *reg_data, + const struct rockchip_cpuclk_rate_table *rates, + int nrates, void __iomem *reg_base, spinlock_t *lock); + #define PNAME(x) static const char *x[] __initconst enum rockchip_clk_branch_type { @@ -329,6 +361,13 @@ void rockchip_clk_register_branches(struct rockchip_clk_branch *clk_list, unsigned int nr_clk); void rockchip_clk_register_plls(struct rockchip_pll_clock *pll_list, unsigned int nr_pll, int grf_lock_offset); +void rockchip_clk_register_armclk(unsigned int lookup_id, const char *name, + const char **parent_names, u8 num_parents, + const struct rockchip_cpuclk_reg_data *reg_data, + const struct rockchip_cpuclk_rate_table *rates, + int nrates); +void rockchip_clk_protect_critical(const char *clocks[], int nclocks); +void rockchip_register_restart_notifier(unsigned int reg); #define ROCKCHIP_SOFTRST_HIWORD_MASK BIT(0) diff --git a/drivers/clk/samsung/clk-exynos3250.c b/drivers/clk/samsung/clk-exynos3250.c index dc85f8e7a2d7..6e6cca392082 100644 --- a/drivers/clk/samsung/clk-exynos3250.c +++ b/drivers/clk/samsung/clk-exynos3250.c @@ -110,7 +110,14 @@ enum exynos3250_plls { nr_plls }; +/* list of PLLs in DMC block to be registered */ +enum exynos3250_dmc_plls { + bpll, epll, + nr_dmc_plls +}; + static void __iomem *reg_base; +static void __iomem *dmc_reg_base; /* * Support for CMU save/restore across system suspends @@ -266,6 +273,7 @@ PNAME(group_sclk_cam_blk_p) = { "xxti", "xusbxti", "none", "none", "none", "none", "div_mpll_pre", "mout_epll_user", "mout_vpll", + "none", "none", "none", "div_cam_blk_320", }; PNAME(group_sclk_fimd0_p) = { "xxti", "xusbxti", "m_bitclkhsdiv4_2l", "none", @@ -353,8 +361,8 @@ static struct samsung_mux_clock mux_clks[] __initdata = { /* SRC_FSYS */ MUX(CLK_MOUT_TSADC, "mout_tsadc", group_sclk_p, SRC_FSYS, 28, 4), - MUX(CLK_MOUT_MMC1, "mout_mmc1", group_sclk_p, SRC_FSYS, 4, 3), - MUX(CLK_MOUT_MMC0, "mout_mmc0", group_sclk_p, SRC_FSYS, 0, 3), + MUX(CLK_MOUT_MMC1, "mout_mmc1", group_sclk_p, SRC_FSYS, 4, 4), + MUX(CLK_MOUT_MMC0, "mout_mmc0", group_sclk_p, SRC_FSYS, 0, 4), /* SRC_PERIL0 */ MUX(CLK_MOUT_UART1, "mout_uart1", group_sclk_p, SRC_PERIL0, 4, 4), @@ -423,7 +431,7 @@ static struct samsung_div_clock div_clks[] __initdata = { DIV(CLK_DIV_SPI1_ISP, "div_spi1_isp", "mout_spi1_isp", DIV_ISP, 16, 4), DIV_F(CLK_DIV_SPI0_ISP_PRE, "div_spi0_isp_pre", "div_spi0_isp", DIV_ISP, 8, 8, CLK_SET_RATE_PARENT, 0), - DIV(CLK_DIV_SPI0_ISP, "div_spi0_isp", "mout_spi0_isp", DIV_ISP, 0, 4), + DIV(CLK_DIV_SPI0_ISP, "div_spi0_isp", "mout_spi0_isp", DIV_ISP, 4, 4), /* DIV_FSYS0 */ DIV_F(CLK_DIV_TSADC_PRE, "div_tsadc_pre", "div_tsadc", DIV_FSYS0, 8, 8, @@ -724,6 +732,25 @@ static struct samsung_pll_rate_table exynos3250_pll_rates[] = { { /* sentinel */ } }; +/* EPLL */ +static struct samsung_pll_rate_table exynos3250_epll_rates[] = { + PLL_36XX_RATE(800000000, 200, 3, 1, 0), + PLL_36XX_RATE(288000000, 96, 2, 2, 0), + PLL_36XX_RATE(192000000, 128, 2, 3, 0), + PLL_36XX_RATE(144000000, 96, 2, 3, 0), + PLL_36XX_RATE( 96000000, 128, 2, 4, 0), + PLL_36XX_RATE( 84000000, 112, 2, 4, 0), + PLL_36XX_RATE( 80000004, 106, 2, 4, 43691), + PLL_36XX_RATE( 73728000, 98, 2, 4, 19923), + PLL_36XX_RATE( 67737598, 270, 3, 5, 62285), + PLL_36XX_RATE( 65535999, 174, 2, 5, 49982), + PLL_36XX_RATE( 50000000, 200, 3, 5, 0), + PLL_36XX_RATE( 49152002, 131, 2, 5, 4719), + PLL_36XX_RATE( 48000000, 128, 2, 5, 0), + PLL_36XX_RATE( 45158401, 180, 3, 5, 41524), + { /* sentinel */ } +}; + /* VPLL */ static struct samsung_pll_rate_table exynos3250_vpll_rates[] = { PLL_36XX_RATE(600000000, 100, 2, 1, 0), @@ -821,3 +848,172 @@ static void __init exynos3250_cmu_init(struct device_node *np) samsung_clk_of_add_provider(np, ctx); } CLK_OF_DECLARE(exynos3250_cmu, "samsung,exynos3250-cmu", exynos3250_cmu_init); + +/* + * CMU DMC + */ + +#define BPLL_LOCK 0x0118 +#define BPLL_CON0 0x0218 +#define BPLL_CON1 0x021c +#define BPLL_CON2 0x0220 +#define SRC_DMC 0x0300 +#define DIV_DMC1 0x0504 +#define GATE_BUS_DMC0 0x0700 +#define GATE_BUS_DMC1 0x0704 +#define GATE_BUS_DMC2 0x0708 +#define GATE_BUS_DMC3 0x070c +#define GATE_SCLK_DMC 0x0800 +#define GATE_IP_DMC0 0x0900 +#define GATE_IP_DMC1 0x0904 +#define EPLL_LOCK 0x1110 +#define EPLL_CON0 0x1114 +#define EPLL_CON1 0x1118 +#define EPLL_CON2 0x111c +#define SRC_EPLL 0x1120 + +/* + * Support for CMU save/restore across system suspends + */ +#ifdef CONFIG_PM_SLEEP +static struct samsung_clk_reg_dump *exynos3250_dmc_clk_regs; + +static unsigned long exynos3250_cmu_dmc_clk_regs[] __initdata = { + BPLL_LOCK, + BPLL_CON0, + BPLL_CON1, + BPLL_CON2, + SRC_DMC, + DIV_DMC1, + GATE_BUS_DMC0, + GATE_BUS_DMC1, + GATE_BUS_DMC2, + GATE_BUS_DMC3, + GATE_SCLK_DMC, + GATE_IP_DMC0, + GATE_IP_DMC1, + EPLL_LOCK, + EPLL_CON0, + EPLL_CON1, + EPLL_CON2, + SRC_EPLL, +}; + +static int exynos3250_dmc_clk_suspend(void) +{ + samsung_clk_save(dmc_reg_base, exynos3250_dmc_clk_regs, + ARRAY_SIZE(exynos3250_cmu_dmc_clk_regs)); + return 0; +} + +static void exynos3250_dmc_clk_resume(void) +{ + samsung_clk_restore(dmc_reg_base, exynos3250_dmc_clk_regs, + ARRAY_SIZE(exynos3250_cmu_dmc_clk_regs)); +} + +static struct syscore_ops exynos3250_dmc_clk_syscore_ops = { + .suspend = exynos3250_dmc_clk_suspend, + .resume = exynos3250_dmc_clk_resume, +}; + +static void exynos3250_dmc_clk_sleep_init(void) +{ + exynos3250_dmc_clk_regs = + samsung_clk_alloc_reg_dump(exynos3250_cmu_dmc_clk_regs, + ARRAY_SIZE(exynos3250_cmu_dmc_clk_regs)); + if (!exynos3250_dmc_clk_regs) { + pr_warn("%s: Failed to allocate sleep save data\n", __func__); + goto err; + } + + register_syscore_ops(&exynos3250_dmc_clk_syscore_ops); + return; +err: + kfree(exynos3250_dmc_clk_regs); +} +#else +static inline void exynos3250_dmc_clk_sleep_init(void) { } +#endif + +PNAME(mout_epll_p) = { "fin_pll", "fout_epll", }; +PNAME(mout_bpll_p) = { "fin_pll", "fout_bpll", }; +PNAME(mout_mpll_mif_p) = { "fin_pll", "sclk_mpll_mif", }; +PNAME(mout_dphy_p) = { "mout_mpll_mif", "mout_bpll", }; + +static struct samsung_mux_clock dmc_mux_clks[] __initdata = { + /* + * NOTE: Following table is sorted by register address in ascending + * order and then bitfield shift in descending order, as it is done + * in the User's Manual. When adding new entries, please make sure + * that the order is preserved, to avoid merge conflicts and make + * further work with defined data easier. + */ + + /* SRC_DMC */ + MUX(CLK_MOUT_MPLL_MIF, "mout_mpll_mif", mout_mpll_mif_p, SRC_DMC, 12, 1), + MUX(CLK_MOUT_BPLL, "mout_bpll", mout_bpll_p, SRC_DMC, 10, 1), + MUX(CLK_MOUT_DPHY, "mout_dphy", mout_dphy_p, SRC_DMC, 8, 1), + MUX(CLK_MOUT_DMC_BUS, "mout_dmc_bus", mout_dphy_p, SRC_DMC, 4, 1), + + /* SRC_EPLL */ + MUX(CLK_MOUT_EPLL, "mout_epll", mout_epll_p, SRC_EPLL, 4, 1), +}; + +static struct samsung_div_clock dmc_div_clks[] __initdata = { + /* + * NOTE: Following table is sorted by register address in ascending + * order and then bitfield shift in descending order, as it is done + * in the User's Manual. When adding new entries, please make sure + * that the order is preserved, to avoid merge conflicts and make + * further work with defined data easier. + */ + + /* DIV_DMC1 */ + DIV(CLK_DIV_DMC, "div_dmc", "div_dmc_pre", DIV_DMC1, 27, 3), + DIV(CLK_DIV_DPHY, "div_dphy", "mout_dphy", DIV_DMC1, 23, 3), + DIV(CLK_DIV_DMC_PRE, "div_dmc_pre", "mout_dmc_bus", DIV_DMC1, 19, 2), + DIV(CLK_DIV_DMCP, "div_dmcp", "div_dmcd", DIV_DMC1, 15, 3), + DIV(CLK_DIV_DMCD, "div_dmcd", "div_dmc", DIV_DMC1, 11, 3), +}; + +static struct samsung_pll_clock exynos3250_dmc_plls[nr_dmc_plls] __initdata = { + [bpll] = PLL(pll_35xx, CLK_FOUT_BPLL, "fout_bpll", "fin_pll", + BPLL_LOCK, BPLL_CON0, NULL), + [epll] = PLL(pll_36xx, CLK_FOUT_EPLL, "fout_epll", "fin_pll", + EPLL_LOCK, EPLL_CON0, NULL), +}; + +static void __init exynos3250_cmu_dmc_init(struct device_node *np) +{ + struct samsung_clk_provider *ctx; + + dmc_reg_base = of_iomap(np, 0); + if (!dmc_reg_base) + panic("%s: failed to map registers\n", __func__); + + ctx = samsung_clk_init(np, dmc_reg_base, NR_CLKS_DMC); + if (!ctx) + panic("%s: unable to allocate context.\n", __func__); + + exynos3250_dmc_plls[bpll].rate_table = exynos3250_pll_rates; + exynos3250_dmc_plls[epll].rate_table = exynos3250_epll_rates; + + pr_err("CLK registering epll bpll: %d, %d, %d, %d\n", + exynos3250_dmc_plls[bpll].rate_table[0].rate, + exynos3250_dmc_plls[bpll].rate_table[0].mdiv, + exynos3250_dmc_plls[bpll].rate_table[0].pdiv, + exynos3250_dmc_plls[bpll].rate_table[0].sdiv + ); + samsung_clk_register_pll(ctx, exynos3250_dmc_plls, + ARRAY_SIZE(exynos3250_dmc_plls), dmc_reg_base); + + samsung_clk_register_mux(ctx, dmc_mux_clks, ARRAY_SIZE(dmc_mux_clks)); + samsung_clk_register_div(ctx, dmc_div_clks, ARRAY_SIZE(dmc_div_clks)); + + exynos3250_dmc_clk_sleep_init(); + + samsung_clk_of_add_provider(np, ctx); +} +CLK_OF_DECLARE(exynos3250_cmu_dmc, "samsung,exynos3250-cmu-dmc", + exynos3250_cmu_dmc_init); diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index ac163d7f5bc3..940f02837b82 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -517,7 +517,7 @@ static struct samsung_fixed_factor_clock exynos4_fixed_factor_clks[] __initdata FFACTOR(0, "sclk_apll_div_2", "sclk_apll", 1, 2, 0), FFACTOR(0, "fout_mpll_div_2", "fout_mpll", 1, 2, 0), FFACTOR(0, "fout_apll_div_2", "fout_apll", 1, 2, 0), - FFACTOR(0, "arm_clk_div_2", "arm_clk", 1, 2, 0), + FFACTOR(0, "arm_clk_div_2", "div_core2", 1, 2, 0), }; static struct samsung_fixed_factor_clock exynos4210_fixed_factor_clks[] __initdata = { @@ -535,7 +535,7 @@ static struct samsung_fixed_factor_clock exynos4x12_fixed_factor_clks[] __initda static struct samsung_mux_clock exynos4_mux_clks[] __initdata = { MUX_FA(CLK_MOUT_APLL, "mout_apll", mout_apll_p, SRC_CPU, 0, 1, CLK_SET_RATE_PARENT, 0, "mout_apll"), - MUX(0, "mout_hdmi", mout_hdmi_p, SRC_TV, 0, 1), + MUX(CLK_MOUT_HDMI, "mout_hdmi", mout_hdmi_p, SRC_TV, 0, 1), MUX(0, "mout_mfc1", sclk_evpll_p, SRC_MFC, 4, 1), MUX(0, "mout_mfc", mout_mfc_p, SRC_MFC, 8, 1), MUX_F(CLK_MOUT_G3D1, "mout_g3d1", sclk_evpll_p, SRC_G3D, 4, 1, @@ -569,7 +569,7 @@ static struct samsung_mux_clock exynos4210_mux_clks[] __initdata = { MUX(0, "mout_aclk100", sclk_ampll_p4210, SRC_TOP0, 16, 1), MUX(0, "mout_aclk160", sclk_ampll_p4210, SRC_TOP0, 20, 1), MUX(0, "mout_aclk133", sclk_ampll_p4210, SRC_TOP0, 24, 1), - MUX(0, "mout_mixer", mout_mixer_p4210, SRC_TV, 4, 1), + MUX(CLK_MOUT_MIXER, "mout_mixer", mout_mixer_p4210, SRC_TV, 4, 1), MUX(0, "mout_dac", mout_dac_p4210, SRC_TV, 8, 1), MUX(0, "mout_g2d0", sclk_ampll_p4210, E4210_SRC_IMAGE, 0, 1), MUX(0, "mout_g2d1", sclk_evpll_p, E4210_SRC_IMAGE, 4, 1), @@ -719,7 +719,7 @@ static struct samsung_div_clock exynos4_div_clks[] __initdata = { DIV(0, "div_periph", "div_core2", DIV_CPU0, 12, 3), DIV(0, "div_atb", "mout_core", DIV_CPU0, 16, 3), DIV(0, "div_pclk_dbg", "div_atb", DIV_CPU0, 20, 3), - DIV(0, "div_core2", "div_core", DIV_CPU0, 28, 3), + DIV(CLK_ARM_CLK, "div_core2", "div_core", DIV_CPU0, 28, 3), DIV(0, "div_copy", "mout_hpm", DIV_CPU1, 0, 3), DIV(0, "div_hpm", "div_copy", DIV_CPU1, 4, 3), DIV(0, "div_clkout_cpu", "mout_clkout_cpu", CLKOUT_CMU_CPU, 8, 6), @@ -733,8 +733,7 @@ static struct samsung_div_clock exynos4_div_clks[] __initdata = { DIV(0, "div_csis0", "mout_csis0", DIV_CAM, 24, 4), DIV(0, "div_csis1", "mout_csis1", DIV_CAM, 28, 4), DIV(CLK_SCLK_MFC, "sclk_mfc", "mout_mfc", DIV_MFC, 0, 4), - DIV_F(0, "div_g3d", "mout_g3d", DIV_G3D, 0, 4, - CLK_SET_RATE_PARENT, 0), + DIV(CLK_SCLK_G3D, "sclk_g3d", "mout_g3d", DIV_G3D, 0, 4), DIV(0, "div_fimd0", "mout_fimd0", DIV_LCD0, 0, 4), DIV(0, "div_mipi0", "mout_mipi0", DIV_LCD0, 16, 4), DIV(0, "div_audio0", "mout_audio0", DIV_MAUDIO, 0, 4), @@ -769,7 +768,6 @@ static struct samsung_div_clock exynos4_div_clks[] __initdata = { DIV(0, "div_spi_pre2", "div_spi2", DIV_PERIL2, 8, 8), DIV(0, "div_audio1", "mout_audio1", DIV_PERIL4, 0, 4), DIV(0, "div_audio2", "mout_audio2", DIV_PERIL4, 16, 4), - DIV(CLK_ARM_CLK, "arm_clk", "div_core2", DIV_CPU0, 28, 3), DIV(CLK_SCLK_APLL, "sclk_apll", "mout_apll", DIV_CPU0, 24, 3), DIV_F(0, "div_mipi_pre0", "div_mipi0", DIV_LCD0, 20, 4, CLK_SET_RATE_PARENT, 0), @@ -857,8 +855,7 @@ static struct samsung_gate_clock exynos4_gate_clks[] __initdata = { 0), GATE(CLK_TSI, "tsi", "aclk133", GATE_IP_FSYS, 4, 0, 0), GATE(CLK_SROMC, "sromc", "aclk133", GATE_IP_FSYS, 11, 0, 0), - GATE(CLK_SCLK_G3D, "sclk_g3d", "div_g3d", GATE_IP_G3D, 0, - CLK_SET_RATE_PARENT, 0), + GATE(CLK_G3D, "g3d", "aclk200", GATE_IP_G3D, 0, 0, 0), GATE(CLK_PPMUG3D, "ppmug3d", "aclk200", GATE_IP_G3D, 1, 0, 0), GATE(CLK_USB_DEVICE, "usb_device", "aclk133", GATE_IP_FSYS, 13, 0, 0), GATE(CLK_ONENAND, "onenand", "aclk133", GATE_IP_FSYS, 15, 0, 0), @@ -1183,6 +1180,7 @@ static struct samsung_gate_clock exynos4x12_gate_clks[] __initdata = { GATE(CLK_SPI1_ISP, "spi1_isp", "aclk200", E4X12_GATE_ISP1, 13, CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(CLK_G2D, "g2d", "aclk200", GATE_IP_DMC, 23, 0, 0), + GATE(CLK_SMMU_G2D, "smmu_g2d", "aclk200", GATE_IP_DMC, 24, 0, 0), GATE(CLK_TMU_APBIF, "tmu_apbif", "aclk100", E4X12_GATE_IP_PERIR, 17, 0, 0), }; @@ -1486,7 +1484,7 @@ static void __init exynos4_clk_init(struct device_node *np, exynos4_soc == EXYNOS4210 ? "Exynos4210" : "Exynos4x12", _get_rate("sclk_apll"), _get_rate("sclk_mpll"), _get_rate("sclk_epll"), _get_rate("sclk_vpll"), - _get_rate("arm_clk")); + _get_rate("div_core2")); } diff --git a/drivers/clk/samsung/clk-exynos5260.c b/drivers/clk/samsung/clk-exynos5260.c index ce3de97e5f11..2527e39aadcf 100644 --- a/drivers/clk/samsung/clk-exynos5260.c +++ b/drivers/clk/samsung/clk-exynos5260.c @@ -1581,7 +1581,7 @@ struct samsung_fixed_rate_clock fixed_rate_clks[] __initdata = { FRATE(PHYCLK_HDMI_LINK_O_TMDS_CLKHI, "phyclk_hdmi_link_o_tmds_clkhi", NULL, CLK_IS_ROOT, 125000000), FRATE(PHYCLK_MIPI_DPHY_4L_M_TXBYTECLKHS, - "phyclk_mipi_dphy_4l_m_txbyteclkhs" , NULL, + "phyclk_mipi_dphy_4l_m_txbyte_clkhs" , NULL, CLK_IS_ROOT, 187500000), FRATE(PHYCLK_DPTX_PHY_O_REF_CLK_24M, "phyclk_dptx_phy_o_ref_clk_24m", NULL, CLK_IS_ROOT, 24000000), diff --git a/drivers/clk/samsung/clk-s3c2410-dclk.c b/drivers/clk/samsung/clk-s3c2410-dclk.c index 0449cc0458ed..f4f29ed6bd25 100644 --- a/drivers/clk/samsung/clk-s3c2410-dclk.c +++ b/drivers/clk/samsung/clk-s3c2410-dclk.c @@ -426,7 +426,6 @@ MODULE_DEVICE_TABLE(platform, s3c24xx_dclk_driver_ids); static struct platform_driver s3c24xx_dclk_driver = { .driver = { .name = "s3c24xx-dclk", - .owner = THIS_MODULE, .pm = &s3c24xx_dclk_pm_ops, }, .probe = s3c24xx_dclk_probe, diff --git a/drivers/clk/samsung/clk-s3c2412.c b/drivers/clk/samsung/clk-s3c2412.c index 34af09f6a155..2ceedaf8ce18 100644 --- a/drivers/clk/samsung/clk-s3c2412.c +++ b/drivers/clk/samsung/clk-s3c2412.c @@ -14,6 +14,7 @@ #include <linux/of.h> #include <linux/of_address.h> #include <linux/syscore_ops.h> +#include <linux/reboot.h> #include <dt-bindings/clock/s3c2412.h> @@ -26,6 +27,7 @@ #define CLKCON 0x0c #define CLKDIVN 0x14 #define CLKSRC 0x1c +#define SWRST 0x30 /* list of PLLs to be registered */ enum s3c2412_plls { @@ -204,6 +206,28 @@ struct samsung_clock_alias s3c2412_aliases[] __initdata = { ALIAS(MSYSCLK, NULL, "fclk"), }; +static int s3c2412_restart(struct notifier_block *this, + unsigned long mode, void *cmd) +{ + /* errata "Watch-dog/Software Reset Problem" specifies that + * this reset must be done with the SYSCLK sourced from + * EXTCLK instead of FOUT to avoid a glitch in the reset + * mechanism. + * + * See the watchdog section of the S3C2412 manual for more + * information on this fix. + */ + + __raw_writel(0x00, reg_base + CLKSRC); + __raw_writel(0x533C2412, reg_base + SWRST); + return NOTIFY_DONE; +} + +static struct notifier_block s3c2412_restart_handler = { + .notifier_call = s3c2412_restart, + .priority = 129, +}; + /* * fixed rate clocks generated outside the soc * Only necessary until the devicetree-move is complete @@ -233,6 +257,7 @@ void __init s3c2412_common_clk_init(struct device_node *np, unsigned long xti_f, unsigned long ext_f, void __iomem *base) { struct samsung_clk_provider *ctx; + int ret; reg_base = base; if (np) { @@ -267,6 +292,10 @@ void __init s3c2412_common_clk_init(struct device_node *np, unsigned long xti_f, s3c2412_clk_sleep_init(); samsung_clk_of_add_provider(np, ctx); + + ret = register_restart_handler(&s3c2412_restart_handler); + if (ret) + pr_warn("cannot register restart handler, %d\n", ret); } static void __init s3c2412_clk_init(struct device_node *np) diff --git a/drivers/clk/samsung/clk-s3c2443.c b/drivers/clk/samsung/clk-s3c2443.c index c92f853fca9f..0c3c182b902a 100644 --- a/drivers/clk/samsung/clk-s3c2443.c +++ b/drivers/clk/samsung/clk-s3c2443.c @@ -14,6 +14,7 @@ #include <linux/of.h> #include <linux/of_address.h> #include <linux/syscore_ops.h> +#include <linux/reboot.h> #include <dt-bindings/clock/s3c2443.h> @@ -33,6 +34,7 @@ #define HCLKCON 0x30 #define PCLKCON 0x34 #define SCLKCON 0x38 +#define SWRST 0x44 /* the soc types */ enum supported_socs { @@ -354,6 +356,18 @@ struct samsung_clock_alias s3c2450_aliases[] __initdata = { ALIAS(PCLK_I2C1, "s3c2410-i2c.1", "i2c"), }; +static int s3c2443_restart(struct notifier_block *this, + unsigned long mode, void *cmd) +{ + __raw_writel(0x533c2443, reg_base + SWRST); + return NOTIFY_DONE; +} + +static struct notifier_block s3c2443_restart_handler = { + .notifier_call = s3c2443_restart, + .priority = 129, +}; + /* * fixed rate clocks generated outside the soc * Only necessary until the devicetree-move is complete @@ -378,6 +392,7 @@ void __init s3c2443_common_clk_init(struct device_node *np, unsigned long xti_f, void __iomem *base) { struct samsung_clk_provider *ctx; + int ret; reg_base = base; if (np) { @@ -447,6 +462,10 @@ void __init s3c2443_common_clk_init(struct device_node *np, unsigned long xti_f, s3c2443_clk_sleep_init(); samsung_clk_of_add_provider(np, ctx); + + ret = register_restart_handler(&s3c2443_restart_handler); + if (ret) + pr_warn("cannot register restart handler, %d\n", ret); } static void __init s3c2416_clk_init(struct device_node *np) diff --git a/drivers/clk/shmobile/clk-rcar-gen2.c b/drivers/clk/shmobile/clk-rcar-gen2.c index dff7f79a19b9..e996425d06a9 100644 --- a/drivers/clk/shmobile/clk-rcar-gen2.c +++ b/drivers/clk/shmobile/clk-rcar-gen2.c @@ -202,6 +202,7 @@ static const struct clk_div_table cpg_sdh_div_table[] = { }; static const struct clk_div_table cpg_sd01_div_table[] = { + { 4, 8 }, { 5, 12 }, { 6, 16 }, { 7, 18 }, { 8, 24 }, { 10, 36 }, { 11, 48 }, { 12, 10 }, { 0, 0 }, }; diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile index 6850cba35871..7ddc2b553846 100644 --- a/drivers/clk/sunxi/Makefile +++ b/drivers/clk/sunxi/Makefile @@ -5,6 +5,8 @@ obj-y += clk-sunxi.o clk-factors.o obj-y += clk-a10-hosc.o obj-y += clk-a20-gmac.o +obj-y += clk-mod0.o +obj-y += clk-sun8i-mbus.o obj-$(CONFIG_MFD_SUN6I_PRCM) += \ clk-sun6i-ar100.o clk-sun6i-apb0.o clk-sun6i-apb0-gates.o \ diff --git a/drivers/clk/sunxi/clk-factors.c b/drivers/clk/sunxi/clk-factors.c index 2057c8ac648f..f83ba097126c 100644 --- a/drivers/clk/sunxi/clk-factors.c +++ b/drivers/clk/sunxi/clk-factors.c @@ -9,18 +9,18 @@ */ #include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> #include <linux/module.h> +#include <linux/of_address.h> #include <linux/slab.h> -#include <linux/io.h> -#include <linux/err.h> #include <linux/string.h> -#include <linux/delay.h> - #include "clk-factors.h" /* - * DOC: basic adjustable factor-based clock that cannot gate + * DOC: basic adjustable factor-based clock * * Traits of this clock: * prepare - clk_prepare only ensures that parents are prepared @@ -32,6 +32,8 @@ #define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw) +#define FACTORS_MAX_PARENTS 5 + #define SETMASK(len, pos) (((1U << (len)) - 1) << (pos)) #define CLRMASK(len, pos) (~(SETMASK(len, pos))) #define FACTOR_GET(bit, len, reg) (((reg) & SETMASK(len, bit)) >> (bit)) @@ -147,9 +149,96 @@ static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate, return 0; } -const struct clk_ops clk_factors_ops = { +static 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, }; + +struct clk * __init sunxi_factors_register(struct device_node *node, + const struct factors_data *data, + spinlock_t *lock) +{ + struct clk *clk; + struct clk_factors *factors; + struct clk_gate *gate = NULL; + struct clk_mux *mux = NULL; + struct clk_hw *gate_hw = NULL; + struct clk_hw *mux_hw = NULL; + const char *clk_name = node->name; + const char *parents[FACTORS_MAX_PARENTS]; + void __iomem *reg; + int i = 0; + + reg = of_iomap(node, 0); + + /* if we have a mux, we will have >1 parents */ + while (i < FACTORS_MAX_PARENTS && + (parents[i] = of_clk_get_parent_name(node, i)) != NULL) + i++; + + /* + * some factor clocks, such as pll5 and pll6, may have multiple + * outputs, and have their name designated in factors_data + */ + if (data->name) + clk_name = data->name; + else + of_property_read_string(node, "clock-output-names", &clk_name); + + factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL); + if (!factors) + return NULL; + + /* set up factors properties */ + factors->reg = reg; + factors->config = data->table; + factors->get_factors = data->getter; + factors->lock = lock; + + /* Add a gate if this factor clock can be gated */ + if (data->enable) { + gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL); + if (!gate) { + kfree(factors); + return NULL; + } + + /* set up gate properties */ + gate->reg = reg; + gate->bit_idx = data->enable; + gate->lock = factors->lock; + gate_hw = &gate->hw; + } + + /* Add a mux if this factor clock can be muxed */ + if (data->mux) { + mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL); + if (!mux) { + kfree(factors); + kfree(gate); + return NULL; + } + + /* set up gate properties */ + mux->reg = reg; + mux->shift = data->mux; + mux->mask = SUNXI_FACTORS_MUX_MASK; + mux->lock = factors->lock; + mux_hw = &mux->hw; + } + + clk = clk_register_composite(NULL, clk_name, + parents, i, + mux_hw, &clk_mux_ops, + &factors->hw, &clk_factors_ops, + gate_hw, &clk_gate_ops, 0); + + if (!IS_ERR(clk)) { + of_clk_add_provider(node, of_clk_src_simple_get, clk); + clk_register_clkdev(clk, clk_name, NULL); + } + + return clk; +} diff --git a/drivers/clk/sunxi/clk-factors.h b/drivers/clk/sunxi/clk-factors.h index d2d0efa39379..9913840018d3 100644 --- a/drivers/clk/sunxi/clk-factors.h +++ b/drivers/clk/sunxi/clk-factors.h @@ -3,9 +3,12 @@ #include <linux/clk-provider.h> #include <linux/clkdev.h> +#include <linux/spinlock.h> #define SUNXI_FACTORS_NOT_APPLICABLE (0) +#define SUNXI_FACTORS_MUX_MASK 0x3 + struct clk_factors_config { u8 nshift; u8 nwidth; @@ -18,6 +21,14 @@ struct clk_factors_config { u8 n_start; }; +struct factors_data { + int enable; + int mux; + struct clk_factors_config *table; + void (*getter) (u32 *rate, u32 parent_rate, u8 *n, u8 *k, u8 *m, u8 *p); + const char *name; +}; + struct clk_factors { struct clk_hw hw; void __iomem *reg; @@ -26,5 +37,8 @@ struct clk_factors { spinlock_t *lock; }; -extern const struct clk_ops clk_factors_ops; +struct clk * __init sunxi_factors_register(struct device_node *node, + const struct factors_data *data, + spinlock_t *lock); + #endif diff --git a/drivers/clk/sunxi/clk-mod0.c b/drivers/clk/sunxi/clk-mod0.c new file mode 100644 index 000000000000..4a563850ee6e --- /dev/null +++ b/drivers/clk/sunxi/clk-mod0.c @@ -0,0 +1,283 @@ +/* + * Copyright 2013 Emilio López + * + * Emilio López <emilio@elopez.com.ar> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk-provider.h> +#include <linux/clkdev.h> +#include <linux/of_address.h> + +#include "clk-factors.h" + +/** + * sun4i_get_mod0_factors() - calculates m, n factors for MOD0-style clocks + * MOD0 rate is calculated as follows + * rate = (parent_rate >> p) / (m + 1); + */ + +static void sun4i_a10_get_mod0_factors(u32 *freq, u32 parent_rate, + u8 *n, u8 *k, u8 *m, u8 *p) +{ + u8 div, calcm, calcp; + + /* These clocks can only divide, so we will never be able to achieve + * frequencies higher than the parent frequency */ + if (*freq > parent_rate) + *freq = parent_rate; + + div = DIV_ROUND_UP(parent_rate, *freq); + + if (div < 16) + calcp = 0; + else if (div / 2 < 16) + calcp = 1; + else if (div / 4 < 16) + calcp = 2; + else + calcp = 3; + + calcm = DIV_ROUND_UP(div, 1 << calcp); + + *freq = (parent_rate >> calcp) / calcm; + + /* we were called to round the frequency, we can now return */ + if (n == NULL) + return; + + *m = calcm - 1; + *p = calcp; +} + +/* user manual says "n" but it's really "p" */ +static struct clk_factors_config sun4i_a10_mod0_config = { + .mshift = 0, + .mwidth = 4, + .pshift = 16, + .pwidth = 2, +}; + +static const struct factors_data sun4i_a10_mod0_data __initconst = { + .enable = 31, + .mux = 24, + .table = &sun4i_a10_mod0_config, + .getter = sun4i_a10_get_mod0_factors, +}; + +static DEFINE_SPINLOCK(sun4i_a10_mod0_lock); + +static void __init sun4i_a10_mod0_setup(struct device_node *node) +{ + sunxi_factors_register(node, &sun4i_a10_mod0_data, &sun4i_a10_mod0_lock); +} +CLK_OF_DECLARE(sun4i_a10_mod0, "allwinner,sun4i-a10-mod0-clk", sun4i_a10_mod0_setup); + +static DEFINE_SPINLOCK(sun5i_a13_mbus_lock); + +static void __init sun5i_a13_mbus_setup(struct device_node *node) +{ + struct clk *mbus = sunxi_factors_register(node, &sun4i_a10_mod0_data, &sun5i_a13_mbus_lock); + + /* The MBUS clocks needs to be always enabled */ + __clk_get(mbus); + clk_prepare_enable(mbus); +} +CLK_OF_DECLARE(sun5i_a13_mbus, "allwinner,sun5i-a13-mbus-clk", sun5i_a13_mbus_setup); + +struct mmc_phase_data { + u8 offset; +}; + +struct mmc_phase { + struct clk_hw hw; + void __iomem *reg; + struct mmc_phase_data *data; + spinlock_t *lock; +}; + +#define to_mmc_phase(_hw) container_of(_hw, struct mmc_phase, hw) + +static int mmc_get_phase(struct clk_hw *hw) +{ + struct clk *mmc, *mmc_parent, *clk = hw->clk; + struct mmc_phase *phase = to_mmc_phase(hw); + unsigned int mmc_rate, mmc_parent_rate; + u16 step, mmc_div; + u32 value; + u8 delay; + + value = readl(phase->reg); + delay = (value >> phase->data->offset) & 0x3; + + if (!delay) + return 180; + + /* Get the main MMC clock */ + mmc = clk_get_parent(clk); + if (!mmc) + return -EINVAL; + + /* And its rate */ + mmc_rate = clk_get_rate(mmc); + if (!mmc_rate) + return -EINVAL; + + /* Now, get the MMC parent (most likely some PLL) */ + mmc_parent = clk_get_parent(mmc); + if (!mmc_parent) + return -EINVAL; + + /* And its rate */ + mmc_parent_rate = clk_get_rate(mmc_parent); + if (!mmc_parent_rate) + return -EINVAL; + + /* Get MMC clock divider */ + mmc_div = mmc_parent_rate / mmc_rate; + + step = DIV_ROUND_CLOSEST(360, mmc_div); + return delay * step; +} + +static int mmc_set_phase(struct clk_hw *hw, int degrees) +{ + struct clk *mmc, *mmc_parent, *clk = hw->clk; + struct mmc_phase *phase = to_mmc_phase(hw); + unsigned int mmc_rate, mmc_parent_rate; + unsigned long flags; + u32 value; + u8 delay; + + /* Get the main MMC clock */ + mmc = clk_get_parent(clk); + if (!mmc) + return -EINVAL; + + /* And its rate */ + mmc_rate = clk_get_rate(mmc); + if (!mmc_rate) + return -EINVAL; + + /* Now, get the MMC parent (most likely some PLL) */ + mmc_parent = clk_get_parent(mmc); + if (!mmc_parent) + return -EINVAL; + + /* And its rate */ + mmc_parent_rate = clk_get_rate(mmc_parent); + if (!mmc_parent_rate) + return -EINVAL; + + if (degrees != 180) { + u16 step, mmc_div; + + /* Get MMC clock divider */ + mmc_div = mmc_parent_rate / mmc_rate; + + /* + * We can only outphase the clocks by multiple of the + * PLL's period. + * + * Since the MMC clock in only a divider, and the + * formula to get the outphasing in degrees is deg = + * 360 * delta / period + * + * If we simplify this formula, we can see that the + * only thing that we're concerned about is the number + * of period we want to outphase our clock from, and + * the divider set by the MMC clock. + */ + step = DIV_ROUND_CLOSEST(360, mmc_div); + delay = DIV_ROUND_CLOSEST(degrees, step); + } else { + delay = 0; + } + + spin_lock_irqsave(phase->lock, flags); + value = readl(phase->reg); + value &= ~GENMASK(phase->data->offset + 3, phase->data->offset); + value |= delay << phase->data->offset; + writel(value, phase->reg); + spin_unlock_irqrestore(phase->lock, flags); + + return 0; +} + +static const struct clk_ops mmc_clk_ops = { + .get_phase = mmc_get_phase, + .set_phase = mmc_set_phase, +}; + +static void __init sun4i_a10_mmc_phase_setup(struct device_node *node, + struct mmc_phase_data *data) +{ + const char *parent_names[1] = { of_clk_get_parent_name(node, 0) }; + struct clk_init_data init = { + .num_parents = 1, + .parent_names = parent_names, + .ops = &mmc_clk_ops, + }; + + struct mmc_phase *phase; + struct clk *clk; + + phase = kmalloc(sizeof(*phase), GFP_KERNEL); + if (!phase) + return; + + phase->hw.init = &init; + + phase->reg = of_iomap(node, 0); + if (!phase->reg) + goto err_free; + + phase->data = data; + phase->lock = &sun4i_a10_mod0_lock; + + if (of_property_read_string(node, "clock-output-names", &init.name)) + init.name = node->name; + + clk = clk_register(NULL, &phase->hw); + if (IS_ERR(clk)) + goto err_unmap; + + of_clk_add_provider(node, of_clk_src_simple_get, clk); + + return; + +err_unmap: + iounmap(phase->reg); +err_free: + kfree(phase); +} + + +static struct mmc_phase_data mmc_output_clk = { + .offset = 8, +}; + +static struct mmc_phase_data mmc_sample_clk = { + .offset = 20, +}; + +static void __init sun4i_a10_mmc_output_setup(struct device_node *node) +{ + sun4i_a10_mmc_phase_setup(node, &mmc_output_clk); +} +CLK_OF_DECLARE(sun4i_a10_mmc_output, "allwinner,sun4i-a10-mmc-output-clk", sun4i_a10_mmc_output_setup); + +static void __init sun4i_a10_mmc_sample_setup(struct device_node *node) +{ + sun4i_a10_mmc_phase_setup(node, &mmc_sample_clk); +} +CLK_OF_DECLARE(sun4i_a10_mmc_sample, "allwinner,sun4i-a10-mmc-sample-clk", sun4i_a10_mmc_sample_setup); diff --git a/drivers/clk/sunxi/clk-sun6i-apb0-gates.c b/drivers/clk/sunxi/clk-sun6i-apb0-gates.c index e10d0521ec76..64f3e46d383c 100644 --- a/drivers/clk/sunxi/clk-sun6i-apb0-gates.c +++ b/drivers/clk/sunxi/clk-sun6i-apb0-gates.c @@ -99,7 +99,6 @@ static int sun6i_a31_apb0_gates_clk_probe(struct platform_device *pdev) static struct platform_driver sun6i_a31_apb0_gates_clk_driver = { .driver = { .name = "sun6i-a31-apb0-gates-clk", - .owner = THIS_MODULE, .of_match_table = sun6i_a31_apb0_gates_clk_dt_ids, }, .probe = sun6i_a31_apb0_gates_clk_probe, diff --git a/drivers/clk/sunxi/clk-sun6i-apb0.c b/drivers/clk/sunxi/clk-sun6i-apb0.c index 1fa23371c8c6..70763600aeae 100644 --- a/drivers/clk/sunxi/clk-sun6i-apb0.c +++ b/drivers/clk/sunxi/clk-sun6i-apb0.c @@ -65,7 +65,6 @@ static const struct of_device_id sun6i_a31_apb0_clk_dt_ids[] = { static struct platform_driver sun6i_a31_apb0_clk_driver = { .driver = { .name = "sun6i-a31-apb0-clk", - .owner = THIS_MODULE, .of_match_table = sun6i_a31_apb0_clk_dt_ids, }, .probe = sun6i_a31_apb0_clk_probe, diff --git a/drivers/clk/sunxi/clk-sun6i-ar100.c b/drivers/clk/sunxi/clk-sun6i-ar100.c index eca8ca025b6a..acca53290be2 100644 --- a/drivers/clk/sunxi/clk-sun6i-ar100.c +++ b/drivers/clk/sunxi/clk-sun6i-ar100.c @@ -221,7 +221,6 @@ static const struct of_device_id sun6i_a31_ar100_clk_dt_ids[] = { static struct platform_driver sun6i_a31_ar100_clk_driver = { .driver = { .name = "sun6i-a31-ar100-clk", - .owner = THIS_MODULE, .of_match_table = sun6i_a31_ar100_clk_dt_ids, }, .probe = sun6i_a31_ar100_clk_probe, diff --git a/drivers/clk/sunxi/clk-sun8i-apb0.c b/drivers/clk/sunxi/clk-sun8i-apb0.c index 1f5ba9b4b8cd..155d0022194f 100644 --- a/drivers/clk/sunxi/clk-sun8i-apb0.c +++ b/drivers/clk/sunxi/clk-sun8i-apb0.c @@ -56,7 +56,6 @@ static const struct of_device_id sun8i_a23_apb0_clk_dt_ids[] = { static struct platform_driver sun8i_a23_apb0_clk_driver = { .driver = { .name = "sun8i-a23-apb0-clk", - .owner = THIS_MODULE, .of_match_table = sun8i_a23_apb0_clk_dt_ids, }, .probe = sun8i_a23_apb0_clk_probe, diff --git a/drivers/clk/sunxi/clk-sun8i-mbus.c b/drivers/clk/sunxi/clk-sun8i-mbus.c new file mode 100644 index 000000000000..8e49b44cee41 --- /dev/null +++ b/drivers/clk/sunxi/clk-sun8i-mbus.c @@ -0,0 +1,78 @@ +/* + * Copyright 2014 Chen-Yu Tsai + * + * Chen-Yu Tsai <wens@csie.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk-provider.h> +#include <linux/clkdev.h> +#include <linux/of_address.h> + +#include "clk-factors.h" + +/** + * sun8i_a23_get_mbus_factors() - calculates m factor for MBUS clocks + * MBUS rate is calculated as follows + * rate = parent_rate / (m + 1); + */ + +static void sun8i_a23_get_mbus_factors(u32 *freq, u32 parent_rate, + u8 *n, u8 *k, u8 *m, u8 *p) +{ + u8 div; + + /* + * These clocks can only divide, so we will never be able to + * achieve frequencies higher than the parent frequency + */ + if (*freq > parent_rate) + *freq = parent_rate; + + div = DIV_ROUND_UP(parent_rate, *freq); + + if (div > 8) + div = 8; + + *freq = parent_rate / div; + + /* we were called to round the frequency, we can now return */ + if (m == NULL) + return; + + *m = div - 1; +} + +static struct clk_factors_config sun8i_a23_mbus_config = { + .mshift = 0, + .mwidth = 3, +}; + +static const struct factors_data sun8i_a23_mbus_data __initconst = { + .enable = 31, + .mux = 24, + .table = &sun8i_a23_mbus_config, + .getter = sun8i_a23_get_mbus_factors, +}; + +static DEFINE_SPINLOCK(sun8i_a23_mbus_lock); + +static void __init sun8i_a23_mbus_setup(struct device_node *node) +{ + struct clk *mbus = sunxi_factors_register(node, &sun8i_a23_mbus_data, + &sun8i_a23_mbus_lock); + + /* The MBUS clocks needs to be always enabled */ + __clk_get(mbus); + clk_prepare_enable(mbus); +} +CLK_OF_DECLARE(sun8i_a23_mbus, "allwinner,sun8i-a23-mbus-clk", sun8i_a23_mbus_setup); diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index b654b7b1d137..d5dc951264ca 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -19,6 +19,7 @@ #include <linux/of.h> #include <linux/of_address.h> #include <linux/reset-controller.h> +#include <linux/spinlock.h> #include "clk-factors.h" @@ -319,46 +320,6 @@ static void sun4i_get_apb1_factors(u32 *freq, u32 parent_rate, -/** - * sun4i_get_mod0_factors() - calculates m, n factors for MOD0-style clocks - * MOD0 rate is calculated as follows - * rate = (parent_rate >> p) / (m + 1); - */ - -static void sun4i_get_mod0_factors(u32 *freq, u32 parent_rate, - u8 *n, u8 *k, u8 *m, u8 *p) -{ - u8 div, calcm, calcp; - - /* These clocks can only divide, so we will never be able to achieve - * frequencies higher than the parent frequency */ - if (*freq > parent_rate) - *freq = parent_rate; - - div = DIV_ROUND_UP(parent_rate, *freq); - - if (div < 16) - calcp = 0; - else if (div / 2 < 16) - calcp = 1; - else if (div / 4 < 16) - calcp = 2; - else - calcp = 3; - - calcm = DIV_ROUND_UP(div, 1 << calcp); - - *freq = (parent_rate >> calcp) / calcm; - - /* we were called to round the frequency, we can now return */ - if (n == NULL) - return; - - *m = calcm - 1; - *p = calcp; -} - - /** * sun7i_a20_get_out_factors() - calculates m, p factors for CLK_OUT_A/B @@ -440,16 +401,6 @@ EXPORT_SYMBOL(clk_sunxi_mmc_phase_control); * sunxi_factors_clk_setup() - Setup function for factor clocks */ -#define SUNXI_FACTORS_MUX_MASK 0x3 - -struct factors_data { - int enable; - int mux; - struct clk_factors_config *table; - void (*getter) (u32 *rate, u32 parent_rate, u8 *n, u8 *k, u8 *m, u8 *p); - const char *name; -}; - static struct clk_factors_config sun4i_pll1_config = { .nshift = 8, .nwidth = 5, @@ -504,14 +455,6 @@ static struct clk_factors_config sun4i_apb1_config = { }; /* user manual says "n" but it's really "p" */ -static struct clk_factors_config sun4i_mod0_config = { - .mshift = 0, - .mwidth = 4, - .pshift = 16, - .pwidth = 2, -}; - -/* user manual says "n" but it's really "p" */ static struct clk_factors_config sun7i_a20_out_config = { .mshift = 8, .mwidth = 5, @@ -568,13 +511,6 @@ static const struct factors_data sun4i_apb1_data __initconst = { .getter = sun4i_get_apb1_factors, }; -static const struct factors_data sun4i_mod0_data __initconst = { - .enable = 31, - .mux = 24, - .table = &sun4i_mod0_config, - .getter = sun4i_get_mod0_factors, -}; - static const struct factors_data sun7i_a20_out_data __initconst = { .enable = 31, .mux = 24, @@ -583,89 +519,9 @@ static const struct factors_data sun7i_a20_out_data __initconst = { }; static struct clk * __init sunxi_factors_clk_setup(struct device_node *node, - const struct factors_data *data) + const struct factors_data *data) { - struct clk *clk; - struct clk_factors *factors; - struct clk_gate *gate = NULL; - struct clk_mux *mux = NULL; - struct clk_hw *gate_hw = NULL; - struct clk_hw *mux_hw = NULL; - const char *clk_name = node->name; - const char *parents[SUNXI_MAX_PARENTS]; - void __iomem *reg; - int i = 0; - - reg = of_iomap(node, 0); - - /* if we have a mux, we will have >1 parents */ - while (i < SUNXI_MAX_PARENTS && - (parents[i] = of_clk_get_parent_name(node, i)) != NULL) - i++; - - /* - * some factor clocks, such as pll5 and pll6, may have multiple - * outputs, and have their name designated in factors_data - */ - if (data->name) - clk_name = data->name; - else - of_property_read_string(node, "clock-output-names", &clk_name); - - factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL); - if (!factors) - return NULL; - - /* Add a gate if this factor clock can be gated */ - if (data->enable) { - gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL); - if (!gate) { - kfree(factors); - return NULL; - } - - /* set up gate properties */ - gate->reg = reg; - gate->bit_idx = data->enable; - gate->lock = &clk_lock; - gate_hw = &gate->hw; - } - - /* Add a mux if this factor clock can be muxed */ - if (data->mux) { - mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL); - if (!mux) { - kfree(factors); - kfree(gate); - return NULL; - } - - /* set up gate properties */ - mux->reg = reg; - mux->shift = data->mux; - mux->mask = SUNXI_FACTORS_MUX_MASK; - mux->lock = &clk_lock; - mux_hw = &mux->hw; - } - - /* set up factors properties */ - factors->reg = reg; - factors->config = data->table; - factors->get_factors = data->getter; - factors->lock = &clk_lock; - - clk = clk_register_composite(NULL, clk_name, - parents, i, - mux_hw, &clk_mux_ops, - &factors->hw, &clk_factors_ops, - gate_hw, &clk_gate_ops, 0); - - if (!IS_ERR(clk)) { - of_clk_add_provider(node, of_clk_src_simple_get, clk); - clk_register_clkdev(clk, clk_name, NULL); - } - - return clk; + return sunxi_factors_register(node, data, &clk_lock); } @@ -762,10 +618,19 @@ static const struct div_data sun4i_ahb_data __initconst = { .width = 2, }; +static const struct clk_div_table sun4i_apb0_table[] __initconst = { + { .val = 0, .div = 2 }, + { .val = 1, .div = 2 }, + { .val = 2, .div = 4 }, + { .val = 3, .div = 8 }, + { } /* sentinel */ +}; + static const struct div_data sun4i_apb0_data __initconst = { .shift = 8, .pow = 1, .width = 2, + .table = sun4i_apb0_table, }; static const struct div_data sun6i_a31_apb2_div_data __initconst = { @@ -1199,7 +1064,6 @@ static const struct of_device_id clk_factors_match[] __initconst = { {.compatible = "allwinner,sun7i-a20-pll4-clk", .data = &sun7i_a20_pll4_data,}, {.compatible = "allwinner,sun6i-a31-pll6-clk", .data = &sun6i_a31_pll6_data,}, {.compatible = "allwinner,sun4i-a10-apb1-clk", .data = &sun4i_apb1_data,}, - {.compatible = "allwinner,sun4i-a10-mod0-clk", .data = &sun4i_mod0_data,}, {.compatible = "allwinner,sun7i-a20-out-clk", .data = &sun7i_a20_out_data,}, {} }; @@ -1311,7 +1175,6 @@ static void __init sun4i_a10_init_clocks(struct device_node *node) CLK_OF_DECLARE(sun4i_a10_clk_init, "allwinner,sun4i-a10", sun4i_a10_init_clocks); static const char *sun5i_critical_clocks[] __initdata = { - "mbus", "pll5_ddr", "ahb_sdram", }; diff --git a/drivers/clk/tegra/clk-tegra124.c b/drivers/clk/tegra/clk-tegra124.c index 9525c684d149..e3a85842ce0c 100644 --- a/drivers/clk/tegra/clk-tegra124.c +++ b/drivers/clk/tegra/clk-tegra124.c @@ -1166,6 +1166,12 @@ static void __init tegra124_pll_init(void __iomem *clk_base, clk_register_clkdev(clk, "pll_c_out1", NULL); clks[TEGRA124_CLK_PLL_C_OUT1] = clk; + /* PLLC_UD */ + clk = clk_register_fixed_factor(NULL, "pll_c_ud", "pll_c", + CLK_SET_RATE_PARENT, 1, 1); + clk_register_clkdev(clk, "pll_c_ud", NULL); + clks[TEGRA124_CLK_PLL_C_UD] = clk; + /* PLLC2 */ clk = tegra_clk_register_pllc("pll_c2", "pll_ref", clk_base, pmc, 0, &pll_c2_params, NULL); @@ -1198,6 +1204,8 @@ static void __init tegra124_pll_init(void __iomem *clk_base, /* PLLM_UD */ clk = clk_register_fixed_factor(NULL, "pll_m_ud", "pll_m", CLK_SET_RATE_PARENT, 1, 1); + clk_register_clkdev(clk, "pll_m_ud", NULL); + clks[TEGRA124_CLK_PLL_M_UD] = clk; /* PLLU */ val = readl(clk_base + pll_u_params.base_reg); diff --git a/drivers/clk/tegra/clk.c b/drivers/clk/tegra/clk.c index f87c609e8f72..97dc8595c3cd 100644 --- a/drivers/clk/tegra/clk.c +++ b/drivers/clk/tegra/clk.c @@ -207,8 +207,13 @@ void __init tegra_init_from_table(struct tegra_clk_init_table *tbl, for (; tbl->clk_id < clk_max; tbl++) { clk = clks[tbl->clk_id]; - if (IS_ERR_OR_NULL(clk)) - return; + if (IS_ERR_OR_NULL(clk)) { + pr_err("%s: invalid entry %ld in clks array for id %d\n", + __func__, PTR_ERR(clk), tbl->clk_id); + WARN_ON(1); + + continue; + } if (tbl->parent_id < clk_max) { struct clk *parent = clks[tbl->parent_id]; diff --git a/drivers/clk/ti/clk-dra7-atl.c b/drivers/clk/ti/clk-dra7-atl.c index af29359677da..59bb4b39d12e 100644 --- a/drivers/clk/ti/clk-dra7-atl.c +++ b/drivers/clk/ti/clk-dra7-atl.c @@ -203,6 +203,7 @@ static void __init of_dra7_atl_clock_setup(struct device_node *node) if (!IS_ERR(clk)) { of_clk_add_provider(node, of_clk_src_simple_get, clk); + kfree(parent_names); return; } cleanup: @@ -228,6 +229,7 @@ static int of_dra7_atl_clk_probe(struct platform_device *pdev) cinfo->iobase = of_iomap(node, 0); cinfo->dev = &pdev->dev; pm_runtime_enable(cinfo->dev); + pm_runtime_irq_safe(cinfo->dev); pm_runtime_get_sync(cinfo->dev); atl_write(cinfo, DRA7_ATL_PCLKMUX_REG(0), DRA7_ATL_PCLKMUX); @@ -301,7 +303,6 @@ MODULE_DEVICE_TABLE(of, of_dra7_atl_clk_match_tbl); static struct platform_driver dra7_atl_clk_driver = { .driver = { .name = "dra7-atl", - .owner = THIS_MODULE, .of_match_table = of_dra7_atl_clk_match_tbl, }, .probe = of_dra7_atl_clk_probe, diff --git a/drivers/clk/ti/clk.c b/drivers/clk/ti/clk.c index b1a6f7144f3f..337abe5909e1 100644 --- a/drivers/clk/ti/clk.c +++ b/drivers/clk/ti/clk.c @@ -25,8 +25,8 @@ #undef pr_fmt #define pr_fmt(fmt) "%s: " fmt, __func__ -static int ti_dt_clk_memmap_index; struct ti_clk_ll_ops *ti_clk_ll_ops; +static struct device_node *clocks_node_ptr[CLK_MAX_MEMMAPS]; /** * ti_dt_clocks_register - register DT alias clocks during boot @@ -108,9 +108,21 @@ void __iomem *ti_clk_get_reg_addr(struct device_node *node, int index) struct clk_omap_reg *reg; u32 val; u32 tmp; + int i; reg = (struct clk_omap_reg *)&tmp; - reg->index = ti_dt_clk_memmap_index; + + for (i = 0; i < CLK_MAX_MEMMAPS; i++) { + if (clocks_node_ptr[i] == node->parent) + break; + } + + if (i == CLK_MAX_MEMMAPS) { + pr_err("clk-provider not found for %s!\n", node->name); + return NULL; + } + + reg->index = i; if (of_property_read_u32_index(node, "reg", index, &val)) { pr_err("%s must have reg[%d]!\n", node->name, index); @@ -127,20 +139,14 @@ void __iomem *ti_clk_get_reg_addr(struct device_node *node, int index) * @parent: master node * @index: internal index for clk_reg_ops * - * Initializes a master clock IP block and its child clock nodes. - * Regmap is provided for accessing the register space for the - * IP block and all the clocks under it. + * Initializes a master clock IP block. This basically sets up the + * mapping from clocks node to the memory map index. All the clocks + * are then initialized through the common of_clk_init call, and the + * clocks will access their memory maps based on the node layout. */ void ti_dt_clk_init_provider(struct device_node *parent, int index) { - const struct of_device_id *match; - struct device_node *np; struct device_node *clocks; - of_clk_init_cb_t clk_init_cb; - struct clk_init_item *retry; - struct clk_init_item *tmp; - - ti_dt_clk_memmap_index = index; /* get clocks for this parent */ clocks = of_get_child_by_name(parent, "clocks"); @@ -149,19 +155,31 @@ void ti_dt_clk_init_provider(struct device_node *parent, int index) return; } - for_each_child_of_node(clocks, np) { - match = of_match_node(&__clk_of_table, np); - if (!match) - continue; - clk_init_cb = (of_clk_init_cb_t)match->data; - pr_debug("%s: initializing: %s\n", __func__, np->name); - clk_init_cb(np); - } + /* add clocks node info */ + clocks_node_ptr[index] = clocks; +} - list_for_each_entry_safe(retry, tmp, &retry_list, link) { - pr_debug("retry-init: %s\n", retry->node->name); - retry->func(retry->hw, retry->node); - list_del(&retry->link); - kfree(retry); +/** + * ti_dt_clk_init_retry_clks - init clocks from the retry list + * + * Initializes any clocks that have failed to initialize before, + * reasons being missing parent node(s) during earlier init. This + * typically happens only for DPLLs which need to have both of their + * parent clocks ready during init. + */ +void ti_dt_clk_init_retry_clks(void) +{ + struct clk_init_item *retry; + struct clk_init_item *tmp; + int retries = 5; + + while (!list_empty(&retry_list) && retries) { + list_for_each_entry_safe(retry, tmp, &retry_list, link) { + pr_debug("retry-init: %s\n", retry->node->name); + retry->func(retry->hw, retry->node); + list_del(&retry->link); + kfree(retry); + } + retries--; } } diff --git a/drivers/clk/ti/clockdomain.c b/drivers/clk/ti/clockdomain.c index f1e0038d76ac..b4c5faccaece 100644 --- a/drivers/clk/ti/clockdomain.c +++ b/drivers/clk/ti/clockdomain.c @@ -36,6 +36,11 @@ static void __init of_ti_clockdomain_setup(struct device_node *node) for (i = 0; i < num_clks; i++) { clk = of_clk_get(node, i); + if (IS_ERR(clk)) { + pr_err("%s: Failed get %s' clock nr %d (%ld)\n", + __func__, node->full_name, i, PTR_ERR(clk)); + continue; + } if (__clk_get_flags(clk) & CLK_IS_BASIC) { pr_warn("can't setup clkdm for basic clk %s\n", __clk_get_name(clk)); diff --git a/drivers/clk/ti/divider.c b/drivers/clk/ti/divider.c index a837f703be65..bff2b5b8ff59 100644 --- a/drivers/clk/ti/divider.c +++ b/drivers/clk/ti/divider.c @@ -300,8 +300,8 @@ static struct clk *_register_divider(struct device *dev, const char *name, return clk; } -static struct clk_div_table -__init *ti_clk_get_div_table(struct device_node *node) +static struct clk_div_table * +__init ti_clk_get_div_table(struct device_node *node) { struct clk_div_table *table; const __be32 *divspec; diff --git a/drivers/clk/zynq/clkc.c b/drivers/clk/zynq/clkc.c index 246cf1226eaa..9037bebd69f7 100644 --- a/drivers/clk/zynq/clkc.c +++ b/drivers/clk/zynq/clkc.c @@ -85,24 +85,22 @@ static DEFINE_SPINLOCK(canmioclk_lock); static DEFINE_SPINLOCK(dbgclk_lock); static DEFINE_SPINLOCK(aperclk_lock); -static const char dummy_nm[] __initconst = "dummy_name"; - -static const char *armpll_parents[] __initdata = {"armpll_int", "ps_clk"}; -static const char *ddrpll_parents[] __initdata = {"ddrpll_int", "ps_clk"}; -static const char *iopll_parents[] __initdata = {"iopll_int", "ps_clk"}; -static const char *gem0_mux_parents[] __initdata = {"gem0_div1", dummy_nm}; -static const char *gem1_mux_parents[] __initdata = {"gem1_div1", dummy_nm}; -static const char *can0_mio_mux2_parents[] __initdata = {"can0_gate", +static const char *armpll_parents[] __initconst = {"armpll_int", "ps_clk"}; +static const char *ddrpll_parents[] __initconst = {"ddrpll_int", "ps_clk"}; +static const char *iopll_parents[] __initconst = {"iopll_int", "ps_clk"}; +static const char *gem0_mux_parents[] __initconst = {"gem0_div1", "dummy_name"}; +static const char *gem1_mux_parents[] __initconst = {"gem1_div1", "dummy_name"}; +static const char *can0_mio_mux2_parents[] __initconst = {"can0_gate", "can0_mio_mux"}; -static const char *can1_mio_mux2_parents[] __initdata = {"can1_gate", +static const char *can1_mio_mux2_parents[] __initconst = {"can1_gate", "can1_mio_mux"}; -static const char *dbg_emio_mux_parents[] __initdata = {"dbg_div", - dummy_nm}; +static const char *dbg_emio_mux_parents[] __initconst = {"dbg_div", + "dummy_name"}; -static const char *dbgtrc_emio_input_names[] __initdata = {"trace_emio_clk"}; -static const char *gem0_emio_input_names[] __initdata = {"gem0_emio_clk"}; -static const char *gem1_emio_input_names[] __initdata = {"gem1_emio_clk"}; -static const char *swdt_ext_clk_input_names[] __initdata = {"swdt_ext_clk"}; +static const char *dbgtrc_emio_input_names[] __initconst = {"trace_emio_clk"}; +static const char *gem0_emio_input_names[] __initconst = {"gem0_emio_clk"}; +static const char *gem1_emio_input_names[] __initconst = {"gem1_emio_clk"}; +static const char *swdt_ext_clk_input_names[] __initconst = {"swdt_ext_clk"}; static void __init zynq_clk_register_fclk(enum zynq_clk fclk, const char *clk_name, void __iomem *fclk_ctrl_reg, @@ -230,6 +228,7 @@ static void __init zynq_clk_setup(struct device_node *np) const char *periph_parents[4]; const char *swdt_ext_clk_mux_parents[2]; const char *can_mio_mux_parents[NUM_MIO_PINS]; + const char *dummy_nm = "dummy_name"; pr_info("Zynq clock init\n"); @@ -619,5 +618,4 @@ void __init zynq_clock_init(void) np_err: of_node_put(np); BUG(); - return; } diff --git a/drivers/clk/zynq/pll.c b/drivers/clk/zynq/pll.c index cec97596fe65..00d72fb5c036 100644 --- a/drivers/clk/zynq/pll.c +++ b/drivers/clk/zynq/pll.c @@ -211,10 +211,8 @@ struct clk *clk_register_zynq_pll(const char *name, const char *parent, }; pll = kmalloc(sizeof(*pll), GFP_KERNEL); - if (!pll) { - pr_err("%s: Could not allocate Zynq PLL clk.\n", __func__); + if (!pll) return ERR_PTR(-ENOMEM); - } /* Populate the struct */ pll->hw.init = &initd; diff --git a/drivers/cpufreq/kirkwood-cpufreq.c b/drivers/cpufreq/kirkwood-cpufreq.c index 37a480680cd0..7906d4acfe40 100644 --- a/drivers/cpufreq/kirkwood-cpufreq.c +++ b/drivers/cpufreq/kirkwood-cpufreq.c @@ -12,7 +12,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/clk.h> -#include <linux/clk-provider.h> #include <linux/cpufreq.h> #include <linux/of_device.h> #include <linux/platform_device.h> @@ -39,8 +38,7 @@ static struct priv * - cpu clk * - ddr clk * - * The frequencies are set at runtime before registering this * - * table. + * The frequencies are set at runtime before registering this table. */ static struct cpufreq_frequency_table kirkwood_freq_table[] = { {0, STATE_CPU_FREQ, 0}, /* CPU uses cpuclk */ @@ -50,9 +48,7 @@ static struct cpufreq_frequency_table kirkwood_freq_table[] = { static unsigned int kirkwood_cpufreq_get_cpu_frequency(unsigned int cpu) { - if (__clk_is_enabled(priv.powersave_clk)) - return kirkwood_freq_table[1].frequency; - return kirkwood_freq_table[0].frequency; + return clk_get_rate(priv.powersave_clk) / 1000; } static int kirkwood_cpufreq_target(struct cpufreq_policy *policy, @@ -70,10 +66,10 @@ static int kirkwood_cpufreq_target(struct cpufreq_policy *policy, switch (state) { case STATE_CPU_FREQ: - clk_disable(priv.powersave_clk); + clk_set_parent(priv.powersave_clk, priv.cpu_clk); break; case STATE_DDR_FREQ: - clk_enable(priv.powersave_clk); + clk_set_parent(priv.powersave_clk, priv.ddr_clk); break; } @@ -150,7 +146,7 @@ static int kirkwood_cpufreq_probe(struct platform_device *pdev) err = PTR_ERR(priv.powersave_clk); goto out_ddr; } - clk_prepare(priv.powersave_clk); + clk_prepare_enable(priv.powersave_clk); of_node_put(np); np = NULL; diff --git a/include/asm-generic/clkdev.h b/include/asm-generic/clkdev.h index 90a32a61dd21..4ff334749ed5 100644 --- a/include/asm-generic/clkdev.h +++ b/include/asm-generic/clkdev.h @@ -15,10 +15,12 @@ #include <linux/slab.h> +#ifndef CONFIG_COMMON_CLK struct clk; static inline int __clk_get(struct clk *clk) { return 1; } static inline void __clk_put(struct clk *clk) { } +#endif static inline struct clk_lookup_alloc *__clkdev_alloc(size_t size) { diff --git a/include/dt-bindings/clock/exynos3250.h b/include/dt-bindings/clock/exynos3250.h index b535e9da7de6..961b9c130ea9 100644 --- a/include/dt-bindings/clock/exynos3250.h +++ b/include/dt-bindings/clock/exynos3250.h @@ -255,4 +255,31 @@ */ #define CLK_NR_CLKS 248 +/* + * CMU DMC + */ + +#define CLK_FOUT_BPLL 1 +#define CLK_FOUT_EPLL 2 + +/* Muxes */ +#define CLK_MOUT_MPLL_MIF 8 +#define CLK_MOUT_BPLL 9 +#define CLK_MOUT_DPHY 10 +#define CLK_MOUT_DMC_BUS 11 +#define CLK_MOUT_EPLL 12 + +/* Dividers */ +#define CLK_DIV_DMC 16 +#define CLK_DIV_DPHY 17 +#define CLK_DIV_DMC_PRE 18 +#define CLK_DIV_DMCP 19 +#define CLK_DIV_DMCD 20 + +/* + * Total number of clocks of main CMU. + * NOTE: Must be equal to last clock ID increased by one. + */ +#define NR_CLKS_DMC 21 + #endif /* _DT_BINDINGS_CLOCK_SAMSUNG_EXYNOS3250_CLOCK_H */ diff --git a/include/dt-bindings/clock/exynos4.h b/include/dt-bindings/clock/exynos4.h index 459bd2bd411f..34fe28c622d0 100644 --- a/include/dt-bindings/clock/exynos4.h +++ b/include/dt-bindings/clock/exynos4.h @@ -115,11 +115,11 @@ #define CLK_SMMU_MFCR 275 #define CLK_G3D 276 #define CLK_G2D 277 -#define CLK_ROTATOR 278 /* Exynos4210 only */ -#define CLK_MDMA 279 /* Exynos4210 only */ -#define CLK_SMMU_G2D 280 /* Exynos4210 only */ -#define CLK_SMMU_ROTATOR 281 /* Exynos4210 only */ -#define CLK_SMMU_MDMA 282 /* Exynos4210 only */ +#define CLK_ROTATOR 278 +#define CLK_MDMA 279 +#define CLK_SMMU_G2D 280 +#define CLK_SMMU_ROTATOR 281 +#define CLK_SMMU_MDMA 282 #define CLK_FIMD0 283 #define CLK_MIE0 284 #define CLK_MDNIE0 285 /* Exynos4412 only */ @@ -234,6 +234,8 @@ #define CLK_MOUT_G3D1 393 #define CLK_MOUT_G3D 394 #define CLK_ACLK400_MCUISP 395 /* Exynos4x12 only */ +#define CLK_MOUT_HDMI 396 +#define CLK_MOUT_MIXER 397 /* gate clocks - ppmu */ #define CLK_PPMULEFT 400 diff --git a/include/dt-bindings/clock/hix5hd2-clock.h b/include/dt-bindings/clock/hix5hd2-clock.h index aad579a75802..fd29c174ba63 100644 --- a/include/dt-bindings/clock/hix5hd2-clock.h +++ b/include/dt-bindings/clock/hix5hd2-clock.h @@ -46,6 +46,7 @@ #define HIX5HD2_SFC_MUX 64 #define HIX5HD2_MMC_MUX 65 #define HIX5HD2_FEPHY_MUX 66 +#define HIX5HD2_SD_MUX 67 /* gate clocks */ #define HIX5HD2_SFC_RST 128 @@ -53,6 +54,32 @@ #define HIX5HD2_MMC_CIU_CLK 130 #define HIX5HD2_MMC_BIU_CLK 131 #define HIX5HD2_MMC_CIU_RST 132 +#define HIX5HD2_FWD_BUS_CLK 133 +#define HIX5HD2_FWD_SYS_CLK 134 +#define HIX5HD2_MAC0_PHY_CLK 135 +#define HIX5HD2_SD_CIU_CLK 136 +#define HIX5HD2_SD_BIU_CLK 137 +#define HIX5HD2_SD_CIU_RST 138 +#define HIX5HD2_WDG0_CLK 139 +#define HIX5HD2_WDG0_RST 140 +#define HIX5HD2_I2C0_CLK 141 +#define HIX5HD2_I2C0_RST 142 +#define HIX5HD2_I2C1_CLK 143 +#define HIX5HD2_I2C1_RST 144 +#define HIX5HD2_I2C2_CLK 145 +#define HIX5HD2_I2C2_RST 146 +#define HIX5HD2_I2C3_CLK 147 +#define HIX5HD2_I2C3_RST 148 +#define HIX5HD2_I2C4_CLK 149 +#define HIX5HD2_I2C4_RST 150 +#define HIX5HD2_I2C5_CLK 151 +#define HIX5HD2_I2C5_RST 152 + +/* complex */ +#define HIX5HD2_MAC0_CLK 192 +#define HIX5HD2_MAC1_CLK 193 +#define HIX5HD2_SATA_CLK 194 +#define HIX5HD2_USB_CLK 195 #define HIX5HD2_NR_CLKS 256 #endif /* __DTS_HIX5HD2_CLOCK_H */ diff --git a/include/dt-bindings/clock/maxim,max77686.h b/include/dt-bindings/clock/maxim,max77686.h new file mode 100644 index 000000000000..7b28b0905869 --- /dev/null +++ b/include/dt-bindings/clock/maxim,max77686.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2014 Google, Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Device Tree binding constants clocks for the Maxim 77686 PMIC. + */ + +#ifndef _DT_BINDINGS_CLOCK_MAXIM_MAX77686_CLOCK_H +#define _DT_BINDINGS_CLOCK_MAXIM_MAX77686_CLOCK_H + +/* Fixed rate clocks. */ + +#define MAX77686_CLK_AP 0 +#define MAX77686_CLK_CP 1 +#define MAX77686_CLK_PMIC 2 + +/* Total number of clocks. */ +#define MAX77686_CLKS_NUM (MAX77686_CLK_PMIC + 1) + +#endif /* _DT_BINDINGS_CLOCK_MAXIM_MAX77686_CLOCK_H */ diff --git a/include/dt-bindings/clock/maxim,max77802.h b/include/dt-bindings/clock/maxim,max77802.h new file mode 100644 index 000000000000..997312edcbb5 --- /dev/null +++ b/include/dt-bindings/clock/maxim,max77802.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2014 Google, Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Device Tree binding constants clocks for the Maxim 77802 PMIC. + */ + +#ifndef _DT_BINDINGS_CLOCK_MAXIM_MAX77802_CLOCK_H +#define _DT_BINDINGS_CLOCK_MAXIM_MAX77802_CLOCK_H + +/* Fixed rate clocks. */ + +#define MAX77802_CLK_32K_AP 0 +#define MAX77802_CLK_32K_CP 1 + +/* Total number of clocks. */ +#define MAX77802_CLKS_NUM (MAX77802_CLK_32K_CP + 1) + +#endif /* _DT_BINDINGS_CLOCK_MAXIM_MAX77802_CLOCK_H */ diff --git a/include/dt-bindings/clock/pxa-clock.h b/include/dt-bindings/clock/pxa-clock.h new file mode 100644 index 000000000000..e65803b1dc7e --- /dev/null +++ b/include/dt-bindings/clock/pxa-clock.h @@ -0,0 +1,77 @@ +/* + * Inspired by original work from pxa2xx-regs.h by Nicolas Pitre + * Copyright (C) 2014 Robert Jarzmik + * + * 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. + */ + +#ifndef __DT_BINDINGS_CLOCK_PXA2XX_H__ +#define __DT_BINDINGS_CLOCK_PXA2XX_H__ + +#define CLK_NONE 0 +#define CLK_1WIRE 1 +#define CLK_AC97 2 +#define CLK_AC97CONF 3 +#define CLK_ASSP 4 +#define CLK_BOOT 5 +#define CLK_BTUART 6 +#define CLK_CAMERA 7 +#define CLK_CIR 8 +#define CLK_CORE 9 +#define CLK_DMC 10 +#define CLK_FFUART 11 +#define CLK_FICP 12 +#define CLK_GPIO 13 +#define CLK_HSIO2 14 +#define CLK_HWUART 15 +#define CLK_I2C 16 +#define CLK_I2S 17 +#define CLK_IM 18 +#define CLK_INC 19 +#define CLK_ISC 20 +#define CLK_KEYPAD 21 +#define CLK_LCD 22 +#define CLK_MEMC 23 +#define CLK_MEMSTK 24 +#define CLK_MINI_IM 25 +#define CLK_MINI_LCD 26 +#define CLK_MMC 27 +#define CLK_MMC1 28 +#define CLK_MMC2 29 +#define CLK_MMC3 30 +#define CLK_MSL 31 +#define CLK_MSL0 32 +#define CLK_MVED 33 +#define CLK_NAND 34 +#define CLK_NSSP 35 +#define CLK_OSTIMER 36 +#define CLK_PWM0 37 +#define CLK_PWM1 38 +#define CLK_PWM2 39 +#define CLK_PWM3 40 +#define CLK_PWRI2C 41 +#define CLK_PXA300_GCU 42 +#define CLK_PXA320_GCU 43 +#define CLK_SMC 44 +#define CLK_SSP 45 +#define CLK_SSP1 46 +#define CLK_SSP2 47 +#define CLK_SSP3 48 +#define CLK_SSP4 49 +#define CLK_STUART 50 +#define CLK_TOUCH 51 +#define CLK_TPM 52 +#define CLK_UDC 53 +#define CLK_USB 54 +#define CLK_USB2 55 +#define CLK_USBH 56 +#define CLK_USBHOST 57 +#define CLK_USIM 58 +#define CLK_USIM1 59 +#define CLK_USMI0 60 +#define CLK_MAX 61 + +#endif diff --git a/include/dt-bindings/clock/rk3188-cru-common.h b/include/dt-bindings/clock/rk3188-cru-common.h index 750ee60e75fb..6a370503c954 100644 --- a/include/dt-bindings/clock/rk3188-cru-common.h +++ b/include/dt-bindings/clock/rk3188-cru-common.h @@ -20,6 +20,7 @@ #define PLL_GPLL 4 #define CORE_PERI 5 #define CORE_L2C 6 +#define ARMCLK 7 /* sclk gates (special clocks) */ #define SCLK_UART0 64 diff --git a/include/dt-bindings/clock/rk3288-cru.h b/include/dt-bindings/clock/rk3288-cru.h index ebcb460ea4ad..100a08c47692 100644 --- a/include/dt-bindings/clock/rk3288-cru.h +++ b/include/dt-bindings/clock/rk3288-cru.h @@ -19,6 +19,7 @@ #define PLL_CPLL 3 #define PLL_GPLL 4 #define PLL_NPLL 5 +#define ARMCLK 6 /* sclk gates (special clocks) */ #define SCLK_GPU 64 @@ -61,6 +62,15 @@ #define SCLK_LCDC_PWM1 101 #define SCLK_MAC_RX 102 #define SCLK_MAC_TX 103 +#define SCLK_EDP_24M 104 +#define SCLK_EDP 105 +#define SCLK_RGA 106 +#define SCLK_ISP 107 +#define SCLK_ISP_JPE 108 +#define SCLK_HDMI_HDCP 109 +#define SCLK_HDMI_CEC 110 +#define SCLK_HEVC_CABAC 111 +#define SCLK_HEVC_CORE 112 #define DCLK_VOP0 190 #define DCLK_VOP1 191 @@ -75,6 +85,16 @@ #define ACLK_VOP1 198 #define ACLK_CRYPTO 199 #define ACLK_RGA 200 +#define ACLK_RGA_NIU 201 +#define ACLK_IEP 202 +#define ACLK_VIO0_NIU 203 +#define ACLK_VIP 204 +#define ACLK_ISP 205 +#define ACLK_VIO1_NIU 206 +#define ACLK_HEVC 207 +#define ACLK_VCODEC 208 +#define ACLK_CPU 209 +#define ACLK_PERI 210 /* pclk gates */ #define PCLK_GPIO0 320 @@ -112,6 +132,15 @@ #define PCLK_PS2C 352 #define PCLK_TIMER 353 #define PCLK_TZPC 354 +#define PCLK_EDP_CTRL 355 +#define PCLK_MIPI_DSI0 356 +#define PCLK_MIPI_DSI1 357 +#define PCLK_MIPI_CSI 358 +#define PCLK_LVDS_PHY 359 +#define PCLK_HDMI_CTRL 360 +#define PCLK_VIO2_H2P 361 +#define PCLK_CPU 362 +#define PCLK_PERI 363 /* hclk gates */ #define HCLK_GPS 448 @@ -137,8 +166,16 @@ #define HCLK_IEP 468 #define HCLK_ISP 469 #define HCLK_RGA 470 +#define HCLK_VIO_AHB_ARBI 471 +#define HCLK_VIO_NIU 472 +#define HCLK_VIP 473 +#define HCLK_VIO2_H2P 474 +#define HCLK_HEVC 475 +#define HCLK_VCODEC 476 +#define HCLK_CPU 477 +#define HCLK_PERI 478 -#define CLK_NR_CLKS (HCLK_RGA + 1) +#define CLK_NR_CLKS (HCLK_PERI + 1) /* soft-reset indices */ #define SRST_CORE0 0 @@ -276,3 +313,46 @@ #define SRST_USBHOST1_CON 140 #define SRST_USB_ADP 141 #define SRST_ACC_EFUSE 142 + +#define SRST_CORESIGHT 144 +#define SRST_PD_CORE_AHB_NOC 145 +#define SRST_PD_CORE_APB_NOC 146 +#define SRST_PD_CORE_MP_AXI 147 +#define SRST_GIC 148 +#define SRST_LCDC_PWM0 149 +#define SRST_LCDC_PWM1 150 +#define SRST_VIO0_H2P_BRG 151 +#define SRST_VIO1_H2P_BRG 152 +#define SRST_RGA_H2P_BRG 153 +#define SRST_HEVC 154 +#define SRST_TSADC 159 + +#define SRST_DDRPHY0 160 +#define SRST_DDRPHY0_APB 161 +#define SRST_DDRCTRL0 162 +#define SRST_DDRCTRL0_APB 163 +#define SRST_DDRPHY0_CTRL 164 +#define SRST_DDRPHY1 165 +#define SRST_DDRPHY1_APB 166 +#define SRST_DDRCTRL1 167 +#define SRST_DDRCTRL1_APB 168 +#define SRST_DDRPHY1_CTRL 169 +#define SRST_DDRMSCH0 170 +#define SRST_DDRMSCH1 171 +#define SRST_CRYPTO 174 +#define SRST_C2C_HOST 175 + +#define SRST_LCDC1_AXI 176 +#define SRST_LCDC1_AHB 177 +#define SRST_LCDC1_DCLK 178 +#define SRST_UART0 179 +#define SRST_UART1 180 +#define SRST_UART2 181 +#define SRST_UART3 182 +#define SRST_UART4 183 +#define SRST_SIMC 186 +#define SRST_PS2C 187 +#define SRST_TSP 188 +#define SRST_TSP_CLKIN0 189 +#define SRST_TSP_CLKIN1 190 +#define SRST_TSP_27M 191 diff --git a/include/dt-bindings/clock/tegra124-car.h b/include/dt-bindings/clock/tegra124-car.h index 8a4c5892890f..6bac637fd635 100644 --- a/include/dt-bindings/clock/tegra124-car.h +++ b/include/dt-bindings/clock/tegra124-car.h @@ -337,6 +337,10 @@ #define TEGRA124_CLK_DSIB_MUX 310 #define TEGRA124_CLK_SOR0_LVDS 311 #define TEGRA124_CLK_XUSB_SS_DIV2 312 -#define TEGRA124_CLK_CLK_MAX 313 + +#define TEGRA124_CLK_PLL_M_UD 313 +#define TEGRA124_CLK_PLL_C_UD 314 + +#define TEGRA124_CLK_CLK_MAX 315 #endif /* _DT_BINDINGS_CLOCK_TEGRA124_CAR_H */ diff --git a/include/linux/clk-private.h b/include/linux/clk-private.h index efbf70b9fd84..0ca5f6046920 100644 --- a/include/linux/clk-private.h +++ b/include/linux/clk-private.h @@ -46,8 +46,10 @@ struct clk { unsigned int enable_count; unsigned int prepare_count; unsigned long accuracy; + int phase; struct hlist_head children; struct hlist_node child_node; + struct hlist_node debug_node; unsigned int notifier_count; #ifdef CONFIG_DEBUG_FS struct dentry *dentry; diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 411dd7eb2653..be21af149f11 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -13,6 +13,7 @@ #include <linux/clk.h> #include <linux/io.h> +#include <linux/of.h> #ifdef CONFIG_COMMON_CLK @@ -129,6 +130,14 @@ struct dentry; * set then clock accuracy will be initialized to parent accuracy * or 0 (perfect clock) if clock has no parent. * + * @get_phase: Queries the hardware to get the current phase of a clock. + * Returned values are 0-359 degrees on success, negative + * error codes on failure. + * + * @set_phase: Shift the phase this clock signal in degrees specified + * by the second argument. Valid values for degrees are + * 0-359. Return 0 on success, otherwise -EERROR. + * * @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 @@ -177,6 +186,8 @@ struct clk_ops { unsigned long parent_rate, u8 index); unsigned long (*recalc_accuracy)(struct clk_hw *hw, unsigned long parent_accuracy); + int (*get_phase)(struct clk_hw *hw); + int (*set_phase)(struct clk_hw *hw, int degrees); void (*init)(struct clk_hw *hw); int (*debug_init)(struct clk_hw *hw, struct dentry *dentry); }; @@ -488,6 +499,28 @@ struct clk *clk_register_composite(struct device *dev, const char *name, struct clk_hw *gate_hw, const struct clk_ops *gate_ops, unsigned long flags); +/*** + * struct clk_gpio_gate - gpio gated clock + * + * @hw: handle between common and hardware-specific interfaces + * @gpiod: gpio descriptor + * + * Clock with a gpio control for enabling and disabling the parent clock. + * Implements .enable, .disable and .is_enabled + */ + +struct clk_gpio { + struct clk_hw hw; + struct gpio_desc *gpiod; +}; + +extern const struct clk_ops clk_gpio_gate_ops; +struct clk *clk_register_gpio_gate(struct device *dev, const char *name, + const char *parent_name, struct gpio_desc *gpio, + unsigned long flags); + +void of_gpio_clk_gate_setup(struct device_node *node); + /** * clk_register - allocate a new clock, register it and return an opaque cookie * @dev: device that is registering this clock diff --git a/include/linux/clk.h b/include/linux/clk.h index afb44bfaf8d1..c7f258a81761 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -106,6 +106,25 @@ int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb); */ long clk_get_accuracy(struct clk *clk); +/** + * clk_set_phase - adjust the phase shift of a clock signal + * @clk: clock signal source + * @degrees: number of degrees the signal is shifted + * + * Shifts the phase of a clock signal by the specified degrees. Returns 0 on + * success, -EERROR otherwise. + */ +int clk_set_phase(struct clk *clk, int degrees); + +/** + * clk_get_phase - return the phase shift of a clock signal + * @clk: clock signal source + * + * Returns the phase shift of a clock node in degrees, otherwise returns + * -EERROR. + */ +int clk_get_phase(struct clk *clk); + #else static inline long clk_get_accuracy(struct clk *clk) @@ -113,6 +132,16 @@ static inline long clk_get_accuracy(struct clk *clk) return -ENOTSUPP; } +static inline long clk_set_phase(struct clk *clk, int phase) +{ + return -ENOTSUPP; +} + +static inline long clk_get_phase(struct clk *clk) +{ + return -ENOTSUPP; +} + #endif /** diff --git a/include/linux/clk/ti.h b/include/linux/clk/ti.h index e8d8a35034a5..f75acbf70e96 100644 --- a/include/linux/clk/ti.h +++ b/include/linux/clk/ti.h @@ -292,6 +292,7 @@ void omap2xxx_clkt_vps_init(void); void __iomem *ti_clk_get_reg_addr(struct device_node *node, int index); void ti_dt_clocks_register(struct ti_dt_clk *oclks); void ti_dt_clk_init_provider(struct device_node *np, int index); +void ti_dt_clk_init_retry_clks(void); void ti_dt_clockdomains_setup(void); int ti_clk_retry_init(struct device_node *node, struct clk_hw *hw, ti_of_clk_init_cb_t func); |