diff --git a/Documentation/clk.txt b/Documentation/clk.txt index 0e4f90aa1c13..f463bdc37f88 100644 --- a/Documentation/clk.txt +++ b/Documentation/clk.txt @@ -230,30 +230,7 @@ clk_register(...) See the basic clock types in drivers/clk/clk-*.c for examples. - Part 5 - static initialization of clock data - -For platforms with many clocks (often numbering into the hundreds) it -may be desirable to statically initialize some clock data. This -presents a problem since the definition of struct clk should be hidden -from everyone except for the clock core in drivers/clk/clk.c. - -To get around this problem struct clk's definition is exposed in -include/linux/clk-private.h along with some macros for more easily -initializing instances of the basic clock types. These clocks must -still be initialized with the common clock framework via a call to -__clk_init. - -clk-private.h must NEVER be included by code which implements struct -clk_ops callbacks, nor must it be included by any logic which pokes -around inside of struct clk at run-time. To do so is a layering -violation. - -To better enforce this policy, always follow this simple rule: any -statically initialized clock data MUST be defined in a separate file -from the logic that implements its ops. Basically separate the logic -from the data and all is well. - - Part 6 - Disabling clock gating of unused clocks + Part 5 - Disabling clock gating of unused clocks Sometimes during development it can be useful to be able to bypass the default disabling of unused clocks. For example, if drivers aren't enabling @@ -264,7 +241,7 @@ are sorted out. To bypass this disabling, include "clk_ignore_unused" in the bootargs to the kernel. - Part 7 - Locking + Part 6 - Locking The common clock framework uses two global locks, the prepare lock and the enable lock. diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt new file mode 100644 index 000000000000..936166fbee09 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt @@ -0,0 +1,23 @@ +Mediatek apmixedsys controller +============================== + +The Mediatek apmixedsys controller provides the PLLs to the system. + +Required Properties: + +- compatible: Should be: + - "mediatek,mt8135-apmixedsys" + - "mediatek,mt8173-apmixedsys" +- #clock-cells: Must be 1 + +The apmixedsys controller uses the common clk binding from +Documentation/devicetree/bindings/clock/clock-bindings.txt +The available clocks are defined in dt-bindings/clock/mt*-clk.h. + +Example: + +apmixedsys: clock-controller@10209000 { + compatible = "mediatek,mt8173-apmixedsys"; + reg = <0 0x10209000 0 0x1000>; + #clock-cells = <1>; +}; diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt new file mode 100644 index 000000000000..f6cd3e4192ff --- /dev/null +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt @@ -0,0 +1,30 @@ +Mediatek infracfg controller +============================ + +The Mediatek infracfg controller provides various clocks and reset +outputs to the system. + +Required Properties: + +- compatible: Should be: + - "mediatek,mt8135-infracfg", "syscon" + - "mediatek,mt8173-infracfg", "syscon" +- #clock-cells: Must be 1 +- #reset-cells: Must be 1 + +The infracfg controller uses the common clk binding from +Documentation/devicetree/bindings/clock/clock-bindings.txt +The available clocks are defined in dt-bindings/clock/mt*-clk.h. +Also it uses the common reset controller binding from +Documentation/devicetree/bindings/reset/reset.txt. +The available reset outputs are defined in +dt-bindings/reset-controller/mt*-resets.h + +Example: + +infracfg: power-controller@10001000 { + compatible = "mediatek,mt8173-infracfg", "syscon"; + reg = <0 0x10001000 0 0x1000>; + #clock-cells = <1>; + #reset-cells = <1>; +}; diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt new file mode 100644 index 000000000000..f25b85499a6f --- /dev/null +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt @@ -0,0 +1,30 @@ +Mediatek pericfg controller +=========================== + +The Mediatek pericfg controller provides various clocks and reset +outputs to the system. + +Required Properties: + +- compatible: Should be: + - "mediatek,mt8135-pericfg", "syscon" + - "mediatek,mt8173-pericfg", "syscon" +- #clock-cells: Must be 1 +- #reset-cells: Must be 1 + +The pericfg controller uses the common clk binding from +Documentation/devicetree/bindings/clock/clock-bindings.txt +The available clocks are defined in dt-bindings/clock/mt*-clk.h. +Also it uses the common reset controller binding from +Documentation/devicetree/bindings/reset/reset.txt. +The available reset outputs are defined in +dt-bindings/reset-controller/mt*-resets.h + +Example: + +pericfg: power-controller@10003000 { + compatible = "mediatek,mt8173-pericfg", "syscon"; + reg = <0 0x10003000 0 0x1000>; + #clock-cells = <1>; + #reset-cells = <1>; +}; diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt new file mode 100644 index 000000000000..f9e917994ced --- /dev/null +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt @@ -0,0 +1,23 @@ +Mediatek topckgen controller +============================ + +The Mediatek topckgen controller provides various clocks to the system. + +Required Properties: + +- compatible: Should be: + - "mediatek,mt8135-topckgen" + - "mediatek,mt8173-topckgen" +- #clock-cells: Must be 1 + +The topckgen controller uses the common clk binding from +Documentation/devicetree/bindings/clock/clock-bindings.txt +The available clocks are defined in dt-bindings/clock/mt*-clk.h. + +Example: + +topckgen: power-controller@10000000 { + compatible = "mediatek,mt8173-topckgen"; + reg = <0 0x10000000 0 0x1000>; + #clock-cells = <1>; +}; diff --git a/Documentation/devicetree/bindings/clock/amlogic,meson8b-clkc.txt b/Documentation/devicetree/bindings/clock/amlogic,meson8b-clkc.txt new file mode 100644 index 000000000000..2b7b3fa588d7 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/amlogic,meson8b-clkc.txt @@ -0,0 +1,40 @@ +* Amlogic Meson8b Clock and Reset Unit + +The Amlogic Meson8b clock controller generates and supplies clock to various +controllers within the SoC. + +Required Properties: + +- compatible: should be "amlogic,meson8b-clkc" +- reg: it must be composed by two tuples: + 0) physical base address of the xtal register and length of memory + mapped region. + 1) physical base address of the clock controller and length of memory + mapped region. + +- #clock-cells: should be 1. + +Each clock is assigned an identifier and client nodes can use this identifier +to specify the clock which they consume. All available clocks are defined as +preprocessor macros in the dt-bindings/clock/meson8b-clkc.h header and can be +used in device tree sources. + +Example: Clock controller node: + + clkc: clock-controller@c1104000 { + #clock-cells = <1>; + compatible = "amlogic,meson8b-clkc"; + reg = <0xc1108000 0x4>, <0xc1104000 0x460>; + }; + + +Example: UART controller node that consumes the clock generated by the clock + controller: + + uart_AO: serial@c81004c0 { + compatible = "amlogic,meson-uart"; + reg = <0xc81004c0 0x14>; + interrupts = <0 90 1>; + clocks = <&clkc CLKID_CLK81>; + status = "disabled"; + }; diff --git a/Documentation/devicetree/bindings/clock/bcm-cygnus-clock.txt b/Documentation/devicetree/bindings/clock/bcm-cygnus-clock.txt deleted file mode 100644 index 00d26edec8bc..000000000000 --- a/Documentation/devicetree/bindings/clock/bcm-cygnus-clock.txt +++ /dev/null @@ -1,34 +0,0 @@ -Broadcom Cygnus Clocks - -This binding uses the common clock binding: -Documentation/devicetree/bindings/clock/clock-bindings.txt - -Currently various "fixed" clocks are declared for peripheral drivers that use -the common clock framework to reference their core clocks. Proper support of -these clocks will be added later - -Device tree example: - - clocks { - #address-cells = <1>; - #size-cells = <1>; - ranges; - - osc: oscillator { - compatible = "fixed-clock"; - #clock-cells = <1>; - clock-frequency = <25000000>; - }; - - apb_clk: apb_clk { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <1000000000>; - }; - - periph_clk: periph_clk { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <500000000>; - }; - }; diff --git a/Documentation/devicetree/bindings/clock/brcm,iproc-clocks.txt b/Documentation/devicetree/bindings/clock/brcm,iproc-clocks.txt new file mode 100644 index 000000000000..da8d9bb5751c --- /dev/null +++ b/Documentation/devicetree/bindings/clock/brcm,iproc-clocks.txt @@ -0,0 +1,132 @@ +Broadcom iProc Family Clocks + +This binding uses the common clock binding: + Documentation/devicetree/bindings/clock/clock-bindings.txt + +The iProc clock controller manages clocks that are common to the iProc family. +An SoC from the iProc family may have several PPLs, e.g., ARMPLL, GENPLL, +LCPLL0, MIPIPLL, and etc., all derived from an onboard crystal. Each PLL +comprises of several leaf clocks + +Required properties for a PLL and its leaf clocks: + +- compatible: + Should have a value of the form "brcm,-". For example, GENPLL on +Cygnus has a compatible string of "brcm,cygnus-genpll" + +- #clock-cells: + Have a value of <1> since there are more than 1 leaf clock of a given PLL + +- reg: + Define the base and range of the I/O address space that contain the iProc +clock control registers required for the PLL + +- clocks: + The input parent clock phandle for the PLL. For most iProc PLLs, this is an +onboard crystal with a fixed rate + +- clock-output-names: + An ordered list of strings defining the names of the clocks + +Example: + + osc: oscillator { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <25000000>; + }; + + genpll: genpll { + #clock-cells = <1>; + compatible = "brcm,cygnus-genpll"; + reg = <0x0301d000 0x2c>, <0x0301c020 0x4>; + clocks = <&osc>; + clock-output-names = "genpll", "axi21", "250mhz", "ihost_sys", + "enet_sw", "audio_125", "can"; + }; + +Required properties for ASIU clocks: + +ASIU clocks are a special case. These clocks are derived directly from the +reference clock of the onboard crystal + +- compatible: + Should have a value of the form "brcm,-asiu-clk". For example, ASIU +clocks for Cygnus have a compatible string of "brcm,cygnus-asiu-clk" + +- #clock-cells: + Have a value of <1> since there are more than 1 ASIU clocks + +- reg: + Define the base and range of the I/O address space that contain the iProc +clock control registers required for ASIU clocks + +- clocks: + The input parent clock phandle for the ASIU clock, i.e., the onboard +crystal + +- clock-output-names: + An ordered list of strings defining the names of the ASIU clocks + +Example: + + osc: oscillator { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <25000000>; + }; + + asiu_clks: asiu_clks { + #clock-cells = <1>; + compatible = "brcm,cygnus-asiu-clk"; + reg = <0x0301d048 0xc>, <0x180aa024 0x4>; + clocks = <&osc>; + clock-output-names = "keypad", "adc/touch", "pwm"; + }; + +Cygnus +------ +PLL and leaf clock compatible strings for Cygnus are: + "brcm,cygnus-armpll" + "brcm,cygnus-genpll" + "brcm,cygnus-lcpll0" + "brcm,cygnus-mipipll" + "brcm,cygnus-asiu-clk" + +The following table defines the set of PLL/clock index and ID for Cygnus. +These clock IDs are defined in: + "include/dt-bindings/clock/bcm-cygnus.h" + + Clock Source (Parent) Index ID + --- ----- ----- --------- + crystal N/A N/A N/A + + armpll crystal N/A N/A + + keypad crystal (ASIU) 0 BCM_CYGNUS_ASIU_KEYPAD_CLK + adc/tsc crystal (ASIU) 1 BCM_CYGNUS_ASIU_ADC_CLK + pwm crystal (ASIU) 2 BCM_CYGNUS_ASIU_PWM_CLK + + genpll crystal 0 BCM_CYGNUS_GENPLL + axi21 genpll 1 BCM_CYGNUS_GENPLL_AXI21_CLK + 250mhz genpll 2 BCM_CYGNUS_GENPLL_250MHZ_CLK + ihost_sys genpll 3 BCM_CYGNUS_GENPLL_IHOST_SYS_CLK + enet_sw genpll 4 BCM_CYGNUS_GENPLL_ENET_SW_CLK + audio_125 genpll 5 BCM_CYGNUS_GENPLL_AUDIO_125_CLK + can genpll 6 BCM_CYGNUS_GENPLL_CAN_CLK + + lcpll0 crystal 0 BCM_CYGNUS_LCPLL0 + pcie_phy lcpll0 1 BCM_CYGNUS_LCPLL0_PCIE_PHY_REF_CLK + ddr_phy lcpll0 2 BCM_CYGNUS_LCPLL0_DDR_PHY_CLK + sdio lcpll0 3 BCM_CYGNUS_LCPLL0_SDIO_CLK + usb_phy lcpll0 4 BCM_CYGNUS_LCPLL0_USB_PHY_REF_CLK + smart_card lcpll0 5 BCM_CYGNUS_LCPLL0_SMART_CARD_CLK + ch5_unused lcpll0 6 BCM_CYGNUS_LCPLL0_CH5_UNUSED + + mipipll crystal 0 BCM_CYGNUS_MIPIPLL + ch0_unused mipipll 1 BCM_CYGNUS_MIPIPLL_CH0_UNUSED + ch1_lcd mipipll 2 BCM_CYGNUS_MIPIPLL_CH1_LCD + ch2_v3d mipipll 3 BCM_CYGNUS_MIPIPLL_CH2_V3D + ch3_unused mipipll 4 BCM_CYGNUS_MIPIPLL_CH3_UNUSED + ch4_unused mipipll 5 BCM_CYGNUS_MIPIPLL_CH4_UNUSED + ch5_unused mipipll 6 BCM_CYGNUS_MIPIPLL_CH5_UNUSED diff --git a/Documentation/devicetree/bindings/clock/clock-bindings.txt b/Documentation/devicetree/bindings/clock/clock-bindings.txt index 06fc6d541c89..2ec489eebe72 100644 --- a/Documentation/devicetree/bindings/clock/clock-bindings.txt +++ b/Documentation/devicetree/bindings/clock/clock-bindings.txt @@ -138,9 +138,10 @@ Some platforms may require initial configuration of default parent clocks and clock frequencies. Such a configuration can be specified in a device tree node through assigned-clocks, assigned-clock-parents and assigned-clock-rates properties. The assigned-clock-parents property should contain a list of parent -clocks in form of phandle and clock specifier pairs, the assigned-clock-parents -property the list of assigned clock frequency values - corresponding to clocks -listed in the assigned-clocks property. +clocks in the form of a phandle and clock specifier pair and the +assigned-clock-rates property should contain a list of frequencies in Hz. Both +these properties should correspond to the clocks listed in the assigned-clocks +property. To skip setting parent or rate of a clock its corresponding entry should be set to 0, or can be omitted if it is not followed by any non-zero entry. diff --git a/Documentation/devicetree/bindings/clock/csr,atlas7-car.txt b/Documentation/devicetree/bindings/clock/csr,atlas7-car.txt new file mode 100644 index 000000000000..54d6d1358339 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/csr,atlas7-car.txt @@ -0,0 +1,55 @@ +* Clock and reset bindings for CSR atlas7 + +Required properties: +- compatible: Should be "sirf,atlas7-car" +- reg: Address and length of the register set +- #clock-cells: Should be <1> +- #reset-cells: Should be <1> + +The clock consumer should specify the desired clock by having the clock +ID in its "clocks" phandle cell. +The ID list atlas7_clks defined in drivers/clk/sirf/clk-atlas7.c + +The reset consumer should specify the desired reset by having the reset +ID in its "reset" phandle cell. +The ID list atlas7_reset_unit defined in drivers/clk/sirf/clk-atlas7.c + +Examples: Clock and reset controller node: + +car: clock-controller@18620000 { + compatible = "sirf,atlas7-car"; + reg = <0x18620000 0x1000>; + #clock-cells = <1>; + #reset-cells = <1>; +}; + +Examples: Consumers using clock or reset: + +timer@10dc0000 { + compatible = "sirf,macro-tick"; + reg = <0x10dc0000 0x1000>; + clocks = <&car 54>; + interrupts = <0 0 0>, + <0 1 0>, + <0 2 0>, + <0 49 0>, + <0 50 0>, + <0 51 0>; +}; + +uart1: uart@18020000 { + cell-index = <1>; + compatible = "sirf,macro-uart"; + reg = <0x18020000 0x1000>; + clocks = <&clks 95>; + interrupts = <0 18 0>; + fifosize = <32>; +}; + +vpp@13110000 { + compatible = "sirf,prima2-vpp"; + reg = <0x13110000 0x10000>; + interrupts = <0 31 0>; + clocks = <&car 85>; + resets = <&car 29>; +}; diff --git a/Documentation/devicetree/bindings/clock/emev2-clock.txt b/Documentation/devicetree/bindings/clock/emev2-clock.txt index 60bbb1a8c69a..268ca615459e 100644 --- a/Documentation/devicetree/bindings/clock/emev2-clock.txt +++ b/Documentation/devicetree/bindings/clock/emev2-clock.txt @@ -52,7 +52,7 @@ usia_u0_sclk: usia_u0_sclk { Example of consumer: -uart@e1020000 { +serial@e1020000 { compatible = "renesas,em-uart"; reg = <0xe1020000 0x38>; interrupts = <0 8 0>; diff --git a/Documentation/devicetree/bindings/clock/keystone-pll.txt b/Documentation/devicetree/bindings/clock/keystone-pll.txt index 225990f79b7c..47570d207215 100644 --- a/Documentation/devicetree/bindings/clock/keystone-pll.txt +++ b/Documentation/devicetree/bindings/clock/keystone-pll.txt @@ -15,8 +15,8 @@ Required properties: - compatible : shall be "ti,keystone,main-pll-clock" or "ti,keystone,pll-clock" - clocks : parent clock phandle - reg - pll control0 and pll multipler registers -- reg-names : control and multiplier. The multiplier is applicable only for - main pll clock +- reg-names : control, multiplier and post-divider. The multiplier and + post-divider registers are applicable only for main pll clock - fixed-postdiv : fixed post divider value. If absent, use clkod register bits for postdiv @@ -25,8 +25,8 @@ Example: #clock-cells = <0>; compatible = "ti,keystone,main-pll-clock"; clocks = <&refclksys>; - reg = <0x02620350 4>, <0x02310110 4>; - reg-names = "control", "multiplier"; + reg = <0x02620350 4>, <0x02310110 4>, <0x02310108 4>; + reg-names = "control", "multiplier", "post-divider"; fixed-postdiv = <2>; }; diff --git a/Documentation/devicetree/bindings/clock/lpc1850-ccu.txt b/Documentation/devicetree/bindings/clock/lpc1850-ccu.txt new file mode 100644 index 000000000000..fa97c12014ac --- /dev/null +++ b/Documentation/devicetree/bindings/clock/lpc1850-ccu.txt @@ -0,0 +1,77 @@ +* NXP LPC1850 Clock Control Unit (CCU) + +Each CGU base clock has several clock branches which can be turned on +or off independently by the Clock Control Units CCU1 or CCU2. The +branch clocks are distributed between CCU1 and CCU2. + + - Above text taken from NXP LPC1850 User Manual. + +This binding uses the common clock binding: + Documentation/devicetree/bindings/clock/clock-bindings.txt + +Required properties: +- compatible: + Should be "nxp,lpc1850-ccu" +- reg: + Shall define the base and range of the address space + containing clock control registers +- #clock-cells: + Shall have value <1>. The permitted clock-specifier values + are the branch clock names defined in table below. +- clocks: + Shall contain a list of phandles for the base clocks routed + from the CGU to the specific CCU. See mapping of base clocks + and CCU in table below. +- clock-names: + Shall contain a list of names for the base clock routed + from the CGU to the specific CCU. Valid CCU clock names: + "base_usb0_clk", "base_periph_clk", "base_usb1_clk", + "base_cpu_clk", "base_spifi_clk", "base_spi_clk", + "base_apb1_clk", "base_apb3_clk", "base_adchs_clk", + "base_sdio_clk", "base_ssp0_clk", "base_ssp1_clk", + "base_uart0_clk", "base_uart1_clk", "base_uart2_clk", + "base_uart3_clk", "base_audio_clk" + +Which branch clocks that are available on the CCU depends on the +specific LPC part. Check the user manual for your specific part. + +A list of CCU clocks can be found in dt-bindings/clock/lpc18xx-ccu.h. + +Example board file: + +soc { + ccu1: clock-controller@40051000 { + compatible = "nxp,lpc1850-ccu"; + reg = <0x40051000 0x1000>; + #clock-cells = <1>; + clocks = <&cgu BASE_APB3_CLK>, <&cgu BASE_APB1_CLK>, + <&cgu BASE_SPIFI_CLK>, <&cgu BASE_CPU_CLK>, + <&cgu BASE_PERIPH_CLK>, <&cgu BASE_USB0_CLK>, + <&cgu BASE_USB1_CLK>, <&cgu BASE_SPI_CLK>; + clock-names = "base_apb3_clk", "base_apb1_clk", + "base_spifi_clk", "base_cpu_clk", + "base_periph_clk", "base_usb0_clk", + "base_usb1_clk", "base_spi_clk"; + }; + + ccu2: clock-controller@40052000 { + compatible = "nxp,lpc1850-ccu"; + reg = <0x40052000 0x1000>; + #clock-cells = <1>; + clocks = <&cgu BASE_AUDIO_CLK>, <&cgu BASE_UART3_CLK>, + <&cgu BASE_UART2_CLK>, <&cgu BASE_UART1_CLK>, + <&cgu BASE_UART0_CLK>, <&cgu BASE_SSP1_CLK>, + <&cgu BASE_SSP0_CLK>, <&cgu BASE_SDIO_CLK>; + clock-names = "base_audio_clk", "base_uart3_clk", + "base_uart2_clk", "base_uart1_clk", + "base_uart0_clk", "base_ssp1_clk", + "base_ssp0_clk", "base_sdio_clk"; + }; + + /* A user of CCU brach clocks */ + uart1: serial@40082000 { + ... + clocks = <&ccu2 CLK_APB0_UART1>, <&ccu1 CLK_CPU_UART1>; + ... + }; +}; diff --git a/Documentation/devicetree/bindings/clock/lpc1850-cgu.txt b/Documentation/devicetree/bindings/clock/lpc1850-cgu.txt new file mode 100644 index 000000000000..2cc32a9a945a --- /dev/null +++ b/Documentation/devicetree/bindings/clock/lpc1850-cgu.txt @@ -0,0 +1,131 @@ +* NXP LPC1850 Clock Generation Unit (CGU) + +The CGU generates multiple independent clocks for the core and the +peripheral blocks of the LPC18xx. Each independent clock is called +a base clock and itself is one of the inputs to the two Clock +Control Units (CCUs) which control the branch clocks to the +individual peripherals. + +The CGU selects the inputs to the clock generators from multiple +clock sources, controls the clock generation, and routes the outputs +of the clock generators through the clock source bus to the output +stages. Each output stage provides an independent clock source and +corresponds to one of the base clocks for the LPC18xx. + + - Above text taken from NXP LPC1850 User Manual. + + +This binding uses the common clock binding: + Documentation/devicetree/bindings/clock/clock-bindings.txt + +Required properties: +- compatible: + Should be "nxp,lpc1850-cgu" +- reg: + Shall define the base and range of the address space + containing clock control registers +- #clock-cells: + Shall have value <1>. The permitted clock-specifier values + are the base clock numbers defined below. +- clocks: + Shall contain a list of phandles for the external input + sources to the CGU. The list shall be in the following + order: xtal, 32khz, enet_rx_clk, enet_tx_clk, gp_clkin. +- clock-indices: + Shall be an ordered list of numbers defining the base clock + number provided by the CGU. +- clock-output-names: + Shall be an ordered list of strings defining the names of + the clocks provided by the CGU. + +Which base clocks that are available on the CGU depends on the +specific LPC part. Base clocks are numbered from 0 to 27. + +Number: Name: Description: + 0 BASE_SAFE_CLK Base safe clock (always on) for WWDT + 1 BASE_USB0_CLK Base clock for USB0 + 2 BASE_PERIPH_CLK Base clock for Cortex-M0SUB subsystem, + SPI, and SGPIO + 3 BASE_USB1_CLK Base clock for USB1 + 4 BASE_CPU_CLK System base clock for ARM Cortex-M core + and APB peripheral blocks #0 and #2 + 5 BASE_SPIFI_CLK Base clock for SPIFI + 6 BASE_SPI_CLK Base clock for SPI + 7 BASE_PHY_RX_CLK Base clock for Ethernet PHY Receive clock + 8 BASE_PHY_TX_CLK Base clock for Ethernet PHY Transmit clock + 9 BASE_APB1_CLK Base clock for APB peripheral block # 1 +10 BASE_APB3_CLK Base clock for APB peripheral block # 3 +11 BASE_LCD_CLK Base clock for LCD +12 BASE_ADCHS_CLK Base clock for ADCHS +13 BASE_SDIO_CLK Base clock for SD/MMC +14 BASE_SSP0_CLK Base clock for SSP0 +15 BASE_SSP1_CLK Base clock for SSP1 +16 BASE_UART0_CLK Base clock for UART0 +17 BASE_UART1_CLK Base clock for UART1 +18 BASE_UART2_CLK Base clock for UART2 +19 BASE_UART3_CLK Base clock for UART3 +20 BASE_OUT_CLK Base clock for CLKOUT pin +24-21 - Reserved +25 BASE_AUDIO_CLK Base clock for audio system (I2S) +26 BASE_CGU_OUT0_CLK Base clock for CGU_OUT0 clock output +27 BASE_CGU_OUT1_CLK Base clock for CGU_OUT1 clock output + +BASE_PERIPH_CLK and BASE_SPI_CLK is only available on LPC43xx. +BASE_ADCHS_CLK is only available on LPC4370. + + +Example board file: + +/ { + clocks { + xtal: xtal { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <12000000>; + }; + + xtal32: xtal32 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <32768>; + }; + + enet_rx_clk: enet_rx_clk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + clock-output-names = "enet_rx_clk"; + }; + + enet_tx_clk: enet_tx_clk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + clock-output-names = "enet_tx_clk"; + }; + + gp_clkin: gp_clkin { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + clock-output-names = "gp_clkin"; + }; + }; + + soc { + cgu: clock-controller@40050000 { + compatible = "nxp,lpc1850-cgu"; + reg = <0x40050000 0x1000>; + #clock-cells = <1>; + clocks = <&xtal>, <&creg_clk 1>, <&enet_rx_clk>, <&enet_tx_clk>, <&gp_clkin>; + }; + + /* A CGU and CCU clock consumer */ + lcdc: lcdc@40008000 { + ... + clocks = <&cgu BASE_LCD_CLK>, <&ccu1 CLK_CPU_LCD>; + clock-names = "clcdclk", "apb_pclk"; + ... + }; + }; +}; diff --git a/Documentation/devicetree/bindings/clock/marvell,pxa1928.txt b/Documentation/devicetree/bindings/clock/marvell,pxa1928.txt new file mode 100644 index 000000000000..809c5a2d8d9d --- /dev/null +++ b/Documentation/devicetree/bindings/clock/marvell,pxa1928.txt @@ -0,0 +1,21 @@ +* Marvell PXA1928 Clock Controllers + +The PXA1928 clock subsystem generates and supplies clock to various +controllers within the PXA1928 SoC. The PXA1928 contains 3 clock controller +blocks called APMU, MPMU, and APBC roughly corresponding to internal buses. + +Required Properties: + +- compatible: should be one of the following. + - "marvell,pxa1928-apmu" - APMU controller compatible + - "marvell,pxa1928-mpmu" - MPMU controller compatible + - "marvell,pxa1928-apbc" - APBC controller compatible +- reg: physical base address of the clock controller and length of memory mapped + region. +- #clock-cells: should be 1. +- #reset-cells: should be 1. + +Each clock is assigned an identifier and client nodes use the clock controller +phandle and this identifier to specify the clock which they consume. + +All these identifiers can be found in . diff --git a/Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt b/Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt index 31c7c0c1ce8f..660e64912cce 100644 --- a/Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt +++ b/Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt @@ -19,6 +19,7 @@ ID Clock Peripheral 9 pex1 PCIe Cntrl 1 15 sata0 SATA Host 0 17 sdio SDHCI Host +23 crypto CESA (crypto engine) 25 tdm Time Division Mplx 28 ddr DDR Cntrl 30 sata1 SATA Host 0 diff --git a/Documentation/devicetree/bindings/clock/nvidia,tegra124-car.txt b/Documentation/devicetree/bindings/clock/nvidia,tegra124-car.txt index c6620bc96703..7f02fb4ca4ad 100644 --- a/Documentation/devicetree/bindings/clock/nvidia,tegra124-car.txt +++ b/Documentation/devicetree/bindings/clock/nvidia,tegra124-car.txt @@ -20,15 +20,38 @@ Required properties : - #reset-cells : Should be 1. In clock consumers, this cell represents the bit number in the CAR's array of CLK_RST_CONTROLLER_RST_DEVICES_* registers. +- nvidia,external-memory-controller : phandle of the EMC driver. + +The node should contain a "emc-timings" subnode for each supported RAM type (see +field RAM_CODE in register PMC_STRAPPING_OPT_A). + +Required properties for "emc-timings" nodes : +- nvidia,ram-code : Should contain the value of RAM_CODE this timing set + is used for. + +Each "emc-timings" node should contain a "timing" subnode for every supported +EMC clock rate. + +Required properties for "timing" nodes : +- clock-frequency : Should contain the memory clock rate to which this timing +relates. +- nvidia,parent-clock-frequency : Should contain the rate at which the current +parent of the EMC clock should be running at this timing. +- clocks : Must contain an entry for each entry in clock-names. + See ../clocks/clock-bindings.txt for details. +- clock-names : Must include the following entries: + - emc-parent : the clock that should be the parent of the EMC clock at this +timing. Example SoC include file: / { - tegra_car: clock { + tegra_car: clock@60006000 { compatible = "nvidia,tegra124-car"; reg = <0x60006000 0x1000>; #clock-cells = <1>; #reset-cells = <1>; + nvidia,external-memory-controller = <&emc>; }; usb@c5004000 { @@ -62,4 +85,23 @@ Example board file: &tegra_car { clocks = <&clk_32k> <&osc>; }; + + clock@60006000 { + emc-timings-3 { + nvidia,ram-code = <3>; + + timing-12750000 { + clock-frequency = <12750000>; + nvidia,parent-clock-frequency = <408000000>; + clocks = <&tegra_car TEGRA124_CLK_PLL_P>; + clock-names = "emc-parent"; + }; + timing-20400000 { + clock-frequency = <20400000>; + nvidia,parent-clock-frequency = <408000000>; + clocks = <&tegra_car TEGRA124_CLK_PLL_P>; + clock-names = "emc-parent"; + }; + }; + }; }; diff --git a/Documentation/devicetree/bindings/clock/renesas,cpg-div6-clocks.txt b/Documentation/devicetree/bindings/clock/renesas,cpg-div6-clocks.txt index 054f65f9319c..5ddb68418655 100644 --- a/Documentation/devicetree/bindings/clock/renesas,cpg-div6-clocks.txt +++ b/Documentation/devicetree/bindings/clock/renesas,cpg-div6-clocks.txt @@ -10,9 +10,11 @@ Required Properties: - "renesas,r8a73a4-div6-clock" for R8A73A4 (R-Mobile APE6) DIV6 clocks - "renesas,r8a7740-div6-clock" for R8A7740 (R-Mobile A1) DIV6 clocks - "renesas,r8a7790-div6-clock" for R8A7790 (R-Car H2) DIV6 clocks - - "renesas,r8a7791-div6-clock" for R8A7791 (R-Car M2) DIV6 clocks + - "renesas,r8a7791-div6-clock" for R8A7791 (R-Car M2-W) DIV6 clocks + - "renesas,r8a7793-div6-clock" for R8A7793 (R-Car M2-N) DIV6 clocks + - "renesas,r8a7794-div6-clock" for R8A7794 (R-Car E2) DIV6 clocks - "renesas,sh73a0-div6-clock" for SH73A0 (SH-Mobile AG5) DIV6 clocks - - "renesas,cpg-div6-clock" for generic DIV6 clocks + and "renesas,cpg-div6-clock" as a fallback. - reg: Base address and length of the memory resource used by the DIV6 clock - clocks: Reference to the parent clock(s); either one, four, or eight clocks must be specified. For clocks with multiple parents, invalid diff --git a/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt b/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt index 0a80fa70ca26..16ed18155160 100644 --- a/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt +++ b/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt @@ -13,12 +13,14 @@ Required Properties: - "renesas,r7s72100-mstp-clocks" for R7S72100 (RZ) MSTP gate clocks - "renesas,r8a73a4-mstp-clocks" for R8A73A4 (R-Mobile APE6) MSTP gate clocks - "renesas,r8a7740-mstp-clocks" for R8A7740 (R-Mobile A1) MSTP gate clocks + - "renesas,r8a7778-mstp-clocks" for R8A7778 (R-Car M1) MSTP gate clocks - "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,r8a7791-mstp-clocks" for R8A7791 (R-Car M2-W) MSTP gate clocks + - "renesas,r8a7793-mstp-clocks" for R8A7793 (R-Car M2-N) 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 + and "renesas,cpg-mstp-clocks" as a fallback. - reg: Base address and length of the I/O mapped registers used by the MSTP clocks. The first register is the clock control register and is mandatory. The second register is the clock status register and is optional when not 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 b02944fba9de..56f111bd3e45 100644 --- a/Documentation/devicetree/bindings/clock/renesas,rcar-gen2-cpg-clocks.txt +++ b/Documentation/devicetree/bindings/clock/renesas,rcar-gen2-cpg-clocks.txt @@ -10,7 +10,7 @@ Required Properties: - "renesas,r8a7791-cpg-clocks" for the r8a7791 CPG - "renesas,r8a7793-cpg-clocks" for the r8a7793 CPG - "renesas,r8a7794-cpg-clocks" for the r8a7794 CPG - - "renesas,rcar-gen2-cpg-clocks" for the generic R-Car Gen2 CPG + and "renesas,rcar-gen2-cpg-clocks" as a fallback. - reg: Base address and length of the memory resource used by the CPG diff --git a/Documentation/devicetree/bindings/clock/renesas,rz-cpg-clocks.txt b/Documentation/devicetree/bindings/clock/renesas,rz-cpg-clocks.txt index 98a257492522..b0f7ddb8cdb1 100644 --- a/Documentation/devicetree/bindings/clock/renesas,rz-cpg-clocks.txt +++ b/Documentation/devicetree/bindings/clock/renesas,rz-cpg-clocks.txt @@ -7,7 +7,7 @@ Required Properties: - compatible: Must be one of - "renesas,r7s72100-cpg-clocks" for the r7s72100 CPG - - "renesas,rz-cpg-clocks" for the generic RZ CPG + and "renesas,rz-cpg-clocks" as a fallback. - reg: Base address and length of the memory resource used by the CPG - clocks: References to possible parent clocks. Order must match clock modes in the datasheet. For the r7s72100, this is extal, usb_x1. diff --git a/Documentation/devicetree/bindings/clock/st,stm32-rcc.txt b/Documentation/devicetree/bindings/clock/st,stm32-rcc.txt new file mode 100644 index 000000000000..fee3205cdff9 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/st,stm32-rcc.txt @@ -0,0 +1,65 @@ +STMicroelectronics STM32 Reset and Clock Controller +=================================================== + +The RCC IP is both a reset and a clock controller. This documentation only +describes the clock part. + +Please also refer to clock-bindings.txt in this directory for common clock +controller binding usage. + +Required properties: +- compatible: Should be "st,stm32f42xx-rcc" +- reg: should be register base and length as documented in the + datasheet +- #clock-cells: 2, device nodes should specify the clock in their "clocks" + property, containing a phandle to the clock device node, an index selecting + between gated clocks and other clocks and an index specifying the clock to + use. + +Example: + + rcc: rcc@40023800 { + #clock-cells = <2> + compatible = "st,stm32f42xx-rcc", "st,stm32-rcc"; + reg = <0x40023800 0x400>; + }; + +Specifying gated clocks +======================= + +The primary index must be set to 0. + +The secondary index is the bit number within the RCC register bank, starting +from the first RCC clock enable register (RCC_AHB1ENR, address offset 0x30). + +It is calculated as: index = register_offset / 4 * 32 + bit_offset. +Where bit_offset is the bit offset within the register (LSB is 0, MSB is 31). + +Example: + + /* Gated clock, AHB1 bit 0 (GPIOA) */ + ... { + clocks = <&rcc 0 0> + }; + + /* Gated clock, AHB2 bit 4 (CRYP) */ + ... { + clocks = <&rcc 0 36> + }; + +Specifying other clocks +======================= + +The primary index must be set to 1. + +The secondary index is bound with the following magic numbers: + + 0 SYSTICK + 1 FCLK + +Example: + + /* Misc clock, FCLK */ + ... { + clocks = <&rcc 1 1> + }; diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt index 4fa11af3d378..8a47b77abfca 100644 --- a/Documentation/devicetree/bindings/clock/sunxi.txt +++ b/Documentation/devicetree/bindings/clock/sunxi.txt @@ -67,6 +67,7 @@ Required properties: "allwinner,sun4i-a10-usb-clk" - for usb gates + resets on A10 / A20 "allwinner,sun5i-a13-usb-clk" - for usb gates + resets on A13 "allwinner,sun6i-a31-usb-clk" - for usb gates + resets on A31 + "allwinner,sun8i-a23-usb-clk" - for usb gates + resets on A23 "allwinner,sun9i-a80-usb-mod-clk" - for usb gates + resets on A80 "allwinner,sun9i-a80-usb-phy-clk" - for usb phy gates + resets on A80 diff --git a/Documentation/devicetree/bindings/clock/ti,cdce925.txt b/Documentation/devicetree/bindings/clock/ti,cdce925.txt new file mode 100644 index 000000000000..4c7669ad681b --- /dev/null +++ b/Documentation/devicetree/bindings/clock/ti,cdce925.txt @@ -0,0 +1,42 @@ +Binding for TO CDCE925 programmable I2C clock synthesizers. + +Reference +This binding uses the common clock binding[1]. + +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt +[2] http://www.ti.com/product/cdce925 + +The driver provides clock sources for each output Y1 through Y5. + +Required properties: + - compatible: Shall be "ti,cdce925" + - reg: I2C device address. + - clocks: Points to a fixed parent clock that provides the input frequency. + - #clock-cells: From common clock bindings: Shall be 1. + +Optional properties: + - xtal-load-pf: Crystal load-capacitor value to fine-tune performance on a + board, or to compensate for external influences. + +For both PLL1 and PLL2 an optional child node can be used to specify spread +spectrum clocking parameters for a board. + - spread-spectrum: SSC mode as defined in the data sheet. + - spread-spectrum-center: Use "centered" mode instead of "max" mode. When + present, the clock runs at the requested frequency on average. Otherwise + the requested frequency is the maximum value of the SCC range. + + +Example: + + clockgen: cdce925pw@64 { + compatible = "cdce925"; + reg = <0x64>; + clocks = <&xtal_27Mhz>; + #clock-cells = <1>; + xtal-load-pf = <5>; + /* PLL options to get SSC 1% centered */ + PLL2 { + spread-spectrum = <4>; + spread-spectrum-center; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 7628df31b630..993d4cfd5aa0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2763,7 +2763,7 @@ F: Documentation/devicetree/bindings/media/coda.txt F: drivers/media/platform/coda/ COMMON CLK FRAMEWORK -M: Mike Turquette +M: Michael Turquette M: Stephen Boyd L: linux-clk@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux.git diff --git a/arch/arm/boot/dts/atlas7.dtsi b/arch/arm/boot/dts/atlas7.dtsi index a753178abc85..5dfd3a44bf82 100644 --- a/arch/arm/boot/dts/atlas7.dtsi +++ b/arch/arm/boot/dts/atlas7.dtsi @@ -38,6 +38,21 @@ }; }; + clocks { + xinw { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <32768>; + clock-output-names = "xinw"; + }; + xin { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <26000000>; + clock-output-names = "xin"; + }; + }; + noc { compatible = "simple-bus"; #address-cells = <1>; diff --git a/arch/arm/mach-exynos/exynos.c b/arch/arm/mach-exynos/exynos.c index 4bd8b7653817..5f8ddcdeeacf 100644 --- a/arch/arm/mach-exynos/exynos.c +++ b/arch/arm/mach-exynos/exynos.c @@ -224,6 +224,25 @@ static void __init exynos_init_irq(void) exynos_map_pmu(); } +static const struct of_device_id exynos_cpufreq_matches[] = { + { .compatible = "samsung,exynos4210", .data = "cpufreq-dt" }, + { /* sentinel */ } +}; + +static void __init exynos_cpufreq_init(void) +{ + struct device_node *root = of_find_node_by_path("/"); + const struct of_device_id *match; + + match = of_match_node(exynos_cpufreq_matches, root); + if (!match) { + platform_device_register_simple("exynos-cpufreq", -1, NULL, 0); + return; + } + + platform_device_register_simple(match->data, -1, NULL, 0); +} + static void __init exynos_dt_machine_init(void) { /* @@ -246,7 +265,7 @@ static void __init exynos_dt_machine_init(void) of_machine_is_compatible("samsung,exynos5250")) platform_device_register(&exynos_cpuidle); - platform_device_register_simple("exynos-cpufreq", -1, NULL, 0); + exynos_cpufreq_init(); of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); } diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 9897f353bf1a..42f7120ca9ce 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -78,6 +78,23 @@ config COMMON_CLK_SI570 This driver supports Silicon Labs 570/571/598/599 programmable clock generators. +config COMMON_CLK_CDCE925 + tristate "Clock driver for TI CDCE925 devices" + depends on I2C + depends on OF + select REGMAP_I2C + help + ---help--- + This driver supports the TI CDCE925 programmable clock synthesizer. + The chip contains two PLLs with spread-spectrum clocking support and + five output dividers. The driver only supports the following setup, + and uses a fixed setting for the output muxes. + Y1 is derived from the input clock + Y2 and Y3 derive from PLL1 + Y4 and Y5 derive from PLL2 + Given a target output frequency, the driver will set the PLL and + divider to best approximate the desired output. + config COMMON_CLK_S2MPS11 tristate "Clock driver for S2MPS1X/S5M8767 MFD" depends on MFD_SEC_CORE @@ -150,11 +167,13 @@ config COMMON_CLK_CDCE706 ---help--- This driver supports TI CDCE706 programmable 3-PLL clock synthesizer. +source "drivers/clk/bcm/Kconfig" +source "drivers/clk/hisilicon/Kconfig" source "drivers/clk/qcom/Kconfig" endmenu -source "drivers/clk/bcm/Kconfig" source "drivers/clk/mvebu/Kconfig" source "drivers/clk/samsung/Kconfig" +source "drivers/clk/tegra/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 8732e4c5bf3c..c4cf075a2320 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -38,6 +38,8 @@ obj-$(CONFIG_COMMON_CLK_RK808) += clk-rk808.o obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o obj-$(CONFIG_COMMON_CLK_SI570) += clk-si570.o +obj-$(CONFIG_COMMON_CLK_CDCE925) += clk-cdce925.o +obj-$(CONFIG_ARCH_STM32) += clk-stm32f4.o obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o obj-$(CONFIG_ARCH_U300) += clk-u300.o obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o @@ -45,19 +47,20 @@ obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o obj-$(CONFIG_COMMON_CLK_XGENE) += clk-xgene.o obj-$(CONFIG_COMMON_CLK_PWM) += clk-pwm.o obj-$(CONFIG_COMMON_CLK_AT91) += at91/ -obj-$(CONFIG_ARCH_BCM_MOBILE) += bcm/ +obj-$(CONFIG_ARCH_BCM) += bcm/ obj-$(CONFIG_ARCH_BERLIN) += berlin/ -obj-$(CONFIG_ARCH_HI3xxx) += hisilicon/ -obj-$(CONFIG_ARCH_HIP04) += hisilicon/ -obj-$(CONFIG_ARCH_HIX5HD2) += hisilicon/ +obj-$(CONFIG_ARCH_HISI) += hisilicon/ obj-$(CONFIG_ARCH_MXC) += imx/ obj-$(CONFIG_MACH_INGENIC) += ingenic/ obj-$(CONFIG_COMMON_CLK_KEYSTONE) += keystone/ +obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/ ifeq ($(CONFIG_COMMON_CLK), y) obj-$(CONFIG_ARCH_MMP) += mmp/ endif obj-$(CONFIG_PLAT_ORION) += mvebu/ +obj-$(CONFIG_ARCH_MESON) += meson/ obj-$(CONFIG_ARCH_MXS) += mxs/ +obj-$(CONFIG_ARCH_LPC18XX) += nxp/ obj-$(CONFIG_MACH_PISTACHIO) += pistachio/ obj-$(CONFIG_COMMON_CLK_PXA) += pxa/ obj-$(CONFIG_COMMON_CLK_QCOM) += qcom/ diff --git a/drivers/clk/at91/clk-main.c b/drivers/clk/at91/clk-main.c index 59fa3cc96c9e..c2400456a044 100644 --- a/drivers/clk/at91/clk-main.c +++ b/drivers/clk/at91/clk-main.c @@ -614,7 +614,7 @@ void __init of_at91sam9x5_clk_main_setup(struct device_node *np, const char *name = np->name; int i; - num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); + num_parents = of_clk_get_parent_count(np); if (num_parents <= 0 || num_parents > 2) return; diff --git a/drivers/clk/at91/clk-master.c b/drivers/clk/at91/clk-master.c index c1af80bcdf20..f98eafe9b12d 100644 --- a/drivers/clk/at91/clk-master.c +++ b/drivers/clk/at91/clk-master.c @@ -224,7 +224,7 @@ of_at91_clk_master_setup(struct device_node *np, struct at91_pmc *pmc, const char *name = np->name; struct clk_master_characteristics *characteristics; - num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); + num_parents = of_clk_get_parent_count(np); if (num_parents <= 0 || num_parents > MASTER_SOURCE_MAX) return; diff --git a/drivers/clk/at91/clk-programmable.c b/drivers/clk/at91/clk-programmable.c index 86c8a073dcc3..8c86c0f7847a 100644 --- a/drivers/clk/at91/clk-programmable.c +++ b/drivers/clk/at91/clk-programmable.c @@ -237,7 +237,7 @@ of_at91_clk_prog_setup(struct device_node *np, struct at91_pmc *pmc, const char *name; struct device_node *progclknp; - num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); + num_parents = of_clk_get_parent_count(np); if (num_parents <= 0 || num_parents > PROG_SOURCE_MAX) return; diff --git a/drivers/clk/at91/clk-slow.c b/drivers/clk/at91/clk-slow.c index 2f13bd5246b5..98a84a865fe1 100644 --- a/drivers/clk/at91/clk-slow.c +++ b/drivers/clk/at91/clk-slow.c @@ -373,7 +373,7 @@ void __init of_at91sam9x5_clk_slow_setup(struct device_node *np, const char *name = np->name; int i; - num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); + num_parents = of_clk_get_parent_count(np); if (num_parents <= 0 || num_parents > 2) return; @@ -451,7 +451,7 @@ void __init of_at91sam9260_clk_slow_setup(struct device_node *np, const char *name = np->name; int i; - num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); + num_parents = of_clk_get_parent_count(np); if (num_parents != 2) return; diff --git a/drivers/clk/at91/clk-smd.c b/drivers/clk/at91/clk-smd.c index 144d47ecfe63..3817ea865ca2 100644 --- a/drivers/clk/at91/clk-smd.c +++ b/drivers/clk/at91/clk-smd.c @@ -150,7 +150,7 @@ void __init of_at91sam9x5_clk_smd_setup(struct device_node *np, const char *parent_names[SMD_SOURCE_MAX]; const char *name = np->name; - num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); + num_parents = of_clk_get_parent_count(np); if (num_parents <= 0 || num_parents > SMD_SOURCE_MAX) return; diff --git a/drivers/clk/at91/clk-usb.c b/drivers/clk/at91/clk-usb.c index 0b7c3e8840ba..b0cbd2b1ff59 100644 --- a/drivers/clk/at91/clk-usb.c +++ b/drivers/clk/at91/clk-usb.c @@ -378,7 +378,7 @@ void __init of_at91sam9x5_clk_usb_setup(struct device_node *np, const char *parent_names[USB_SOURCE_MAX]; const char *name = np->name; - num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); + num_parents = of_clk_get_parent_count(np); if (num_parents <= 0 || num_parents > USB_SOURCE_MAX) return; diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c index 3f27d21fb729..39be2be82b0a 100644 --- a/drivers/clk/at91/pmc.c +++ b/drivers/clk/at91/pmc.c @@ -153,7 +153,7 @@ static int pmc_irq_domain_xlate(struct irq_domain *d, return 0; } -static struct irq_domain_ops pmc_irq_ops = { +static const struct irq_domain_ops pmc_irq_ops = { .map = pmc_irq_map, .xlate = pmc_irq_domain_xlate, }; diff --git a/drivers/clk/bcm/Kconfig b/drivers/clk/bcm/Kconfig index 75506e53075b..88febf53b276 100644 --- a/drivers/clk/bcm/Kconfig +++ b/drivers/clk/bcm/Kconfig @@ -7,3 +7,12 @@ config CLK_BCM_KONA Enable common clock framework support for Broadcom SoCs using "Kona" style clock control units, including those in the BCM281xx and BCM21664 families. + +config COMMON_CLK_IPROC + bool "Broadcom iProc clock support" + depends on ARCH_BCM_IPROC + depends on COMMON_CLK + default ARCH_BCM_IPROC + help + Enable common clock framework support for Broadcom SoCs + based on the iProc architecture diff --git a/drivers/clk/bcm/Makefile b/drivers/clk/bcm/Makefile index 6297d05a9a10..8a7a477862c7 100644 --- a/drivers/clk/bcm/Makefile +++ b/drivers/clk/bcm/Makefile @@ -2,3 +2,5 @@ obj-$(CONFIG_CLK_BCM_KONA) += clk-kona.o obj-$(CONFIG_CLK_BCM_KONA) += clk-kona-setup.o obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm281xx.o obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm21664.o +obj-$(CONFIG_COMMON_CLK_IPROC) += clk-iproc-armpll.o clk-iproc-pll.o clk-iproc-asiu.o +obj-$(CONFIG_ARCH_BCM_CYGNUS) += clk-cygnus.o diff --git a/drivers/clk/bcm/clk-cygnus.c b/drivers/clk/bcm/clk-cygnus.c new file mode 100644 index 000000000000..316c60337661 --- /dev/null +++ b/drivers/clk/bcm/clk-cygnus.c @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2014 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "clk-iproc.h" + +#define reg_val(o, s, w) { .offset = o, .shift = s, .width = w, } + +#define aon_val(o, pw, ps, is) { .offset = o, .pwr_width = pw, \ + .pwr_shift = ps, .iso_shift = is } + +#define sw_ctrl_val(o, s) { .offset = o, .shift = s, } + +#define asiu_div_val(o, es, hs, hw, ls, lw) \ + { .offset = o, .en_shift = es, .high_shift = hs, \ + .high_width = hw, .low_shift = ls, .low_width = lw } + +#define reset_val(o, rs, prs, kis, kiw, kps, kpw, kas, kaw) { .offset = o, \ + .reset_shift = rs, .p_reset_shift = prs, .ki_shift = kis, \ + .ki_width = kiw, .kp_shift = kps, .kp_width = kpw, .ka_shift = kas, \ + .ka_width = kaw } + +#define vco_ctrl_val(uo, lo) { .u_offset = uo, .l_offset = lo } + +#define enable_val(o, es, hs, bs) { .offset = o, .enable_shift = es, \ + .hold_shift = hs, .bypass_shift = bs } + +#define asiu_gate_val(o, es) { .offset = o, .en_shift = es } + +static void __init cygnus_armpll_init(struct device_node *node) +{ + iproc_armpll_setup(node); +} +CLK_OF_DECLARE(cygnus_armpll, "brcm,cygnus-armpll", cygnus_armpll_init); + +static const struct iproc_pll_ctrl genpll = { + .flags = IPROC_CLK_AON | IPROC_CLK_PLL_HAS_NDIV_FRAC | + IPROC_CLK_PLL_NEEDS_SW_CFG, + .aon = aon_val(0x0, 2, 1, 0), + .reset = reset_val(0x0, 11, 10, 4, 3, 0, 4, 7, 3), + .sw_ctrl = sw_ctrl_val(0x10, 31), + .ndiv_int = reg_val(0x10, 20, 10), + .ndiv_frac = reg_val(0x10, 0, 20), + .pdiv = reg_val(0x14, 0, 4), + .vco_ctrl = vco_ctrl_val(0x18, 0x1c), + .status = reg_val(0x28, 12, 1), +}; + +static const struct iproc_clk_ctrl genpll_clk[] = { + [BCM_CYGNUS_GENPLL_AXI21_CLK] = { + .channel = BCM_CYGNUS_GENPLL_AXI21_CLK, + .flags = IPROC_CLK_AON, + .enable = enable_val(0x4, 6, 0, 12), + .mdiv = reg_val(0x20, 0, 8), + }, + [BCM_CYGNUS_GENPLL_250MHZ_CLK] = { + .channel = BCM_CYGNUS_GENPLL_250MHZ_CLK, + .flags = IPROC_CLK_AON, + .enable = enable_val(0x4, 7, 1, 13), + .mdiv = reg_val(0x20, 10, 8), + }, + [BCM_CYGNUS_GENPLL_IHOST_SYS_CLK] = { + .channel = BCM_CYGNUS_GENPLL_IHOST_SYS_CLK, + .flags = IPROC_CLK_AON, + .enable = enable_val(0x4, 8, 2, 14), + .mdiv = reg_val(0x20, 20, 8), + }, + [BCM_CYGNUS_GENPLL_ENET_SW_CLK] = { + .channel = BCM_CYGNUS_GENPLL_ENET_SW_CLK, + .flags = IPROC_CLK_AON, + .enable = enable_val(0x4, 9, 3, 15), + .mdiv = reg_val(0x24, 0, 8), + }, + [BCM_CYGNUS_GENPLL_AUDIO_125_CLK] = { + .channel = BCM_CYGNUS_GENPLL_AUDIO_125_CLK, + .flags = IPROC_CLK_AON, + .enable = enable_val(0x4, 10, 4, 16), + .mdiv = reg_val(0x24, 10, 8), + }, + [BCM_CYGNUS_GENPLL_CAN_CLK] = { + .channel = BCM_CYGNUS_GENPLL_CAN_CLK, + .flags = IPROC_CLK_AON, + .enable = enable_val(0x4, 11, 5, 17), + .mdiv = reg_val(0x24, 20, 8), + }, +}; + +static void __init cygnus_genpll_clk_init(struct device_node *node) +{ + iproc_pll_clk_setup(node, &genpll, NULL, 0, genpll_clk, + ARRAY_SIZE(genpll_clk)); +} +CLK_OF_DECLARE(cygnus_genpll, "brcm,cygnus-genpll", cygnus_genpll_clk_init); + +static const struct iproc_pll_ctrl lcpll0 = { + .flags = IPROC_CLK_AON | IPROC_CLK_PLL_NEEDS_SW_CFG, + .aon = aon_val(0x0, 2, 5, 4), + .reset = reset_val(0x0, 31, 30, 27, 3, 23, 4, 19, 4), + .sw_ctrl = sw_ctrl_val(0x4, 31), + .ndiv_int = reg_val(0x4, 16, 10), + .pdiv = reg_val(0x4, 26, 4), + .vco_ctrl = vco_ctrl_val(0x10, 0x14), + .status = reg_val(0x18, 12, 1), +}; + +static const struct iproc_clk_ctrl lcpll0_clk[] = { + [BCM_CYGNUS_LCPLL0_PCIE_PHY_REF_CLK] = { + .channel = BCM_CYGNUS_LCPLL0_PCIE_PHY_REF_CLK, + .flags = IPROC_CLK_AON, + .enable = enable_val(0x0, 7, 1, 13), + .mdiv = reg_val(0x8, 0, 8), + }, + [BCM_CYGNUS_LCPLL0_DDR_PHY_CLK] = { + .channel = BCM_CYGNUS_LCPLL0_DDR_PHY_CLK, + .flags = IPROC_CLK_AON, + .enable = enable_val(0x0, 8, 2, 14), + .mdiv = reg_val(0x8, 10, 8), + }, + [BCM_CYGNUS_LCPLL0_SDIO_CLK] = { + .channel = BCM_CYGNUS_LCPLL0_SDIO_CLK, + .flags = IPROC_CLK_AON, + .enable = enable_val(0x0, 9, 3, 15), + .mdiv = reg_val(0x8, 20, 8), + }, + [BCM_CYGNUS_LCPLL0_USB_PHY_REF_CLK] = { + .channel = BCM_CYGNUS_LCPLL0_USB_PHY_REF_CLK, + .flags = IPROC_CLK_AON, + .enable = enable_val(0x0, 10, 4, 16), + .mdiv = reg_val(0xc, 0, 8), + }, + [BCM_CYGNUS_LCPLL0_SMART_CARD_CLK] = { + .channel = BCM_CYGNUS_LCPLL0_SMART_CARD_CLK, + .flags = IPROC_CLK_AON, + .enable = enable_val(0x0, 11, 5, 17), + .mdiv = reg_val(0xc, 10, 8), + }, + [BCM_CYGNUS_LCPLL0_CH5_UNUSED] = { + .channel = BCM_CYGNUS_LCPLL0_CH5_UNUSED, + .flags = IPROC_CLK_AON, + .enable = enable_val(0x0, 12, 6, 18), + .mdiv = reg_val(0xc, 20, 8), + }, +}; + +static void __init cygnus_lcpll0_clk_init(struct device_node *node) +{ + iproc_pll_clk_setup(node, &lcpll0, NULL, 0, lcpll0_clk, + ARRAY_SIZE(lcpll0_clk)); +} +CLK_OF_DECLARE(cygnus_lcpll0, "brcm,cygnus-lcpll0", cygnus_lcpll0_clk_init); + +/* + * MIPI PLL VCO frequency parameter table + */ +static const struct iproc_pll_vco_param mipipll_vco_params[] = { + /* rate (Hz) ndiv_int ndiv_frac pdiv */ + { 750000000UL, 30, 0, 1 }, + { 1000000000UL, 40, 0, 1 }, + { 1350000000ul, 54, 0, 1 }, + { 2000000000UL, 80, 0, 1 }, + { 2100000000UL, 84, 0, 1 }, + { 2250000000UL, 90, 0, 1 }, + { 2500000000UL, 100, 0, 1 }, + { 2700000000UL, 54, 0, 0 }, + { 2975000000UL, 119, 0, 1 }, + { 3100000000UL, 124, 0, 1 }, + { 3150000000UL, 126, 0, 1 }, +}; + +static const struct iproc_pll_ctrl mipipll = { + .flags = IPROC_CLK_PLL_ASIU | IPROC_CLK_PLL_HAS_NDIV_FRAC | + IPROC_CLK_NEEDS_READ_BACK, + .aon = aon_val(0x0, 4, 17, 16), + .asiu = asiu_gate_val(0x0, 3), + .reset = reset_val(0x0, 11, 10, 4, 3, 0, 4, 7, 4), + .ndiv_int = reg_val(0x10, 20, 10), + .ndiv_frac = reg_val(0x10, 0, 20), + .pdiv = reg_val(0x14, 0, 4), + .vco_ctrl = vco_ctrl_val(0x18, 0x1c), + .status = reg_val(0x28, 12, 1), +}; + +static const struct iproc_clk_ctrl mipipll_clk[] = { + [BCM_CYGNUS_MIPIPLL_CH0_UNUSED] = { + .channel = BCM_CYGNUS_MIPIPLL_CH0_UNUSED, + .flags = IPROC_CLK_NEEDS_READ_BACK, + .enable = enable_val(0x4, 12, 6, 18), + .mdiv = reg_val(0x20, 0, 8), + }, + [BCM_CYGNUS_MIPIPLL_CH1_LCD] = { + .channel = BCM_CYGNUS_MIPIPLL_CH1_LCD, + .flags = IPROC_CLK_NEEDS_READ_BACK, + .enable = enable_val(0x4, 13, 7, 19), + .mdiv = reg_val(0x20, 10, 8), + }, + [BCM_CYGNUS_MIPIPLL_CH2_V3D] = { + .channel = BCM_CYGNUS_MIPIPLL_CH2_V3D, + .flags = IPROC_CLK_NEEDS_READ_BACK, + .enable = enable_val(0x4, 14, 8, 20), + .mdiv = reg_val(0x20, 20, 8), + }, + [BCM_CYGNUS_MIPIPLL_CH3_UNUSED] = { + .channel = BCM_CYGNUS_MIPIPLL_CH3_UNUSED, + .flags = IPROC_CLK_NEEDS_READ_BACK, + .enable = enable_val(0x4, 15, 9, 21), + .mdiv = reg_val(0x24, 0, 8), + }, + [BCM_CYGNUS_MIPIPLL_CH4_UNUSED] = { + .channel = BCM_CYGNUS_MIPIPLL_CH4_UNUSED, + .flags = IPROC_CLK_NEEDS_READ_BACK, + .enable = enable_val(0x4, 16, 10, 22), + .mdiv = reg_val(0x24, 10, 8), + }, + [BCM_CYGNUS_MIPIPLL_CH5_UNUSED] = { + .channel = BCM_CYGNUS_MIPIPLL_CH5_UNUSED, + .flags = IPROC_CLK_NEEDS_READ_BACK, + .enable = enable_val(0x4, 17, 11, 23), + .mdiv = reg_val(0x24, 20, 8), + }, +}; + +static void __init cygnus_mipipll_clk_init(struct device_node *node) +{ + iproc_pll_clk_setup(node, &mipipll, mipipll_vco_params, + ARRAY_SIZE(mipipll_vco_params), mipipll_clk, + ARRAY_SIZE(mipipll_clk)); +} +CLK_OF_DECLARE(cygnus_mipipll, "brcm,cygnus-mipipll", cygnus_mipipll_clk_init); + +static const struct iproc_asiu_div asiu_div[] = { + [BCM_CYGNUS_ASIU_KEYPAD_CLK] = asiu_div_val(0x0, 31, 16, 10, 0, 10), + [BCM_CYGNUS_ASIU_ADC_CLK] = asiu_div_val(0x4, 31, 16, 10, 0, 10), + [BCM_CYGNUS_ASIU_PWM_CLK] = asiu_div_val(0x8, 31, 16, 10, 0, 10), +}; + +static const struct iproc_asiu_gate asiu_gate[] = { + [BCM_CYGNUS_ASIU_KEYPAD_CLK] = asiu_gate_val(0x0, 7), + [BCM_CYGNUS_ASIU_ADC_CLK] = asiu_gate_val(0x0, 9), + [BCM_CYGNUS_ASIU_PWM_CLK] = asiu_gate_val(IPROC_CLK_INVALID_OFFSET, 0), +}; + +static void __init cygnus_asiu_init(struct device_node *node) +{ + iproc_asiu_setup(node, asiu_div, asiu_gate, ARRAY_SIZE(asiu_div)); +} +CLK_OF_DECLARE(cygnus_asiu_clk, "brcm,cygnus-asiu-clk", cygnus_asiu_init); diff --git a/drivers/clk/bcm/clk-iproc-armpll.c b/drivers/clk/bcm/clk-iproc-armpll.c new file mode 100644 index 000000000000..a196ee28a17a --- /dev/null +++ b/drivers/clk/bcm/clk-iproc-armpll.c @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2014 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define IPROC_CLK_MAX_FREQ_POLICY 0x3 +#define IPROC_CLK_POLICY_FREQ_OFFSET 0x008 +#define IPROC_CLK_POLICY_FREQ_POLICY_FREQ_SHIFT 8 +#define IPROC_CLK_POLICY_FREQ_POLICY_FREQ_MASK 0x7 + +#define IPROC_CLK_PLLARMA_OFFSET 0xc00 +#define IPROC_CLK_PLLARMA_LOCK_SHIFT 28 +#define IPROC_CLK_PLLARMA_PDIV_SHIFT 24 +#define IPROC_CLK_PLLARMA_PDIV_MASK 0xf +#define IPROC_CLK_PLLARMA_NDIV_INT_SHIFT 8 +#define IPROC_CLK_PLLARMA_NDIV_INT_MASK 0x3ff + +#define IPROC_CLK_PLLARMB_OFFSET 0xc04 +#define IPROC_CLK_PLLARMB_NDIV_FRAC_MASK 0xfffff + +#define IPROC_CLK_PLLARMC_OFFSET 0xc08 +#define IPROC_CLK_PLLARMC_BYPCLK_EN_SHIFT 8 +#define IPROC_CLK_PLLARMC_MDIV_MASK 0xff + +#define IPROC_CLK_PLLARMCTL5_OFFSET 0xc20 +#define IPROC_CLK_PLLARMCTL5_H_MDIV_MASK 0xff + +#define IPROC_CLK_PLLARM_OFFSET_OFFSET 0xc24 +#define IPROC_CLK_PLLARM_SW_CTL_SHIFT 29 +#define IPROC_CLK_PLLARM_NDIV_INT_OFFSET_SHIFT 20 +#define IPROC_CLK_PLLARM_NDIV_INT_OFFSET_MASK 0xff +#define IPROC_CLK_PLLARM_NDIV_FRAC_OFFSET_MASK 0xfffff + +#define IPROC_CLK_ARM_DIV_OFFSET 0xe00 +#define IPROC_CLK_ARM_DIV_PLL_SELECT_OVERRIDE_SHIFT 4 +#define IPROC_CLK_ARM_DIV_ARM_PLL_SELECT_MASK 0xf + +#define IPROC_CLK_POLICY_DBG_OFFSET 0xec0 +#define IPROC_CLK_POLICY_DBG_ACT_FREQ_SHIFT 12 +#define IPROC_CLK_POLICY_DBG_ACT_FREQ_MASK 0x7 + +enum iproc_arm_pll_fid { + ARM_PLL_FID_CRYSTAL_CLK = 0, + ARM_PLL_FID_SYS_CLK = 2, + ARM_PLL_FID_CH0_SLOW_CLK = 6, + ARM_PLL_FID_CH1_FAST_CLK = 7 +}; + +struct iproc_arm_pll { + struct clk_hw hw; + void __iomem *base; + unsigned long rate; +}; + +#define to_iproc_arm_pll(hw) container_of(hw, struct iproc_arm_pll, hw) + +static unsigned int __get_fid(struct iproc_arm_pll *pll) +{ + u32 val; + unsigned int policy, fid, active_fid; + + val = readl(pll->base + IPROC_CLK_ARM_DIV_OFFSET); + if (val & (1 << IPROC_CLK_ARM_DIV_PLL_SELECT_OVERRIDE_SHIFT)) + policy = val & IPROC_CLK_ARM_DIV_ARM_PLL_SELECT_MASK; + else + policy = 0; + + /* something is seriously wrong */ + BUG_ON(policy > IPROC_CLK_MAX_FREQ_POLICY); + + val = readl(pll->base + IPROC_CLK_POLICY_FREQ_OFFSET); + fid = (val >> (IPROC_CLK_POLICY_FREQ_POLICY_FREQ_SHIFT * policy)) & + IPROC_CLK_POLICY_FREQ_POLICY_FREQ_MASK; + + val = readl(pll->base + IPROC_CLK_POLICY_DBG_OFFSET); + active_fid = IPROC_CLK_POLICY_DBG_ACT_FREQ_MASK & + (val >> IPROC_CLK_POLICY_DBG_ACT_FREQ_SHIFT); + if (fid != active_fid) { + pr_debug("%s: fid override %u->%u\n", __func__, fid, + active_fid); + fid = active_fid; + } + + pr_debug("%s: active fid: %u\n", __func__, fid); + + return fid; +} + +/* + * Determine the mdiv (post divider) based on the frequency ID being used. + * There are 4 sources that can be used to derive the output clock rate: + * - 25 MHz Crystal + * - System clock + * - PLL channel 0 (slow clock) + * - PLL channel 1 (fast clock) + */ +static int __get_mdiv(struct iproc_arm_pll *pll) +{ + unsigned int fid; + int mdiv; + u32 val; + + fid = __get_fid(pll); + + switch (fid) { + case ARM_PLL_FID_CRYSTAL_CLK: + case ARM_PLL_FID_SYS_CLK: + mdiv = 1; + break; + + case ARM_PLL_FID_CH0_SLOW_CLK: + val = readl(pll->base + IPROC_CLK_PLLARMC_OFFSET); + mdiv = val & IPROC_CLK_PLLARMC_MDIV_MASK; + if (mdiv == 0) + mdiv = 256; + break; + + case ARM_PLL_FID_CH1_FAST_CLK: + val = readl(pll->base + IPROC_CLK_PLLARMCTL5_OFFSET); + mdiv = val & IPROC_CLK_PLLARMCTL5_H_MDIV_MASK; + if (mdiv == 0) + mdiv = 256; + break; + + default: + mdiv = -EFAULT; + } + + return mdiv; +} + +static unsigned int __get_ndiv(struct iproc_arm_pll *pll) +{ + u32 val; + unsigned int ndiv_int, ndiv_frac, ndiv; + + val = readl(pll->base + IPROC_CLK_PLLARM_OFFSET_OFFSET); + if (val & (1 << IPROC_CLK_PLLARM_SW_CTL_SHIFT)) { + /* + * offset mode is active. Read the ndiv from the PLLARM OFFSET + * register + */ + ndiv_int = (val >> IPROC_CLK_PLLARM_NDIV_INT_OFFSET_SHIFT) & + IPROC_CLK_PLLARM_NDIV_INT_OFFSET_MASK; + if (ndiv_int == 0) + ndiv_int = 256; + + ndiv_frac = val & IPROC_CLK_PLLARM_NDIV_FRAC_OFFSET_MASK; + } else { + /* offset mode not active */ + val = readl(pll->base + IPROC_CLK_PLLARMA_OFFSET); + ndiv_int = (val >> IPROC_CLK_PLLARMA_NDIV_INT_SHIFT) & + IPROC_CLK_PLLARMA_NDIV_INT_MASK; + if (ndiv_int == 0) + ndiv_int = 1024; + + val = readl(pll->base + IPROC_CLK_PLLARMB_OFFSET); + ndiv_frac = val & IPROC_CLK_PLLARMB_NDIV_FRAC_MASK; + } + + ndiv = (ndiv_int << 20) | ndiv_frac; + + return ndiv; +} + +/* + * The output frequency of the ARM PLL is calculated based on the ARM PLL + * divider values: + * pdiv = ARM PLL pre-divider + * ndiv = ARM PLL multiplier + * mdiv = ARM PLL post divider + * + * The frequency is calculated by: + * ((ndiv * parent clock rate) / pdiv) / mdiv + */ +static unsigned long iproc_arm_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct iproc_arm_pll *pll = to_iproc_arm_pll(hw); + u32 val; + int mdiv; + u64 ndiv; + unsigned int pdiv; + + /* in bypass mode, use parent rate */ + val = readl(pll->base + IPROC_CLK_PLLARMC_OFFSET); + if (val & (1 << IPROC_CLK_PLLARMC_BYPCLK_EN_SHIFT)) { + pll->rate = parent_rate; + return pll->rate; + } + + /* PLL needs to be locked */ + val = readl(pll->base + IPROC_CLK_PLLARMA_OFFSET); + if (!(val & (1 << IPROC_CLK_PLLARMA_LOCK_SHIFT))) { + pll->rate = 0; + return 0; + } + + pdiv = (val >> IPROC_CLK_PLLARMA_PDIV_SHIFT) & + IPROC_CLK_PLLARMA_PDIV_MASK; + if (pdiv == 0) + pdiv = 16; + + ndiv = __get_ndiv(pll); + mdiv = __get_mdiv(pll); + if (mdiv <= 0) { + pll->rate = 0; + return 0; + } + pll->rate = (ndiv * parent_rate) >> 20; + pll->rate = (pll->rate / pdiv) / mdiv; + + pr_debug("%s: ARM PLL rate: %lu. parent rate: %lu\n", __func__, + pll->rate, parent_rate); + pr_debug("%s: ndiv_int: %u, pdiv: %u, mdiv: %d\n", __func__, + (unsigned int)(ndiv >> 20), pdiv, mdiv); + + return pll->rate; +} + +static const struct clk_ops iproc_arm_pll_ops = { + .recalc_rate = iproc_arm_pll_recalc_rate, +}; + +void __init iproc_armpll_setup(struct device_node *node) +{ + int ret; + struct clk *clk; + struct iproc_arm_pll *pll; + struct clk_init_data init; + const char *parent_name; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (WARN_ON(!pll)) + return; + + pll->base = of_iomap(node, 0); + if (WARN_ON(!pll->base)) + goto err_free_pll; + + init.name = node->name; + init.ops = &iproc_arm_pll_ops; + init.flags = 0; + parent_name = of_clk_get_parent_name(node, 0); + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + pll->hw.init = &init; + + clk = clk_register(NULL, &pll->hw); + if (WARN_ON(IS_ERR(clk))) + goto err_iounmap; + + ret = of_clk_add_provider(node, of_clk_src_simple_get, clk); + if (WARN_ON(ret)) + goto err_clk_unregister; + + return; + +err_clk_unregister: + clk_unregister(clk); +err_iounmap: + iounmap(pll->base); +err_free_pll: + kfree(pll); +} diff --git a/drivers/clk/bcm/clk-iproc-asiu.c b/drivers/clk/bcm/clk-iproc-asiu.c new file mode 100644 index 000000000000..e19c09cd9645 --- /dev/null +++ b/drivers/clk/bcm/clk-iproc-asiu.c @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2014 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clk-iproc.h" + +struct iproc_asiu; + +struct iproc_asiu_clk { + struct clk_hw hw; + const char *name; + struct iproc_asiu *asiu; + unsigned long rate; + struct iproc_asiu_div div; + struct iproc_asiu_gate gate; +}; + +struct iproc_asiu { + void __iomem *div_base; + void __iomem *gate_base; + + struct clk_onecell_data clk_data; + struct iproc_asiu_clk *clks; +}; + +#define to_asiu_clk(hw) container_of(hw, struct iproc_asiu_clk, hw) + +static int iproc_asiu_clk_enable(struct clk_hw *hw) +{ + struct iproc_asiu_clk *clk = to_asiu_clk(hw); + struct iproc_asiu *asiu = clk->asiu; + u32 val; + + /* some clocks at the ASIU level are always enabled */ + if (clk->gate.offset == IPROC_CLK_INVALID_OFFSET) + return 0; + + val = readl(asiu->gate_base + clk->gate.offset); + val |= (1 << clk->gate.en_shift); + writel(val, asiu->gate_base + clk->gate.offset); + + return 0; +} + +static void iproc_asiu_clk_disable(struct clk_hw *hw) +{ + struct iproc_asiu_clk *clk = to_asiu_clk(hw); + struct iproc_asiu *asiu = clk->asiu; + u32 val; + + /* some clocks at the ASIU level are always enabled */ + if (clk->gate.offset == IPROC_CLK_INVALID_OFFSET) + return; + + val = readl(asiu->gate_base + clk->gate.offset); + val &= ~(1 << clk->gate.en_shift); + writel(val, asiu->gate_base + clk->gate.offset); +} + +static unsigned long iproc_asiu_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct iproc_asiu_clk *clk = to_asiu_clk(hw); + struct iproc_asiu *asiu = clk->asiu; + u32 val; + unsigned int div_h, div_l; + + if (parent_rate == 0) { + clk->rate = 0; + return 0; + } + + /* if clock divisor is not enabled, simply return parent rate */ + val = readl(asiu->div_base + clk->div.offset); + if ((val & (1 << clk->div.en_shift)) == 0) { + clk->rate = parent_rate; + return parent_rate; + } + + /* clock rate = parent rate / (high_div + 1) + (low_div + 1) */ + div_h = (val >> clk->div.high_shift) & bit_mask(clk->div.high_width); + div_h++; + div_l = (val >> clk->div.low_shift) & bit_mask(clk->div.low_width); + div_l++; + + clk->rate = parent_rate / (div_h + div_l); + pr_debug("%s: rate: %lu. parent rate: %lu div_h: %u div_l: %u\n", + __func__, clk->rate, parent_rate, div_h, div_l); + + return clk->rate; +} + +static long iproc_asiu_clk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + unsigned int div; + + if (rate == 0 || *parent_rate == 0) + return -EINVAL; + + if (rate == *parent_rate) + return *parent_rate; + + div = DIV_ROUND_UP(*parent_rate, rate); + if (div < 2) + return *parent_rate; + + return *parent_rate / div; +} + +static int iproc_asiu_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct iproc_asiu_clk *clk = to_asiu_clk(hw); + struct iproc_asiu *asiu = clk->asiu; + unsigned int div, div_h, div_l; + u32 val; + + if (rate == 0 || parent_rate == 0) + return -EINVAL; + + /* simply disable the divisor if one wants the same rate as parent */ + if (rate == parent_rate) { + val = readl(asiu->div_base + clk->div.offset); + val &= ~(1 << clk->div.en_shift); + writel(val, asiu->div_base + clk->div.offset); + return 0; + } + + div = DIV_ROUND_UP(parent_rate, rate); + if (div < 2) + return -EINVAL; + + div_h = div_l = div >> 1; + div_h--; + div_l--; + + val = readl(asiu->div_base + clk->div.offset); + val |= 1 << clk->div.en_shift; + if (div_h) { + val &= ~(bit_mask(clk->div.high_width) + << clk->div.high_shift); + val |= div_h << clk->div.high_shift; + } else { + val &= ~(bit_mask(clk->div.high_width) + << clk->div.high_shift); + } + if (div_l) { + val &= ~(bit_mask(clk->div.low_width) << clk->div.low_shift); + val |= div_l << clk->div.low_shift; + } else { + val &= ~(bit_mask(clk->div.low_width) << clk->div.low_shift); + } + writel(val, asiu->div_base + clk->div.offset); + + return 0; +} + +static const struct clk_ops iproc_asiu_ops = { + .enable = iproc_asiu_clk_enable, + .disable = iproc_asiu_clk_disable, + .recalc_rate = iproc_asiu_clk_recalc_rate, + .round_rate = iproc_asiu_clk_round_rate, + .set_rate = iproc_asiu_clk_set_rate, +}; + +void __init iproc_asiu_setup(struct device_node *node, + const struct iproc_asiu_div *div, + const struct iproc_asiu_gate *gate, + unsigned int num_clks) +{ + int i, ret; + struct iproc_asiu *asiu; + + if (WARN_ON(!gate || !div)) + return; + + asiu = kzalloc(sizeof(*asiu), GFP_KERNEL); + if (WARN_ON(!asiu)) + return; + + asiu->clk_data.clk_num = num_clks; + asiu->clk_data.clks = kcalloc(num_clks, sizeof(*asiu->clk_data.clks), + GFP_KERNEL); + if (WARN_ON(!asiu->clk_data.clks)) + goto err_clks; + + asiu->clks = kcalloc(num_clks, sizeof(*asiu->clks), GFP_KERNEL); + if (WARN_ON(!asiu->clks)) + goto err_asiu_clks; + + asiu->div_base = of_iomap(node, 0); + if (WARN_ON(!asiu->div_base)) + goto err_iomap_div; + + asiu->gate_base = of_iomap(node, 1); + if (WARN_ON(!asiu->gate_base)) + goto err_iomap_gate; + + for (i = 0; i < num_clks; i++) { + struct clk_init_data init; + struct clk *clk; + const char *parent_name; + struct iproc_asiu_clk *asiu_clk; + const char *clk_name; + + clk_name = kzalloc(IPROC_CLK_NAME_LEN, GFP_KERNEL); + if (WARN_ON(!clk_name)) + goto err_clk_register; + + ret = of_property_read_string_index(node, "clock-output-names", + i, &clk_name); + if (WARN_ON(ret)) + goto err_clk_register; + + asiu_clk = &asiu->clks[i]; + asiu_clk->name = clk_name; + asiu_clk->asiu = asiu; + asiu_clk->div = div[i]; + asiu_clk->gate = gate[i]; + init.name = clk_name; + init.ops = &iproc_asiu_ops; + init.flags = 0; + parent_name = of_clk_get_parent_name(node, 0); + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + asiu_clk->hw.init = &init; + + clk = clk_register(NULL, &asiu_clk->hw); + if (WARN_ON(IS_ERR(clk))) + goto err_clk_register; + asiu->clk_data.clks[i] = clk; + } + + ret = of_clk_add_provider(node, of_clk_src_onecell_get, + &asiu->clk_data); + if (WARN_ON(ret)) + goto err_clk_register; + + return; + +err_clk_register: + for (i = 0; i < num_clks; i++) + kfree(asiu->clks[i].name); + iounmap(asiu->gate_base); + +err_iomap_gate: + iounmap(asiu->div_base); + +err_iomap_div: + kfree(asiu->clks); + +err_asiu_clks: + kfree(asiu->clk_data.clks); + +err_clks: + kfree(asiu); +} diff --git a/drivers/clk/bcm/clk-iproc-pll.c b/drivers/clk/bcm/clk-iproc-pll.c new file mode 100644 index 000000000000..46fb84bc2674 --- /dev/null +++ b/drivers/clk/bcm/clk-iproc-pll.c @@ -0,0 +1,716 @@ +/* + * Copyright (C) 2014 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clk-iproc.h" + +#define PLL_VCO_HIGH_SHIFT 19 +#define PLL_VCO_LOW_SHIFT 30 + +/* number of delay loops waiting for PLL to lock */ +#define LOCK_DELAY 100 + +/* number of VCO frequency bands */ +#define NUM_FREQ_BANDS 8 + +#define NUM_KP_BANDS 3 +enum kp_band { + KP_BAND_MID = 0, + KP_BAND_HIGH, + KP_BAND_HIGH_HIGH +}; + +static const unsigned int kp_table[NUM_KP_BANDS][NUM_FREQ_BANDS] = { + { 5, 6, 6, 7, 7, 8, 9, 10 }, + { 4, 4, 5, 5, 6, 7, 8, 9 }, + { 4, 5, 5, 6, 7, 8, 9, 10 }, +}; + +static const unsigned long ref_freq_table[NUM_FREQ_BANDS][2] = { + { 10000000, 12500000 }, + { 12500000, 15000000 }, + { 15000000, 20000000 }, + { 20000000, 25000000 }, + { 25000000, 50000000 }, + { 50000000, 75000000 }, + { 75000000, 100000000 }, + { 100000000, 125000000 }, +}; + +enum vco_freq_range { + VCO_LOW = 700000000U, + VCO_MID = 1200000000U, + VCO_HIGH = 2200000000U, + VCO_HIGH_HIGH = 3100000000U, + VCO_MAX = 4000000000U, +}; + +struct iproc_pll; + +struct iproc_clk { + struct clk_hw hw; + const char *name; + struct iproc_pll *pll; + unsigned long rate; + const struct iproc_clk_ctrl *ctrl; +}; + +struct iproc_pll { + void __iomem *pll_base; + void __iomem *pwr_base; + void __iomem *asiu_base; + + const struct iproc_pll_ctrl *ctrl; + const struct iproc_pll_vco_param *vco_param; + unsigned int num_vco_entries; + + struct clk_onecell_data clk_data; + struct iproc_clk *clks; +}; + +#define to_iproc_clk(hw) container_of(hw, struct iproc_clk, hw) + +/* + * Based on the target frequency, find a match from the VCO frequency parameter + * table and return its index + */ +static int pll_get_rate_index(struct iproc_pll *pll, unsigned int target_rate) +{ + int i; + + for (i = 0; i < pll->num_vco_entries; i++) + if (target_rate == pll->vco_param[i].rate) + break; + + if (i >= pll->num_vco_entries) + return -EINVAL; + + return i; +} + +static int get_kp(unsigned long ref_freq, enum kp_band kp_index) +{ + int i; + + if (ref_freq < ref_freq_table[0][0]) + return -EINVAL; + + for (i = 0; i < NUM_FREQ_BANDS; i++) { + if (ref_freq >= ref_freq_table[i][0] && + ref_freq < ref_freq_table[i][1]) + return kp_table[kp_index][i]; + } + return -EINVAL; +} + +static int pll_wait_for_lock(struct iproc_pll *pll) +{ + int i; + const struct iproc_pll_ctrl *ctrl = pll->ctrl; + + for (i = 0; i < LOCK_DELAY; i++) { + u32 val = readl(pll->pll_base + ctrl->status.offset); + + if (val & (1 << ctrl->status.shift)) + return 0; + udelay(10); + } + + return -EIO; +} + +static void __pll_disable(struct iproc_pll *pll) +{ + const struct iproc_pll_ctrl *ctrl = pll->ctrl; + u32 val; + + if (ctrl->flags & IPROC_CLK_PLL_ASIU) { + val = readl(pll->asiu_base + ctrl->asiu.offset); + val &= ~(1 << ctrl->asiu.en_shift); + writel(val, pll->asiu_base + ctrl->asiu.offset); + } + + /* latch input value so core power can be shut down */ + val = readl(pll->pwr_base + ctrl->aon.offset); + val |= (1 << ctrl->aon.iso_shift); + writel(val, pll->pwr_base + ctrl->aon.offset); + + /* power down the core */ + val &= ~(bit_mask(ctrl->aon.pwr_width) << ctrl->aon.pwr_shift); + writel(val, pll->pwr_base + ctrl->aon.offset); +} + +static int __pll_enable(struct iproc_pll *pll) +{ + const struct iproc_pll_ctrl *ctrl = pll->ctrl; + u32 val; + + /* power up the PLL and make sure it's not latched */ + val = readl(pll->pwr_base + ctrl->aon.offset); + val |= bit_mask(ctrl->aon.pwr_width) << ctrl->aon.pwr_shift; + val &= ~(1 << ctrl->aon.iso_shift); + writel(val, pll->pwr_base + ctrl->aon.offset); + + /* certain PLLs also need to be ungated from the ASIU top level */ + if (ctrl->flags & IPROC_CLK_PLL_ASIU) { + val = readl(pll->asiu_base + ctrl->asiu.offset); + val |= (1 << ctrl->asiu.en_shift); + writel(val, pll->asiu_base + ctrl->asiu.offset); + } + + return 0; +} + +static void __pll_put_in_reset(struct iproc_pll *pll) +{ + u32 val; + const struct iproc_pll_ctrl *ctrl = pll->ctrl; + const struct iproc_pll_reset_ctrl *reset = &ctrl->reset; + + val = readl(pll->pll_base + reset->offset); + val &= ~(1 << reset->reset_shift | 1 << reset->p_reset_shift); + writel(val, pll->pll_base + reset->offset); + if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK)) + readl(pll->pll_base + reset->offset); +} + +static void __pll_bring_out_reset(struct iproc_pll *pll, unsigned int kp, + unsigned int ka, unsigned int ki) +{ + u32 val; + const struct iproc_pll_ctrl *ctrl = pll->ctrl; + const struct iproc_pll_reset_ctrl *reset = &ctrl->reset; + + val = readl(pll->pll_base + reset->offset); + val &= ~(bit_mask(reset->ki_width) << reset->ki_shift | + bit_mask(reset->kp_width) << reset->kp_shift | + bit_mask(reset->ka_width) << reset->ka_shift); + val |= ki << reset->ki_shift | kp << reset->kp_shift | + ka << reset->ka_shift; + val |= 1 << reset->reset_shift | 1 << reset->p_reset_shift; + writel(val, pll->pll_base + reset->offset); + if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK)) + readl(pll->pll_base + reset->offset); +} + +static int pll_set_rate(struct iproc_clk *clk, unsigned int rate_index, + unsigned long parent_rate) +{ + struct iproc_pll *pll = clk->pll; + const struct iproc_pll_vco_param *vco = &pll->vco_param[rate_index]; + const struct iproc_pll_ctrl *ctrl = pll->ctrl; + int ka = 0, ki, kp, ret; + unsigned long rate = vco->rate; + u32 val; + enum kp_band kp_index; + unsigned long ref_freq; + + /* + * reference frequency = parent frequency / PDIV + * If PDIV = 0, then it becomes a multiplier (x2) + */ + if (vco->pdiv == 0) + ref_freq = parent_rate * 2; + else + ref_freq = parent_rate / vco->pdiv; + + /* determine Ki and Kp index based on target VCO frequency */ + if (rate >= VCO_LOW && rate < VCO_HIGH) { + ki = 4; + kp_index = KP_BAND_MID; + } else if (rate >= VCO_HIGH && rate && rate < VCO_HIGH_HIGH) { + ki = 3; + kp_index = KP_BAND_HIGH; + } else if (rate >= VCO_HIGH_HIGH && rate < VCO_MAX) { + ki = 3; + kp_index = KP_BAND_HIGH_HIGH; + } else { + pr_err("%s: pll: %s has invalid rate: %lu\n", __func__, + clk->name, rate); + return -EINVAL; + } + + kp = get_kp(ref_freq, kp_index); + if (kp < 0) { + pr_err("%s: pll: %s has invalid kp\n", __func__, clk->name); + return kp; + } + + ret = __pll_enable(pll); + if (ret) { + pr_err("%s: pll: %s fails to enable\n", __func__, clk->name); + return ret; + } + + /* put PLL in reset */ + __pll_put_in_reset(pll); + + writel(0, pll->pll_base + ctrl->vco_ctrl.u_offset); + if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK)) + readl(pll->pll_base + ctrl->vco_ctrl.u_offset); + val = readl(pll->pll_base + ctrl->vco_ctrl.l_offset); + + if (rate >= VCO_LOW && rate < VCO_MID) + val |= (1 << PLL_VCO_LOW_SHIFT); + + if (rate < VCO_HIGH) + val &= ~(1 << PLL_VCO_HIGH_SHIFT); + else + val |= (1 << PLL_VCO_HIGH_SHIFT); + + writel(val, pll->pll_base + ctrl->vco_ctrl.l_offset); + if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK)) + readl(pll->pll_base + ctrl->vco_ctrl.l_offset); + + /* program integer part of NDIV */ + val = readl(pll->pll_base + ctrl->ndiv_int.offset); + val &= ~(bit_mask(ctrl->ndiv_int.width) << ctrl->ndiv_int.shift); + val |= vco->ndiv_int << ctrl->ndiv_int.shift; + writel(val, pll->pll_base + ctrl->ndiv_int.offset); + if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK)) + readl(pll->pll_base + ctrl->ndiv_int.offset); + + /* program fractional part of NDIV */ + if (ctrl->flags & IPROC_CLK_PLL_HAS_NDIV_FRAC) { + val = readl(pll->pll_base + ctrl->ndiv_frac.offset); + val &= ~(bit_mask(ctrl->ndiv_frac.width) << + ctrl->ndiv_frac.shift); + val |= vco->ndiv_frac << ctrl->ndiv_frac.shift; + writel(val, pll->pll_base + ctrl->ndiv_frac.offset); + if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK)) + readl(pll->pll_base + ctrl->ndiv_frac.offset); + } + + /* program PDIV */ + val = readl(pll->pll_base + ctrl->pdiv.offset); + val &= ~(bit_mask(ctrl->pdiv.width) << ctrl->pdiv.shift); + val |= vco->pdiv << ctrl->pdiv.shift; + writel(val, pll->pll_base + ctrl->pdiv.offset); + if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK)) + readl(pll->pll_base + ctrl->pdiv.offset); + + __pll_bring_out_reset(pll, kp, ka, ki); + + ret = pll_wait_for_lock(pll); + if (ret < 0) { + pr_err("%s: pll: %s failed to lock\n", __func__, clk->name); + return ret; + } + + return 0; +} + +static int iproc_pll_enable(struct clk_hw *hw) +{ + struct iproc_clk *clk = to_iproc_clk(hw); + struct iproc_pll *pll = clk->pll; + + return __pll_enable(pll); +} + +static void iproc_pll_disable(struct clk_hw *hw) +{ + struct iproc_clk *clk = to_iproc_clk(hw); + struct iproc_pll *pll = clk->pll; + const struct iproc_pll_ctrl *ctrl = pll->ctrl; + + if (ctrl->flags & IPROC_CLK_AON) + return; + + __pll_disable(pll); +} + +static unsigned long iproc_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct iproc_clk *clk = to_iproc_clk(hw); + struct iproc_pll *pll = clk->pll; + const struct iproc_pll_ctrl *ctrl = pll->ctrl; + u32 val; + u64 ndiv; + unsigned int ndiv_int, ndiv_frac, pdiv; + + if (parent_rate == 0) + return 0; + + /* PLL needs to be locked */ + val = readl(pll->pll_base + ctrl->status.offset); + if ((val & (1 << ctrl->status.shift)) == 0) { + clk->rate = 0; + return 0; + } + + /* + * PLL output frequency = + * + * ((ndiv_int + ndiv_frac / 2^20) * (parent clock rate / pdiv) + */ + val = readl(pll->pll_base + ctrl->ndiv_int.offset); + ndiv_int = (val >> ctrl->ndiv_int.shift) & + bit_mask(ctrl->ndiv_int.width); + ndiv = ndiv_int << ctrl->ndiv_int.shift; + + if (ctrl->flags & IPROC_CLK_PLL_HAS_NDIV_FRAC) { + val = readl(pll->pll_base + ctrl->ndiv_frac.offset); + ndiv_frac = (val >> ctrl->ndiv_frac.shift) & + bit_mask(ctrl->ndiv_frac.width); + + if (ndiv_frac != 0) + ndiv = (ndiv_int << ctrl->ndiv_int.shift) | ndiv_frac; + } + + val = readl(pll->pll_base + ctrl->pdiv.offset); + pdiv = (val >> ctrl->pdiv.shift) & bit_mask(ctrl->pdiv.width); + + clk->rate = (ndiv * parent_rate) >> ctrl->ndiv_int.shift; + + if (pdiv == 0) + clk->rate *= 2; + else + clk->rate /= pdiv; + + return clk->rate; +} + +static long iproc_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + unsigned i; + struct iproc_clk *clk = to_iproc_clk(hw); + struct iproc_pll *pll = clk->pll; + + if (rate == 0 || *parent_rate == 0 || !pll->vco_param) + return -EINVAL; + + for (i = 0; i < pll->num_vco_entries; i++) { + if (rate <= pll->vco_param[i].rate) + break; + } + + if (i == pll->num_vco_entries) + i--; + + return pll->vco_param[i].rate; +} + +static int iproc_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct iproc_clk *clk = to_iproc_clk(hw); + struct iproc_pll *pll = clk->pll; + int rate_index, ret; + + rate_index = pll_get_rate_index(pll, rate); + if (rate_index < 0) + return rate_index; + + ret = pll_set_rate(clk, rate_index, parent_rate); + return ret; +} + +static const struct clk_ops iproc_pll_ops = { + .enable = iproc_pll_enable, + .disable = iproc_pll_disable, + .recalc_rate = iproc_pll_recalc_rate, + .round_rate = iproc_pll_round_rate, + .set_rate = iproc_pll_set_rate, +}; + +static int iproc_clk_enable(struct clk_hw *hw) +{ + struct iproc_clk *clk = to_iproc_clk(hw); + const struct iproc_clk_ctrl *ctrl = clk->ctrl; + struct iproc_pll *pll = clk->pll; + u32 val; + + /* channel enable is active low */ + val = readl(pll->pll_base + ctrl->enable.offset); + val &= ~(1 << ctrl->enable.enable_shift); + writel(val, pll->pll_base + ctrl->enable.offset); + + /* also make sure channel is not held */ + val = readl(pll->pll_base + ctrl->enable.offset); + val &= ~(1 << ctrl->enable.hold_shift); + writel(val, pll->pll_base + ctrl->enable.offset); + if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK)) + readl(pll->pll_base + ctrl->enable.offset); + + return 0; +} + +static void iproc_clk_disable(struct clk_hw *hw) +{ + struct iproc_clk *clk = to_iproc_clk(hw); + const struct iproc_clk_ctrl *ctrl = clk->ctrl; + struct iproc_pll *pll = clk->pll; + u32 val; + + if (ctrl->flags & IPROC_CLK_AON) + return; + + val = readl(pll->pll_base + ctrl->enable.offset); + val |= 1 << ctrl->enable.enable_shift; + writel(val, pll->pll_base + ctrl->enable.offset); + if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK)) + readl(pll->pll_base + ctrl->enable.offset); +} + +static unsigned long iproc_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct iproc_clk *clk = to_iproc_clk(hw); + const struct iproc_clk_ctrl *ctrl = clk->ctrl; + struct iproc_pll *pll = clk->pll; + u32 val; + unsigned int mdiv; + + if (parent_rate == 0) + return 0; + + val = readl(pll->pll_base + ctrl->mdiv.offset); + mdiv = (val >> ctrl->mdiv.shift) & bit_mask(ctrl->mdiv.width); + if (mdiv == 0) + mdiv = 256; + + clk->rate = parent_rate / mdiv; + + return clk->rate; +} + +static long iproc_clk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + unsigned int div; + + if (rate == 0 || *parent_rate == 0) + return -EINVAL; + + if (rate == *parent_rate) + return *parent_rate; + + div = DIV_ROUND_UP(*parent_rate, rate); + if (div < 2) + return *parent_rate; + + if (div > 256) + div = 256; + + return *parent_rate / div; +} + +static int iproc_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct iproc_clk *clk = to_iproc_clk(hw); + const struct iproc_clk_ctrl *ctrl = clk->ctrl; + struct iproc_pll *pll = clk->pll; + u32 val; + unsigned int div; + + if (rate == 0 || parent_rate == 0) + return -EINVAL; + + div = DIV_ROUND_UP(parent_rate, rate); + if (div > 256) + return -EINVAL; + + val = readl(pll->pll_base + ctrl->mdiv.offset); + if (div == 256) { + val &= ~(bit_mask(ctrl->mdiv.width) << ctrl->mdiv.shift); + } else { + val &= ~(bit_mask(ctrl->mdiv.width) << ctrl->mdiv.shift); + val |= div << ctrl->mdiv.shift; + } + writel(val, pll->pll_base + ctrl->mdiv.offset); + if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK)) + readl(pll->pll_base + ctrl->mdiv.offset); + clk->rate = parent_rate / div; + + return 0; +} + +static const struct clk_ops iproc_clk_ops = { + .enable = iproc_clk_enable, + .disable = iproc_clk_disable, + .recalc_rate = iproc_clk_recalc_rate, + .round_rate = iproc_clk_round_rate, + .set_rate = iproc_clk_set_rate, +}; + +/** + * Some PLLs require the PLL SW override bit to be set before changes can be + * applied to the PLL + */ +static void iproc_pll_sw_cfg(struct iproc_pll *pll) +{ + const struct iproc_pll_ctrl *ctrl = pll->ctrl; + + if (ctrl->flags & IPROC_CLK_PLL_NEEDS_SW_CFG) { + u32 val; + + val = readl(pll->pll_base + ctrl->sw_ctrl.offset); + val |= BIT(ctrl->sw_ctrl.shift); + writel(val, pll->pll_base + ctrl->sw_ctrl.offset); + if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK)) + readl(pll->pll_base + ctrl->sw_ctrl.offset); + } +} + +void __init iproc_pll_clk_setup(struct device_node *node, + const struct iproc_pll_ctrl *pll_ctrl, + const struct iproc_pll_vco_param *vco, + unsigned int num_vco_entries, + const struct iproc_clk_ctrl *clk_ctrl, + unsigned int num_clks) +{ + int i, ret; + struct clk *clk; + struct iproc_pll *pll; + struct iproc_clk *iclk; + struct clk_init_data init; + const char *parent_name; + + if (WARN_ON(!pll_ctrl) || WARN_ON(!clk_ctrl)) + return; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (WARN_ON(!pll)) + return; + + pll->clk_data.clk_num = num_clks; + pll->clk_data.clks = kcalloc(num_clks, sizeof(*pll->clk_data.clks), + GFP_KERNEL); + if (WARN_ON(!pll->clk_data.clks)) + goto err_clk_data; + + pll->clks = kcalloc(num_clks, sizeof(*pll->clks), GFP_KERNEL); + if (WARN_ON(!pll->clks)) + goto err_clks; + + pll->pll_base = of_iomap(node, 0); + if (WARN_ON(!pll->pll_base)) + goto err_pll_iomap; + + pll->pwr_base = of_iomap(node, 1); + if (WARN_ON(!pll->pwr_base)) + goto err_pwr_iomap; + + /* some PLLs require gating control at the top ASIU level */ + if (pll_ctrl->flags & IPROC_CLK_PLL_ASIU) { + pll->asiu_base = of_iomap(node, 2); + if (WARN_ON(!pll->asiu_base)) + goto err_asiu_iomap; + } + + /* initialize and register the PLL itself */ + pll->ctrl = pll_ctrl; + + iclk = &pll->clks[0]; + iclk->pll = pll; + iclk->name = node->name; + + init.name = node->name; + init.ops = &iproc_pll_ops; + init.flags = 0; + parent_name = of_clk_get_parent_name(node, 0); + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + iclk->hw.init = &init; + + if (vco) { + pll->num_vco_entries = num_vco_entries; + pll->vco_param = vco; + } + + iproc_pll_sw_cfg(pll); + + clk = clk_register(NULL, &iclk->hw); + if (WARN_ON(IS_ERR(clk))) + goto err_pll_register; + + pll->clk_data.clks[0] = clk; + + /* now initialize and register all leaf clocks */ + for (i = 1; i < num_clks; i++) { + const char *clk_name; + + memset(&init, 0, sizeof(init)); + parent_name = node->name; + + clk_name = kzalloc(IPROC_CLK_NAME_LEN, GFP_KERNEL); + if (WARN_ON(!clk_name)) + goto err_clk_register; + + ret = of_property_read_string_index(node, "clock-output-names", + i, &clk_name); + if (WARN_ON(ret)) + goto err_clk_register; + + iclk = &pll->clks[i]; + iclk->name = clk_name; + iclk->pll = pll; + iclk->ctrl = &clk_ctrl[i]; + + init.name = clk_name; + init.ops = &iproc_clk_ops; + init.flags = 0; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + iclk->hw.init = &init; + + clk = clk_register(NULL, &iclk->hw); + if (WARN_ON(IS_ERR(clk))) + goto err_clk_register; + + pll->clk_data.clks[i] = clk; + } + + ret = of_clk_add_provider(node, of_clk_src_onecell_get, &pll->clk_data); + if (WARN_ON(ret)) + goto err_clk_register; + + return; + +err_clk_register: + for (i = 0; i < num_clks; i++) { + kfree(pll->clks[i].name); + clk_unregister(pll->clk_data.clks[i]); + } + +err_pll_register: + if (pll->asiu_base) + iounmap(pll->asiu_base); + +err_asiu_iomap: + iounmap(pll->pwr_base); + +err_pwr_iomap: + iounmap(pll->pll_base); + +err_pll_iomap: + kfree(pll->clks); + +err_clks: + kfree(pll->clk_data.clks); + +err_clk_data: + kfree(pll); +} diff --git a/drivers/clk/bcm/clk-iproc.h b/drivers/clk/bcm/clk-iproc.h new file mode 100644 index 000000000000..d834b7abd5c6 --- /dev/null +++ b/drivers/clk/bcm/clk-iproc.h @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2014 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _CLK_IPROC_H +#define _CLK_IPROC_H + +#include +#include +#include +#include +#include +#include +#include + +#define IPROC_CLK_NAME_LEN 25 +#define IPROC_CLK_INVALID_OFFSET 0xffffffff +#define bit_mask(width) ((1 << (width)) - 1) + +/* clocks that should not be disabled at runtime */ +#define IPROC_CLK_AON BIT(0) + +/* PLL that requires gating through ASIU */ +#define IPROC_CLK_PLL_ASIU BIT(1) + +/* PLL that has fractional part of the NDIV */ +#define IPROC_CLK_PLL_HAS_NDIV_FRAC BIT(2) + +/* + * Some of the iProc PLL/clocks may have an ASIC bug that requires read back + * of the same register following the write to flush the write transaction into + * the intended register + */ +#define IPROC_CLK_NEEDS_READ_BACK BIT(3) + +/* + * Some PLLs require the PLL SW override bit to be set before changes can be + * applied to the PLL + */ +#define IPROC_CLK_PLL_NEEDS_SW_CFG BIT(4) + +/* + * Parameters for VCO frequency configuration + * + * VCO frequency = + * ((ndiv_int + ndiv_frac / 2^20) * (ref freqeuncy / pdiv) + */ +struct iproc_pll_vco_param { + unsigned long rate; + unsigned int ndiv_int; + unsigned int ndiv_frac; + unsigned int pdiv; +}; + +struct iproc_clk_reg_op { + unsigned int offset; + unsigned int shift; + unsigned int width; +}; + +/* + * Clock gating control at the top ASIU level + */ +struct iproc_asiu_gate { + unsigned int offset; + unsigned int en_shift; +}; + +/* + * Control of powering on/off of a PLL + * + * Before powering off a PLL, input isolation (ISO) needs to be enabled + */ +struct iproc_pll_aon_pwr_ctrl { + unsigned int offset; + unsigned int pwr_width; + unsigned int pwr_shift; + unsigned int iso_shift; +}; + +/* + * Control of the PLL reset, with Ki, Kp, and Ka parameters + */ +struct iproc_pll_reset_ctrl { + unsigned int offset; + unsigned int reset_shift; + unsigned int p_reset_shift; + unsigned int ki_shift; + unsigned int ki_width; + unsigned int kp_shift; + unsigned int kp_width; + unsigned int ka_shift; + unsigned int ka_width; +}; + +/* + * To enable SW control of the PLL + */ +struct iproc_pll_sw_ctrl { + unsigned int offset; + unsigned int shift; +}; + +struct iproc_pll_vco_ctrl { + unsigned int u_offset; + unsigned int l_offset; +}; + +/* + * Main PLL control parameters + */ +struct iproc_pll_ctrl { + unsigned long flags; + struct iproc_pll_aon_pwr_ctrl aon; + struct iproc_asiu_gate asiu; + struct iproc_pll_reset_ctrl reset; + struct iproc_pll_sw_ctrl sw_ctrl; + struct iproc_clk_reg_op ndiv_int; + struct iproc_clk_reg_op ndiv_frac; + struct iproc_clk_reg_op pdiv; + struct iproc_pll_vco_ctrl vco_ctrl; + struct iproc_clk_reg_op status; +}; + +/* + * Controls enabling/disabling a PLL derived clock + */ +struct iproc_clk_enable_ctrl { + unsigned int offset; + unsigned int enable_shift; + unsigned int hold_shift; + unsigned int bypass_shift; +}; + +/* + * Main clock control parameters for clocks derived from the PLLs + */ +struct iproc_clk_ctrl { + unsigned int channel; + unsigned long flags; + struct iproc_clk_enable_ctrl enable; + struct iproc_clk_reg_op mdiv; +}; + +/* + * Divisor of the ASIU clocks + */ +struct iproc_asiu_div { + unsigned int offset; + unsigned int en_shift; + unsigned int high_shift; + unsigned int high_width; + unsigned int low_shift; + unsigned int low_width; +}; + +void __init iproc_armpll_setup(struct device_node *node); +void __init iproc_pll_clk_setup(struct device_node *node, + const struct iproc_pll_ctrl *pll_ctrl, + const struct iproc_pll_vco_param *vco, + unsigned int num_vco_entries, + const struct iproc_clk_ctrl *clk_ctrl, + unsigned int num_clks); +void __init iproc_asiu_setup(struct device_node *node, + const struct iproc_asiu_div *div, + const struct iproc_asiu_gate *gate, + unsigned int num_clks); + +#endif /* _CLK_IPROC_H */ diff --git a/drivers/clk/bcm/clk-kona-setup.c b/drivers/clk/bcm/clk-kona-setup.c index e5aededdd322..deaa7f962b84 100644 --- a/drivers/clk/bcm/clk-kona-setup.c +++ b/drivers/clk/bcm/clk-kona-setup.c @@ -21,8 +21,6 @@ #define selector_clear_exists(sel) ((sel)->width = 0) #define trigger_clear_exists(trig) FLAG_CLEAR(trig, TRIG, EXISTS) -LIST_HEAD(ccu_list); /* The list of set up CCUs */ - /* Validity checking */ static bool ccu_data_offsets_valid(struct ccu_data *ccu) @@ -773,7 +771,6 @@ static void kona_ccu_teardown(struct ccu_data *ccu) of_clk_del_provider(ccu->node); /* safe if never added */ ccu_clks_teardown(ccu); - list_del(&ccu->links); of_node_put(ccu->node); ccu->node = NULL; iounmap(ccu->base); @@ -847,7 +844,6 @@ void __init kona_dt_ccu_setup(struct ccu_data *ccu, goto out_err; } ccu->node = of_node_get(node); - list_add_tail(&ccu->links, &ccu_list); /* * Set up each defined kona clock and save the result in diff --git a/drivers/clk/bcm/clk-kona.c b/drivers/clk/bcm/clk-kona.c index a0ef4f75d457..79a98506c433 100644 --- a/drivers/clk/bcm/clk-kona.c +++ b/drivers/clk/bcm/clk-kona.c @@ -1240,7 +1240,7 @@ static bool __kona_clk_init(struct kona_clk *bcm_clk) default: BUG(); } - return -EINVAL; + return false; } /* Set a CCU and all its clocks into their desired initial state */ diff --git a/drivers/clk/bcm/clk-kona.h b/drivers/clk/bcm/clk-kona.h index 6849a64baf6d..906576ec97b6 100644 --- a/drivers/clk/bcm/clk-kona.h +++ b/drivers/clk/bcm/clk-kona.h @@ -480,7 +480,6 @@ struct ccu_data { spinlock_t lock; /* serialization lock */ bool write_enabled; /* write access is currently enabled */ struct ccu_policy policy; - struct list_head links; /* for ccu_list */ struct device_node *node; struct clk_onecell_data clk_data; const char *name; @@ -492,7 +491,6 @@ struct ccu_data { #define KONA_CCU_COMMON(_prefix, _name, _ccuname) \ .name = #_name "_ccu", \ .lock = __SPIN_LOCK_UNLOCKED(_name ## _ccu_data.lock), \ - .links = LIST_HEAD_INIT(_name ## _ccu_data.links), \ .clk_data = { \ .clk_num = _prefix ## _ ## _ccuname ## _CCU_CLOCK_COUNT, \ } diff --git a/drivers/clk/berlin/berlin2-pll.c b/drivers/clk/berlin/berlin2-pll.c index bdc506b03824..f4b8d324b083 100644 --- a/drivers/clk/berlin/berlin2-pll.c +++ b/drivers/clk/berlin/berlin2-pll.c @@ -25,14 +25,7 @@ #include #include "berlin2-div.h" - -struct berlin2_pll_map { - const u8 vcodiv[16]; - u8 mult; - u8 fbdiv_shift; - u8 rfdiv_shift; - u8 divsel_shift; -}; +#include "berlin2-pll.h" struct berlin2_pll { struct clk_hw hw; diff --git a/drivers/clk/clk-asm9260.c b/drivers/clk/clk-asm9260.c index 88f4ff6916fe..90897af8d9f7 100644 --- a/drivers/clk/clk-asm9260.c +++ b/drivers/clk/clk-asm9260.c @@ -274,7 +274,7 @@ static void __init asm9260_acc_init(struct device_node *np) u32 accuracy = 0; base = of_io_request_and_map(np, 0, np->name); - if (!base) + if (IS_ERR(base)) panic("%s: unable to map resource", np->name); /* register pll */ diff --git a/drivers/clk/clk-axm5516.c b/drivers/clk/clk-axm5516.c index 0f6368ceec4c..c7c91a5ecf8b 100644 --- a/drivers/clk/clk-axm5516.c +++ b/drivers/clk/clk-axm5516.c @@ -556,7 +556,7 @@ static int axmclk_probe(struct platform_device *pdev) return PTR_ERR(regmap); num_clks = ARRAY_SIZE(axmclk_clocks); - pr_info("axmclk: supporting %u clocks\n", num_clks); + pr_info("axmclk: supporting %zu clocks\n", num_clks); priv = devm_kzalloc(dev, sizeof(*priv) + sizeof(*priv->clks) * num_clks, GFP_KERNEL); if (!priv) diff --git a/drivers/clk/clk-cdce706.c b/drivers/clk/clk-cdce706.c index b8e4f8a822e9..f01164fada5d 100644 --- a/drivers/clk/clk-cdce706.c +++ b/drivers/clk/clk-cdce706.c @@ -94,7 +94,7 @@ static const char * const cdce706_source_name[] = { "clk_in0", "clk_in1", }; -static const char *cdce706_clkin_name[] = { +static const char * const cdce706_clkin_name[] = { "clk_in", }; @@ -102,7 +102,7 @@ static const char * const cdce706_pll_name[] = { "pll1", "pll2", "pll3", }; -static const char *cdce706_divider_parent_name[] = { +static const char * const cdce706_divider_parent_name[] = { "clk_in", "pll1", "pll2", "pll2", "pll3", }; @@ -666,6 +666,7 @@ static int cdce706_probe(struct i2c_client *client, static int cdce706_remove(struct i2c_client *client) { + of_clk_del_provider(client->dev.of_node); return 0; } diff --git a/drivers/clk/clk-cdce925.c b/drivers/clk/clk-cdce925.c new file mode 100644 index 000000000000..85fafb41e6ca --- /dev/null +++ b/drivers/clk/clk-cdce925.c @@ -0,0 +1,749 @@ +/* + * Driver for TI Dual PLL CDCE925 clock synthesizer + * + * This driver always connects the Y1 to the input clock, Y2/Y3 to PLL1 + * and Y4/Y5 to PLL2. PLL frequency is set on a first-come-first-serve + * basis. Clients can directly request any frequency that the chip can + * deliver using the standard clk framework. In addition, the device can + * be configured and activated via the devicetree. + * + * Copyright (C) 2014, Topic Embedded Products + * Licenced under GPL + */ +#include +#include +#include +#include +#include +#include +#include + +/* The chip has 2 PLLs which can be routed through dividers to 5 outputs. + * Model this as 2 PLL clocks which are parents to the outputs. + */ +#define NUMBER_OF_PLLS 2 +#define NUMBER_OF_OUTPUTS 5 + +#define CDCE925_REG_GLOBAL1 0x01 +#define CDCE925_REG_Y1SPIPDIVH 0x02 +#define CDCE925_REG_PDIVL 0x03 +#define CDCE925_REG_XCSEL 0x05 +/* PLL parameters start at 0x10, steps of 0x10 */ +#define CDCE925_OFFSET_PLL 0x10 +/* Add CDCE925_OFFSET_PLL * (pll) to these registers before sending */ +#define CDCE925_PLL_MUX_OUTPUTS 0x14 +#define CDCE925_PLL_MULDIV 0x18 + +#define CDCE925_PLL_FREQUENCY_MIN 80000000ul +#define CDCE925_PLL_FREQUENCY_MAX 230000000ul +struct clk_cdce925_chip; + +struct clk_cdce925_output { + struct clk_hw hw; + struct clk_cdce925_chip *chip; + u8 index; + u16 pdiv; /* 1..127 for Y2-Y5; 1..1023 for Y1 */ +}; +#define to_clk_cdce925_output(_hw) \ + container_of(_hw, struct clk_cdce925_output, hw) + +struct clk_cdce925_pll { + struct clk_hw hw; + struct clk_cdce925_chip *chip; + u8 index; + u16 m; /* 1..511 */ + u16 n; /* 1..4095 */ +}; +#define to_clk_cdce925_pll(_hw) container_of(_hw, struct clk_cdce925_pll, hw) + +struct clk_cdce925_chip { + struct regmap *regmap; + struct i2c_client *i2c_client; + struct clk_cdce925_pll pll[NUMBER_OF_PLLS]; + struct clk_cdce925_output clk[NUMBER_OF_OUTPUTS]; + struct clk *dt_clk[NUMBER_OF_OUTPUTS]; + struct clk_onecell_data onecell; +}; + +/* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */ + +static unsigned long cdce925_pll_calculate_rate(unsigned long parent_rate, + u16 n, u16 m) +{ + if ((!m || !n) || (m == n)) + return parent_rate; /* In bypass mode runs at same frequency */ + return mult_frac(parent_rate, (unsigned long)n, (unsigned long)m); +} + +static unsigned long cdce925_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + /* Output frequency of PLL is Fout = (Fin/Pdiv)*(N/M) */ + struct clk_cdce925_pll *data = to_clk_cdce925_pll(hw); + + return cdce925_pll_calculate_rate(parent_rate, data->n, data->m); +} + +static void cdce925_pll_find_rate(unsigned long rate, + unsigned long parent_rate, u16 *n, u16 *m) +{ + unsigned long un; + unsigned long um; + unsigned long g; + + if (rate <= parent_rate) { + /* Can always deliver parent_rate in bypass mode */ + rate = parent_rate; + *n = 0; + *m = 0; + } else { + /* In PLL mode, need to apply min/max range */ + if (rate < CDCE925_PLL_FREQUENCY_MIN) + rate = CDCE925_PLL_FREQUENCY_MIN; + else if (rate > CDCE925_PLL_FREQUENCY_MAX) + rate = CDCE925_PLL_FREQUENCY_MAX; + + g = gcd(rate, parent_rate); + um = parent_rate / g; + un = rate / g; + /* When outside hw range, reduce to fit (rounding errors) */ + while ((un > 4095) || (um > 511)) { + un >>= 1; + um >>= 1; + } + if (un == 0) + un = 1; + if (um == 0) + um = 1; + + *n = un; + *m = um; + } +} + +static long cdce925_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + u16 n, m; + + cdce925_pll_find_rate(rate, *parent_rate, &n, &m); + return (long)cdce925_pll_calculate_rate(*parent_rate, n, m); +} + +static int cdce925_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_cdce925_pll *data = to_clk_cdce925_pll(hw); + + if (!rate || (rate == parent_rate)) { + data->m = 0; /* Bypass mode */ + data->n = 0; + return 0; + } + + if ((rate < CDCE925_PLL_FREQUENCY_MIN) || + (rate > CDCE925_PLL_FREQUENCY_MAX)) { + pr_debug("%s: rate %lu outside PLL range.\n", __func__, rate); + return -EINVAL; + } + + if (rate < parent_rate) { + pr_debug("%s: rate %lu less than parent rate %lu.\n", __func__, + rate, parent_rate); + return -EINVAL; + } + + cdce925_pll_find_rate(rate, parent_rate, &data->n, &data->m); + return 0; +} + + +/* calculate p = max(0, 4 - int(log2 (n/m))) */ +static u8 cdce925_pll_calc_p(u16 n, u16 m) +{ + u8 p; + u16 r = n / m; + + if (r >= 16) + return 0; + p = 4; + while (r > 1) { + r >>= 1; + --p; + } + return p; +} + +/* Returns VCO range bits for VCO1_0_RANGE */ +static u8 cdce925_pll_calc_range_bits(struct clk_hw *hw, u16 n, u16 m) +{ + struct clk *parent = clk_get_parent(hw->clk); + unsigned long rate = clk_get_rate(parent); + + rate = mult_frac(rate, (unsigned long)n, (unsigned long)m); + if (rate >= 175000000) + return 0x3; + if (rate >= 150000000) + return 0x02; + if (rate >= 125000000) + return 0x01; + return 0x00; +} + +/* I2C clock, hence everything must happen in (un)prepare because this + * may sleep */ +static int cdce925_pll_prepare(struct clk_hw *hw) +{ + struct clk_cdce925_pll *data = to_clk_cdce925_pll(hw); + u16 n = data->n; + u16 m = data->m; + u16 r; + u8 q; + u8 p; + u16 nn; + u8 pll[4]; /* Bits are spread out over 4 byte registers */ + u8 reg_ofs = data->index * CDCE925_OFFSET_PLL; + unsigned i; + + if ((!m || !n) || (m == n)) { + /* Set PLL mux to bypass mode, leave the rest as is */ + regmap_update_bits(data->chip->regmap, + reg_ofs + CDCE925_PLL_MUX_OUTPUTS, 0x80, 0x80); + } else { + /* According to data sheet: */ + /* p = max(0, 4 - int(log2 (n/m))) */ + p = cdce925_pll_calc_p(n, m); + /* nn = n * 2^p */ + nn = n * BIT(p); + /* q = int(nn/m) */ + q = nn / m; + if ((q < 16) || (1 > 64)) { + pr_debug("%s invalid q=%d\n", __func__, q); + return -EINVAL; + } + r = nn - (m*q); + if (r > 511) { + pr_debug("%s invalid r=%d\n", __func__, r); + return -EINVAL; + } + pr_debug("%s n=%d m=%d p=%d q=%d r=%d\n", __func__, + n, m, p, q, r); + /* encode into register bits */ + pll[0] = n >> 4; + pll[1] = ((n & 0x0F) << 4) | ((r >> 5) & 0x0F); + pll[2] = ((r & 0x1F) << 3) | ((q >> 3) & 0x07); + pll[3] = ((q & 0x07) << 5) | (p << 2) | + cdce925_pll_calc_range_bits(hw, n, m); + /* Write to registers */ + for (i = 0; i < ARRAY_SIZE(pll); ++i) + regmap_write(data->chip->regmap, + reg_ofs + CDCE925_PLL_MULDIV + i, pll[i]); + /* Enable PLL */ + regmap_update_bits(data->chip->regmap, + reg_ofs + CDCE925_PLL_MUX_OUTPUTS, 0x80, 0x00); + } + + return 0; +} + +static void cdce925_pll_unprepare(struct clk_hw *hw) +{ + struct clk_cdce925_pll *data = to_clk_cdce925_pll(hw); + u8 reg_ofs = data->index * CDCE925_OFFSET_PLL; + + regmap_update_bits(data->chip->regmap, + reg_ofs + CDCE925_PLL_MUX_OUTPUTS, 0x80, 0x80); +} + +static const struct clk_ops cdce925_pll_ops = { + .prepare = cdce925_pll_prepare, + .unprepare = cdce925_pll_unprepare, + .recalc_rate = cdce925_pll_recalc_rate, + .round_rate = cdce925_pll_round_rate, + .set_rate = cdce925_pll_set_rate, +}; + + +static void cdce925_clk_set_pdiv(struct clk_cdce925_output *data, u16 pdiv) +{ + switch (data->index) { + case 0: + regmap_update_bits(data->chip->regmap, + CDCE925_REG_Y1SPIPDIVH, + 0x03, (pdiv >> 8) & 0x03); + regmap_write(data->chip->regmap, 0x03, pdiv & 0xFF); + break; + case 1: + regmap_update_bits(data->chip->regmap, 0x16, 0x7F, pdiv); + break; + case 2: + regmap_update_bits(data->chip->regmap, 0x17, 0x7F, pdiv); + break; + case 3: + regmap_update_bits(data->chip->regmap, 0x26, 0x7F, pdiv); + break; + case 4: + regmap_update_bits(data->chip->regmap, 0x27, 0x7F, pdiv); + break; + } +} + +static void cdce925_clk_activate(struct clk_cdce925_output *data) +{ + switch (data->index) { + case 0: + regmap_update_bits(data->chip->regmap, + CDCE925_REG_Y1SPIPDIVH, 0x0c, 0x0c); + break; + case 1: + case 2: + regmap_update_bits(data->chip->regmap, 0x14, 0x03, 0x03); + break; + case 3: + case 4: + regmap_update_bits(data->chip->regmap, 0x24, 0x03, 0x03); + break; + } +} + +static int cdce925_clk_prepare(struct clk_hw *hw) +{ + struct clk_cdce925_output *data = to_clk_cdce925_output(hw); + + cdce925_clk_set_pdiv(data, data->pdiv); + cdce925_clk_activate(data); + return 0; +} + +static void cdce925_clk_unprepare(struct clk_hw *hw) +{ + struct clk_cdce925_output *data = to_clk_cdce925_output(hw); + + /* Disable clock by setting divider to "0" */ + cdce925_clk_set_pdiv(data, 0); +} + +static unsigned long cdce925_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_cdce925_output *data = to_clk_cdce925_output(hw); + + if (data->pdiv) + return parent_rate / data->pdiv; + return 0; +} + +static u16 cdce925_calc_divider(unsigned long rate, + unsigned long parent_rate) +{ + unsigned long divider; + + if (!rate) + return 0; + if (rate >= parent_rate) + return 1; + + divider = DIV_ROUND_CLOSEST(parent_rate, rate); + if (divider > 0x7F) + divider = 0x7F; + + return (u16)divider; +} + +static unsigned long cdce925_clk_best_parent_rate( + struct clk_hw *hw, unsigned long rate) +{ + struct clk *pll = clk_get_parent(hw->clk); + struct clk *root = clk_get_parent(pll); + unsigned long root_rate = clk_get_rate(root); + unsigned long best_rate_error = rate; + u16 pdiv_min; + u16 pdiv_max; + u16 pdiv_best; + u16 pdiv_now; + + if (root_rate % rate == 0) + return root_rate; /* Don't need the PLL, use bypass */ + + pdiv_min = (u16)max(1ul, DIV_ROUND_UP(CDCE925_PLL_FREQUENCY_MIN, rate)); + pdiv_max = (u16)min(127ul, CDCE925_PLL_FREQUENCY_MAX / rate); + + if (pdiv_min > pdiv_max) + return 0; /* No can do? */ + + pdiv_best = pdiv_min; + for (pdiv_now = pdiv_min; pdiv_now < pdiv_max; ++pdiv_now) { + unsigned long target_rate = rate * pdiv_now; + long pll_rate = clk_round_rate(pll, target_rate); + unsigned long actual_rate; + unsigned long rate_error; + + if (pll_rate <= 0) + continue; + actual_rate = pll_rate / pdiv_now; + rate_error = abs((long)actual_rate - (long)rate); + if (rate_error < best_rate_error) { + pdiv_best = pdiv_now; + best_rate_error = rate_error; + } + /* TODO: Consider PLL frequency based on smaller n/m values + * and pick the better one if the error is equal */ + } + + return rate * pdiv_best; +} + +static long cdce925_clk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + unsigned long l_parent_rate = *parent_rate; + u16 divider = cdce925_calc_divider(rate, l_parent_rate); + + if (l_parent_rate / divider != rate) { + l_parent_rate = cdce925_clk_best_parent_rate(hw, rate); + divider = cdce925_calc_divider(rate, l_parent_rate); + *parent_rate = l_parent_rate; + } + + if (divider) + return (long)(l_parent_rate / divider); + return 0; +} + +static int cdce925_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_cdce925_output *data = to_clk_cdce925_output(hw); + + data->pdiv = cdce925_calc_divider(rate, parent_rate); + + return 0; +} + +static const struct clk_ops cdce925_clk_ops = { + .prepare = cdce925_clk_prepare, + .unprepare = cdce925_clk_unprepare, + .recalc_rate = cdce925_clk_recalc_rate, + .round_rate = cdce925_clk_round_rate, + .set_rate = cdce925_clk_set_rate, +}; + + +static u16 cdce925_y1_calc_divider(unsigned long rate, + unsigned long parent_rate) +{ + unsigned long divider; + + if (!rate) + return 0; + if (rate >= parent_rate) + return 1; + + divider = DIV_ROUND_CLOSEST(parent_rate, rate); + if (divider > 0x3FF) /* Y1 has 10-bit divider */ + divider = 0x3FF; + + return (u16)divider; +} + +static long cdce925_clk_y1_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + unsigned long l_parent_rate = *parent_rate; + u16 divider = cdce925_y1_calc_divider(rate, l_parent_rate); + + if (divider) + return (long)(l_parent_rate / divider); + return 0; +} + +static int cdce925_clk_y1_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_cdce925_output *data = to_clk_cdce925_output(hw); + + data->pdiv = cdce925_y1_calc_divider(rate, parent_rate); + + return 0; +} + +static const struct clk_ops cdce925_clk_y1_ops = { + .prepare = cdce925_clk_prepare, + .unprepare = cdce925_clk_unprepare, + .recalc_rate = cdce925_clk_recalc_rate, + .round_rate = cdce925_clk_y1_round_rate, + .set_rate = cdce925_clk_y1_set_rate, +}; + + +static struct regmap_config cdce925_regmap_config = { + .name = "configuration0", + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .max_register = 0x2F, +}; + +#define CDCE925_I2C_COMMAND_BLOCK_TRANSFER 0x00 +#define CDCE925_I2C_COMMAND_BYTE_TRANSFER 0x80 + +static int cdce925_regmap_i2c_write( + void *context, const void *data, size_t count) +{ + struct device *dev = context; + struct i2c_client *i2c = to_i2c_client(dev); + int ret; + u8 reg_data[2]; + + if (count != 2) + return -ENOTSUPP; + + /* First byte is command code */ + reg_data[0] = CDCE925_I2C_COMMAND_BYTE_TRANSFER | ((u8 *)data)[0]; + reg_data[1] = ((u8 *)data)[1]; + + dev_dbg(&i2c->dev, "%s(%zu) %#x %#x\n", __func__, count, + reg_data[0], reg_data[1]); + + ret = i2c_master_send(i2c, reg_data, count); + if (likely(ret == count)) + return 0; + else if (ret < 0) + return ret; + else + return -EIO; +} + +static int cdce925_regmap_i2c_read(void *context, + const void *reg, size_t reg_size, void *val, size_t val_size) +{ + struct device *dev = context; + struct i2c_client *i2c = to_i2c_client(dev); + struct i2c_msg xfer[2]; + int ret; + u8 reg_data[2]; + + if (reg_size != 1) + return -ENOTSUPP; + + xfer[0].addr = i2c->addr; + xfer[0].flags = 0; + xfer[0].buf = reg_data; + if (val_size == 1) { + reg_data[0] = + CDCE925_I2C_COMMAND_BYTE_TRANSFER | ((u8 *)reg)[0]; + xfer[0].len = 1; + } else { + reg_data[0] = + CDCE925_I2C_COMMAND_BLOCK_TRANSFER | ((u8 *)reg)[0]; + reg_data[1] = val_size; + xfer[0].len = 2; + } + + xfer[1].addr = i2c->addr; + xfer[1].flags = I2C_M_RD; + xfer[1].len = val_size; + xfer[1].buf = val; + + ret = i2c_transfer(i2c->adapter, xfer, 2); + if (likely(ret == 2)) { + dev_dbg(&i2c->dev, "%s(%zu, %zu) %#x %#x\n", __func__, + reg_size, val_size, reg_data[0], *((u8 *)val)); + return 0; + } else if (ret < 0) + return ret; + else + return -EIO; +} + +/* The CDCE925 uses a funky way to read/write registers. Bulk mode is + * just weird, so just use the single byte mode exclusively. */ +static struct regmap_bus regmap_cdce925_bus = { + .write = cdce925_regmap_i2c_write, + .read = cdce925_regmap_i2c_read, +}; + +static int cdce925_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct clk_cdce925_chip *data; + struct device_node *node = client->dev.of_node; + const char *parent_name; + const char *pll_clk_name[NUMBER_OF_PLLS] = {NULL,}; + struct clk_init_data init; + struct clk *clk; + u32 value; + int i; + int err; + struct device_node *np_output; + char child_name[6]; + + dev_dbg(&client->dev, "%s\n", __func__); + data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->i2c_client = client; + data->regmap = devm_regmap_init(&client->dev, ®map_cdce925_bus, + &client->dev, &cdce925_regmap_config); + if (IS_ERR(data->regmap)) { + dev_err(&client->dev, "failed to allocate register map\n"); + return PTR_ERR(data->regmap); + } + i2c_set_clientdata(client, data); + + parent_name = of_clk_get_parent_name(node, 0); + if (!parent_name) { + dev_err(&client->dev, "missing parent clock\n"); + return -ENODEV; + } + dev_dbg(&client->dev, "parent is: %s\n", parent_name); + + if (of_property_read_u32(node, "xtal-load-pf", &value) == 0) + regmap_write(data->regmap, + CDCE925_REG_XCSEL, (value << 3) & 0xF8); + /* PWDN bit */ + regmap_update_bits(data->regmap, CDCE925_REG_GLOBAL1, BIT(4), 0); + + /* Set input source for Y1 to be the XTAL */ + regmap_update_bits(data->regmap, 0x02, BIT(7), 0); + + init.ops = &cdce925_pll_ops; + init.flags = 0; + init.parent_names = &parent_name; + init.num_parents = parent_name ? 1 : 0; + + /* Register PLL clocks */ + for (i = 0; i < NUMBER_OF_PLLS; ++i) { + pll_clk_name[i] = kasprintf(GFP_KERNEL, "%s.pll%d", + client->dev.of_node->name, i); + init.name = pll_clk_name[i]; + data->pll[i].chip = data; + data->pll[i].hw.init = &init; + data->pll[i].index = i; + clk = devm_clk_register(&client->dev, &data->pll[i].hw); + if (IS_ERR(clk)) { + dev_err(&client->dev, "Failed register PLL %d\n", i); + err = PTR_ERR(clk); + goto error; + } + sprintf(child_name, "PLL%d", i+1); + np_output = of_get_child_by_name(node, child_name); + if (!np_output) + continue; + if (!of_property_read_u32(np_output, + "clock-frequency", &value)) { + err = clk_set_rate(clk, value); + if (err) + dev_err(&client->dev, + "unable to set PLL frequency %ud\n", + value); + } + if (!of_property_read_u32(np_output, + "spread-spectrum", &value)) { + u8 flag = of_property_read_bool(np_output, + "spread-spectrum-center") ? 0x80 : 0x00; + regmap_update_bits(data->regmap, + 0x16 + (i*CDCE925_OFFSET_PLL), + 0x80, flag); + regmap_update_bits(data->regmap, + 0x12 + (i*CDCE925_OFFSET_PLL), + 0x07, value & 0x07); + } + } + + /* Register output clock Y1 */ + init.ops = &cdce925_clk_y1_ops; + init.flags = 0; + init.num_parents = 1; + init.parent_names = &parent_name; /* Mux Y1 to input */ + init.name = kasprintf(GFP_KERNEL, "%s.Y1", client->dev.of_node->name); + data->clk[0].chip = data; + data->clk[0].hw.init = &init; + data->clk[0].index = 0; + data->clk[0].pdiv = 1; + clk = devm_clk_register(&client->dev, &data->clk[0].hw); + kfree(init.name); /* clock framework made a copy of the name */ + if (IS_ERR(clk)) { + dev_err(&client->dev, "clock registration Y1 failed\n"); + err = PTR_ERR(clk); + goto error; + } + data->dt_clk[0] = clk; + + /* Register output clocks Y2 .. Y5*/ + init.ops = &cdce925_clk_ops; + init.flags = CLK_SET_RATE_PARENT; + init.num_parents = 1; + for (i = 1; i < NUMBER_OF_OUTPUTS; ++i) { + init.name = kasprintf(GFP_KERNEL, "%s.Y%d", + client->dev.of_node->name, i+1); + data->clk[i].chip = data; + data->clk[i].hw.init = &init; + data->clk[i].index = i; + data->clk[i].pdiv = 1; + switch (i) { + case 1: + case 2: + /* Mux Y2/3 to PLL1 */ + init.parent_names = &pll_clk_name[0]; + break; + case 3: + case 4: + /* Mux Y4/5 to PLL2 */ + init.parent_names = &pll_clk_name[1]; + break; + } + clk = devm_clk_register(&client->dev, &data->clk[i].hw); + kfree(init.name); /* clock framework made a copy of the name */ + if (IS_ERR(clk)) { + dev_err(&client->dev, "clock registration failed\n"); + err = PTR_ERR(clk); + goto error; + } + data->dt_clk[i] = clk; + } + + /* Register the output clocks */ + data->onecell.clk_num = NUMBER_OF_OUTPUTS; + data->onecell.clks = data->dt_clk; + err = of_clk_add_provider(client->dev.of_node, of_clk_src_onecell_get, + &data->onecell); + if (err) + dev_err(&client->dev, "unable to add OF clock provider\n"); + + err = 0; + +error: + for (i = 0; i < NUMBER_OF_PLLS; ++i) + /* clock framework made a copy of the name */ + kfree(pll_clk_name[i]); + + return err; +} + +static const struct i2c_device_id cdce925_id[] = { + { "cdce925", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, cdce925_id); + +static const struct of_device_id clk_cdce925_of_match[] = { + { .compatible = "ti,cdce925" }, + { }, +}; +MODULE_DEVICE_TABLE(of, clk_cdce925_of_match); + +static struct i2c_driver cdce925_driver = { + .driver = { + .name = "cdce925", + .of_match_table = of_match_ptr(clk_cdce925_of_match), + }, + .probe = cdce925_probe, + .id_table = cdce925_id, +}; +module_i2c_driver(cdce925_driver); + +MODULE_AUTHOR("Mike Looijmans "); +MODULE_DESCRIPTION("cdce925 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c index 956b7e54fa1c..616f5aef3c26 100644 --- a/drivers/clk/clk-composite.c +++ b/drivers/clk/clk-composite.c @@ -188,7 +188,7 @@ static void clk_composite_disable(struct clk_hw *hw) } struct clk *clk_register_composite(struct device *dev, const char *name, - const char **parent_names, int num_parents, + const char * const *parent_names, int num_parents, struct clk_hw *mux_hw, const struct clk_ops *mux_ops, struct clk_hw *rate_hw, const struct clk_ops *rate_ops, struct clk_hw *gate_hw, const struct clk_ops *gate_ops, @@ -200,10 +200,8 @@ struct clk *clk_register_composite(struct device *dev, const char *name, struct clk_ops *clk_composite_ops; composite = kzalloc(sizeof(*composite), GFP_KERNEL); - if (!composite) { - pr_err("%s: could not allocate composite clk\n", __func__); + if (!composite) return ERR_PTR(-ENOMEM); - } init.name = name; init.flags = flags | CLK_IS_BASIC; diff --git a/drivers/clk/clk-conf.c b/drivers/clk/clk-conf.c index 48a65b2b4027..43a218f35b19 100644 --- a/drivers/clk/clk-conf.c +++ b/drivers/clk/clk-conf.c @@ -106,8 +106,9 @@ static int __set_clk_rates(struct device_node *node, bool clk_supplier) rc = clk_set_rate(clk, rate); if (rc < 0) - pr_err("clk: couldn't set %s clock rate: %d\n", - __clk_get_name(clk), rc); + pr_err("clk: couldn't set %s clk rate to %d (%d), current rate: %ld\n", + __clk_get_name(clk), rate, rc, + clk_get_rate(clk)); clk_put(clk); } index++; @@ -124,7 +125,7 @@ static int __set_clk_rates(struct device_node *node, bool clk_supplier) * and sets any specified clock parents and rates. The @clk_supplier argument * should be set to true if @node may be also a clock supplier of any clock * listed in its 'assigned-clocks' or 'assigned-clock-parents' properties. - * If @clk_supplier is false the function exits returnning 0 as soon as it + * If @clk_supplier is false the function exits returning 0 as soon as it * determines the @node is also a supplier of any of the clocks. */ int of_clk_set_defaults(struct device_node *node, bool clk_supplier) diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 25006a8bb8e6..706b5783c360 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -430,11 +430,9 @@ static struct clk *_register_divider(struct device *dev, const char *name, } /* allocate the divider */ - div = kzalloc(sizeof(struct clk_divider), GFP_KERNEL); - if (!div) { - pr_err("%s: could not allocate divider clk\n", __func__); + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) return ERR_PTR(-ENOMEM); - } init.name = name; init.ops = &clk_divider_ops; diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c index d9e3f671c2ea..fccabe497f6e 100644 --- a/drivers/clk/clk-fixed-factor.c +++ b/drivers/clk/clk-fixed-factor.c @@ -55,10 +55,16 @@ static long clk_factor_round_rate(struct clk_hw *hw, unsigned long rate, static int clk_factor_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { + /* + * We must report success but we can do so unconditionally because + * clk_factor_round_rate returns values that ensure this call is a + * nop. + */ + return 0; } -struct clk_ops clk_fixed_factor_ops = { +const struct clk_ops clk_fixed_factor_ops = { .round_rate = clk_factor_round_rate, .set_rate = clk_factor_set_rate, .recalc_rate = clk_factor_recalc_rate, @@ -74,10 +80,8 @@ struct clk *clk_register_fixed_factor(struct device *dev, const char *name, struct clk *clk; fix = kmalloc(sizeof(*fix), GFP_KERNEL); - if (!fix) { - pr_err("%s: could not allocate fixed factor clk\n", __func__); + if (!fix) return ERR_PTR(-ENOMEM); - } /* struct clk_fixed_factor assignments */ fix->mult = mult; diff --git a/drivers/clk/clk-fixed-rate.c b/drivers/clk/clk-fixed-rate.c index 0fc56ab6e844..f85ec8d1711f 100644 --- a/drivers/clk/clk-fixed-rate.c +++ b/drivers/clk/clk-fixed-rate.c @@ -65,11 +65,9 @@ struct clk *clk_register_fixed_rate_with_accuracy(struct device *dev, struct clk_init_data init; /* allocate fixed-rate clock */ - fixed = kzalloc(sizeof(struct clk_fixed_rate), GFP_KERNEL); - if (!fixed) { - pr_err("%s: could not allocate fixed clk\n", __func__); + fixed = kzalloc(sizeof(*fixed), GFP_KERNEL); + if (!fixed) return ERR_PTR(-ENOMEM); - } init.name = name; init.ops = &clk_fixed_rate_ops; diff --git a/drivers/clk/clk-fractional-divider.c b/drivers/clk/clk-fractional-divider.c index 6aa72d9d79ba..140eb5844dc4 100644 --- a/drivers/clk/clk-fractional-divider.c +++ b/drivers/clk/clk-fractional-divider.c @@ -109,10 +109,8 @@ struct clk *clk_register_fractional_divider(struct device *dev, struct clk *clk; fd = kzalloc(sizeof(*fd), GFP_KERNEL); - if (!fd) { - dev_err(dev, "could not allocate fractional divider clk\n"); + if (!fd) return ERR_PTR(-ENOMEM); - } init.name = name; init.ops = &clk_fractional_divider_ops; diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c index 3f0e4200cb5d..551dd0672794 100644 --- a/drivers/clk/clk-gate.c +++ b/drivers/clk/clk-gate.c @@ -135,11 +135,9 @@ struct clk *clk_register_gate(struct device *dev, const char *name, } /* allocate the gate */ - gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL); - if (!gate) { - pr_err("%s: could not allocate gated clk\n", __func__); + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) return ERR_PTR(-ENOMEM); - } init.name = name; init.ops = &clk_gate_ops; diff --git a/drivers/clk/clk-gpio-gate.c b/drivers/clk/clk-gpio-gate.c index a71cabedda93..f564e624fb93 100644 --- a/drivers/clk/clk-gpio-gate.c +++ b/drivers/clk/clk-gpio-gate.c @@ -189,7 +189,7 @@ static struct clk *of_clk_gpio_gate_delayed_register_get( /** * of_gpio_gate_clk_setup() - Setup function for gpio controlled clock */ -void __init of_gpio_gate_clk_setup(struct device_node *node) +static void __init of_gpio_gate_clk_setup(struct device_node *node) { struct clk_gpio_gate_delayed_register_data *data; @@ -203,6 +203,5 @@ void __init of_gpio_gate_clk_setup(struct device_node *node) 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-ls1x.c b/drivers/clk/clk-ls1x.c index ca80103ac188..d4c61985f448 100644 --- a/drivers/clk/clk-ls1x.c +++ b/drivers/clk/clk-ls1x.c @@ -80,9 +80,9 @@ static struct clk *__init clk_register_pll(struct device *dev, return clk; } -static const char const *cpu_parents[] = { "cpu_clk_div", "osc_33m_clk", }; -static const char const *ahb_parents[] = { "ahb_clk_div", "osc_33m_clk", }; -static const char const *dc_parents[] = { "dc_clk_div", "osc_33m_clk", }; +static const char * const cpu_parents[] = { "cpu_clk_div", "osc_33m_clk", }; +static const char * const ahb_parents[] = { "ahb_clk_div", "osc_33m_clk", }; +static const char * const dc_parents[] = { "dc_clk_div", "osc_33m_clk", }; void __init ls1x_clk_init(void) { diff --git a/drivers/clk/clk-max-gen.c b/drivers/clk/clk-max-gen.c index 6505049d50f1..35af9cb6da4f 100644 --- a/drivers/clk/clk-max-gen.c +++ b/drivers/clk/clk-max-gen.c @@ -31,6 +31,8 @@ #include #include +#include "clk-max-gen.h" + struct max_gen_clk { struct regmap *regmap; u32 mask; diff --git a/drivers/clk/clk-moxart.c b/drivers/clk/clk-moxart.c index 30a3b6999e10..5181b89c3cb2 100644 --- a/drivers/clk/clk-moxart.c +++ b/drivers/clk/clk-moxart.c @@ -15,7 +15,7 @@ #include #include -void __init moxart_of_pll_clk_init(struct device_node *node) +static void __init moxart_of_pll_clk_init(struct device_node *node) { static void __iomem *base; struct clk *clk, *ref_clk; @@ -53,7 +53,7 @@ void __init moxart_of_pll_clk_init(struct device_node *node) CLK_OF_DECLARE(moxart_pll_clock, "moxa,moxart-pll-clock", moxart_of_pll_clk_init); -void __init moxart_of_apb_clk_init(struct device_node *node) +static void __init moxart_of_apb_clk_init(struct device_node *node) { static void __iomem *base; struct clk *clk, *pll_clk; diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c index 69a094c3783d..6066a01b20ea 100644 --- a/drivers/clk/clk-mux.c +++ b/drivers/clk/clk-mux.c @@ -114,7 +114,8 @@ const struct clk_ops clk_mux_ro_ops = { EXPORT_SYMBOL_GPL(clk_mux_ro_ops); struct clk *clk_register_mux_table(struct device *dev, const char *name, - const char **parent_names, u8 num_parents, unsigned long flags, + const char * const *parent_names, u8 num_parents, + unsigned long flags, void __iomem *reg, u8 shift, u32 mask, u8 clk_mux_flags, u32 *table, spinlock_t *lock) { @@ -166,7 +167,8 @@ struct clk *clk_register_mux_table(struct device *dev, const char *name, EXPORT_SYMBOL_GPL(clk_register_mux_table); struct clk *clk_register_mux(struct device *dev, const char *name, - const char **parent_names, u8 num_parents, unsigned long flags, + const char * const *parent_names, u8 num_parents, + unsigned long flags, void __iomem *reg, u8 shift, u8 width, u8 clk_mux_flags, spinlock_t *lock) { diff --git a/drivers/clk/clk-si5351.c b/drivers/clk/clk-si5351.c index 30335d3b99af..e39e1e680b3c 100644 --- a/drivers/clk/clk-si5351.c +++ b/drivers/clk/clk-si5351.c @@ -552,7 +552,8 @@ static const struct clk_ops si5351_pll_ops = { * MSx_P2[19:0] = 128 * b - c * floor(128 * b/c) = (128*b) mod c * MSx_P3[19:0] = c * - * MS[6,7] are integer (P1) divide only, P2 = 0, P3 = 0 + * MS[6,7] are integer (P1) divide only, P1 = divide value, + * P2 and P3 are not applicable * * for 150MHz < fOUT <= 160MHz: * @@ -606,9 +607,6 @@ static unsigned long si5351_msynth_recalc_rate(struct clk_hw *hw, if (!hwdata->params.valid) si5351_read_parameters(hwdata->drvdata, reg, &hwdata->params); - if (hwdata->params.p3 == 0) - return parent_rate; - /* * multisync0-5: fOUT = (128 * P3 * fIN) / (P1*P3 + P2 + 512*P3) * multisync6-7: fOUT = fIN / P1 @@ -616,6 +614,8 @@ static unsigned long si5351_msynth_recalc_rate(struct clk_hw *hw, rate = parent_rate; if (hwdata->num > 5) { m = hwdata->params.p1; + } else if (hwdata->params.p3 == 0) { + return parent_rate; } else if ((si5351_reg_read(hwdata->drvdata, reg + 2) & SI5351_OUTPUT_CLK_DIVBY4) == SI5351_OUTPUT_CLK_DIVBY4) { m = 4; @@ -679,6 +679,16 @@ static long si5351_msynth_round_rate(struct clk_hw *hw, unsigned long rate, c = 1; *parent_rate = a * rate; + } else if (hwdata->num >= 6) { + /* determine the closest integer divider */ + a = DIV_ROUND_CLOSEST(*parent_rate, rate); + if (a < SI5351_MULTISYNTH_A_MIN) + a = SI5351_MULTISYNTH_A_MIN; + if (a > SI5351_MULTISYNTH67_A_MAX) + a = SI5351_MULTISYNTH67_A_MAX; + + b = 0; + c = 1; } else { unsigned long rfrac, denom; @@ -692,9 +702,7 @@ static long si5351_msynth_round_rate(struct clk_hw *hw, unsigned long rate, a = *parent_rate / rate; if (a < SI5351_MULTISYNTH_A_MIN) a = SI5351_MULTISYNTH_A_MIN; - if (hwdata->num >= 6 && a > SI5351_MULTISYNTH67_A_MAX) - a = SI5351_MULTISYNTH67_A_MAX; - else if (a > SI5351_MULTISYNTH_A_MAX) + if (a > SI5351_MULTISYNTH_A_MAX) a = SI5351_MULTISYNTH_A_MAX; /* find best approximation for b/c = fVCO mod fOUT */ @@ -723,6 +731,10 @@ static long si5351_msynth_round_rate(struct clk_hw *hw, unsigned long rate, hwdata->params.p3 = 1; hwdata->params.p2 = 0; hwdata->params.p1 = 0; + } else if (hwdata->num >= 6) { + hwdata->params.p3 = 0; + hwdata->params.p2 = 0; + hwdata->params.p1 = a; } else { hwdata->params.p3 = c; hwdata->params.p2 = (128 * b) % c; diff --git a/drivers/clk/clk-stm32f4.c b/drivers/clk/clk-stm32f4.c new file mode 100644 index 000000000000..b9b12a742970 --- /dev/null +++ b/drivers/clk/clk-stm32f4.c @@ -0,0 +1,380 @@ +/* + * Author: Daniel Thompson + * + * Inspired by clk-asm9260.c . + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#define STM32F4_RCC_PLLCFGR 0x04 +#define STM32F4_RCC_CFGR 0x08 +#define STM32F4_RCC_AHB1ENR 0x30 +#define STM32F4_RCC_AHB2ENR 0x34 +#define STM32F4_RCC_AHB3ENR 0x38 +#define STM32F4_RCC_APB1ENR 0x40 +#define STM32F4_RCC_APB2ENR 0x44 + +struct stm32f4_gate_data { + u8 offset; + u8 bit_idx; + const char *name; + const char *parent_name; + unsigned long flags; +}; + +static const struct stm32f4_gate_data stm32f4_gates[] __initconst = { + { STM32F4_RCC_AHB1ENR, 0, "gpioa", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 1, "gpiob", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 2, "gpioc", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 3, "gpiod", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 4, "gpioe", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 5, "gpiof", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 6, "gpiog", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 7, "gpioh", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 8, "gpioi", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 9, "gpioj", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 10, "gpiok", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 12, "crc", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 18, "bkpsra", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 20, "ccmdatam", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 21, "dma1", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 22, "dma2", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 23, "dma2d", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 25, "ethmac", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 26, "ethmactx", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 27, "ethmacrx", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 28, "ethmacptp", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 29, "otghs", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 30, "otghsulpi", "ahb_div" }, + + { STM32F4_RCC_AHB2ENR, 0, "dcmi", "ahb_div" }, + { STM32F4_RCC_AHB2ENR, 4, "cryp", "ahb_div" }, + { STM32F4_RCC_AHB2ENR, 5, "hash", "ahb_div" }, + { STM32F4_RCC_AHB2ENR, 6, "rng", "pll48" }, + { STM32F4_RCC_AHB2ENR, 7, "otgfs", "pll48" }, + + { STM32F4_RCC_AHB3ENR, 0, "fmc", "ahb_div", + CLK_IGNORE_UNUSED }, + + { STM32F4_RCC_APB1ENR, 0, "tim2", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 1, "tim3", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 2, "tim4", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 3, "tim5", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 4, "tim6", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 5, "tim7", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 6, "tim12", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 7, "tim13", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 8, "tim14", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 11, "wwdg", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 14, "spi2", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 15, "spi3", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 17, "uart2", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 18, "uart3", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 19, "uart4", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 20, "uart5", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 21, "i2c1", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 22, "i2c2", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 23, "i2c3", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 25, "can1", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 26, "can2", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 28, "pwr", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 29, "dac", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 30, "uart7", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 31, "uart8", "apb1_div" }, + + { STM32F4_RCC_APB2ENR, 0, "tim1", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 1, "tim8", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 4, "usart1", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 5, "usart6", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 8, "adc1", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 9, "adc2", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 10, "adc3", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 11, "sdio", "pll48" }, + { STM32F4_RCC_APB2ENR, 12, "spi1", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 13, "spi4", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 14, "syscfg", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 16, "tim9", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 17, "tim10", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 18, "tim11", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 20, "spi5", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 21, "spi6", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 22, "sai1", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 26, "ltdc", "apb2_div" }, +}; + +/* + * MAX_CLKS is the maximum value in the enumeration below plus the combined + * hweight of stm32f42xx_gate_map (plus one). + */ +#define MAX_CLKS 74 + +enum { SYSTICK, FCLK }; + +/* + * This bitmask tells us which bit offsets (0..192) on STM32F4[23]xxx + * have gate bits associated with them. Its combined hweight is 71. + */ +static const u64 stm32f42xx_gate_map[] = { 0x000000f17ef417ffull, + 0x0000000000000001ull, + 0x04777f33f6fec9ffull }; + +static struct clk *clks[MAX_CLKS]; +static DEFINE_SPINLOCK(stm32f4_clk_lock); +static void __iomem *base; + +/* + * "Multiplier" device for APBx clocks. + * + * The APBx dividers are power-of-two dividers and, if *not* running in 1:1 + * mode, they also tap out the one of the low order state bits to run the + * timers. ST datasheets represent this feature as a (conditional) clock + * multiplier. + */ +struct clk_apb_mul { + struct clk_hw hw; + u8 bit_idx; +}; + +#define to_clk_apb_mul(_hw) container_of(_hw, struct clk_apb_mul, hw) + +static unsigned long clk_apb_mul_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_apb_mul *am = to_clk_apb_mul(hw); + + if (readl(base + STM32F4_RCC_CFGR) & BIT(am->bit_idx)) + return parent_rate * 2; + + return parent_rate; +} + +static long clk_apb_mul_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_apb_mul *am = to_clk_apb_mul(hw); + unsigned long mult = 1; + + if (readl(base + STM32F4_RCC_CFGR) & BIT(am->bit_idx)) + mult = 2; + + if (__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT) { + unsigned long best_parent = rate / mult; + + *prate = + __clk_round_rate(__clk_get_parent(hw->clk), best_parent); + } + + return *prate * mult; +} + +static int clk_apb_mul_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + /* + * We must report success but we can do so unconditionally because + * clk_apb_mul_round_rate returns values that ensure this call is a + * nop. + */ + + return 0; +} + +static const struct clk_ops clk_apb_mul_factor_ops = { + .round_rate = clk_apb_mul_round_rate, + .set_rate = clk_apb_mul_set_rate, + .recalc_rate = clk_apb_mul_recalc_rate, +}; + +static struct clk *clk_register_apb_mul(struct device *dev, const char *name, + const char *parent_name, + unsigned long flags, u8 bit_idx) +{ + struct clk_apb_mul *am; + struct clk_init_data init; + struct clk *clk; + + am = kzalloc(sizeof(*am), GFP_KERNEL); + if (!am) + return ERR_PTR(-ENOMEM); + + am->bit_idx = bit_idx; + am->hw.init = &init; + + init.name = name; + init.ops = &clk_apb_mul_factor_ops; + init.flags = flags; + init.parent_names = &parent_name; + init.num_parents = 1; + + clk = clk_register(dev, &am->hw); + + if (IS_ERR(clk)) + kfree(am); + + return clk; +} + +/* + * Decode current PLL state and (statically) model the state we inherit from + * the bootloader. + */ +static void stm32f4_rcc_register_pll(const char *hse_clk, const char *hsi_clk) +{ + unsigned long pllcfgr = readl(base + STM32F4_RCC_PLLCFGR); + + unsigned long pllm = pllcfgr & 0x3f; + unsigned long plln = (pllcfgr >> 6) & 0x1ff; + unsigned long pllp = BIT(((pllcfgr >> 16) & 3) + 1); + const char *pllsrc = pllcfgr & BIT(22) ? hse_clk : hsi_clk; + unsigned long pllq = (pllcfgr >> 24) & 0xf; + + clk_register_fixed_factor(NULL, "vco", pllsrc, 0, plln, pllm); + clk_register_fixed_factor(NULL, "pll", "vco", 0, 1, pllp); + clk_register_fixed_factor(NULL, "pll48", "vco", 0, 1, pllq); +} + +/* + * Converts the primary and secondary indices (as they appear in DT) to an + * offset into our struct clock array. + */ +static int stm32f4_rcc_lookup_clk_idx(u8 primary, u8 secondary) +{ + u64 table[ARRAY_SIZE(stm32f42xx_gate_map)]; + + if (primary == 1) { + if (WARN_ON(secondary > FCLK)) + return -EINVAL; + return secondary; + } + + memcpy(table, stm32f42xx_gate_map, sizeof(table)); + + /* only bits set in table can be used as indices */ + if (WARN_ON(secondary > 8 * sizeof(table) || + 0 == (table[BIT_ULL_WORD(secondary)] & + BIT_ULL_MASK(secondary)))) + return -EINVAL; + + /* mask out bits above our current index */ + table[BIT_ULL_WORD(secondary)] &= + GENMASK_ULL(secondary % BITS_PER_LONG_LONG, 0); + + return FCLK + hweight64(table[0]) + + (BIT_ULL_WORD(secondary) >= 1 ? hweight64(table[1]) : 0) + + (BIT_ULL_WORD(secondary) >= 2 ? hweight64(table[2]) : 0); +} + +static struct clk * +stm32f4_rcc_lookup_clk(struct of_phandle_args *clkspec, void *data) +{ + int i = stm32f4_rcc_lookup_clk_idx(clkspec->args[0], clkspec->args[1]); + + if (i < 0) + return ERR_PTR(-EINVAL); + + return clks[i]; +} + +static const char *sys_parents[] __initdata = { "hsi", NULL, "pll" }; + +static const struct clk_div_table ahb_div_table[] = { + { 0x0, 1 }, { 0x1, 1 }, { 0x2, 1 }, { 0x3, 1 }, + { 0x4, 1 }, { 0x5, 1 }, { 0x6, 1 }, { 0x7, 1 }, + { 0x8, 2 }, { 0x9, 4 }, { 0xa, 8 }, { 0xb, 16 }, + { 0xc, 64 }, { 0xd, 128 }, { 0xe, 256 }, { 0xf, 512 }, + { 0 }, +}; + +static const struct clk_div_table apb_div_table[] = { + { 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 }, + { 4, 2 }, { 5, 4 }, { 6, 8 }, { 7, 16 }, + { 0 }, +}; + +static void __init stm32f4_rcc_init(struct device_node *np) +{ + const char *hse_clk; + int n; + + base = of_iomap(np, 0); + if (!base) { + pr_err("%s: unable to map resource", np->name); + return; + } + + hse_clk = of_clk_get_parent_name(np, 0); + + clk_register_fixed_rate_with_accuracy(NULL, "hsi", NULL, 0, + 16000000, 160000); + stm32f4_rcc_register_pll(hse_clk, "hsi"); + + sys_parents[1] = hse_clk; + clk_register_mux_table( + NULL, "sys", sys_parents, ARRAY_SIZE(sys_parents), 0, + base + STM32F4_RCC_CFGR, 0, 3, 0, NULL, &stm32f4_clk_lock); + + clk_register_divider_table(NULL, "ahb_div", "sys", + CLK_SET_RATE_PARENT, base + STM32F4_RCC_CFGR, + 4, 4, 0, ahb_div_table, &stm32f4_clk_lock); + + clk_register_divider_table(NULL, "apb1_div", "ahb_div", + CLK_SET_RATE_PARENT, base + STM32F4_RCC_CFGR, + 10, 3, 0, apb_div_table, &stm32f4_clk_lock); + clk_register_apb_mul(NULL, "apb1_mul", "apb1_div", + CLK_SET_RATE_PARENT, 12); + + clk_register_divider_table(NULL, "apb2_div", "ahb_div", + CLK_SET_RATE_PARENT, base + STM32F4_RCC_CFGR, + 13, 3, 0, apb_div_table, &stm32f4_clk_lock); + clk_register_apb_mul(NULL, "apb2_mul", "apb2_div", + CLK_SET_RATE_PARENT, 15); + + clks[SYSTICK] = clk_register_fixed_factor(NULL, "systick", "ahb_div", + 0, 1, 8); + clks[FCLK] = clk_register_fixed_factor(NULL, "fclk", "ahb_div", + 0, 1, 1); + + for (n = 0; n < ARRAY_SIZE(stm32f4_gates); n++) { + const struct stm32f4_gate_data *gd = &stm32f4_gates[n]; + unsigned int secondary = + 8 * (gd->offset - STM32F4_RCC_AHB1ENR) + gd->bit_idx; + int idx = stm32f4_rcc_lookup_clk_idx(0, secondary); + + if (idx < 0) + goto fail; + + clks[idx] = clk_register_gate( + NULL, gd->name, gd->parent_name, gd->flags, + base + gd->offset, gd->bit_idx, 0, &stm32f4_clk_lock); + + if (IS_ERR(clks[n])) { + pr_err("%s: Unable to register leaf clock %s\n", + np->full_name, gd->name); + goto fail; + } + } + + of_clk_add_provider(np, stm32f4_rcc_lookup_clk, NULL); + return; +fail: + iounmap(base); +} +CLK_OF_DECLARE(stm32f4_rcc, "st,stm32f42xx-rcc", stm32f4_rcc_init); diff --git a/drivers/clk/clk-u300.c b/drivers/clk/clk-u300.c index 406bfc1375b2..18bf5e576b93 100644 --- a/drivers/clk/clk-u300.c +++ b/drivers/clk/clk-u300.c @@ -12,6 +12,7 @@ #include #include #include +#include /* APP side SYSCON registers */ /* CLK Control Register 16bit (R/W) */ diff --git a/drivers/clk/clk-xgene.c b/drivers/clk/clk-xgene.c index dd8a62d8f11f..f26b3ac36b27 100644 --- a/drivers/clk/clk-xgene.c +++ b/drivers/clk/clk-xgene.c @@ -42,12 +42,12 @@ static DEFINE_SPINLOCK(clk_lock); -static inline u32 xgene_clk_read(void *csr) +static inline u32 xgene_clk_read(void __iomem *csr) { return readl_relaxed(csr); } -static inline void xgene_clk_write(u32 data, void *csr) +static inline void xgene_clk_write(u32 data, void __iomem *csr) { return writel_relaxed(data, csr); } @@ -119,7 +119,7 @@ static unsigned long xgene_clk_pll_recalc_rate(struct clk_hw *hw, return fvco / nout; } -const struct clk_ops xgene_clk_pll_ops = { +static const struct clk_ops xgene_clk_pll_ops = { .is_enabled = xgene_clk_pll_is_enabled, .recalc_rate = xgene_clk_pll_recalc_rate, }; @@ -167,7 +167,7 @@ static void xgene_pllclk_init(struct device_node *np, enum xgene_pll_type pll_ty { const char *clk_name = np->full_name; struct clk *clk; - void *reg; + void __iomem *reg; reg = of_iomap(np, 0); if (reg == NULL) { @@ -222,20 +222,22 @@ static int xgene_clk_enable(struct clk_hw *hw) struct xgene_clk *pclk = to_xgene_clk(hw); unsigned long flags = 0; u32 data; + phys_addr_t reg; if (pclk->lock) spin_lock_irqsave(pclk->lock, flags); if (pclk->param.csr_reg != NULL) { pr_debug("%s clock enabled\n", pclk->name); + reg = __pa(pclk->param.csr_reg); /* First enable the clock */ data = xgene_clk_read(pclk->param.csr_reg + pclk->param.reg_clk_offset); data |= pclk->param.reg_clk_mask; xgene_clk_write(data, pclk->param.csr_reg + pclk->param.reg_clk_offset); - pr_debug("%s clock PADDR base 0x%016LX clk offset 0x%08X mask 0x%08X value 0x%08X\n", - pclk->name, __pa(pclk->param.csr_reg), + pr_debug("%s clock PADDR base %pa clk offset 0x%08X mask 0x%08X value 0x%08X\n", + pclk->name, ®, pclk->param.reg_clk_offset, pclk->param.reg_clk_mask, data); @@ -245,8 +247,8 @@ static int xgene_clk_enable(struct clk_hw *hw) data &= ~pclk->param.reg_csr_mask; xgene_clk_write(data, pclk->param.csr_reg + pclk->param.reg_csr_offset); - pr_debug("%s CSR RESET PADDR base 0x%016LX csr offset 0x%08X mask 0x%08X value 0x%08X\n", - pclk->name, __pa(pclk->param.csr_reg), + pr_debug("%s CSR RESET PADDR base %pa csr offset 0x%08X mask 0x%08X value 0x%08X\n", + pclk->name, ®, pclk->param.reg_csr_offset, pclk->param.reg_csr_mask, data); } @@ -386,7 +388,7 @@ static long xgene_clk_round_rate(struct clk_hw *hw, unsigned long rate, return parent_rate / divider; } -const struct clk_ops xgene_clk_ops = { +static const struct clk_ops xgene_clk_ops = { .enable = xgene_clk_enable, .disable = xgene_clk_disable, .is_enabled = xgene_clk_is_enabled, @@ -456,7 +458,7 @@ static void __init xgene_devclk_init(struct device_node *np) parameters.csr_reg = NULL; parameters.divider_reg = NULL; for (i = 0; i < 2; i++) { - void *map_res; + void __iomem *map_res; rc = of_address_to_resource(np, i, &res); if (rc != 0) { if (i == 0) { diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 5b0f41868b42..ddb4b541016f 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "clk.h" @@ -37,13 +38,6 @@ static HLIST_HEAD(clk_root_list); static HLIST_HEAD(clk_orphan_list); static LIST_HEAD(clk_notifier_list); -static long clk_core_get_accuracy(struct clk_core *clk); -static unsigned long clk_core_get_rate(struct clk_core *clk); -static int clk_core_get_phase(struct clk_core *clk); -static bool clk_core_is_prepared(struct clk_core *clk); -static bool clk_core_is_enabled(struct clk_core *clk); -static struct clk_core *clk_core_lookup(const char *name); - /*** private data structures ***/ struct clk_core { @@ -68,11 +62,11 @@ struct clk_core { int phase; struct hlist_head children; struct hlist_node child_node; - struct hlist_node debug_node; struct hlist_head clks; unsigned int notifier_count; #ifdef CONFIG_DEBUG_FS struct dentry *dentry; + struct hlist_node debug_node; #endif struct kref ref; }; @@ -145,382 +139,71 @@ static void clk_enable_unlock(unsigned long flags) spin_unlock_irqrestore(&enable_lock, flags); } -/*** debugfs support ***/ - -#ifdef CONFIG_DEBUG_FS -#include - -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, - &clk_orphan_list, - NULL, -}; - -static struct hlist_head *orphan_list[] = { - &clk_orphan_list, - NULL, -}; - -static void clk_summary_show_one(struct seq_file *s, struct clk_core *c, - int level) +static bool clk_core_is_prepared(struct clk_core *core) { - if (!c) - return; + /* + * .is_prepared is optional for clocks that can prepare + * fall back to software usage counter if it is missing + */ + if (!core->ops->is_prepared) + return core->prepare_count; - 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_core_get_rate(c), - clk_core_get_accuracy(c), clk_core_get_phase(c)); + return core->ops->is_prepared(core->hw); } -static void clk_summary_show_subtree(struct seq_file *s, struct clk_core *c, - int level) +static bool clk_core_is_enabled(struct clk_core *core) { - struct clk_core *child; + /* + * .is_enabled is only mandatory for clocks that gate + * fall back to software usage counter if .is_enabled is missing + */ + if (!core->ops->is_enabled) + return core->enable_count; - if (!c) - return; - - clk_summary_show_one(s, c, level); - - hlist_for_each_entry(child, &c->children, child_node) - clk_summary_show_subtree(s, child, level + 1); + return core->ops->is_enabled(core->hw); } -static int clk_summary_show(struct seq_file *s, void *data) -{ - struct clk_core *c; - struct hlist_head **lists = (struct hlist_head **)s->private; - - seq_puts(s, " clock enable_cnt prepare_cnt rate accuracy phase\n"); - seq_puts(s, "----------------------------------------------------------------------------------------\n"); - - clk_prepare_lock(); - - for (; *lists; lists++) - hlist_for_each_entry(c, *lists, child_node) - clk_summary_show_subtree(s, c, 0); - - clk_prepare_unlock(); - - return 0; -} - - -static int clk_summary_open(struct inode *inode, struct file *file) -{ - return single_open(file, clk_summary_show, inode->i_private); -} - -static const struct file_operations clk_summary_fops = { - .open = clk_summary_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static void clk_dump_one(struct seq_file *s, struct clk_core *c, int level) -{ - if (!c) - return; - - seq_printf(s, "\"%s\": { ", c->name); - seq_printf(s, "\"enable_count\": %d,", c->enable_count); - seq_printf(s, "\"prepare_count\": %d,", c->prepare_count); - seq_printf(s, "\"rate\": %lu", clk_core_get_rate(c)); - seq_printf(s, "\"accuracy\": %lu", clk_core_get_accuracy(c)); - seq_printf(s, "\"phase\": %d", clk_core_get_phase(c)); -} - -static void clk_dump_subtree(struct seq_file *s, struct clk_core *c, int level) -{ - struct clk_core *child; - - if (!c) - return; - - clk_dump_one(s, c, level); - - hlist_for_each_entry(child, &c->children, child_node) { - seq_printf(s, ","); - clk_dump_subtree(s, child, level + 1); - } - - seq_printf(s, "}"); -} - -static int clk_dump(struct seq_file *s, void *data) -{ - struct clk_core *c; - bool first_node = true; - struct hlist_head **lists = (struct hlist_head **)s->private; - - seq_printf(s, "{"); - - clk_prepare_lock(); - - for (; *lists; lists++) { - hlist_for_each_entry(c, *lists, child_node) { - if (!first_node) - seq_puts(s, ","); - first_node = false; - clk_dump_subtree(s, c, 0); - } - } - - clk_prepare_unlock(); - - seq_printf(s, "}"); - return 0; -} - - -static int clk_dump_open(struct inode *inode, struct file *file) -{ - return single_open(file, clk_dump, inode->i_private); -} - -static const struct file_operations clk_dump_fops = { - .open = clk_dump_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int clk_debug_create_one(struct clk_core *clk, struct dentry *pdentry) -{ - struct dentry *d; - int ret = -ENOMEM; - - if (!clk || !pdentry) { - ret = -EINVAL; - goto out; - } - - d = debugfs_create_dir(clk->name, pdentry); - if (!d) - goto out; - - clk->dentry = d; - - d = debugfs_create_u32("clk_rate", S_IRUGO, clk->dentry, - (u32 *)&clk->rate); - if (!d) - goto err_out; - - d = debugfs_create_u32("clk_accuracy", S_IRUGO, clk->dentry, - (u32 *)&clk->accuracy); - 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) - goto err_out; - - d = debugfs_create_u32("clk_prepare_count", S_IRUGO, clk->dentry, - (u32 *)&clk->prepare_count); - if (!d) - goto err_out; - - d = debugfs_create_u32("clk_enable_count", S_IRUGO, clk->dentry, - (u32 *)&clk->enable_count); - if (!d) - goto err_out; - - d = debugfs_create_u32("clk_notifier_count", S_IRUGO, clk->dentry, - (u32 *)&clk->notifier_count); - if (!d) - goto err_out; - - if (clk->ops->debug_init) { - ret = clk->ops->debug_init(clk->hw, clk->dentry); - if (ret) - goto err_out; - } - - ret = 0; - goto out; - -err_out: - debugfs_remove_recursive(clk->dentry); - clk->dentry = NULL; -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 - * - * 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. - */ -static int clk_debug_register(struct clk_core *clk) -{ - int ret = 0; - - mutex_lock(&clk_debug_lock); - hlist_add_head(&clk->debug_node, &clk_debug_list); - - if (!inited) - goto unlock; - - ret = clk_debug_create_one(clk, rootdir); -unlock: - mutex_unlock(&clk_debug_lock); - - return ret; -} - - /** - * clk_debug_unregister - remove a clk node from the debugfs clk tree - * @clk: the clk being removed from the debugfs clk tree - * - * 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. - */ -static void clk_debug_unregister(struct clk_core *clk) -{ - mutex_lock(&clk_debug_lock); - hlist_del_init(&clk->debug_node); - debugfs_remove_recursive(clk->dentry); - clk->dentry = NULL; - mutex_unlock(&clk_debug_lock); -} - -struct dentry *clk_debugfs_add_file(struct clk_hw *hw, char *name, umode_t mode, - void *data, const struct file_operations *fops) -{ - struct dentry *d = NULL; - - if (hw->core->dentry) - d = debugfs_create_file(name, mode, hw->core->dentry, data, - fops); - - return d; -} -EXPORT_SYMBOL_GPL(clk_debugfs_add_file); - -/** - * clk_debug_init - lazily create the debugfs clk tree visualization - * - * clks are often initialized very early during boot before memory can - * be dynamically allocated and well before debugfs is setup. - * clk_debug_init walks the clk tree hierarchy while holding - * prepare_lock and creates the topology as part of a late_initcall, - * thus insuring that clks initialized very early will still be - * represented in the debugfs clk tree. This function should only be - * called once at boot-time, and all other clks added dynamically will - * be done so with clk_debug_register. - */ -static int __init clk_debug_init(void) -{ - struct clk_core *clk; - struct dentry *d; - - rootdir = debugfs_create_dir("clk", NULL); - - if (!rootdir) - return -ENOMEM; - - d = debugfs_create_file("clk_summary", S_IRUGO, rootdir, &all_lists, - &clk_summary_fops); - if (!d) - return -ENOMEM; - - d = debugfs_create_file("clk_dump", S_IRUGO, rootdir, &all_lists, - &clk_dump_fops); - if (!d) - return -ENOMEM; - - d = debugfs_create_file("clk_orphan_summary", S_IRUGO, rootdir, - &orphan_list, &clk_summary_fops); - if (!d) - return -ENOMEM; - - d = debugfs_create_file("clk_orphan_dump", S_IRUGO, rootdir, - &orphan_list, &clk_dump_fops); - if (!d) - return -ENOMEM; - - mutex_lock(&clk_debug_lock); - hlist_for_each_entry(clk, &clk_debug_list, debug_node) - clk_debug_create_one(clk, rootdir); - - inited = 1; - mutex_unlock(&clk_debug_lock); - - return 0; -} -late_initcall(clk_debug_init); -#else -static inline int clk_debug_register(struct clk_core *clk) { return 0; } -static inline void clk_debug_reparent(struct clk_core *clk, - struct clk_core *new_parent) -{ -} -static inline void clk_debug_unregister(struct clk_core *clk) -{ -} -#endif - -/* caller must hold prepare_lock */ -static void clk_unprepare_unused_subtree(struct clk_core *clk) +static void clk_unprepare_unused_subtree(struct clk_core *core) { struct clk_core *child; lockdep_assert_held(&prepare_lock); - hlist_for_each_entry(child, &clk->children, child_node) + hlist_for_each_entry(child, &core->children, child_node) clk_unprepare_unused_subtree(child); - if (clk->prepare_count) + if (core->prepare_count) return; - if (clk->flags & CLK_IGNORE_UNUSED) + if (core->flags & CLK_IGNORE_UNUSED) return; - if (clk_core_is_prepared(clk)) { - trace_clk_unprepare(clk); - if (clk->ops->unprepare_unused) - clk->ops->unprepare_unused(clk->hw); - else if (clk->ops->unprepare) - clk->ops->unprepare(clk->hw); - trace_clk_unprepare_complete(clk); + if (clk_core_is_prepared(core)) { + trace_clk_unprepare(core); + if (core->ops->unprepare_unused) + core->ops->unprepare_unused(core->hw); + else if (core->ops->unprepare) + core->ops->unprepare(core->hw); + trace_clk_unprepare_complete(core); } } -/* caller must hold prepare_lock */ -static void clk_disable_unused_subtree(struct clk_core *clk) +static void clk_disable_unused_subtree(struct clk_core *core) { struct clk_core *child; unsigned long flags; lockdep_assert_held(&prepare_lock); - hlist_for_each_entry(child, &clk->children, child_node) + hlist_for_each_entry(child, &core->children, child_node) clk_disable_unused_subtree(child); flags = clk_enable_lock(); - if (clk->enable_count) + if (core->enable_count) goto unlock_out; - if (clk->flags & CLK_IGNORE_UNUSED) + if (core->flags & CLK_IGNORE_UNUSED) goto unlock_out; /* @@ -528,13 +211,13 @@ static void clk_disable_unused_subtree(struct clk_core *clk) * sequence. call .disable_unused if available, otherwise fall * back to .disable */ - if (clk_core_is_enabled(clk)) { - trace_clk_disable(clk); - if (clk->ops->disable_unused) - clk->ops->disable_unused(clk->hw); - else if (clk->ops->disable) - clk->ops->disable(clk->hw); - trace_clk_disable_complete(clk); + if (clk_core_is_enabled(core)) { + trace_clk_disable(core); + if (core->ops->disable_unused) + core->ops->disable_unused(core->hw); + else if (core->ops->disable) + core->ops->disable(core->hw); + trace_clk_disable_complete(core); } unlock_out: @@ -551,7 +234,7 @@ __setup("clk_ignore_unused", clk_ignore_unused_setup); static int clk_disable_unused(void) { - struct clk_core *clk; + struct clk_core *core; if (clk_ignore_unused) { pr_warn("clk: Not disabling unused clocks\n"); @@ -560,17 +243,17 @@ static int clk_disable_unused(void) clk_prepare_lock(); - hlist_for_each_entry(clk, &clk_root_list, child_node) - clk_disable_unused_subtree(clk); + hlist_for_each_entry(core, &clk_root_list, child_node) + clk_disable_unused_subtree(core); - hlist_for_each_entry(clk, &clk_orphan_list, child_node) - clk_disable_unused_subtree(clk); + hlist_for_each_entry(core, &clk_orphan_list, child_node) + clk_disable_unused_subtree(core); - hlist_for_each_entry(clk, &clk_root_list, child_node) - clk_unprepare_unused_subtree(clk); + hlist_for_each_entry(core, &clk_root_list, child_node) + clk_unprepare_unused_subtree(core); - hlist_for_each_entry(clk, &clk_orphan_list, child_node) - clk_unprepare_unused_subtree(clk); + hlist_for_each_entry(core, &clk_orphan_list, child_node) + clk_unprepare_unused_subtree(core); clk_prepare_unlock(); @@ -608,151 +291,16 @@ struct clk *__clk_get_parent(struct clk *clk) } EXPORT_SYMBOL_GPL(__clk_get_parent); -static struct clk_core *clk_core_get_parent_by_index(struct clk_core *clk, - u8 index) -{ - if (!clk || index >= clk->num_parents) - return NULL; - else if (!clk->parents) - return clk_core_lookup(clk->parent_names[index]); - else if (!clk->parents[index]) - return clk->parents[index] = - clk_core_lookup(clk->parent_names[index]); - else - return clk->parents[index]; -} - -struct clk *clk_get_parent_by_index(struct clk *clk, u8 index) -{ - struct clk_core *parent; - - if (!clk) - return NULL; - - parent = clk_core_get_parent_by_index(clk->core, index); - - return !parent ? NULL : parent->hw->clk; -} -EXPORT_SYMBOL_GPL(clk_get_parent_by_index); - -unsigned int __clk_get_enable_count(struct clk *clk) -{ - return !clk ? 0 : clk->core->enable_count; -} - -static unsigned long clk_core_get_rate_nolock(struct clk_core *clk) -{ - unsigned long ret; - - if (!clk) { - ret = 0; - goto out; - } - - ret = clk->rate; - - if (clk->flags & CLK_IS_ROOT) - goto out; - - if (!clk->parent) - ret = 0; - -out: - return ret; -} - -unsigned long __clk_get_rate(struct clk *clk) -{ - if (!clk) - return 0; - - return clk_core_get_rate_nolock(clk->core); -} -EXPORT_SYMBOL_GPL(__clk_get_rate); - -static unsigned long __clk_get_accuracy(struct clk_core *clk) -{ - if (!clk) - return 0; - - return clk->accuracy; -} - -unsigned long __clk_get_flags(struct clk *clk) -{ - return !clk ? 0 : clk->core->flags; -} -EXPORT_SYMBOL_GPL(__clk_get_flags); - -static bool clk_core_is_prepared(struct clk_core *clk) -{ - int ret; - - if (!clk) - return false; - - /* - * .is_prepared is optional for clocks that can prepare - * fall back to software usage counter if it is missing - */ - if (!clk->ops->is_prepared) { - ret = clk->prepare_count ? 1 : 0; - goto out; - } - - ret = clk->ops->is_prepared(clk->hw); -out: - return !!ret; -} - -bool __clk_is_prepared(struct clk *clk) -{ - if (!clk) - return false; - - return clk_core_is_prepared(clk->core); -} - -static bool clk_core_is_enabled(struct clk_core *clk) -{ - int ret; - - if (!clk) - return false; - - /* - * .is_enabled is only mandatory for clocks that gate - * fall back to software usage counter if .is_enabled is missing - */ - if (!clk->ops->is_enabled) { - ret = clk->enable_count ? 1 : 0; - goto out; - } - - ret = clk->ops->is_enabled(clk->hw); -out: - return !!ret; -} - -bool __clk_is_enabled(struct clk *clk) -{ - if (!clk) - return false; - - return clk_core_is_enabled(clk->core); -} -EXPORT_SYMBOL_GPL(__clk_is_enabled); - static struct clk_core *__clk_lookup_subtree(const char *name, - struct clk_core *clk) + struct clk_core *core) { struct clk_core *child; struct clk_core *ret; - if (!strcmp(clk->name, name)) - return clk; + if (!strcmp(core->name, name)) + return core; - hlist_for_each_entry(child, &clk->children, child_node) { + hlist_for_each_entry(child, &core->children, child_node) { ret = __clk_lookup_subtree(name, child); if (ret) return ret; @@ -786,6 +334,99 @@ static struct clk_core *clk_core_lookup(const char *name) return NULL; } +static struct clk_core *clk_core_get_parent_by_index(struct clk_core *core, + u8 index) +{ + if (!core || index >= core->num_parents) + return NULL; + else if (!core->parents) + return clk_core_lookup(core->parent_names[index]); + else if (!core->parents[index]) + return core->parents[index] = + clk_core_lookup(core->parent_names[index]); + else + return core->parents[index]; +} + +struct clk *clk_get_parent_by_index(struct clk *clk, u8 index) +{ + struct clk_core *parent; + + if (!clk) + return NULL; + + parent = clk_core_get_parent_by_index(clk->core, index); + + return !parent ? NULL : parent->hw->clk; +} +EXPORT_SYMBOL_GPL(clk_get_parent_by_index); + +unsigned int __clk_get_enable_count(struct clk *clk) +{ + return !clk ? 0 : clk->core->enable_count; +} + +static unsigned long clk_core_get_rate_nolock(struct clk_core *core) +{ + unsigned long ret; + + if (!core) { + ret = 0; + goto out; + } + + ret = core->rate; + + if (core->flags & CLK_IS_ROOT) + goto out; + + if (!core->parent) + ret = 0; + +out: + return ret; +} + +unsigned long __clk_get_rate(struct clk *clk) +{ + if (!clk) + return 0; + + return clk_core_get_rate_nolock(clk->core); +} +EXPORT_SYMBOL_GPL(__clk_get_rate); + +static unsigned long __clk_get_accuracy(struct clk_core *core) +{ + if (!core) + return 0; + + return core->accuracy; +} + +unsigned long __clk_get_flags(struct clk *clk) +{ + return !clk ? 0 : clk->core->flags; +} +EXPORT_SYMBOL_GPL(__clk_get_flags); + +bool __clk_is_prepared(struct clk *clk) +{ + if (!clk) + return false; + + return clk_core_is_prepared(clk->core); +} + +bool __clk_is_enabled(struct clk *clk) +{ + if (!clk) + return false; + + return clk_core_is_enabled(clk->core); +} +EXPORT_SYMBOL_GPL(__clk_is_enabled); + static bool mux_is_better_rate(unsigned long rate, unsigned long now, unsigned long best, unsigned long flags) { @@ -853,7 +494,7 @@ struct clk *__clk_lookup(const char *name) return !core ? NULL : core->hw->clk; } -static void clk_core_get_boundaries(struct clk_core *clk, +static void clk_core_get_boundaries(struct clk_core *core, unsigned long *min_rate, unsigned long *max_rate) { @@ -862,10 +503,10 @@ static void clk_core_get_boundaries(struct clk_core *clk, *min_rate = 0; *max_rate = ULONG_MAX; - hlist_for_each_entry(clk_user, &clk->clks, clks_node) + hlist_for_each_entry(clk_user, &core->clks, clks_node) *min_rate = max(*min_rate, clk_user->min_rate); - hlist_for_each_entry(clk_user, &clk->clks, clks_node) + hlist_for_each_entry(clk_user, &core->clks, clks_node) *max_rate = min(*max_rate, clk_user->max_rate); } @@ -901,26 +542,28 @@ EXPORT_SYMBOL_GPL(__clk_mux_determine_rate_closest); /*** clk api ***/ -static void clk_core_unprepare(struct clk_core *clk) +static void clk_core_unprepare(struct clk_core *core) { - if (!clk) + lockdep_assert_held(&prepare_lock); + + if (!core) return; - if (WARN_ON(clk->prepare_count == 0)) + if (WARN_ON(core->prepare_count == 0)) return; - if (--clk->prepare_count > 0) + if (--core->prepare_count > 0) return; - WARN_ON(clk->enable_count > 0); + WARN_ON(core->enable_count > 0); - trace_clk_unprepare(clk); + trace_clk_unprepare(core); - if (clk->ops->unprepare) - clk->ops->unprepare(clk->hw); + if (core->ops->unprepare) + core->ops->unprepare(core->hw); - trace_clk_unprepare_complete(clk); - clk_core_unprepare(clk->parent); + trace_clk_unprepare_complete(core); + clk_core_unprepare(core->parent); } /** @@ -945,32 +588,34 @@ void clk_unprepare(struct clk *clk) } EXPORT_SYMBOL_GPL(clk_unprepare); -static int clk_core_prepare(struct clk_core *clk) +static int clk_core_prepare(struct clk_core *core) { int ret = 0; - if (!clk) + lockdep_assert_held(&prepare_lock); + + if (!core) return 0; - if (clk->prepare_count == 0) { - ret = clk_core_prepare(clk->parent); + if (core->prepare_count == 0) { + ret = clk_core_prepare(core->parent); if (ret) return ret; - trace_clk_prepare(clk); + trace_clk_prepare(core); - if (clk->ops->prepare) - ret = clk->ops->prepare(clk->hw); + if (core->ops->prepare) + ret = core->ops->prepare(core->hw); - trace_clk_prepare_complete(clk); + trace_clk_prepare_complete(core); if (ret) { - clk_core_unprepare(clk->parent); + clk_core_unprepare(core->parent); return ret; } } - clk->prepare_count++; + core->prepare_count++; return 0; } @@ -1002,33 +647,27 @@ int clk_prepare(struct clk *clk) } EXPORT_SYMBOL_GPL(clk_prepare); -static void clk_core_disable(struct clk_core *clk) +static void clk_core_disable(struct clk_core *core) { - if (!clk) + lockdep_assert_held(&enable_lock); + + if (!core) return; - if (WARN_ON(clk->enable_count == 0)) + if (WARN_ON(core->enable_count == 0)) return; - if (--clk->enable_count > 0) + if (--core->enable_count > 0) return; - trace_clk_disable(clk); + trace_clk_disable(core); - if (clk->ops->disable) - clk->ops->disable(clk->hw); + if (core->ops->disable) + core->ops->disable(core->hw); - trace_clk_disable_complete(clk); + trace_clk_disable_complete(core); - clk_core_disable(clk->parent); -} - -static void __clk_disable(struct clk *clk) -{ - if (!clk) - return; - - clk_core_disable(clk->core); + clk_core_disable(core->parent); } /** @@ -1051,52 +690,46 @@ void clk_disable(struct clk *clk) return; flags = clk_enable_lock(); - __clk_disable(clk); + clk_core_disable(clk->core); clk_enable_unlock(flags); } EXPORT_SYMBOL_GPL(clk_disable); -static int clk_core_enable(struct clk_core *clk) +static int clk_core_enable(struct clk_core *core) { int ret = 0; - if (!clk) + lockdep_assert_held(&enable_lock); + + if (!core) return 0; - if (WARN_ON(clk->prepare_count == 0)) + if (WARN_ON(core->prepare_count == 0)) return -ESHUTDOWN; - if (clk->enable_count == 0) { - ret = clk_core_enable(clk->parent); + if (core->enable_count == 0) { + ret = clk_core_enable(core->parent); if (ret) return ret; - trace_clk_enable(clk); + trace_clk_enable(core); - if (clk->ops->enable) - ret = clk->ops->enable(clk->hw); + if (core->ops->enable) + ret = core->ops->enable(core->hw); - trace_clk_enable_complete(clk); + trace_clk_enable_complete(core); if (ret) { - clk_core_disable(clk->parent); + clk_core_disable(core->parent); return ret; } } - clk->enable_count++; + core->enable_count++; return 0; } -static int __clk_enable(struct clk *clk) -{ - if (!clk) - return 0; - - return clk_core_enable(clk->core); -} - /** * clk_enable - ungate a clock * @clk: the clk being ungated @@ -1115,15 +748,18 @@ int clk_enable(struct clk *clk) unsigned long flags; int ret; + if (!clk) + return 0; + flags = clk_enable_lock(); - ret = __clk_enable(clk); + ret = clk_core_enable(clk->core); clk_enable_unlock(flags); return ret; } EXPORT_SYMBOL_GPL(clk_enable); -static unsigned long clk_core_round_rate_nolock(struct clk_core *clk, +static unsigned long clk_core_round_rate_nolock(struct clk_core *core, unsigned long rate, unsigned long min_rate, unsigned long max_rate) @@ -1134,25 +770,25 @@ static unsigned long clk_core_round_rate_nolock(struct clk_core *clk, lockdep_assert_held(&prepare_lock); - if (!clk) + if (!core) return 0; - parent = clk->parent; + parent = core->parent; if (parent) parent_rate = parent->rate; - if (clk->ops->determine_rate) { + if (core->ops->determine_rate) { parent_hw = parent ? parent->hw : NULL; - return clk->ops->determine_rate(clk->hw, rate, + return core->ops->determine_rate(core->hw, rate, min_rate, max_rate, &parent_rate, &parent_hw); - } else if (clk->ops->round_rate) - return clk->ops->round_rate(clk->hw, rate, &parent_rate); - else if (clk->flags & CLK_SET_RATE_PARENT) - return clk_core_round_rate_nolock(clk->parent, rate, min_rate, + } else if (core->ops->round_rate) + return core->ops->round_rate(core->hw, rate, &parent_rate); + else if (core->flags & CLK_SET_RATE_PARENT) + return clk_core_round_rate_nolock(core->parent, rate, min_rate, max_rate); else - return clk->rate; + return core->rate; } /** @@ -1162,8 +798,7 @@ static unsigned long clk_core_round_rate_nolock(struct clk_core *clk, * @min_rate: returned rate must be greater than this rate * @max_rate: returned rate must be less than this rate * - * Caller must hold prepare_lock. Useful for clk_ops such as .set_rate and - * .determine_rate. + * Useful for clk_ops such as .set_rate and .determine_rate. */ unsigned long __clk_determine_rate(struct clk_hw *hw, unsigned long rate, @@ -1182,7 +817,7 @@ EXPORT_SYMBOL_GPL(__clk_determine_rate); * @clk: round the rate of this clock * @rate: the rate which is to be rounded * - * Caller must hold prepare_lock. Useful for clk_ops such as .set_rate + * Useful for clk_ops such as .set_rate */ unsigned long __clk_round_rate(struct clk *clk, unsigned long rate) { @@ -1224,7 +859,7 @@ EXPORT_SYMBOL_GPL(clk_round_rate); /** * __clk_notify - call clk notifier chain - * @clk: struct clk * that is changing rate + * @core: clk that is changing rate * @msg: clk notifier type (see include/linux/clk.h) * @old_rate: old clk rate * @new_rate: new clk rate @@ -1236,7 +871,7 @@ EXPORT_SYMBOL_GPL(clk_round_rate); * called if all went well, or NOTIFY_STOP or NOTIFY_BAD immediately if * a driver returns that. */ -static int __clk_notify(struct clk_core *clk, unsigned long msg, +static int __clk_notify(struct clk_core *core, unsigned long msg, unsigned long old_rate, unsigned long new_rate) { struct clk_notifier *cn; @@ -1247,7 +882,7 @@ static int __clk_notify(struct clk_core *clk, unsigned long msg, cnd.new_rate = new_rate; list_for_each_entry(cn, &clk_notifier_list, node) { - if (cn->clk->core == clk) { + if (cn->clk->core == core) { cnd.clk = cn->clk; ret = srcu_notifier_call_chain(&cn->notifier_head, msg, &cnd); @@ -1259,44 +894,42 @@ static int __clk_notify(struct clk_core *clk, unsigned long msg, /** * __clk_recalc_accuracies - * @clk: first clk in the subtree + * @core: first clk in the subtree * * Walks the subtree of clks starting with clk and recalculates accuracies as * it goes. Note that if a clk does not implement the .recalc_accuracy - * callback then it is assumed that the clock will take on the accuracy of it's + * callback then it is assumed that the clock will take on the accuracy of its * parent. - * - * Caller must hold prepare_lock. */ -static void __clk_recalc_accuracies(struct clk_core *clk) +static void __clk_recalc_accuracies(struct clk_core *core) { unsigned long parent_accuracy = 0; struct clk_core *child; lockdep_assert_held(&prepare_lock); - if (clk->parent) - parent_accuracy = clk->parent->accuracy; + if (core->parent) + parent_accuracy = core->parent->accuracy; - if (clk->ops->recalc_accuracy) - clk->accuracy = clk->ops->recalc_accuracy(clk->hw, + if (core->ops->recalc_accuracy) + core->accuracy = core->ops->recalc_accuracy(core->hw, parent_accuracy); else - clk->accuracy = parent_accuracy; + core->accuracy = parent_accuracy; - hlist_for_each_entry(child, &clk->children, child_node) + hlist_for_each_entry(child, &core->children, child_node) __clk_recalc_accuracies(child); } -static long clk_core_get_accuracy(struct clk_core *clk) +static long clk_core_get_accuracy(struct clk_core *core) { unsigned long accuracy; clk_prepare_lock(); - if (clk && (clk->flags & CLK_GET_ACCURACY_NOCACHE)) - __clk_recalc_accuracies(clk); + if (core && (core->flags & CLK_GET_ACCURACY_NOCACHE)) + __clk_recalc_accuracies(core); - accuracy = __clk_get_accuracy(clk); + accuracy = __clk_get_accuracy(core); clk_prepare_unlock(); return accuracy; @@ -1320,17 +953,17 @@ long clk_get_accuracy(struct clk *clk) } EXPORT_SYMBOL_GPL(clk_get_accuracy); -static unsigned long clk_recalc(struct clk_core *clk, +static unsigned long clk_recalc(struct clk_core *core, unsigned long parent_rate) { - if (clk->ops->recalc_rate) - return clk->ops->recalc_rate(clk->hw, parent_rate); + if (core->ops->recalc_rate) + return core->ops->recalc_rate(core->hw, parent_rate); return parent_rate; } /** * __clk_recalc_rates - * @clk: first clk in the subtree + * @core: first clk in the subtree * @msg: notification type (see include/linux/clk.h) * * Walks the subtree of clks starting with clk and recalculates rates as it @@ -1339,10 +972,8 @@ static unsigned long clk_recalc(struct clk_core *clk, * * clk_recalc_rates also propagates the POST_RATE_CHANGE notification, * if necessary. - * - * Caller must hold prepare_lock. */ -static void __clk_recalc_rates(struct clk_core *clk, unsigned long msg) +static void __clk_recalc_rates(struct clk_core *core, unsigned long msg) { unsigned long old_rate; unsigned long parent_rate = 0; @@ -1350,34 +981,34 @@ static void __clk_recalc_rates(struct clk_core *clk, unsigned long msg) lockdep_assert_held(&prepare_lock); - old_rate = clk->rate; + old_rate = core->rate; - if (clk->parent) - parent_rate = clk->parent->rate; + if (core->parent) + parent_rate = core->parent->rate; - clk->rate = clk_recalc(clk, parent_rate); + core->rate = clk_recalc(core, parent_rate); /* * ignore NOTIFY_STOP and NOTIFY_BAD return values for POST_RATE_CHANGE * & ABORT_RATE_CHANGE notifiers */ - if (clk->notifier_count && msg) - __clk_notify(clk, msg, old_rate, clk->rate); + if (core->notifier_count && msg) + __clk_notify(core, msg, old_rate, core->rate); - hlist_for_each_entry(child, &clk->children, child_node) + hlist_for_each_entry(child, &core->children, child_node) __clk_recalc_rates(child, msg); } -static unsigned long clk_core_get_rate(struct clk_core *clk) +static unsigned long clk_core_get_rate(struct clk_core *core) { unsigned long rate; clk_prepare_lock(); - if (clk && (clk->flags & CLK_GET_RATE_NOCACHE)) - __clk_recalc_rates(clk, 0); + if (core && (core->flags & CLK_GET_RATE_NOCACHE)) + __clk_recalc_rates(core, 0); - rate = clk_core_get_rate_nolock(clk); + rate = clk_core_get_rate_nolock(core); clk_prepare_unlock(); return rate; @@ -1400,15 +1031,15 @@ unsigned long clk_get_rate(struct clk *clk) } EXPORT_SYMBOL_GPL(clk_get_rate); -static int clk_fetch_parent_index(struct clk_core *clk, +static int clk_fetch_parent_index(struct clk_core *core, struct clk_core *parent) { int i; - if (!clk->parents) { - clk->parents = kcalloc(clk->num_parents, + if (!core->parents) { + core->parents = kcalloc(core->num_parents, sizeof(struct clk *), GFP_KERNEL); - if (!clk->parents) + if (!core->parents) return -ENOMEM; } @@ -1417,15 +1048,15 @@ static int clk_fetch_parent_index(struct clk_core *clk, * or if not yet cached, use string name comparison and cache * them now to avoid future calls to clk_core_lookup. */ - for (i = 0; i < clk->num_parents; i++) { - if (clk->parents[i] == parent) + for (i = 0; i < core->num_parents; i++) { + if (core->parents[i] == parent) return i; - if (clk->parents[i]) + if (core->parents[i]) continue; - if (!strcmp(clk->parent_names[i], parent->name)) { - clk->parents[i] = clk_core_lookup(parent->name); + if (!strcmp(core->parent_names[i], parent->name)) { + core->parents[i] = clk_core_lookup(parent->name); return i; } } @@ -1433,28 +1064,28 @@ static int clk_fetch_parent_index(struct clk_core *clk, return -EINVAL; } -static void clk_reparent(struct clk_core *clk, struct clk_core *new_parent) +static void clk_reparent(struct clk_core *core, struct clk_core *new_parent) { - hlist_del(&clk->child_node); + hlist_del(&core->child_node); if (new_parent) { /* avoid duplicate POST_RATE_CHANGE notifications */ - if (new_parent->new_child == clk) + if (new_parent->new_child == core) new_parent->new_child = NULL; - hlist_add_head(&clk->child_node, &new_parent->children); + hlist_add_head(&core->child_node, &new_parent->children); } else { - hlist_add_head(&clk->child_node, &clk_orphan_list); + hlist_add_head(&core->child_node, &clk_orphan_list); } - clk->parent = new_parent; + core->parent = new_parent; } -static struct clk_core *__clk_set_parent_before(struct clk_core *clk, +static struct clk_core *__clk_set_parent_before(struct clk_core *core, struct clk_core *parent) { unsigned long flags; - struct clk_core *old_parent = clk->parent; + struct clk_core *old_parent = core->parent; /* * Migrate prepare state between parents and prevent race with @@ -1473,17 +1104,17 @@ static struct clk_core *__clk_set_parent_before(struct clk_core *clk, * * See also: Comment for clk_set_parent() below. */ - if (clk->prepare_count) { + if (core->prepare_count) { clk_core_prepare(parent); flags = clk_enable_lock(); clk_core_enable(parent); - clk_core_enable(clk); + clk_core_enable(core); clk_enable_unlock(flags); } /* update the clk tree topology */ flags = clk_enable_lock(); - clk_reparent(clk, parent); + clk_reparent(core, parent); clk_enable_unlock(flags); return old_parent; @@ -1508,31 +1139,31 @@ static void __clk_set_parent_after(struct clk_core *core, } } -static int __clk_set_parent(struct clk_core *clk, struct clk_core *parent, +static int __clk_set_parent(struct clk_core *core, struct clk_core *parent, u8 p_index) { unsigned long flags; int ret = 0; struct clk_core *old_parent; - old_parent = __clk_set_parent_before(clk, parent); + old_parent = __clk_set_parent_before(core, parent); - trace_clk_set_parent(clk, parent); + trace_clk_set_parent(core, parent); /* change clock input source */ - if (parent && clk->ops->set_parent) - ret = clk->ops->set_parent(clk->hw, p_index); + if (parent && core->ops->set_parent) + ret = core->ops->set_parent(core->hw, p_index); - trace_clk_set_parent_complete(clk, parent); + trace_clk_set_parent_complete(core, parent); if (ret) { flags = clk_enable_lock(); - clk_reparent(clk, old_parent); + clk_reparent(core, old_parent); clk_enable_unlock(flags); - if (clk->prepare_count) { + if (core->prepare_count) { flags = clk_enable_lock(); - clk_core_disable(clk); + clk_core_disable(core); clk_core_disable(parent); clk_enable_unlock(flags); clk_core_unprepare(parent); @@ -1540,14 +1171,14 @@ static int __clk_set_parent(struct clk_core *clk, struct clk_core *parent, return ret; } - __clk_set_parent_after(clk, parent, old_parent); + __clk_set_parent_after(core, parent, old_parent); return 0; } /** * __clk_speculate_rates - * @clk: first clk in the subtree + * @core: first clk in the subtree * @parent_rate: the "future" rate of clk's parent * * Walks the subtree of clks starting with clk, speculating rates as it @@ -1558,10 +1189,8 @@ static int __clk_set_parent(struct clk_core *clk, struct clk_core *parent, * subtree have subscribed to the notifications. Note that if a clk does not * implement the .recalc_rate callback then it is assumed that the clock will * take on the rate of its parent. - * - * Caller must hold prepare_lock. */ -static int __clk_speculate_rates(struct clk_core *clk, +static int __clk_speculate_rates(struct clk_core *core, unsigned long parent_rate) { struct clk_core *child; @@ -1570,19 +1199,19 @@ static int __clk_speculate_rates(struct clk_core *clk, lockdep_assert_held(&prepare_lock); - new_rate = clk_recalc(clk, parent_rate); + new_rate = clk_recalc(core, parent_rate); /* abort rate change if a driver returns NOTIFY_BAD or NOTIFY_STOP */ - if (clk->notifier_count) - ret = __clk_notify(clk, PRE_RATE_CHANGE, clk->rate, new_rate); + if (core->notifier_count) + ret = __clk_notify(core, PRE_RATE_CHANGE, core->rate, new_rate); if (ret & NOTIFY_STOP_MASK) { pr_debug("%s: clk notifier callback for clock %s aborted with error %d\n", - __func__, clk->name, ret); + __func__, core->name, ret); goto out; } - hlist_for_each_entry(child, &clk->children, child_node) { + hlist_for_each_entry(child, &core->children, child_node) { ret = __clk_speculate_rates(child, new_rate); if (ret & NOTIFY_STOP_MASK) break; @@ -1592,20 +1221,20 @@ out: return ret; } -static void clk_calc_subtree(struct clk_core *clk, unsigned long new_rate, +static void clk_calc_subtree(struct clk_core *core, unsigned long new_rate, struct clk_core *new_parent, u8 p_index) { struct clk_core *child; - clk->new_rate = new_rate; - clk->new_parent = new_parent; - clk->new_parent_index = p_index; + core->new_rate = new_rate; + core->new_parent = new_parent; + core->new_parent_index = p_index; /* include clk in new parent's PRE_RATE_CHANGE notifications */ - clk->new_child = NULL; - if (new_parent && new_parent != clk->parent) - new_parent->new_child = clk; + core->new_child = NULL; + if (new_parent && new_parent != core->parent) + new_parent->new_child = core; - hlist_for_each_entry(child, &clk->children, child_node) { + hlist_for_each_entry(child, &core->children, child_node) { child->new_rate = clk_recalc(child, new_rate); clk_calc_subtree(child, child->new_rate, NULL, 0); } @@ -1615,10 +1244,10 @@ static void clk_calc_subtree(struct clk_core *clk, unsigned long new_rate, * calculate the new rates returning the topmost clock that has to be * changed. */ -static struct clk_core *clk_calc_new_rates(struct clk_core *clk, +static struct clk_core *clk_calc_new_rates(struct clk_core *core, unsigned long rate) { - struct clk_core *top = clk; + struct clk_core *top = core; struct clk_core *old_parent, *parent; struct clk_hw *parent_hw; unsigned long best_parent_rate = 0; @@ -1629,20 +1258,20 @@ static struct clk_core *clk_calc_new_rates(struct clk_core *clk, long ret; /* sanity */ - if (IS_ERR_OR_NULL(clk)) + if (IS_ERR_OR_NULL(core)) return NULL; /* save parent rate, if it exists */ - parent = old_parent = clk->parent; + parent = old_parent = core->parent; if (parent) best_parent_rate = parent->rate; - clk_core_get_boundaries(clk, &min_rate, &max_rate); + clk_core_get_boundaries(core, &min_rate, &max_rate); /* find the closest rate and parent clk/rate */ - if (clk->ops->determine_rate) { + if (core->ops->determine_rate) { parent_hw = parent ? parent->hw : NULL; - ret = clk->ops->determine_rate(clk->hw, rate, + ret = core->ops->determine_rate(core->hw, rate, min_rate, max_rate, &best_parent_rate, @@ -1652,8 +1281,8 @@ static struct clk_core *clk_calc_new_rates(struct clk_core *clk, new_rate = ret; parent = parent_hw ? parent_hw->core : NULL; - } else if (clk->ops->round_rate) { - ret = clk->ops->round_rate(clk->hw, rate, + } else if (core->ops->round_rate) { + ret = core->ops->round_rate(core->hw, rate, &best_parent_rate); if (ret < 0) return NULL; @@ -1661,9 +1290,9 @@ static struct clk_core *clk_calc_new_rates(struct clk_core *clk, new_rate = ret; if (new_rate < min_rate || new_rate > max_rate) return NULL; - } else if (!parent || !(clk->flags & CLK_SET_RATE_PARENT)) { + } else if (!parent || !(core->flags & CLK_SET_RATE_PARENT)) { /* pass-through clock without adjustable parent */ - clk->new_rate = clk->rate; + core->new_rate = core->rate; return NULL; } else { /* pass-through clock with adjustable parent */ @@ -1674,28 +1303,28 @@ static struct clk_core *clk_calc_new_rates(struct clk_core *clk, /* some clocks must be gated to change parent */ if (parent != old_parent && - (clk->flags & CLK_SET_PARENT_GATE) && clk->prepare_count) { + (core->flags & CLK_SET_PARENT_GATE) && core->prepare_count) { pr_debug("%s: %s not gated but wants to reparent\n", - __func__, clk->name); + __func__, core->name); return NULL; } /* try finding the new parent index */ - if (parent && clk->num_parents > 1) { - p_index = clk_fetch_parent_index(clk, parent); + if (parent && core->num_parents > 1) { + p_index = clk_fetch_parent_index(core, parent); if (p_index < 0) { pr_debug("%s: clk %s can not be parent of clk %s\n", - __func__, parent->name, clk->name); + __func__, parent->name, core->name); return NULL; } } - if ((clk->flags & CLK_SET_RATE_PARENT) && parent && + if ((core->flags & CLK_SET_RATE_PARENT) && parent && best_parent_rate != parent->rate) top = clk_calc_new_rates(parent, best_parent_rate); out: - clk_calc_subtree(clk, new_rate, parent, p_index); + clk_calc_subtree(core, new_rate, parent, p_index); return top; } @@ -1705,33 +1334,33 @@ out: * so that in case of an error we can walk down the whole tree again and * abort the change. */ -static struct clk_core *clk_propagate_rate_change(struct clk_core *clk, +static struct clk_core *clk_propagate_rate_change(struct clk_core *core, unsigned long event) { struct clk_core *child, *tmp_clk, *fail_clk = NULL; int ret = NOTIFY_DONE; - if (clk->rate == clk->new_rate) + if (core->rate == core->new_rate) return NULL; - if (clk->notifier_count) { - ret = __clk_notify(clk, event, clk->rate, clk->new_rate); + if (core->notifier_count) { + ret = __clk_notify(core, event, core->rate, core->new_rate); if (ret & NOTIFY_STOP_MASK) - fail_clk = clk; + fail_clk = core; } - hlist_for_each_entry(child, &clk->children, child_node) { + hlist_for_each_entry(child, &core->children, child_node) { /* Skip children who will be reparented to another clock */ - if (child->new_parent && child->new_parent != clk) + if (child->new_parent && child->new_parent != core) continue; tmp_clk = clk_propagate_rate_change(child, event); if (tmp_clk) fail_clk = tmp_clk; } - /* handle the new child who might not be in clk->children yet */ - if (clk->new_child) { - tmp_clk = clk_propagate_rate_change(clk->new_child, event); + /* handle the new child who might not be in core->children yet */ + if (core->new_child) { + tmp_clk = clk_propagate_rate_change(core->new_child, event); if (tmp_clk) fail_clk = tmp_clk; } @@ -1743,7 +1372,7 @@ static struct clk_core *clk_propagate_rate_change(struct clk_core *clk, * walk down a subtree and set the new rates notifying the rate * change on the way */ -static void clk_change_rate(struct clk_core *clk) +static void clk_change_rate(struct clk_core *core) { struct clk_core *child; struct hlist_node *tmp; @@ -1752,77 +1381,80 @@ static void clk_change_rate(struct clk_core *clk) bool skip_set_rate = false; struct clk_core *old_parent; - old_rate = clk->rate; + old_rate = core->rate; - if (clk->new_parent) - best_parent_rate = clk->new_parent->rate; - else if (clk->parent) - best_parent_rate = clk->parent->rate; + if (core->new_parent) + best_parent_rate = core->new_parent->rate; + else if (core->parent) + best_parent_rate = core->parent->rate; - if (clk->new_parent && clk->new_parent != clk->parent) { - old_parent = __clk_set_parent_before(clk, clk->new_parent); - trace_clk_set_parent(clk, clk->new_parent); + if (core->new_parent && core->new_parent != core->parent) { + old_parent = __clk_set_parent_before(core, core->new_parent); + trace_clk_set_parent(core, core->new_parent); - if (clk->ops->set_rate_and_parent) { + if (core->ops->set_rate_and_parent) { skip_set_rate = true; - clk->ops->set_rate_and_parent(clk->hw, clk->new_rate, + core->ops->set_rate_and_parent(core->hw, core->new_rate, best_parent_rate, - clk->new_parent_index); - } else if (clk->ops->set_parent) { - clk->ops->set_parent(clk->hw, clk->new_parent_index); + core->new_parent_index); + } else if (core->ops->set_parent) { + core->ops->set_parent(core->hw, core->new_parent_index); } - trace_clk_set_parent_complete(clk, clk->new_parent); - __clk_set_parent_after(clk, clk->new_parent, old_parent); + trace_clk_set_parent_complete(core, core->new_parent); + __clk_set_parent_after(core, core->new_parent, old_parent); } - trace_clk_set_rate(clk, clk->new_rate); + trace_clk_set_rate(core, core->new_rate); - if (!skip_set_rate && clk->ops->set_rate) - clk->ops->set_rate(clk->hw, clk->new_rate, best_parent_rate); + if (!skip_set_rate && core->ops->set_rate) + core->ops->set_rate(core->hw, core->new_rate, best_parent_rate); - trace_clk_set_rate_complete(clk, clk->new_rate); + trace_clk_set_rate_complete(core, core->new_rate); - clk->rate = clk_recalc(clk, best_parent_rate); + core->rate = clk_recalc(core, best_parent_rate); - if (clk->notifier_count && old_rate != clk->rate) - __clk_notify(clk, POST_RATE_CHANGE, old_rate, clk->rate); + if (core->notifier_count && old_rate != core->rate) + __clk_notify(core, POST_RATE_CHANGE, old_rate, core->rate); + + if (core->flags & CLK_RECALC_NEW_RATES) + (void)clk_calc_new_rates(core, core->new_rate); /* * Use safe iteration, as change_rate can actually swap parents * for certain clock types. */ - hlist_for_each_entry_safe(child, tmp, &clk->children, child_node) { + hlist_for_each_entry_safe(child, tmp, &core->children, child_node) { /* Skip children who will be reparented to another clock */ - if (child->new_parent && child->new_parent != clk) + if (child->new_parent && child->new_parent != core) continue; clk_change_rate(child); } - /* handle the new child who might not be in clk->children yet */ - if (clk->new_child) - clk_change_rate(clk->new_child); + /* handle the new child who might not be in core->children yet */ + if (core->new_child) + clk_change_rate(core->new_child); } -static int clk_core_set_rate_nolock(struct clk_core *clk, +static int clk_core_set_rate_nolock(struct clk_core *core, unsigned long req_rate) { struct clk_core *top, *fail_clk; unsigned long rate = req_rate; int ret = 0; - if (!clk) + if (!core) return 0; /* bail early if nothing to do */ - if (rate == clk_core_get_rate_nolock(clk)) + if (rate == clk_core_get_rate_nolock(core)) return 0; - if ((clk->flags & CLK_SET_RATE_GATE) && clk->prepare_count) + if ((core->flags & CLK_SET_RATE_GATE) && core->prepare_count) return -EBUSY; /* calculate new rates and get the topmost changed clock */ - top = clk_calc_new_rates(clk, rate); + top = clk_calc_new_rates(core, rate); if (!top) return -EINVAL; @@ -1838,7 +1470,7 @@ static int clk_core_set_rate_nolock(struct clk_core *clk, /* change the rates */ clk_change_rate(top); - clk->req_rate = req_rate; + core->req_rate = req_rate; return ret; } @@ -1977,55 +1609,63 @@ EXPORT_SYMBOL_GPL(clk_get_parent); * .parents array exists, and if so use it to avoid an expensive tree * traversal. If .parents does not exist then walk the tree. */ -static struct clk_core *__clk_init_parent(struct clk_core *clk) +static struct clk_core *__clk_init_parent(struct clk_core *core) { struct clk_core *ret = NULL; u8 index; /* handle the trivial cases */ - if (!clk->num_parents) + if (!core->num_parents) goto out; - if (clk->num_parents == 1) { - if (IS_ERR_OR_NULL(clk->parent)) - clk->parent = clk_core_lookup(clk->parent_names[0]); - ret = clk->parent; + if (core->num_parents == 1) { + if (IS_ERR_OR_NULL(core->parent)) + core->parent = clk_core_lookup(core->parent_names[0]); + ret = core->parent; goto out; } - if (!clk->ops->get_parent) { - WARN(!clk->ops->get_parent, + if (!core->ops->get_parent) { + WARN(!core->ops->get_parent, "%s: multi-parent clocks must implement .get_parent\n", __func__); goto out; }; /* - * Do our best to cache parent clocks in clk->parents. This prevents - * unnecessary and expensive lookups. We don't set clk->parent here; + * Do our best to cache parent clocks in core->parents. This prevents + * unnecessary and expensive lookups. We don't set core->parent here; * that is done by the calling function. */ - index = clk->ops->get_parent(clk->hw); + index = core->ops->get_parent(core->hw); - if (!clk->parents) - clk->parents = - kcalloc(clk->num_parents, sizeof(struct clk *), + if (!core->parents) + core->parents = + kcalloc(core->num_parents, sizeof(struct clk *), GFP_KERNEL); - ret = clk_core_get_parent_by_index(clk, index); + ret = clk_core_get_parent_by_index(core, index); out: return ret; } -static void clk_core_reparent(struct clk_core *clk, +static void clk_core_reparent(struct clk_core *core, struct clk_core *new_parent) { - clk_reparent(clk, new_parent); - __clk_recalc_accuracies(clk); - __clk_recalc_rates(clk, POST_RATE_CHANGE); + clk_reparent(core, new_parent); + __clk_recalc_accuracies(core); + __clk_recalc_rates(core, POST_RATE_CHANGE); +} + +void clk_hw_reparent(struct clk_hw *hw, struct clk_hw *new_parent) +{ + if (!hw) + return; + + clk_core_reparent(hw->core, !new_parent ? NULL : new_parent->core); } /** @@ -2062,61 +1702,61 @@ bool clk_has_parent(struct clk *clk, struct clk *parent) } EXPORT_SYMBOL_GPL(clk_has_parent); -static int clk_core_set_parent(struct clk_core *clk, struct clk_core *parent) +static int clk_core_set_parent(struct clk_core *core, struct clk_core *parent) { int ret = 0; int p_index = 0; unsigned long p_rate = 0; - if (!clk) + if (!core) return 0; /* prevent racing with updates to the clock topology */ clk_prepare_lock(); - if (clk->parent == parent) + if (core->parent == parent) goto out; /* verify ops for for multi-parent clks */ - if ((clk->num_parents > 1) && (!clk->ops->set_parent)) { + if ((core->num_parents > 1) && (!core->ops->set_parent)) { ret = -ENOSYS; goto out; } /* check that we are allowed to re-parent if the clock is in use */ - if ((clk->flags & CLK_SET_PARENT_GATE) && clk->prepare_count) { + if ((core->flags & CLK_SET_PARENT_GATE) && core->prepare_count) { ret = -EBUSY; goto out; } /* try finding the new parent index */ if (parent) { - p_index = clk_fetch_parent_index(clk, parent); + p_index = clk_fetch_parent_index(core, parent); p_rate = parent->rate; if (p_index < 0) { pr_debug("%s: clk %s can not be parent of clk %s\n", - __func__, parent->name, clk->name); + __func__, parent->name, core->name); ret = p_index; goto out; } } /* propagate PRE_RATE_CHANGE notifications */ - ret = __clk_speculate_rates(clk, p_rate); + ret = __clk_speculate_rates(core, p_rate); /* abort if a driver objects */ if (ret & NOTIFY_STOP_MASK) goto out; /* do the re-parent */ - ret = __clk_set_parent(clk, parent, p_index); + ret = __clk_set_parent(core, parent, p_index); /* propagate rate an accuracy recalculation accordingly */ if (ret) { - __clk_recalc_rates(clk, ABORT_RATE_CHANGE); + __clk_recalc_rates(core, ABORT_RATE_CHANGE); } else { - __clk_recalc_rates(clk, POST_RATE_CHANGE); - __clk_recalc_accuracies(clk); + __clk_recalc_rates(core, POST_RATE_CHANGE); + __clk_recalc_accuracies(core); } out: @@ -2201,21 +1841,16 @@ int clk_set_phase(struct clk *clk, int degrees) } EXPORT_SYMBOL_GPL(clk_set_phase); -static int clk_core_get_phase(struct clk_core *clk) +static int clk_core_get_phase(struct clk_core *core) { - int ret = 0; - - if (!clk) - goto out; + int ret; clk_prepare_lock(); - ret = clk->phase; + ret = core->phase; clk_prepare_unlock(); -out: return ret; } -EXPORT_SYMBOL_GPL(clk_get_phase); /** * clk_get_phase - return the phase shift of a clock signal @@ -2231,6 +1866,7 @@ int clk_get_phase(struct clk *clk) return clk_core_get_phase(clk->core); } +EXPORT_SYMBOL_GPL(clk_get_phase); /** * clk_is_match - check if two clk's point to the same hardware clock @@ -2258,6 +1894,337 @@ bool clk_is_match(const struct clk *p, const struct clk *q) } EXPORT_SYMBOL_GPL(clk_is_match); +/*** debugfs support ***/ + +#ifdef CONFIG_DEBUG_FS +#include + +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, + &clk_orphan_list, + NULL, +}; + +static struct hlist_head *orphan_list[] = { + &clk_orphan_list, + NULL, +}; + +static void clk_summary_show_one(struct seq_file *s, struct clk_core *c, + int level) +{ + if (!c) + return; + + 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_core_get_rate(c), + clk_core_get_accuracy(c), clk_core_get_phase(c)); +} + +static void clk_summary_show_subtree(struct seq_file *s, struct clk_core *c, + int level) +{ + struct clk_core *child; + + if (!c) + return; + + clk_summary_show_one(s, c, level); + + hlist_for_each_entry(child, &c->children, child_node) + clk_summary_show_subtree(s, child, level + 1); +} + +static int clk_summary_show(struct seq_file *s, void *data) +{ + struct clk_core *c; + struct hlist_head **lists = (struct hlist_head **)s->private; + + seq_puts(s, " clock enable_cnt prepare_cnt rate accuracy phase\n"); + seq_puts(s, "----------------------------------------------------------------------------------------\n"); + + clk_prepare_lock(); + + for (; *lists; lists++) + hlist_for_each_entry(c, *lists, child_node) + clk_summary_show_subtree(s, c, 0); + + clk_prepare_unlock(); + + return 0; +} + + +static int clk_summary_open(struct inode *inode, struct file *file) +{ + return single_open(file, clk_summary_show, inode->i_private); +} + +static const struct file_operations clk_summary_fops = { + .open = clk_summary_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void clk_dump_one(struct seq_file *s, struct clk_core *c, int level) +{ + if (!c) + return; + + /* This should be JSON format, i.e. elements separated with a comma */ + seq_printf(s, "\"%s\": { ", c->name); + seq_printf(s, "\"enable_count\": %d,", c->enable_count); + seq_printf(s, "\"prepare_count\": %d,", c->prepare_count); + seq_printf(s, "\"rate\": %lu,", clk_core_get_rate(c)); + seq_printf(s, "\"accuracy\": %lu,", clk_core_get_accuracy(c)); + seq_printf(s, "\"phase\": %d", clk_core_get_phase(c)); +} + +static void clk_dump_subtree(struct seq_file *s, struct clk_core *c, int level) +{ + struct clk_core *child; + + if (!c) + return; + + clk_dump_one(s, c, level); + + hlist_for_each_entry(child, &c->children, child_node) { + seq_printf(s, ","); + clk_dump_subtree(s, child, level + 1); + } + + seq_printf(s, "}"); +} + +static int clk_dump(struct seq_file *s, void *data) +{ + struct clk_core *c; + bool first_node = true; + struct hlist_head **lists = (struct hlist_head **)s->private; + + seq_printf(s, "{"); + + clk_prepare_lock(); + + for (; *lists; lists++) { + hlist_for_each_entry(c, *lists, child_node) { + if (!first_node) + seq_puts(s, ","); + first_node = false; + clk_dump_subtree(s, c, 0); + } + } + + clk_prepare_unlock(); + + seq_puts(s, "}\n"); + return 0; +} + + +static int clk_dump_open(struct inode *inode, struct file *file) +{ + return single_open(file, clk_dump, inode->i_private); +} + +static const struct file_operations clk_dump_fops = { + .open = clk_dump_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int clk_debug_create_one(struct clk_core *core, struct dentry *pdentry) +{ + struct dentry *d; + int ret = -ENOMEM; + + if (!core || !pdentry) { + ret = -EINVAL; + goto out; + } + + d = debugfs_create_dir(core->name, pdentry); + if (!d) + goto out; + + core->dentry = d; + + d = debugfs_create_u32("clk_rate", S_IRUGO, core->dentry, + (u32 *)&core->rate); + if (!d) + goto err_out; + + d = debugfs_create_u32("clk_accuracy", S_IRUGO, core->dentry, + (u32 *)&core->accuracy); + if (!d) + goto err_out; + + d = debugfs_create_u32("clk_phase", S_IRUGO, core->dentry, + (u32 *)&core->phase); + if (!d) + goto err_out; + + d = debugfs_create_x32("clk_flags", S_IRUGO, core->dentry, + (u32 *)&core->flags); + if (!d) + goto err_out; + + d = debugfs_create_u32("clk_prepare_count", S_IRUGO, core->dentry, + (u32 *)&core->prepare_count); + if (!d) + goto err_out; + + d = debugfs_create_u32("clk_enable_count", S_IRUGO, core->dentry, + (u32 *)&core->enable_count); + if (!d) + goto err_out; + + d = debugfs_create_u32("clk_notifier_count", S_IRUGO, core->dentry, + (u32 *)&core->notifier_count); + if (!d) + goto err_out; + + if (core->ops->debug_init) { + ret = core->ops->debug_init(core->hw, core->dentry); + if (ret) + goto err_out; + } + + ret = 0; + goto out; + +err_out: + debugfs_remove_recursive(core->dentry); + core->dentry = NULL; +out: + return ret; +} + +/** + * clk_debug_register - add a clk node to the debugfs clk directory + * @core: the clk being added to the debugfs clk directory + * + * Dynamically adds a clk to the debugfs clk directory if debugfs has been + * initialized. Otherwise it bails out early since the debugfs clk directory + * will be created lazily by clk_debug_init as part of a late_initcall. + */ +static int clk_debug_register(struct clk_core *core) +{ + int ret = 0; + + mutex_lock(&clk_debug_lock); + hlist_add_head(&core->debug_node, &clk_debug_list); + + if (!inited) + goto unlock; + + ret = clk_debug_create_one(core, rootdir); +unlock: + mutex_unlock(&clk_debug_lock); + + return ret; +} + + /** + * clk_debug_unregister - remove a clk node from the debugfs clk directory + * @core: the clk being removed from the debugfs clk directory + * + * Dynamically removes a clk and all its child nodes from the + * debugfs clk directory if clk->dentry points to debugfs created by + * clk_debug_register in __clk_init. + */ +static void clk_debug_unregister(struct clk_core *core) +{ + mutex_lock(&clk_debug_lock); + hlist_del_init(&core->debug_node); + debugfs_remove_recursive(core->dentry); + core->dentry = NULL; + mutex_unlock(&clk_debug_lock); +} + +struct dentry *clk_debugfs_add_file(struct clk_hw *hw, char *name, umode_t mode, + void *data, const struct file_operations *fops) +{ + struct dentry *d = NULL; + + if (hw->core->dentry) + d = debugfs_create_file(name, mode, hw->core->dentry, data, + fops); + + return d; +} +EXPORT_SYMBOL_GPL(clk_debugfs_add_file); + +/** + * clk_debug_init - lazily populate the debugfs clk directory + * + * clks are often initialized very early during boot before memory can be + * dynamically allocated and well before debugfs is setup. This function + * populates the debugfs clk directory once at boot-time when we know that + * debugfs is setup. It should only be called once at boot-time, all other clks + * added dynamically will be done so with clk_debug_register. + */ +static int __init clk_debug_init(void) +{ + struct clk_core *core; + struct dentry *d; + + rootdir = debugfs_create_dir("clk", NULL); + + if (!rootdir) + return -ENOMEM; + + d = debugfs_create_file("clk_summary", S_IRUGO, rootdir, &all_lists, + &clk_summary_fops); + if (!d) + return -ENOMEM; + + d = debugfs_create_file("clk_dump", S_IRUGO, rootdir, &all_lists, + &clk_dump_fops); + if (!d) + return -ENOMEM; + + d = debugfs_create_file("clk_orphan_summary", S_IRUGO, rootdir, + &orphan_list, &clk_summary_fops); + if (!d) + return -ENOMEM; + + d = debugfs_create_file("clk_orphan_dump", S_IRUGO, rootdir, + &orphan_list, &clk_dump_fops); + if (!d) + return -ENOMEM; + + mutex_lock(&clk_debug_lock); + hlist_for_each_entry(core, &clk_debug_list, debug_node) + clk_debug_create_one(core, rootdir); + + inited = 1; + mutex_unlock(&clk_debug_lock); + + return 0; +} +late_initcall(clk_debug_init); +#else +static inline int clk_debug_register(struct clk_core *core) { return 0; } +static inline void clk_debug_reparent(struct clk_core *core, + struct clk_core *new_parent) +{ +} +static inline void clk_debug_unregister(struct clk_core *core) +{ +} +#endif + /** * __clk_init - initialize the data structures in a struct clk * @dev: device initializing this clk, placeholder for now @@ -2271,67 +2238,67 @@ static int __clk_init(struct device *dev, struct clk *clk_user) int i, ret = 0; struct clk_core *orphan; struct hlist_node *tmp2; - struct clk_core *clk; + struct clk_core *core; unsigned long rate; if (!clk_user) return -EINVAL; - clk = clk_user->core; + core = clk_user->core; clk_prepare_lock(); /* check to see if a clock with this name is already registered */ - if (clk_core_lookup(clk->name)) { + if (clk_core_lookup(core->name)) { pr_debug("%s: clk %s already initialized\n", - __func__, clk->name); + __func__, core->name); ret = -EEXIST; goto out; } /* check that clk_ops are sane. See Documentation/clk.txt */ - if (clk->ops->set_rate && - !((clk->ops->round_rate || clk->ops->determine_rate) && - clk->ops->recalc_rate)) { + if (core->ops->set_rate && + !((core->ops->round_rate || core->ops->determine_rate) && + core->ops->recalc_rate)) { pr_warning("%s: %s must implement .round_rate or .determine_rate in addition to .recalc_rate\n", - __func__, clk->name); + __func__, core->name); ret = -EINVAL; goto out; } - if (clk->ops->set_parent && !clk->ops->get_parent) { + if (core->ops->set_parent && !core->ops->get_parent) { pr_warning("%s: %s must implement .get_parent & .set_parent\n", - __func__, clk->name); + __func__, core->name); ret = -EINVAL; goto out; } - if (clk->ops->set_rate_and_parent && - !(clk->ops->set_parent && clk->ops->set_rate)) { + if (core->ops->set_rate_and_parent && + !(core->ops->set_parent && core->ops->set_rate)) { pr_warn("%s: %s must implement .set_parent & .set_rate\n", - __func__, clk->name); + __func__, core->name); ret = -EINVAL; goto out; } /* throw a WARN if any entries in parent_names are NULL */ - for (i = 0; i < clk->num_parents; i++) - WARN(!clk->parent_names[i], + for (i = 0; i < core->num_parents; i++) + WARN(!core->parent_names[i], "%s: invalid NULL in %s's .parent_names\n", - __func__, clk->name); + __func__, core->name); /* * Allocate an array of struct clk *'s to avoid unnecessary string * look-ups of clk's possible parents. This can fail for clocks passed - * in to clk_init during early boot; thus any access to clk->parents[] + * in to clk_init during early boot; thus any access to core->parents[] * must always check for a NULL pointer and try to populate it if * necessary. * - * If clk->parents is not NULL we skip this entire block. This allows - * for clock drivers to statically initialize clk->parents. + * If core->parents is not NULL we skip this entire block. This allows + * for clock drivers to statically initialize core->parents. */ - if (clk->num_parents > 1 && !clk->parents) { - clk->parents = kcalloc(clk->num_parents, sizeof(struct clk *), + if (core->num_parents > 1 && !core->parents) { + core->parents = kcalloc(core->num_parents, sizeof(struct clk *), GFP_KERNEL); /* * clk_core_lookup returns NULL for parents that have not been @@ -2339,16 +2306,16 @@ static int __clk_init(struct device *dev, struct clk *clk_user) * for a NULL pointer. We can always perform lazy lookups for * missing parents later on. */ - if (clk->parents) - for (i = 0; i < clk->num_parents; i++) - clk->parents[i] = - clk_core_lookup(clk->parent_names[i]); + if (core->parents) + for (i = 0; i < core->num_parents; i++) + core->parents[i] = + clk_core_lookup(core->parent_names[i]); } - clk->parent = __clk_init_parent(clk); + core->parent = __clk_init_parent(core); /* - * Populate clk->parent if parent has already been __clk_init'd. If + * Populate core->parent if parent has already been __clk_init'd. If * parent has not yet been __clk_init'd then place clk in the orphan * list. If clk has set the CLK_IS_ROOT flag then place it in the root * clk list. @@ -2357,13 +2324,13 @@ static int __clk_init(struct device *dev, struct clk *clk_user) * clocks and re-parent any that are children of the clock currently * being clk_init'd. */ - if (clk->parent) - hlist_add_head(&clk->child_node, - &clk->parent->children); - else if (clk->flags & CLK_IS_ROOT) - hlist_add_head(&clk->child_node, &clk_root_list); + if (core->parent) + hlist_add_head(&core->child_node, + &core->parent->children); + else if (core->flags & CLK_IS_ROOT) + hlist_add_head(&core->child_node, &clk_root_list); else - hlist_add_head(&clk->child_node, &clk_orphan_list); + hlist_add_head(&core->child_node, &clk_orphan_list); /* * Set clk's accuracy. The preferred method is to use @@ -2372,23 +2339,23 @@ static int __clk_init(struct device *dev, struct clk *clk_user) * parent (or is orphaned) then accuracy is set to zero (perfect * clock). */ - if (clk->ops->recalc_accuracy) - clk->accuracy = clk->ops->recalc_accuracy(clk->hw, - __clk_get_accuracy(clk->parent)); - else if (clk->parent) - clk->accuracy = clk->parent->accuracy; + if (core->ops->recalc_accuracy) + core->accuracy = core->ops->recalc_accuracy(core->hw, + __clk_get_accuracy(core->parent)); + else if (core->parent) + core->accuracy = core->parent->accuracy; else - clk->accuracy = 0; + core->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); + if (core->ops->get_phase) + core->phase = core->ops->get_phase(core->hw); else - clk->phase = 0; + core->phase = 0; /* * Set clk's rate. The preferred method is to use .recalc_rate. For @@ -2396,14 +2363,14 @@ static int __clk_init(struct device *dev, struct clk *clk_user) * parent's rate. If a clock doesn't have a parent (or is orphaned) * then rate is set to zero. */ - if (clk->ops->recalc_rate) - rate = clk->ops->recalc_rate(clk->hw, - clk_core_get_rate_nolock(clk->parent)); - else if (clk->parent) - rate = clk->parent->rate; + if (core->ops->recalc_rate) + rate = core->ops->recalc_rate(core->hw, + clk_core_get_rate_nolock(core->parent)); + else if (core->parent) + rate = core->parent->rate; else rate = 0; - clk->rate = clk->req_rate = rate; + core->rate = core->req_rate = rate; /* * walk the list of orphan clocks and reparent any that are children of @@ -2412,14 +2379,14 @@ static int __clk_init(struct device *dev, struct clk *clk_user) hlist_for_each_entry_safe(orphan, tmp2, &clk_orphan_list, child_node) { if (orphan->num_parents && orphan->ops->get_parent) { i = orphan->ops->get_parent(orphan->hw); - if (!strcmp(clk->name, orphan->parent_names[i])) - clk_core_reparent(orphan, clk); + if (!strcmp(core->name, orphan->parent_names[i])) + clk_core_reparent(orphan, core); continue; } for (i = 0; i < orphan->num_parents; i++) - if (!strcmp(clk->name, orphan->parent_names[i])) { - clk_core_reparent(orphan, clk); + if (!strcmp(core->name, orphan->parent_names[i])) { + clk_core_reparent(orphan, core); break; } } @@ -2432,15 +2399,15 @@ static int __clk_init(struct device *dev, struct clk *clk_user) * Please consider other ways of solving initialization problems before * using this callback, as its use is discouraged. */ - if (clk->ops->init) - clk->ops->init(clk->hw); + if (core->ops->init) + core->ops->init(core->hw); - kref_init(&clk->ref); + kref_init(&core->ref); out: clk_prepare_unlock(); if (!ret) - clk_debug_register(clk); + clk_debug_register(core); return ret; } @@ -2486,63 +2453,58 @@ void __clk_free_clk(struct clk *clk) * * clk_register is the primary interface for populating the clock tree with new * clock nodes. It returns a pointer to the newly allocated struct clk which - * cannot be dereferenced by driver code but may be used in conjuction with the + * cannot be dereferenced by driver code but may be used in conjunction with the * rest of the clock API. In the event of an error clk_register will return an * error code; drivers must test for an error code after calling clk_register. */ struct clk *clk_register(struct device *dev, struct clk_hw *hw) { int i, ret; - struct clk_core *clk; + struct clk_core *core; - clk = kzalloc(sizeof(*clk), GFP_KERNEL); - if (!clk) { - pr_err("%s: could not allocate clk\n", __func__); + core = kzalloc(sizeof(*core), GFP_KERNEL); + if (!core) { ret = -ENOMEM; goto fail_out; } - clk->name = kstrdup_const(hw->init->name, GFP_KERNEL); - if (!clk->name) { - pr_err("%s: could not allocate clk->name\n", __func__); + core->name = kstrdup_const(hw->init->name, GFP_KERNEL); + if (!core->name) { ret = -ENOMEM; goto fail_name; } - clk->ops = hw->init->ops; + core->ops = hw->init->ops; if (dev && dev->driver) - clk->owner = dev->driver->owner; - clk->hw = hw; - clk->flags = hw->init->flags; - clk->num_parents = hw->init->num_parents; - hw->core = clk; + core->owner = dev->driver->owner; + core->hw = hw; + core->flags = hw->init->flags; + core->num_parents = hw->init->num_parents; + hw->core = core; /* allocate local copy in case parent_names is __initdata */ - clk->parent_names = kcalloc(clk->num_parents, sizeof(char *), + core->parent_names = kcalloc(core->num_parents, sizeof(char *), GFP_KERNEL); - if (!clk->parent_names) { - pr_err("%s: could not allocate clk->parent_names\n", __func__); + if (!core->parent_names) { ret = -ENOMEM; goto fail_parent_names; } /* copy each string name in case parent_names is __initdata */ - for (i = 0; i < clk->num_parents; i++) { - clk->parent_names[i] = kstrdup_const(hw->init->parent_names[i], + for (i = 0; i < core->num_parents; i++) { + core->parent_names[i] = kstrdup_const(hw->init->parent_names[i], GFP_KERNEL); - if (!clk->parent_names[i]) { - pr_err("%s: could not copy parent_names\n", __func__); + if (!core->parent_names[i]) { ret = -ENOMEM; goto fail_parent_names_copy; } } - INIT_HLIST_HEAD(&clk->clks); + INIT_HLIST_HEAD(&core->clks); hw->clk = __clk_create_clk(hw, NULL, NULL); if (IS_ERR(hw->clk)) { - pr_err("%s: could not allocate per-user clk\n", __func__); ret = PTR_ERR(hw->clk); goto fail_parent_names_copy; } @@ -2556,35 +2518,32 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw) fail_parent_names_copy: while (--i >= 0) - kfree_const(clk->parent_names[i]); - kfree(clk->parent_names); + kfree_const(core->parent_names[i]); + kfree(core->parent_names); fail_parent_names: - kfree_const(clk->name); + kfree_const(core->name); fail_name: - kfree(clk); + kfree(core); fail_out: return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(clk_register); -/* - * Free memory allocated for a clock. - * Caller must hold prepare_lock. - */ +/* Free memory allocated for a clock. */ static void __clk_release(struct kref *ref) { - struct clk_core *clk = container_of(ref, struct clk_core, ref); - int i = clk->num_parents; + struct clk_core *core = container_of(ref, struct clk_core, ref); + int i = core->num_parents; lockdep_assert_held(&prepare_lock); - kfree(clk->parents); + kfree(core->parents); while (--i >= 0) - kfree_const(clk->parent_names[i]); + kfree_const(core->parent_names[i]); - kfree(clk->parent_names); - kfree_const(clk->name); - kfree(clk); + kfree(core->parent_names); + kfree_const(core->name); + kfree(core); } /* @@ -3068,6 +3027,27 @@ const char *of_clk_get_parent_name(struct device_node *np, int index) } EXPORT_SYMBOL_GPL(of_clk_get_parent_name); +/** + * of_clk_parent_fill() - Fill @parents with names of @np's parents and return + * number of parents + * @np: Device node pointer associated with clock provider + * @parents: pointer to char array that hold the parents' names + * @size: size of the @parents array + * + * Return: number of parents for the clock node. + */ +int of_clk_parent_fill(struct device_node *np, const char **parents, + unsigned int size) +{ + unsigned int i = 0; + + while (i < size && (parents[i] = of_clk_get_parent_name(np, i)) != NULL) + i++; + + return i; +} +EXPORT_SYMBOL_GPL(of_clk_parent_fill); + struct clock_provider { of_clk_init_cb_t clk_init_cb; struct device_node *np; diff --git a/drivers/clk/hisilicon/Kconfig b/drivers/clk/hisilicon/Kconfig new file mode 100644 index 000000000000..b4165ba75d9f --- /dev/null +++ b/drivers/clk/hisilicon/Kconfig @@ -0,0 +1,6 @@ +config COMMON_CLK_HI6220 + bool "Hi6220 Clock Driver" + depends on ARCH_HISI || COMPILE_TEST + default ARCH_HISI + help + Build the Hisilicon Hi6220 clock driver based on the common clock framework. diff --git a/drivers/clk/hisilicon/Makefile b/drivers/clk/hisilicon/Makefile index 038c02f4d0e7..48f0116a032a 100644 --- a/drivers/clk/hisilicon/Makefile +++ b/drivers/clk/hisilicon/Makefile @@ -2,8 +2,9 @@ # Hisilicon Clock specific Makefile # -obj-y += clk.o clkgate-separated.o +obj-y += clk.o clkgate-separated.o clkdivider-hi6220.o obj-$(CONFIG_ARCH_HI3xxx) += clk-hi3620.o obj-$(CONFIG_ARCH_HIP04) += clk-hip04.o obj-$(CONFIG_ARCH_HIX5HD2) += clk-hix5hd2.o +obj-$(CONFIG_COMMON_CLK_HI6220) += clk-hi6220.o diff --git a/drivers/clk/hisilicon/clk-hi3620.c b/drivers/clk/hisilicon/clk-hi3620.c index 472dd2cb10b3..715d34a5ef9b 100644 --- a/drivers/clk/hisilicon/clk-hi3620.c +++ b/drivers/clk/hisilicon/clk-hi3620.c @@ -38,44 +38,44 @@ #include "clk.h" /* clock parent list */ -static const char *timer0_mux_p[] __initdata = { "osc32k", "timerclk01", }; -static const char *timer1_mux_p[] __initdata = { "osc32k", "timerclk01", }; -static const char *timer2_mux_p[] __initdata = { "osc32k", "timerclk23", }; -static const char *timer3_mux_p[] __initdata = { "osc32k", "timerclk23", }; -static const char *timer4_mux_p[] __initdata = { "osc32k", "timerclk45", }; -static const char *timer5_mux_p[] __initdata = { "osc32k", "timerclk45", }; -static const char *timer6_mux_p[] __initdata = { "osc32k", "timerclk67", }; -static const char *timer7_mux_p[] __initdata = { "osc32k", "timerclk67", }; -static const char *timer8_mux_p[] __initdata = { "osc32k", "timerclk89", }; -static const char *timer9_mux_p[] __initdata = { "osc32k", "timerclk89", }; -static const char *uart0_mux_p[] __initdata = { "osc26m", "pclk", }; -static const char *uart1_mux_p[] __initdata = { "osc26m", "pclk", }; -static const char *uart2_mux_p[] __initdata = { "osc26m", "pclk", }; -static const char *uart3_mux_p[] __initdata = { "osc26m", "pclk", }; -static const char *uart4_mux_p[] __initdata = { "osc26m", "pclk", }; -static const char *spi0_mux_p[] __initdata = { "osc26m", "rclk_cfgaxi", }; -static const char *spi1_mux_p[] __initdata = { "osc26m", "rclk_cfgaxi", }; -static const char *spi2_mux_p[] __initdata = { "osc26m", "rclk_cfgaxi", }; +static const char *const timer0_mux_p[] __initconst = { "osc32k", "timerclk01", }; +static const char *const timer1_mux_p[] __initconst = { "osc32k", "timerclk01", }; +static const char *const timer2_mux_p[] __initconst = { "osc32k", "timerclk23", }; +static const char *const timer3_mux_p[] __initconst = { "osc32k", "timerclk23", }; +static const char *const timer4_mux_p[] __initconst = { "osc32k", "timerclk45", }; +static const char *const timer5_mux_p[] __initconst = { "osc32k", "timerclk45", }; +static const char *const timer6_mux_p[] __initconst = { "osc32k", "timerclk67", }; +static const char *const timer7_mux_p[] __initconst = { "osc32k", "timerclk67", }; +static const char *const timer8_mux_p[] __initconst = { "osc32k", "timerclk89", }; +static const char *const timer9_mux_p[] __initconst = { "osc32k", "timerclk89", }; +static const char *const uart0_mux_p[] __initconst = { "osc26m", "pclk", }; +static const char *const uart1_mux_p[] __initconst = { "osc26m", "pclk", }; +static const char *const uart2_mux_p[] __initconst = { "osc26m", "pclk", }; +static const char *const uart3_mux_p[] __initconst = { "osc26m", "pclk", }; +static const char *const uart4_mux_p[] __initconst = { "osc26m", "pclk", }; +static const char *const spi0_mux_p[] __initconst = { "osc26m", "rclk_cfgaxi", }; +static const char *const spi1_mux_p[] __initconst = { "osc26m", "rclk_cfgaxi", }; +static const char *const spi2_mux_p[] __initconst = { "osc26m", "rclk_cfgaxi", }; /* share axi parent */ -static const char *saxi_mux_p[] __initdata = { "armpll3", "armpll2", }; -static const char *pwm0_mux_p[] __initdata = { "osc32k", "osc26m", }; -static const char *pwm1_mux_p[] __initdata = { "osc32k", "osc26m", }; -static const char *sd_mux_p[] __initdata = { "armpll2", "armpll3", }; -static const char *mmc1_mux_p[] __initdata = { "armpll2", "armpll3", }; -static const char *mmc1_mux2_p[] __initdata = { "osc26m", "mmc1_div", }; -static const char *g2d_mux_p[] __initdata = { "armpll2", "armpll3", }; -static const char *venc_mux_p[] __initdata = { "armpll2", "armpll3", }; -static const char *vdec_mux_p[] __initdata = { "armpll2", "armpll3", }; -static const char *vpp_mux_p[] __initdata = { "armpll2", "armpll3", }; -static const char *edc0_mux_p[] __initdata = { "armpll2", "armpll3", }; -static const char *ldi0_mux_p[] __initdata = { "armpll2", "armpll4", +static const char *const saxi_mux_p[] __initconst = { "armpll3", "armpll2", }; +static const char *const pwm0_mux_p[] __initconst = { "osc32k", "osc26m", }; +static const char *const pwm1_mux_p[] __initconst = { "osc32k", "osc26m", }; +static const char *const sd_mux_p[] __initconst = { "armpll2", "armpll3", }; +static const char *const mmc1_mux_p[] __initconst = { "armpll2", "armpll3", }; +static const char *const mmc1_mux2_p[] __initconst = { "osc26m", "mmc1_div", }; +static const char *const g2d_mux_p[] __initconst = { "armpll2", "armpll3", }; +static const char *const venc_mux_p[] __initconst = { "armpll2", "armpll3", }; +static const char *const vdec_mux_p[] __initconst = { "armpll2", "armpll3", }; +static const char *const vpp_mux_p[] __initconst = { "armpll2", "armpll3", }; +static const char *const edc0_mux_p[] __initconst = { "armpll2", "armpll3", }; +static const char *const ldi0_mux_p[] __initconst = { "armpll2", "armpll4", "armpll3", "armpll5", }; -static const char *edc1_mux_p[] __initdata = { "armpll2", "armpll3", }; -static const char *ldi1_mux_p[] __initdata = { "armpll2", "armpll4", +static const char *const edc1_mux_p[] __initconst = { "armpll2", "armpll3", }; +static const char *const ldi1_mux_p[] __initconst = { "armpll2", "armpll4", "armpll3", "armpll5", }; -static const char *rclk_hsic_p[] __initdata = { "armpll3", "armpll2", }; -static const char *mmc2_mux_p[] __initdata = { "armpll2", "armpll3", }; -static const char *mmc3_mux_p[] __initdata = { "armpll2", "armpll3", }; +static const char *const rclk_hsic_p[] __initconst = { "armpll3", "armpll2", }; +static const char *const mmc2_mux_p[] __initconst = { "armpll2", "armpll3", }; +static const char *const mmc3_mux_p[] __initconst = { "armpll2", "armpll3", }; /* fixed rate clocks */ diff --git a/drivers/clk/hisilicon/clk-hi6220.c b/drivers/clk/hisilicon/clk-hi6220.c new file mode 100644 index 000000000000..4563343b6420 --- /dev/null +++ b/drivers/clk/hisilicon/clk-hi6220.c @@ -0,0 +1,284 @@ +/* + * Hisilicon Hi6220 clock driver + * + * Copyright (c) 2015 Hisilicon Limited. + * + * Author: Bintian Wang + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "clk.h" + + +/* clocks in AO (always on) controller */ +static struct hisi_fixed_rate_clock hi6220_fixed_rate_clks[] __initdata = { + { HI6220_REF32K, "ref32k", NULL, CLK_IS_ROOT, 32764, }, + { HI6220_CLK_TCXO, "clk_tcxo", NULL, CLK_IS_ROOT, 19200000, }, + { HI6220_MMC1_PAD, "mmc1_pad", NULL, CLK_IS_ROOT, 100000000, }, + { HI6220_MMC2_PAD, "mmc2_pad", NULL, CLK_IS_ROOT, 100000000, }, + { HI6220_MMC0_PAD, "mmc0_pad", NULL, CLK_IS_ROOT, 200000000, }, + { HI6220_PLL_BBP, "bbppll0", NULL, CLK_IS_ROOT, 245760000, }, + { HI6220_PLL_GPU, "gpupll", NULL, CLK_IS_ROOT, 1000000000,}, + { HI6220_PLL1_DDR, "ddrpll1", NULL, CLK_IS_ROOT, 1066000000,}, + { HI6220_PLL_SYS, "syspll", NULL, CLK_IS_ROOT, 1200000000,}, + { HI6220_PLL_SYS_MEDIA, "media_syspll", NULL, CLK_IS_ROOT, 1200000000,}, + { HI6220_DDR_SRC, "ddr_sel_src", NULL, CLK_IS_ROOT, 1200000000,}, + { HI6220_PLL_MEDIA, "media_pll", NULL, CLK_IS_ROOT, 1440000000,}, + { HI6220_PLL_DDR, "ddrpll0", NULL, CLK_IS_ROOT, 1600000000,}, +}; + +static struct hisi_fixed_factor_clock hi6220_fixed_factor_clks[] __initdata = { + { HI6220_300M, "clk_300m", "syspll", 1, 4, 0, }, + { HI6220_150M, "clk_150m", "clk_300m", 1, 2, 0, }, + { HI6220_PICOPHY_SRC, "picophy_src", "clk_150m", 1, 4, 0, }, + { HI6220_MMC0_SRC_SEL, "mmc0srcsel", "mmc0_sel", 1, 8, 0, }, + { HI6220_MMC1_SRC_SEL, "mmc1srcsel", "mmc1_sel", 1, 8, 0, }, + { HI6220_MMC2_SRC_SEL, "mmc2srcsel", "mmc2_sel", 1, 8, 0, }, + { HI6220_VPU_CODEC, "vpucodec", "codec_jpeg_aclk", 1, 2, 0, }, + { HI6220_MMC0_SMP, "mmc0_sample", "mmc0_sel", 1, 8, 0, }, + { HI6220_MMC1_SMP, "mmc1_sample", "mmc1_sel", 1, 8, 0, }, + { HI6220_MMC2_SMP, "mmc2_sample", "mmc2_sel", 1, 8, 0, }, +}; + +static struct hisi_gate_clock hi6220_separated_gate_clks_ao[] __initdata = { + { HI6220_WDT0_PCLK, "wdt0_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 12, 0, }, + { HI6220_WDT1_PCLK, "wdt1_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 13, 0, }, + { HI6220_WDT2_PCLK, "wdt2_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 14, 0, }, + { HI6220_TIMER0_PCLK, "timer0_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 15, 0, }, + { HI6220_TIMER1_PCLK, "timer1_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 16, 0, }, + { HI6220_TIMER2_PCLK, "timer2_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 17, 0, }, + { HI6220_TIMER3_PCLK, "timer3_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 18, 0, }, + { HI6220_TIMER4_PCLK, "timer4_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 19, 0, }, + { HI6220_TIMER5_PCLK, "timer5_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 20, 0, }, + { HI6220_TIMER6_PCLK, "timer6_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 21, 0, }, + { HI6220_TIMER7_PCLK, "timer7_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 22, 0, }, + { HI6220_TIMER8_PCLK, "timer8_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 23, 0, }, + { HI6220_UART0_PCLK, "uart0_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 24, 0, }, +}; + +static void __init hi6220_clk_ao_init(struct device_node *np) +{ + struct hisi_clock_data *clk_data_ao; + + clk_data_ao = hisi_clk_init(np, HI6220_AO_NR_CLKS); + if (!clk_data_ao) + return; + + hisi_clk_register_fixed_rate(hi6220_fixed_rate_clks, + ARRAY_SIZE(hi6220_fixed_rate_clks), clk_data_ao); + + hisi_clk_register_fixed_factor(hi6220_fixed_factor_clks, + ARRAY_SIZE(hi6220_fixed_factor_clks), clk_data_ao); + + hisi_clk_register_gate_sep(hi6220_separated_gate_clks_ao, + ARRAY_SIZE(hi6220_separated_gate_clks_ao), clk_data_ao); +} +CLK_OF_DECLARE(hi6220_clk_ao, "hisilicon,hi6220-aoctrl", hi6220_clk_ao_init); + + +/* clocks in sysctrl */ +static const char *mmc0_mux0_p[] __initdata = { "pll_ddr_gate", "syspll", }; +static const char *mmc0_mux1_p[] __initdata = { "mmc0_mux0", "pll_media_gate", }; +static const char *mmc0_src_p[] __initdata = { "mmc0srcsel", "mmc0_div", }; +static const char *mmc1_mux0_p[] __initdata = { "pll_ddr_gate", "syspll", }; +static const char *mmc1_mux1_p[] __initdata = { "mmc1_mux0", "pll_media_gate", }; +static const char *mmc1_src_p[] __initdata = { "mmc1srcsel", "mmc1_div", }; +static const char *mmc2_mux0_p[] __initdata = { "pll_ddr_gate", "syspll", }; +static const char *mmc2_mux1_p[] __initdata = { "mmc2_mux0", "pll_media_gate", }; +static const char *mmc2_src_p[] __initdata = { "mmc2srcsel", "mmc2_div", }; +static const char *mmc0_sample_in[] __initdata = { "mmc0_sample", "mmc0_pad", }; +static const char *mmc1_sample_in[] __initdata = { "mmc1_sample", "mmc1_pad", }; +static const char *mmc2_sample_in[] __initdata = { "mmc2_sample", "mmc2_pad", }; +static const char *uart1_src[] __initdata = { "clk_tcxo", "clk_150m", }; +static const char *uart2_src[] __initdata = { "clk_tcxo", "clk_150m", }; +static const char *uart3_src[] __initdata = { "clk_tcxo", "clk_150m", }; +static const char *uart4_src[] __initdata = { "clk_tcxo", "clk_150m", }; +static const char *hifi_src[] __initdata = { "syspll", "pll_media_gate", }; + +static struct hisi_gate_clock hi6220_separated_gate_clks_sys[] __initdata = { + { HI6220_MMC0_CLK, "mmc0_clk", "mmc0_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x200, 0, 0, }, + { HI6220_MMC0_CIUCLK, "mmc0_ciuclk", "mmc0_smp_in", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x200, 0, 0, }, + { HI6220_MMC1_CLK, "mmc1_clk", "mmc1_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x200, 1, 0, }, + { HI6220_MMC1_CIUCLK, "mmc1_ciuclk", "mmc1_smp_in", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x200, 1, 0, }, + { HI6220_MMC2_CLK, "mmc2_clk", "mmc2_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x200, 2, 0, }, + { HI6220_MMC2_CIUCLK, "mmc2_ciuclk", "mmc2_smp_in", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x200, 2, 0, }, + { HI6220_USBOTG_HCLK, "usbotg_hclk", "clk_bus", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x200, 4, 0, }, + { HI6220_CLK_PICOPHY, "clk_picophy", "cs_dapb", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x200, 5, 0, }, + { HI6220_HIFI, "hifi_clk", "hifi_div", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x210, 0, 0, }, + { HI6220_DACODEC_PCLK, "dacodec_pclk", "clk_bus", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x210, 5, 0, }, + { HI6220_EDMAC_ACLK, "edmac_aclk", "clk_bus", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x220, 2, 0, }, + { HI6220_CS_ATB, "cs_atb", "cs_atb_div", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 0, 0, }, + { HI6220_I2C0_CLK, "i2c0_clk", "clk_150m", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 1, 0, }, + { HI6220_I2C1_CLK, "i2c1_clk", "clk_150m", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 2, 0, }, + { HI6220_I2C2_CLK, "i2c2_clk", "clk_150m", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 3, 0, }, + { HI6220_I2C3_CLK, "i2c3_clk", "clk_150m", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 4, 0, }, + { HI6220_UART1_PCLK, "uart1_pclk", "uart1_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 5, 0, }, + { HI6220_UART2_PCLK, "uart2_pclk", "uart2_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 6, 0, }, + { HI6220_UART3_PCLK, "uart3_pclk", "uart3_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 7, 0, }, + { HI6220_UART4_PCLK, "uart4_pclk", "uart4_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 8, 0, }, + { HI6220_SPI_CLK, "spi_clk", "clk_150m", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 9, 0, }, + { HI6220_TSENSOR_CLK, "tsensor_clk", "clk_bus", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 12, 0, }, + { HI6220_MMU_CLK, "mmu_clk", "ddrc_axi1", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x240, 11, 0, }, + { HI6220_HIFI_SEL, "hifi_sel", "hifi_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 0, 0, }, + { HI6220_MMC0_SYSPLL, "mmc0_syspll", "syspll", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 1, 0, }, + { HI6220_MMC1_SYSPLL, "mmc1_syspll", "syspll", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 2, 0, }, + { HI6220_MMC2_SYSPLL, "mmc2_syspll", "syspll", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 3, 0, }, + { HI6220_MMC0_SEL, "mmc0_sel", "mmc0_mux1", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 6, 0, }, + { HI6220_MMC1_SEL, "mmc1_sel", "mmc1_mux1", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 7, 0, }, + { HI6220_BBPPLL_SEL, "bbppll_sel", "pll0_bbp_gate", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 9, 0, }, + { HI6220_MEDIA_PLL_SRC, "media_pll_src", "pll_media_gate", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 10, 0, }, + { HI6220_MMC2_SEL, "mmc2_sel", "mmc2_mux1", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 11, 0, }, + { HI6220_CS_ATB_SYSPLL, "cs_atb_syspll", "syspll", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 12, 0, }, +}; + +static struct hisi_mux_clock hi6220_mux_clks_sys[] __initdata = { + { HI6220_MMC0_SRC, "mmc0_src", mmc0_src_p, ARRAY_SIZE(mmc0_src_p), CLK_SET_RATE_PARENT, 0x4, 0, 1, 0, }, + { HI6220_MMC0_SMP_IN, "mmc0_smp_in", mmc0_sample_in, ARRAY_SIZE(mmc0_sample_in), CLK_SET_RATE_PARENT, 0x4, 0, 1, 0, }, + { HI6220_MMC1_SRC, "mmc1_src", mmc1_src_p, ARRAY_SIZE(mmc1_src_p), CLK_SET_RATE_PARENT, 0x4, 2, 1, 0, }, + { HI6220_MMC1_SMP_IN, "mmc1_smp_in", mmc1_sample_in, ARRAY_SIZE(mmc1_sample_in), CLK_SET_RATE_PARENT, 0x4, 2, 1, 0, }, + { HI6220_MMC2_SRC, "mmc2_src", mmc2_src_p, ARRAY_SIZE(mmc2_src_p), CLK_SET_RATE_PARENT, 0x4, 4, 1, 0, }, + { HI6220_MMC2_SMP_IN, "mmc2_smp_in", mmc2_sample_in, ARRAY_SIZE(mmc2_sample_in), CLK_SET_RATE_PARENT, 0x4, 4, 1, 0, }, + { HI6220_HIFI_SRC, "hifi_src", hifi_src, ARRAY_SIZE(hifi_src), CLK_SET_RATE_PARENT, 0x400, 0, 1, CLK_MUX_HIWORD_MASK,}, + { HI6220_UART1_SRC, "uart1_src", uart1_src, ARRAY_SIZE(uart1_src), CLK_SET_RATE_PARENT, 0x400, 1, 1, CLK_MUX_HIWORD_MASK,}, + { HI6220_UART2_SRC, "uart2_src", uart2_src, ARRAY_SIZE(uart2_src), CLK_SET_RATE_PARENT, 0x400, 2, 1, CLK_MUX_HIWORD_MASK,}, + { HI6220_UART3_SRC, "uart3_src", uart3_src, ARRAY_SIZE(uart3_src), CLK_SET_RATE_PARENT, 0x400, 3, 1, CLK_MUX_HIWORD_MASK,}, + { HI6220_UART4_SRC, "uart4_src", uart4_src, ARRAY_SIZE(uart4_src), CLK_SET_RATE_PARENT, 0x400, 4, 1, CLK_MUX_HIWORD_MASK,}, + { HI6220_MMC0_MUX0, "mmc0_mux0", mmc0_mux0_p, ARRAY_SIZE(mmc0_mux0_p), CLK_SET_RATE_PARENT, 0x400, 5, 1, CLK_MUX_HIWORD_MASK,}, + { HI6220_MMC1_MUX0, "mmc1_mux0", mmc1_mux0_p, ARRAY_SIZE(mmc1_mux0_p), CLK_SET_RATE_PARENT, 0x400, 11, 1, CLK_MUX_HIWORD_MASK,}, + { HI6220_MMC2_MUX0, "mmc2_mux0", mmc2_mux0_p, ARRAY_SIZE(mmc2_mux0_p), CLK_SET_RATE_PARENT, 0x400, 12, 1, CLK_MUX_HIWORD_MASK,}, + { HI6220_MMC0_MUX1, "mmc0_mux1", mmc0_mux1_p, ARRAY_SIZE(mmc0_mux1_p), CLK_SET_RATE_PARENT, 0x400, 13, 1, CLK_MUX_HIWORD_MASK,}, + { HI6220_MMC1_MUX1, "mmc1_mux1", mmc1_mux1_p, ARRAY_SIZE(mmc1_mux1_p), CLK_SET_RATE_PARENT, 0x400, 14, 1, CLK_MUX_HIWORD_MASK,}, + { HI6220_MMC2_MUX1, "mmc2_mux1", mmc2_mux1_p, ARRAY_SIZE(mmc2_mux1_p), CLK_SET_RATE_PARENT, 0x400, 15, 1, CLK_MUX_HIWORD_MASK,}, +}; + +static struct hi6220_divider_clock hi6220_div_clks_sys[] __initdata = { + { HI6220_CLK_BUS, "clk_bus", "clk_300m", CLK_SET_RATE_PARENT, 0x490, 0, 4, 7, }, + { HI6220_MMC0_DIV, "mmc0_div", "mmc0_syspll", CLK_SET_RATE_PARENT, 0x494, 0, 6, 7, }, + { HI6220_MMC1_DIV, "mmc1_div", "mmc1_syspll", CLK_SET_RATE_PARENT, 0x498, 0, 6, 7, }, + { HI6220_MMC2_DIV, "mmc2_div", "mmc2_syspll", CLK_SET_RATE_PARENT, 0x49c, 0, 6, 7, }, + { HI6220_HIFI_DIV, "hifi_div", "hifi_sel", CLK_SET_RATE_PARENT, 0x4a0, 0, 4, 7, }, + { HI6220_BBPPLL0_DIV, "bbppll0_div", "bbppll_sel", CLK_SET_RATE_PARENT, 0x4a0, 8, 6, 15,}, + { HI6220_CS_DAPB, "cs_dapb", "picophy_src", CLK_SET_RATE_PARENT, 0x4a0, 24, 2, 31,}, + { HI6220_CS_ATB_DIV, "cs_atb_div", "cs_atb_syspll", CLK_SET_RATE_PARENT, 0x4a4, 0, 4, 7, }, +}; + +static void __init hi6220_clk_sys_init(struct device_node *np) +{ + struct hisi_clock_data *clk_data; + + clk_data = hisi_clk_init(np, HI6220_SYS_NR_CLKS); + if (!clk_data) + return; + + hisi_clk_register_gate_sep(hi6220_separated_gate_clks_sys, + ARRAY_SIZE(hi6220_separated_gate_clks_sys), clk_data); + + hisi_clk_register_mux(hi6220_mux_clks_sys, + ARRAY_SIZE(hi6220_mux_clks_sys), clk_data); + + hi6220_clk_register_divider(hi6220_div_clks_sys, + ARRAY_SIZE(hi6220_div_clks_sys), clk_data); +} +CLK_OF_DECLARE(hi6220_clk_sys, "hisilicon,hi6220-sysctrl", hi6220_clk_sys_init); + + +/* clocks in media controller */ +static const char *clk_1000_1200_src[] __initdata = { "pll_gpu_gate", "media_syspll_src", }; +static const char *clk_1440_1200_src[] __initdata = { "media_syspll_src", "media_pll_src", }; +static const char *clk_1000_1440_src[] __initdata = { "pll_gpu_gate", "media_pll_src", }; + +static struct hisi_gate_clock hi6220_separated_gate_clks_media[] __initdata = { + { HI6220_DSI_PCLK, "dsi_pclk", "vpucodec", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 0, 0, }, + { HI6220_G3D_PCLK, "g3d_pclk", "vpucodec", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 1, 0, }, + { HI6220_ACLK_CODEC_VPU, "aclk_codec_vpu", "ade_core_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 3, 0, }, + { HI6220_ISP_SCLK, "isp_sclk", "isp_sclk_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 5, 0, }, + { HI6220_ADE_CORE, "ade_core", "ade_core_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 6, 0, }, + { HI6220_MED_MMU, "media_mmu", "mmu_clk", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 8, 0, }, + { HI6220_CFG_CSI4PHY, "cfg_csi4phy", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 9, 0, }, + { HI6220_CFG_CSI2PHY, "cfg_csi2phy", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 10, 0, }, + { HI6220_ISP_SCLK_GATE, "isp_sclk_gate", "media_pll_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 11, 0, }, + { HI6220_ISP_SCLK_GATE1, "isp_sclk_gate1", "media_pll_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 12, 0, }, + { HI6220_ADE_CORE_GATE, "ade_core_gate", "media_pll_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 14, 0, }, + { HI6220_CODEC_VPU_GATE, "codec_vpu_gate", "clk_1000_1440", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 15, 0, }, + { HI6220_MED_SYSPLL, "media_syspll_src", "media_syspll", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 17, 0, }, +}; + +static struct hisi_mux_clock hi6220_mux_clks_media[] __initdata = { + { HI6220_1440_1200, "clk_1440_1200", clk_1440_1200_src, ARRAY_SIZE(clk_1440_1200_src), CLK_SET_RATE_PARENT, 0x51c, 0, 1, 0, }, + { HI6220_1000_1200, "clk_1000_1200", clk_1000_1200_src, ARRAY_SIZE(clk_1000_1200_src), CLK_SET_RATE_PARENT, 0x51c, 1, 1, 0, }, + { HI6220_1000_1440, "clk_1000_1440", clk_1000_1440_src, ARRAY_SIZE(clk_1000_1440_src), CLK_SET_RATE_PARENT, 0x51c, 6, 1, 0, }, +}; + +static struct hi6220_divider_clock hi6220_div_clks_media[] __initdata = { + { HI6220_CODEC_JPEG, "codec_jpeg_aclk", "media_pll_src", CLK_SET_RATE_PARENT, 0xcbc, 0, 4, 23, }, + { HI6220_ISP_SCLK_SRC, "isp_sclk_src", "isp_sclk_gate", CLK_SET_RATE_PARENT, 0xcbc, 8, 4, 15, }, + { HI6220_ISP_SCLK1, "isp_sclk1", "isp_sclk_gate1", CLK_SET_RATE_PARENT, 0xcbc, 24, 4, 31, }, + { HI6220_ADE_CORE_SRC, "ade_core_src", "ade_core_gate", CLK_SET_RATE_PARENT, 0xcc0, 16, 3, 23, }, + { HI6220_ADE_PIX_SRC, "ade_pix_src", "clk_1440_1200", CLK_SET_RATE_PARENT, 0xcc0, 24, 6, 31, }, + { HI6220_G3D_CLK, "g3d_clk", "clk_1000_1200", CLK_SET_RATE_PARENT, 0xcc4, 8, 4, 15, }, + { HI6220_CODEC_VPU_SRC, "codec_vpu_src", "codec_vpu_gate", CLK_SET_RATE_PARENT, 0xcc4, 24, 6, 31, }, +}; + +static void __init hi6220_clk_media_init(struct device_node *np) +{ + struct hisi_clock_data *clk_data; + + clk_data = hisi_clk_init(np, HI6220_MEDIA_NR_CLKS); + if (!clk_data) + return; + + hisi_clk_register_gate_sep(hi6220_separated_gate_clks_media, + ARRAY_SIZE(hi6220_separated_gate_clks_media), clk_data); + + hisi_clk_register_mux(hi6220_mux_clks_media, + ARRAY_SIZE(hi6220_mux_clks_media), clk_data); + + hi6220_clk_register_divider(hi6220_div_clks_media, + ARRAY_SIZE(hi6220_div_clks_media), clk_data); +} +CLK_OF_DECLARE(hi6220_clk_media, "hisilicon,hi6220-mediactrl", hi6220_clk_media_init); + + +/* clocks in pmctrl */ +static struct hisi_gate_clock hi6220_gate_clks_power[] __initdata = { + { HI6220_PLL_GPU_GATE, "pll_gpu_gate", "gpupll", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x8, 0, 0, }, + { HI6220_PLL1_DDR_GATE, "pll1_ddr_gate", "ddrpll1", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x10, 0, 0, }, + { HI6220_PLL_DDR_GATE, "pll_ddr_gate", "ddrpll0", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x18, 0, 0, }, + { HI6220_PLL_MEDIA_GATE, "pll_media_gate", "media_pll", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x38, 0, 0, }, + { HI6220_PLL0_BBP_GATE, "pll0_bbp_gate", "bbppll0", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x48, 0, 0, }, +}; + +static struct hi6220_divider_clock hi6220_div_clks_power[] __initdata = { + { HI6220_DDRC_SRC, "ddrc_src", "ddr_sel_src", CLK_SET_RATE_PARENT, 0x5a8, 0, 4, 0, }, + { HI6220_DDRC_AXI1, "ddrc_axi1", "ddrc_src", CLK_SET_RATE_PARENT, 0x5a8, 8, 2, 0, }, +}; + +static void __init hi6220_clk_power_init(struct device_node *np) +{ + struct hisi_clock_data *clk_data; + + clk_data = hisi_clk_init(np, HI6220_POWER_NR_CLKS); + if (!clk_data) + return; + + hisi_clk_register_gate(hi6220_gate_clks_power, + ARRAY_SIZE(hi6220_gate_clks_power), clk_data); + + hi6220_clk_register_divider(hi6220_div_clks_power, + ARRAY_SIZE(hi6220_div_clks_power), clk_data); +} +CLK_OF_DECLARE(hi6220_clk_power, "hisilicon,hi6220-pmctrl", hi6220_clk_power_init); diff --git a/drivers/clk/hisilicon/clk-hix5hd2.c b/drivers/clk/hisilicon/clk-hix5hd2.c index f1d239435826..0aaf29da8491 100644 --- a/drivers/clk/hisilicon/clk-hix5hd2.c +++ b/drivers/clk/hisilicon/clk-hix5hd2.c @@ -46,15 +46,15 @@ static struct hisi_fixed_rate_clock hix5hd2_fixed_rate_clks[] __initdata = { { HIX5HD2_FIXED_83M, "83m", NULL, CLK_IS_ROOT, 83333333, }, }; -static const char *sfc_mux_p[] __initdata = { +static const char *const sfc_mux_p[] __initconst = { "24m", "150m", "200m", "100m", "75m", }; static u32 sfc_mux_table[] = {0, 4, 5, 6, 7}; -static const char *sdio_mux_p[] __initdata = { +static const char *const sdio_mux_p[] __initconst = { "75m", "100m", "50m", "15m", }; static u32 sdio_mux_table[] = {0, 1, 2, 3}; -static const char *fephy_mux_p[] __initdata = { "25m", "125m"}; +static const char *const fephy_mux_p[] __initconst = { "25m", "125m"}; static u32 fephy_mux_table[] = {0, 1}; @@ -252,8 +252,9 @@ static struct clk_ops clk_complex_ops = { .disable = clk_complex_disable, }; -void __init hix5hd2_clk_register_complex(struct hix5hd2_complex_clock *clks, - int nums, struct hisi_clock_data *data) +static 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; diff --git a/drivers/clk/hisilicon/clk.c b/drivers/clk/hisilicon/clk.c index a078e84f7b05..c90a89739b03 100644 --- a/drivers/clk/hisilicon/clk.c +++ b/drivers/clk/hisilicon/clk.c @@ -232,3 +232,32 @@ void __init hisi_clk_register_gate_sep(struct hisi_gate_clock *clks, data->clk_data.clks[clks[i].id] = clk; } } + +void __init hi6220_clk_register_divider(struct hi6220_divider_clock *clks, + int nums, struct hisi_clock_data *data) +{ + struct clk *clk; + void __iomem *base = data->base; + int i; + + for (i = 0; i < nums; i++) { + clk = hi6220_register_clkdiv(NULL, clks[i].name, + clks[i].parent_name, + clks[i].flags, + base + clks[i].offset, + clks[i].shift, + clks[i].width, + clks[i].mask_bit, + &hisi_clk_lock); + if (IS_ERR(clk)) { + pr_err("%s: failed to register clock %s\n", + __func__, clks[i].name); + continue; + } + + if (clks[i].alias) + clk_register_clkdev(clk, clks[i].alias, NULL); + + data->clk_data.clks[clks[i].id] = clk; + } +} diff --git a/drivers/clk/hisilicon/clk.h b/drivers/clk/hisilicon/clk.h index 31083ffc0650..b56fbc1c5f27 100644 --- a/drivers/clk/hisilicon/clk.h +++ b/drivers/clk/hisilicon/clk.h @@ -55,7 +55,7 @@ struct hisi_fixed_factor_clock { struct hisi_mux_clock { unsigned int id; const char *name; - const char **parent_names; + const char *const *parent_names; u8 num_parents; unsigned long flags; unsigned long offset; @@ -79,6 +79,18 @@ struct hisi_divider_clock { const char *alias; }; +struct hi6220_divider_clock { + unsigned int id; + const char *name; + const char *parent_name; + unsigned long flags; + unsigned long offset; + u8 shift; + u8 width; + u32 mask_bit; + const char *alias; +}; + struct hisi_gate_clock { unsigned int id; const char *name; @@ -94,18 +106,23 @@ struct clk *hisi_register_clkgate_sep(struct device *, const char *, const char *, unsigned long, void __iomem *, u8, u8, spinlock_t *); +struct clk *hi6220_register_clkdiv(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, void __iomem *reg, + u8 shift, u8 width, u32 mask_bit, spinlock_t *lock); -struct hisi_clock_data __init *hisi_clk_init(struct device_node *, int); -void __init hisi_clk_register_fixed_rate(struct hisi_fixed_rate_clock *, - int, struct hisi_clock_data *); -void __init hisi_clk_register_fixed_factor(struct hisi_fixed_factor_clock *, - int, struct hisi_clock_data *); -void __init hisi_clk_register_mux(struct hisi_mux_clock *, int, - struct hisi_clock_data *); -void __init hisi_clk_register_divider(struct hisi_divider_clock *, +struct hisi_clock_data *hisi_clk_init(struct device_node *, int); +void hisi_clk_register_fixed_rate(struct hisi_fixed_rate_clock *, + int, struct hisi_clock_data *); +void hisi_clk_register_fixed_factor(struct hisi_fixed_factor_clock *, + int, struct hisi_clock_data *); +void hisi_clk_register_mux(struct hisi_mux_clock *, int, + struct hisi_clock_data *); +void hisi_clk_register_divider(struct hisi_divider_clock *, + int, struct hisi_clock_data *); +void hisi_clk_register_gate(struct hisi_gate_clock *, + int, struct hisi_clock_data *); +void hisi_clk_register_gate_sep(struct hisi_gate_clock *, + int, struct hisi_clock_data *); +void hi6220_clk_register_divider(struct hi6220_divider_clock *, int, struct hisi_clock_data *); -void __init hisi_clk_register_gate(struct hisi_gate_clock *, - int, struct hisi_clock_data *); -void __init hisi_clk_register_gate_sep(struct hisi_gate_clock *, - int, struct hisi_clock_data *); #endif /* __HISI_CLK_H */ diff --git a/drivers/clk/hisilicon/clkdivider-hi6220.c b/drivers/clk/hisilicon/clkdivider-hi6220.c new file mode 100644 index 000000000000..113eee8ed23a --- /dev/null +++ b/drivers/clk/hisilicon/clkdivider-hi6220.c @@ -0,0 +1,156 @@ +/* + * Hisilicon hi6220 SoC divider clock driver + * + * Copyright (c) 2015 Hisilicon Limited. + * + * Author: Bintian Wang + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include + +#define div_mask(width) ((1 << (width)) - 1) + +/** + * struct hi6220_clk_divider - divider clock for hi6220 + * + * @hw: handle between common and hardware-specific interfaces + * @reg: register containing divider + * @shift: shift to the divider bit field + * @width: width of the divider bit field + * @mask: mask for setting divider rate + * @table: the div table that the divider supports + * @lock: register lock + */ +struct hi6220_clk_divider { + struct clk_hw hw; + void __iomem *reg; + u8 shift; + u8 width; + u32 mask; + const struct clk_div_table *table; + spinlock_t *lock; +}; + +#define to_hi6220_clk_divider(_hw) \ + container_of(_hw, struct hi6220_clk_divider, hw) + +static unsigned long hi6220_clkdiv_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + unsigned int val; + struct hi6220_clk_divider *dclk = to_hi6220_clk_divider(hw); + + val = readl_relaxed(dclk->reg) >> dclk->shift; + val &= div_mask(dclk->width); + + return divider_recalc_rate(hw, parent_rate, val, dclk->table, + CLK_DIVIDER_ROUND_CLOSEST); +} + +static long hi6220_clkdiv_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct hi6220_clk_divider *dclk = to_hi6220_clk_divider(hw); + + return divider_round_rate(hw, rate, prate, dclk->table, + dclk->width, CLK_DIVIDER_ROUND_CLOSEST); +} + +static int hi6220_clkdiv_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + int value; + unsigned long flags = 0; + u32 data; + struct hi6220_clk_divider *dclk = to_hi6220_clk_divider(hw); + + value = divider_get_val(rate, parent_rate, dclk->table, + dclk->width, CLK_DIVIDER_ROUND_CLOSEST); + + if (dclk->lock) + spin_lock_irqsave(dclk->lock, flags); + + data = readl_relaxed(dclk->reg); + data &= ~(div_mask(dclk->width) << dclk->shift); + data |= value << dclk->shift; + data |= dclk->mask; + + writel_relaxed(data, dclk->reg); + + if (dclk->lock) + spin_unlock_irqrestore(dclk->lock, flags); + + return 0; +} + +static const struct clk_ops hi6220_clkdiv_ops = { + .recalc_rate = hi6220_clkdiv_recalc_rate, + .round_rate = hi6220_clkdiv_round_rate, + .set_rate = hi6220_clkdiv_set_rate, +}; + +struct clk *hi6220_register_clkdiv(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, void __iomem *reg, + u8 shift, u8 width, u32 mask_bit, spinlock_t *lock) +{ + struct hi6220_clk_divider *div; + struct clk *clk; + struct clk_init_data init; + struct clk_div_table *table; + u32 max_div, min_div; + int i; + + /* allocate the divider */ + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) + return ERR_PTR(-ENOMEM); + + /* Init the divider table */ + max_div = div_mask(width) + 1; + min_div = 1; + + table = kcalloc(max_div + 1, sizeof(*table), GFP_KERNEL); + if (!table) { + kfree(div); + return ERR_PTR(-ENOMEM); + } + + for (i = 0; i < max_div; i++) { + table[i].div = min_div + i; + table[i].val = table[i].div - 1; + } + + init.name = name; + init.ops = &hi6220_clkdiv_ops; + init.flags = flags; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + + /* struct hi6220_clk_divider assignments */ + div->reg = reg; + div->shift = shift; + div->width = width; + div->mask = mask_bit ? BIT(mask_bit) : 0; + div->lock = lock; + div->hw.init = &init; + div->table = table; + + /* register the clock */ + clk = clk_register(dev, &div->hw); + if (IS_ERR(clk)) { + kfree(table); + kfree(div); + } + + return clk; +} diff --git a/drivers/clk/keystone/pll.c b/drivers/clk/keystone/pll.c index 0dd8a4b12747..4a375ead70e9 100644 --- a/drivers/clk/keystone/pll.c +++ b/drivers/clk/keystone/pll.c @@ -37,7 +37,8 @@ * Main PLL or any other PLLs in the device such as ARM PLL, DDR PLL * or PA PLL available on keystone2. These PLLs are controlled by * this register. Main PLL is controlled by a PLL controller. - * @pllm: PLL register map address + * @pllm: PLL register map address for multiplier bits + * @pllod: PLL register map address for post divider bits * @pll_ctl0: PLL controller map address * @pllm_lower_mask: multiplier lower mask * @pllm_upper_mask: multiplier upper mask @@ -53,6 +54,7 @@ struct clk_pll_data { u32 phy_pllm; u32 phy_pll_ctl0; void __iomem *pllm; + void __iomem *pllod; void __iomem *pll_ctl0; u32 pllm_lower_mask; u32 pllm_upper_mask; @@ -102,7 +104,11 @@ static unsigned long clk_pllclk_recalc(struct clk_hw *hw, /* read post divider from od bits*/ postdiv = ((val & pll_data->clkod_mask) >> pll_data->clkod_shift) + 1; - else + else if (pll_data->pllod) { + postdiv = readl(pll_data->pllod); + postdiv = ((postdiv & pll_data->clkod_mask) >> + pll_data->clkod_shift) + 1; + } else postdiv = pll_data->postdiv; rate /= (prediv + 1); @@ -172,12 +178,21 @@ static void __init _of_pll_clk_init(struct device_node *node, bool pllctrl) /* assume the PLL has output divider register bits */ pll_data->clkod_mask = CLKOD_MASK; pll_data->clkod_shift = CLKOD_SHIFT; + + /* + * Check if there is an post-divider register. If not + * assume od bits are part of control register. + */ + i = of_property_match_string(node, "reg-names", + "post-divider"); + pll_data->pllod = of_iomap(node, i); } i = of_property_match_string(node, "reg-names", "control"); pll_data->pll_ctl0 = of_iomap(node, i); if (!pll_data->pll_ctl0) { pr_err("%s: ioremap failed\n", __func__); + iounmap(pll_data->pllod); goto out; } @@ -193,6 +208,7 @@ static void __init _of_pll_clk_init(struct device_node *node, bool pllctrl) pll_data->pllm = of_iomap(node, i); if (!pll_data->pllm) { iounmap(pll_data->pll_ctl0); + iounmap(pll_data->pllod); goto out; } } diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile new file mode 100644 index 000000000000..8e4b2a4635b9 --- /dev/null +++ b/drivers/clk/mediatek/Makefile @@ -0,0 +1,4 @@ +obj-y += clk-mtk.o clk-pll.o clk-gate.o +obj-$(CONFIG_RESET_CONTROLLER) += reset.o +obj-y += clk-mt8135.o +obj-y += clk-mt8173.o diff --git a/drivers/clk/mediatek/clk-gate.c b/drivers/clk/mediatek/clk-gate.c new file mode 100644 index 000000000000..57020368a693 --- /dev/null +++ b/drivers/clk/mediatek/clk-gate.c @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include + +#include +#include +#include +#include + +#include "clk-mtk.h" +#include "clk-gate.h" + +static int mtk_cg_bit_is_cleared(struct clk_hw *hw) +{ + struct mtk_clk_gate *cg = to_clk_gate(hw); + u32 val; + + regmap_read(cg->regmap, cg->sta_ofs, &val); + + val &= BIT(cg->bit); + + return val == 0; +} + +static int mtk_cg_bit_is_set(struct clk_hw *hw) +{ + struct mtk_clk_gate *cg = to_clk_gate(hw); + u32 val; + + regmap_read(cg->regmap, cg->sta_ofs, &val); + + val &= BIT(cg->bit); + + return val != 0; +} + +static void mtk_cg_set_bit(struct clk_hw *hw) +{ + struct mtk_clk_gate *cg = to_clk_gate(hw); + + regmap_write(cg->regmap, cg->set_ofs, BIT(cg->bit)); +} + +static void mtk_cg_clr_bit(struct clk_hw *hw) +{ + struct mtk_clk_gate *cg = to_clk_gate(hw); + + regmap_write(cg->regmap, cg->clr_ofs, BIT(cg->bit)); +} + +static int mtk_cg_enable(struct clk_hw *hw) +{ + mtk_cg_clr_bit(hw); + + return 0; +} + +static void mtk_cg_disable(struct clk_hw *hw) +{ + mtk_cg_set_bit(hw); +} + +static int mtk_cg_enable_inv(struct clk_hw *hw) +{ + mtk_cg_set_bit(hw); + + return 0; +} + +static void mtk_cg_disable_inv(struct clk_hw *hw) +{ + mtk_cg_clr_bit(hw); +} + +const struct clk_ops mtk_clk_gate_ops_setclr = { + .is_enabled = mtk_cg_bit_is_cleared, + .enable = mtk_cg_enable, + .disable = mtk_cg_disable, +}; + +const struct clk_ops mtk_clk_gate_ops_setclr_inv = { + .is_enabled = mtk_cg_bit_is_set, + .enable = mtk_cg_enable_inv, + .disable = mtk_cg_disable_inv, +}; + +struct clk *mtk_clk_register_gate( + const char *name, + const char *parent_name, + struct regmap *regmap, + int set_ofs, + int clr_ofs, + int sta_ofs, + u8 bit, + const struct clk_ops *ops) +{ + struct mtk_clk_gate *cg; + struct clk *clk; + struct clk_init_data init = {}; + + cg = kzalloc(sizeof(*cg), GFP_KERNEL); + if (!cg) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.flags = CLK_SET_RATE_PARENT; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + init.ops = ops; + + cg->regmap = regmap; + cg->set_ofs = set_ofs; + cg->clr_ofs = clr_ofs; + cg->sta_ofs = sta_ofs; + cg->bit = bit; + + cg->hw.init = &init; + + clk = clk_register(NULL, &cg->hw); + if (IS_ERR(clk)) + kfree(cg); + + return clk; +} diff --git a/drivers/clk/mediatek/clk-gate.h b/drivers/clk/mediatek/clk-gate.h new file mode 100644 index 000000000000..6b6780b1e9c5 --- /dev/null +++ b/drivers/clk/mediatek/clk-gate.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao + * + * 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. + * + * 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 __DRV_CLK_GATE_H +#define __DRV_CLK_GATE_H + +#include +#include +#include + +struct mtk_clk_gate { + struct clk_hw hw; + struct regmap *regmap; + int set_ofs; + int clr_ofs; + int sta_ofs; + u8 bit; +}; + +static inline struct mtk_clk_gate *to_clk_gate(struct clk_hw *hw) +{ + return container_of(hw, struct mtk_clk_gate, hw); +} + +extern const struct clk_ops mtk_clk_gate_ops_setclr; +extern const struct clk_ops mtk_clk_gate_ops_setclr_inv; + +struct clk *mtk_clk_register_gate( + const char *name, + const char *parent_name, + struct regmap *regmap, + int set_ofs, + int clr_ofs, + int sta_ofs, + u8 bit, + const struct clk_ops *ops); + +#endif /* __DRV_CLK_GATE_H */ diff --git a/drivers/clk/mediatek/clk-mt8135.c b/drivers/clk/mediatek/clk-mt8135.c new file mode 100644 index 000000000000..08b4b849b491 --- /dev/null +++ b/drivers/clk/mediatek/clk-mt8135.c @@ -0,0 +1,644 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include + +#include "clk-mtk.h" +#include "clk-gate.h" + +static DEFINE_SPINLOCK(mt8135_clk_lock); + +static const struct mtk_fixed_factor root_clk_alias[] __initconst = { + FACTOR(CLK_TOP_DSI0_LNTC_DSICLK, "dsi0_lntc_dsiclk", "clk_null", 1, 1), + FACTOR(CLK_TOP_HDMITX_CLKDIG_CTS, "hdmitx_clkdig_cts", "clk_null", 1, 1), + FACTOR(CLK_TOP_CLKPH_MCK, "clkph_mck", "clk_null", 1, 1), + FACTOR(CLK_TOP_CPUM_TCK_IN, "cpum_tck_in", "clk_null", 1, 1), +}; + +static const struct mtk_fixed_factor top_divs[] __initconst = { + FACTOR(CLK_TOP_MAINPLL_806M, "mainpll_806m", "mainpll", 1, 2), + FACTOR(CLK_TOP_MAINPLL_537P3M, "mainpll_537p3m", "mainpll", 1, 3), + FACTOR(CLK_TOP_MAINPLL_322P4M, "mainpll_322p4m", "mainpll", 1, 5), + FACTOR(CLK_TOP_MAINPLL_230P3M, "mainpll_230p3m", "mainpll", 1, 7), + + FACTOR(CLK_TOP_UNIVPLL_624M, "univpll_624m", "univpll", 1, 2), + FACTOR(CLK_TOP_UNIVPLL_416M, "univpll_416m", "univpll", 1, 3), + FACTOR(CLK_TOP_UNIVPLL_249P6M, "univpll_249p6m", "univpll", 1, 5), + FACTOR(CLK_TOP_UNIVPLL_178P3M, "univpll_178p3m", "univpll", 1, 7), + FACTOR(CLK_TOP_UNIVPLL_48M, "univpll_48m", "univpll", 1, 26), + + FACTOR(CLK_TOP_MMPLL_D2, "mmpll_d2", "mmpll", 1, 2), + FACTOR(CLK_TOP_MMPLL_D3, "mmpll_d3", "mmpll", 1, 3), + FACTOR(CLK_TOP_MMPLL_D5, "mmpll_d5", "mmpll", 1, 5), + FACTOR(CLK_TOP_MMPLL_D7, "mmpll_d7", "mmpll", 1, 7), + FACTOR(CLK_TOP_MMPLL_D4, "mmpll_d4", "mmpll_d2", 1, 2), + FACTOR(CLK_TOP_MMPLL_D6, "mmpll_d6", "mmpll_d3", 1, 2), + + FACTOR(CLK_TOP_SYSPLL_D2, "syspll_d2", "mainpll_806m", 1, 1), + FACTOR(CLK_TOP_SYSPLL_D4, "syspll_d4", "mainpll_806m", 1, 2), + FACTOR(CLK_TOP_SYSPLL_D6, "syspll_d6", "mainpll_806m", 1, 3), + FACTOR(CLK_TOP_SYSPLL_D8, "syspll_d8", "mainpll_806m", 1, 4), + FACTOR(CLK_TOP_SYSPLL_D10, "syspll_d10", "mainpll_806m", 1, 5), + FACTOR(CLK_TOP_SYSPLL_D12, "syspll_d12", "mainpll_806m", 1, 6), + FACTOR(CLK_TOP_SYSPLL_D16, "syspll_d16", "mainpll_806m", 1, 8), + FACTOR(CLK_TOP_SYSPLL_D24, "syspll_d24", "mainpll_806m", 1, 12), + + FACTOR(CLK_TOP_SYSPLL_D3, "syspll_d3", "mainpll_537p3m", 1, 1), + + FACTOR(CLK_TOP_SYSPLL_D2P5, "syspll_d2p5", "mainpll_322p4m", 2, 1), + FACTOR(CLK_TOP_SYSPLL_D5, "syspll_d5", "mainpll_322p4m", 1, 1), + + FACTOR(CLK_TOP_SYSPLL_D3P5, "syspll_d3p5", "mainpll_230p3m", 2, 1), + + FACTOR(CLK_TOP_UNIVPLL1_D2, "univpll1_d2", "univpll_624m", 1, 2), + FACTOR(CLK_TOP_UNIVPLL1_D4, "univpll1_d4", "univpll_624m", 1, 4), + FACTOR(CLK_TOP_UNIVPLL1_D6, "univpll1_d6", "univpll_624m", 1, 6), + FACTOR(CLK_TOP_UNIVPLL1_D8, "univpll1_d8", "univpll_624m", 1, 8), + FACTOR(CLK_TOP_UNIVPLL1_D10, "univpll1_d10", "univpll_624m", 1, 10), + + FACTOR(CLK_TOP_UNIVPLL2_D2, "univpll2_d2", "univpll_416m", 1, 2), + FACTOR(CLK_TOP_UNIVPLL2_D4, "univpll2_d4", "univpll_416m", 1, 4), + FACTOR(CLK_TOP_UNIVPLL2_D6, "univpll2_d6", "univpll_416m", 1, 6), + FACTOR(CLK_TOP_UNIVPLL2_D8, "univpll2_d8", "univpll_416m", 1, 8), + + FACTOR(CLK_TOP_UNIVPLL_D3, "univpll_d3", "univpll_416m", 1, 1), + FACTOR(CLK_TOP_UNIVPLL_D5, "univpll_d5", "univpll_249p6m", 1, 1), + FACTOR(CLK_TOP_UNIVPLL_D7, "univpll_d7", "univpll_178p3m", 1, 1), + FACTOR(CLK_TOP_UNIVPLL_D10, "univpll_d10", "univpll_249p6m", 1, 2), + FACTOR(CLK_TOP_UNIVPLL_D26, "univpll_d26", "univpll_48m", 1, 1), + + FACTOR(CLK_TOP_APLL, "apll_ck", "audpll", 1, 1), + FACTOR(CLK_TOP_APLL_D4, "apll_d4", "audpll", 1, 4), + FACTOR(CLK_TOP_APLL_D8, "apll_d8", "audpll", 1, 8), + FACTOR(CLK_TOP_APLL_D16, "apll_d16", "audpll", 1, 16), + FACTOR(CLK_TOP_APLL_D24, "apll_d24", "audpll", 1, 24), + + FACTOR(CLK_TOP_LVDSPLL_D2, "lvdspll_d2", "lvdspll", 1, 2), + FACTOR(CLK_TOP_LVDSPLL_D4, "lvdspll_d4", "lvdspll", 1, 4), + FACTOR(CLK_TOP_LVDSPLL_D8, "lvdspll_d8", "lvdspll", 1, 8), + + FACTOR(CLK_TOP_LVDSTX_CLKDIG_CT, "lvdstx_clkdig_cts", "lvdspll", 1, 1), + FACTOR(CLK_TOP_VPLL_DPIX, "vpll_dpix_ck", "lvdspll", 1, 1), + + FACTOR(CLK_TOP_TVHDMI_H, "tvhdmi_h_ck", "tvdpll", 1, 1), + + FACTOR(CLK_TOP_HDMITX_CLKDIG_D2, "hdmitx_clkdig_d2", "hdmitx_clkdig_cts", 1, 2), + FACTOR(CLK_TOP_HDMITX_CLKDIG_D3, "hdmitx_clkdig_d3", "hdmitx_clkdig_cts", 1, 3), + + FACTOR(CLK_TOP_TVHDMI_D2, "tvhdmi_d2", "tvhdmi_h_ck", 1, 2), + FACTOR(CLK_TOP_TVHDMI_D4, "tvhdmi_d4", "tvhdmi_h_ck", 1, 4), + + FACTOR(CLK_TOP_MEMPLL_MCK_D4, "mempll_mck_d4", "clkph_mck", 1, 4), +}; + +static const char * const axi_parents[] __initconst = { + "clk26m", + "syspll_d3", + "syspll_d4", + "syspll_d6", + "univpll_d5", + "univpll2_d2", + "syspll_d3p5" +}; + +static const char * const smi_parents[] __initconst = { + "clk26m", + "clkph_mck", + "syspll_d2p5", + "syspll_d3", + "syspll_d8", + "univpll_d5", + "univpll1_d2", + "univpll1_d6", + "mmpll_d3", + "mmpll_d4", + "mmpll_d5", + "mmpll_d6", + "mmpll_d7", + "vdecpll", + "lvdspll" +}; + +static const char * const mfg_parents[] __initconst = { + "clk26m", + "univpll1_d4", + "syspll_d2", + "syspll_d2p5", + "syspll_d3", + "univpll_d5", + "univpll1_d2", + "mmpll_d2", + "mmpll_d3", + "mmpll_d4", + "mmpll_d5", + "mmpll_d6", + "mmpll_d7" +}; + +static const char * const irda_parents[] __initconst = { + "clk26m", + "univpll2_d8", + "univpll1_d6" +}; + +static const char * const cam_parents[] __initconst = { + "clk26m", + "syspll_d3", + "syspll_d3p5", + "syspll_d4", + "univpll_d5", + "univpll2_d2", + "univpll_d7", + "univpll1_d4" +}; + +static const char * const aud_intbus_parents[] __initconst = { + "clk26m", + "syspll_d6", + "univpll_d10" +}; + +static const char * const jpg_parents[] __initconst = { + "clk26m", + "syspll_d5", + "syspll_d4", + "syspll_d3", + "univpll_d7", + "univpll2_d2", + "univpll_d5" +}; + +static const char * const disp_parents[] __initconst = { + "clk26m", + "syspll_d3p5", + "syspll_d3", + "univpll2_d2", + "univpll_d5", + "univpll1_d2", + "lvdspll", + "vdecpll" +}; + +static const char * const msdc30_parents[] __initconst = { + "clk26m", + "syspll_d6", + "syspll_d5", + "univpll1_d4", + "univpll2_d4", + "msdcpll" +}; + +static const char * const usb20_parents[] __initconst = { + "clk26m", + "univpll2_d6", + "univpll1_d10" +}; + +static const char * const venc_parents[] __initconst = { + "clk26m", + "syspll_d3", + "syspll_d8", + "univpll_d5", + "univpll1_d6", + "mmpll_d4", + "mmpll_d5", + "mmpll_d6" +}; + +static const char * const spi_parents[] __initconst = { + "clk26m", + "syspll_d6", + "syspll_d8", + "syspll_d10", + "univpll1_d6", + "univpll1_d8" +}; + +static const char * const uart_parents[] __initconst = { + "clk26m", + "univpll2_d8" +}; + +static const char * const mem_parents[] __initconst = { + "clk26m", + "clkph_mck" +}; + +static const char * const camtg_parents[] __initconst = { + "clk26m", + "univpll_d26", + "univpll1_d6", + "syspll_d16", + "syspll_d8" +}; + +static const char * const audio_parents[] __initconst = { + "clk26m", + "syspll_d24" +}; + +static const char * const fix_parents[] __initconst = { + "rtc32k", + "clk26m", + "univpll_d5", + "univpll_d7", + "univpll1_d2", + "univpll1_d4", + "univpll1_d6", + "univpll1_d8" +}; + +static const char * const vdec_parents[] __initconst = { + "clk26m", + "vdecpll", + "clkph_mck", + "syspll_d2p5", + "syspll_d3", + "syspll_d3p5", + "syspll_d4", + "syspll_d5", + "syspll_d6", + "syspll_d8", + "univpll1_d2", + "univpll2_d2", + "univpll_d7", + "univpll_d10", + "univpll2_d4", + "lvdspll" +}; + +static const char * const ddrphycfg_parents[] __initconst = { + "clk26m", + "axi_sel", + "syspll_d12" +}; + +static const char * const dpilvds_parents[] __initconst = { + "clk26m", + "lvdspll", + "lvdspll_d2", + "lvdspll_d4", + "lvdspll_d8" +}; + +static const char * const pmicspi_parents[] __initconst = { + "clk26m", + "univpll2_d6", + "syspll_d8", + "syspll_d10", + "univpll1_d10", + "mempll_mck_d4", + "univpll_d26", + "syspll_d24" +}; + +static const char * const smi_mfg_as_parents[] __initconst = { + "clk26m", + "smi_sel", + "mfg_sel", + "mem_sel" +}; + +static const char * const gcpu_parents[] __initconst = { + "clk26m", + "syspll_d4", + "univpll_d7", + "syspll_d5", + "syspll_d6" +}; + +static const char * const dpi1_parents[] __initconst = { + "clk26m", + "tvhdmi_h_ck", + "tvhdmi_d2", + "tvhdmi_d4" +}; + +static const char * const cci_parents[] __initconst = { + "clk26m", + "mainpll_537p3m", + "univpll_d3", + "syspll_d2p5", + "syspll_d3", + "syspll_d5" +}; + +static const char * const apll_parents[] __initconst = { + "clk26m", + "apll_ck", + "apll_d4", + "apll_d8", + "apll_d16", + "apll_d24" +}; + +static const char * const hdmipll_parents[] __initconst = { + "clk26m", + "hdmitx_clkdig_cts", + "hdmitx_clkdig_d2", + "hdmitx_clkdig_d3" +}; + +static const struct mtk_composite top_muxes[] __initconst = { + /* CLK_CFG_0 */ + MUX_GATE(CLK_TOP_AXI_SEL, "axi_sel", axi_parents, + 0x0140, 0, 3, INVALID_MUX_GATE_BIT), + MUX_GATE(CLK_TOP_SMI_SEL, "smi_sel", smi_parents, 0x0140, 8, 4, 15), + MUX_GATE(CLK_TOP_MFG_SEL, "mfg_sel", mfg_parents, 0x0140, 16, 4, 23), + MUX_GATE(CLK_TOP_IRDA_SEL, "irda_sel", irda_parents, 0x0140, 24, 2, 31), + /* CLK_CFG_1 */ + MUX_GATE(CLK_TOP_CAM_SEL, "cam_sel", cam_parents, 0x0144, 0, 3, 7), + MUX_GATE(CLK_TOP_AUD_INTBUS_SEL, "aud_intbus_sel", aud_intbus_parents, + 0x0144, 8, 2, 15), + MUX_GATE(CLK_TOP_JPG_SEL, "jpg_sel", jpg_parents, 0x0144, 16, 3, 23), + MUX_GATE(CLK_TOP_DISP_SEL, "disp_sel", disp_parents, 0x0144, 24, 3, 31), + /* CLK_CFG_2 */ + MUX_GATE(CLK_TOP_MSDC30_1_SEL, "msdc30_1_sel", msdc30_parents, 0x0148, 0, 3, 7), + MUX_GATE(CLK_TOP_MSDC30_2_SEL, "msdc30_2_sel", msdc30_parents, 0x0148, 8, 3, 15), + MUX_GATE(CLK_TOP_MSDC30_3_SEL, "msdc30_3_sel", msdc30_parents, 0x0148, 16, 3, 23), + MUX_GATE(CLK_TOP_MSDC30_4_SEL, "msdc30_4_sel", msdc30_parents, 0x0148, 24, 3, 31), + /* CLK_CFG_3 */ + MUX_GATE(CLK_TOP_USB20_SEL, "usb20_sel", usb20_parents, 0x014c, 0, 2, 7), + /* CLK_CFG_4 */ + MUX_GATE(CLK_TOP_VENC_SEL, "venc_sel", venc_parents, 0x0150, 8, 3, 15), + MUX_GATE(CLK_TOP_SPI_SEL, "spi_sel", spi_parents, 0x0150, 16, 3, 23), + MUX_GATE(CLK_TOP_UART_SEL, "uart_sel", uart_parents, 0x0150, 24, 2, 31), + /* CLK_CFG_6 */ + MUX_GATE(CLK_TOP_MEM_SEL, "mem_sel", mem_parents, 0x0158, 0, 2, 7), + MUX_GATE(CLK_TOP_CAMTG_SEL, "camtg_sel", camtg_parents, 0x0158, 8, 3, 15), + MUX_GATE(CLK_TOP_AUDIO_SEL, "audio_sel", audio_parents, 0x0158, 24, 2, 31), + /* CLK_CFG_7 */ + MUX_GATE(CLK_TOP_FIX_SEL, "fix_sel", fix_parents, 0x015c, 0, 3, 7), + MUX_GATE(CLK_TOP_VDEC_SEL, "vdec_sel", vdec_parents, 0x015c, 8, 4, 15), + MUX_GATE(CLK_TOP_DDRPHYCFG_SEL, "ddrphycfg_sel", ddrphycfg_parents, + 0x015c, 16, 2, 23), + MUX_GATE(CLK_TOP_DPILVDS_SEL, "dpilvds_sel", dpilvds_parents, 0x015c, 24, 3, 31), + /* CLK_CFG_8 */ + MUX_GATE(CLK_TOP_PMICSPI_SEL, "pmicspi_sel", pmicspi_parents, 0x0164, 0, 3, 7), + MUX_GATE(CLK_TOP_MSDC30_0_SEL, "msdc30_0_sel", msdc30_parents, 0x0164, 8, 3, 15), + MUX_GATE(CLK_TOP_SMI_MFG_AS_SEL, "smi_mfg_as_sel", smi_mfg_as_parents, + 0x0164, 16, 2, 23), + MUX_GATE(CLK_TOP_GCPU_SEL, "gcpu_sel", gcpu_parents, 0x0164, 24, 3, 31), + /* CLK_CFG_9 */ + MUX_GATE(CLK_TOP_DPI1_SEL, "dpi1_sel", dpi1_parents, 0x0168, 0, 2, 7), + MUX_GATE(CLK_TOP_CCI_SEL, "cci_sel", cci_parents, 0x0168, 8, 3, 15), + MUX_GATE(CLK_TOP_APLL_SEL, "apll_sel", apll_parents, 0x0168, 16, 3, 23), + MUX_GATE(CLK_TOP_HDMIPLL_SEL, "hdmipll_sel", hdmipll_parents, 0x0168, 24, 2, 31), +}; + +static const struct mtk_gate_regs infra_cg_regs = { + .set_ofs = 0x0040, + .clr_ofs = 0x0044, + .sta_ofs = 0x0048, +}; + +#define GATE_ICG(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &infra_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +static const struct mtk_gate infra_clks[] __initconst = { + GATE_ICG(CLK_INFRA_PMIC_WRAP, "pmic_wrap_ck", "axi_sel", 23), + GATE_ICG(CLK_INFRA_PMICSPI, "pmicspi_ck", "pmicspi_sel", 22), + GATE_ICG(CLK_INFRA_CCIF1_AP_CTRL, "ccif1_ap_ctrl", "axi_sel", 21), + GATE_ICG(CLK_INFRA_CCIF0_AP_CTRL, "ccif0_ap_ctrl", "axi_sel", 20), + GATE_ICG(CLK_INFRA_KP, "kp_ck", "axi_sel", 16), + GATE_ICG(CLK_INFRA_CPUM, "cpum_ck", "cpum_tck_in", 15), + GATE_ICG(CLK_INFRA_M4U, "m4u_ck", "mem_sel", 8), + GATE_ICG(CLK_INFRA_MFGAXI, "mfgaxi_ck", "axi_sel", 7), + GATE_ICG(CLK_INFRA_DEVAPC, "devapc_ck", "axi_sel", 6), + GATE_ICG(CLK_INFRA_AUDIO, "audio_ck", "aud_intbus_sel", 5), + GATE_ICG(CLK_INFRA_MFG_BUS, "mfg_bus_ck", "axi_sel", 2), + GATE_ICG(CLK_INFRA_SMI, "smi_ck", "smi_sel", 1), + GATE_ICG(CLK_INFRA_DBGCLK, "dbgclk_ck", "axi_sel", 0), +}; + +static const struct mtk_gate_regs peri0_cg_regs = { + .set_ofs = 0x0008, + .clr_ofs = 0x0010, + .sta_ofs = 0x0018, +}; + +static const struct mtk_gate_regs peri1_cg_regs = { + .set_ofs = 0x000c, + .clr_ofs = 0x0014, + .sta_ofs = 0x001c, +}; + +#define GATE_PERI0(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &peri0_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +#define GATE_PERI1(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &peri1_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +static const struct mtk_gate peri_gates[] __initconst = { + /* PERI0 */ + GATE_PERI0(CLK_PERI_I2C5, "i2c5_ck", "axi_sel", 31), + GATE_PERI0(CLK_PERI_I2C4, "i2c4_ck", "axi_sel", 30), + GATE_PERI0(CLK_PERI_I2C3, "i2c3_ck", "axi_sel", 29), + GATE_PERI0(CLK_PERI_I2C2, "i2c2_ck", "axi_sel", 28), + GATE_PERI0(CLK_PERI_I2C1, "i2c1_ck", "axi_sel", 27), + GATE_PERI0(CLK_PERI_I2C0, "i2c0_ck", "axi_sel", 26), + GATE_PERI0(CLK_PERI_UART3, "uart3_ck", "axi_sel", 25), + GATE_PERI0(CLK_PERI_UART2, "uart2_ck", "axi_sel", 24), + GATE_PERI0(CLK_PERI_UART1, "uart1_ck", "axi_sel", 23), + GATE_PERI0(CLK_PERI_UART0, "uart0_ck", "axi_sel", 22), + GATE_PERI0(CLK_PERI_IRDA, "irda_ck", "irda_sel", 21), + GATE_PERI0(CLK_PERI_NLI, "nli_ck", "axi_sel", 20), + GATE_PERI0(CLK_PERI_MD_HIF, "md_hif_ck", "axi_sel", 19), + GATE_PERI0(CLK_PERI_AP_HIF, "ap_hif_ck", "axi_sel", 18), + GATE_PERI0(CLK_PERI_MSDC30_3, "msdc30_3_ck", "msdc30_4_sel", 17), + GATE_PERI0(CLK_PERI_MSDC30_2, "msdc30_2_ck", "msdc30_3_sel", 16), + GATE_PERI0(CLK_PERI_MSDC30_1, "msdc30_1_ck", "msdc30_2_sel", 15), + GATE_PERI0(CLK_PERI_MSDC20_2, "msdc20_2_ck", "msdc30_1_sel", 14), + GATE_PERI0(CLK_PERI_MSDC20_1, "msdc20_1_ck", "msdc30_0_sel", 13), + GATE_PERI0(CLK_PERI_AP_DMA, "ap_dma_ck", "axi_sel", 12), + GATE_PERI0(CLK_PERI_USB1, "usb1_ck", "usb20_sel", 11), + GATE_PERI0(CLK_PERI_USB0, "usb0_ck", "usb20_sel", 10), + GATE_PERI0(CLK_PERI_PWM, "pwm_ck", "axi_sel", 9), + GATE_PERI0(CLK_PERI_PWM7, "pwm7_ck", "axi_sel", 8), + GATE_PERI0(CLK_PERI_PWM6, "pwm6_ck", "axi_sel", 7), + GATE_PERI0(CLK_PERI_PWM5, "pwm5_ck", "axi_sel", 6), + GATE_PERI0(CLK_PERI_PWM4, "pwm4_ck", "axi_sel", 5), + GATE_PERI0(CLK_PERI_PWM3, "pwm3_ck", "axi_sel", 4), + GATE_PERI0(CLK_PERI_PWM2, "pwm2_ck", "axi_sel", 3), + GATE_PERI0(CLK_PERI_PWM1, "pwm1_ck", "axi_sel", 2), + GATE_PERI0(CLK_PERI_THERM, "therm_ck", "axi_sel", 1), + GATE_PERI0(CLK_PERI_NFI, "nfi_ck", "axi_sel", 0), + /* PERI1 */ + GATE_PERI1(CLK_PERI_USBSLV, "usbslv_ck", "axi_sel", 8), + GATE_PERI1(CLK_PERI_USB1_MCU, "usb1_mcu_ck", "axi_sel", 7), + GATE_PERI1(CLK_PERI_USB0_MCU, "usb0_mcu_ck", "axi_sel", 6), + GATE_PERI1(CLK_PERI_GCPU, "gcpu_ck", "gcpu_sel", 5), + GATE_PERI1(CLK_PERI_FHCTL, "fhctl_ck", "clk26m", 4), + GATE_PERI1(CLK_PERI_SPI1, "spi1_ck", "spi_sel", 3), + GATE_PERI1(CLK_PERI_AUXADC, "auxadc_ck", "clk26m", 2), + GATE_PERI1(CLK_PERI_PERI_PWRAP, "peri_pwrap_ck", "axi_sel", 1), + GATE_PERI1(CLK_PERI_I2C6, "i2c6_ck", "axi_sel", 0), +}; + +static const char * const uart_ck_sel_parents[] __initconst = { + "clk26m", + "uart_sel", +}; + +static const struct mtk_composite peri_clks[] __initconst = { + MUX(CLK_PERI_UART0_SEL, "uart0_ck_sel", uart_ck_sel_parents, 0x40c, 0, 1), + MUX(CLK_PERI_UART1_SEL, "uart1_ck_sel", uart_ck_sel_parents, 0x40c, 1, 1), + MUX(CLK_PERI_UART2_SEL, "uart2_ck_sel", uart_ck_sel_parents, 0x40c, 2, 1), + MUX(CLK_PERI_UART3_SEL, "uart3_ck_sel", uart_ck_sel_parents, 0x40c, 3, 1), +}; + +static void __init mtk_topckgen_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + void __iomem *base; + int r; + + base = of_iomap(node, 0); + if (!base) { + pr_err("%s(): ioremap failed\n", __func__); + return; + } + + clk_data = mtk_alloc_clk_data(CLK_TOP_NR_CLK); + + mtk_clk_register_factors(root_clk_alias, ARRAY_SIZE(root_clk_alias), clk_data); + mtk_clk_register_factors(top_divs, ARRAY_SIZE(top_divs), clk_data); + mtk_clk_register_composites(top_muxes, ARRAY_SIZE(top_muxes), base, + &mt8135_clk_lock, clk_data); + + clk_prepare_enable(clk_data->clks[CLK_TOP_CCI_SEL]); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); +} +CLK_OF_DECLARE(mtk_topckgen, "mediatek,mt8135-topckgen", mtk_topckgen_init); + +static void __init mtk_infrasys_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + int r; + + clk_data = mtk_alloc_clk_data(CLK_INFRA_NR_CLK); + + mtk_clk_register_gates(node, infra_clks, ARRAY_SIZE(infra_clks), + clk_data); + + clk_prepare_enable(clk_data->clks[CLK_INFRA_M4U]); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); + + mtk_register_reset_controller(node, 2, 0x30); +} +CLK_OF_DECLARE(mtk_infrasys, "mediatek,mt8135-infracfg", mtk_infrasys_init); + +static void __init mtk_pericfg_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + int r; + void __iomem *base; + + base = of_iomap(node, 0); + if (!base) { + pr_err("%s(): ioremap failed\n", __func__); + return; + } + + clk_data = mtk_alloc_clk_data(CLK_PERI_NR_CLK); + + mtk_clk_register_gates(node, peri_gates, ARRAY_SIZE(peri_gates), + clk_data); + mtk_clk_register_composites(peri_clks, ARRAY_SIZE(peri_clks), base, + &mt8135_clk_lock, clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); + + mtk_register_reset_controller(node, 2, 0); +} +CLK_OF_DECLARE(mtk_pericfg, "mediatek,mt8135-pericfg", mtk_pericfg_init); + +#define MT8135_PLL_FMAX (2000 * MHZ) +#define CON0_MT8135_RST_BAR BIT(27) + +#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, _pcw_shift) { \ + .id = _id, \ + .name = _name, \ + .reg = _reg, \ + .pwr_reg = _pwr_reg, \ + .en_mask = _en_mask, \ + .flags = _flags, \ + .rst_bar_mask = CON0_MT8135_RST_BAR, \ + .fmax = MT8135_PLL_FMAX, \ + .pcwbits = _pcwbits, \ + .pd_reg = _pd_reg, \ + .pd_shift = _pd_shift, \ + .tuner_reg = _tuner_reg, \ + .pcw_reg = _pcw_reg, \ + .pcw_shift = _pcw_shift, \ + } + +static const struct mtk_pll_data plls[] = { + PLL(CLK_APMIXED_ARMPLL1, "armpll1", 0x200, 0x218, 0x80000001, 0, 21, 0x204, 24, 0x0, 0x204, 0), + PLL(CLK_APMIXED_ARMPLL2, "armpll2", 0x2cc, 0x2e4, 0x80000001, 0, 21, 0x2d0, 24, 0x0, 0x2d0, 0), + PLL(CLK_APMIXED_MAINPLL, "mainpll", 0x21c, 0x234, 0xf0000001, HAVE_RST_BAR, 21, 0x21c, 6, 0x0, 0x220, 0), + PLL(CLK_APMIXED_UNIVPLL, "univpll", 0x238, 0x250, 0xf3000001, HAVE_RST_BAR, 7, 0x238, 6, 0x0, 0x238, 9), + PLL(CLK_APMIXED_MMPLL, "mmpll", 0x254, 0x26c, 0xf0000001, HAVE_RST_BAR, 21, 0x254, 6, 0x0, 0x258, 0), + PLL(CLK_APMIXED_MSDCPLL, "msdcpll", 0x278, 0x290, 0x80000001, 0, 21, 0x278, 6, 0x0, 0x27c, 0), + PLL(CLK_APMIXED_TVDPLL, "tvdpll", 0x294, 0x2ac, 0x80000001, 0, 31, 0x294, 6, 0x0, 0x298, 0), + PLL(CLK_APMIXED_LVDSPLL, "lvdspll", 0x2b0, 0x2c8, 0x80000001, 0, 21, 0x2b0, 6, 0x0, 0x2b4, 0), + PLL(CLK_APMIXED_AUDPLL, "audpll", 0x2e8, 0x300, 0x80000001, 0, 31, 0x2e8, 6, 0x2f8, 0x2ec, 0), + PLL(CLK_APMIXED_VDECPLL, "vdecpll", 0x304, 0x31c, 0x80000001, 0, 21, 0x2b0, 6, 0x0, 0x308, 0), +}; + +static void __init mtk_apmixedsys_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + + clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR_CLK); + if (!clk_data) + return; + + mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data); +} +CLK_OF_DECLARE(mtk_apmixedsys, "mediatek,mt8135-apmixedsys", + mtk_apmixedsys_init); diff --git a/drivers/clk/mediatek/clk-mt8173.c b/drivers/clk/mediatek/clk-mt8173.c new file mode 100644 index 000000000000..4b9e04cdf7e8 --- /dev/null +++ b/drivers/clk/mediatek/clk-mt8173.c @@ -0,0 +1,830 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include + +#include "clk-mtk.h" +#include "clk-gate.h" + +#include + +static DEFINE_SPINLOCK(mt8173_clk_lock); + +static const struct mtk_fixed_factor root_clk_alias[] __initconst = { + FACTOR(CLK_TOP_CLKPH_MCK_O, "clkph_mck_o", "clk_null", 1, 1), + FACTOR(CLK_TOP_DPI, "dpi_ck", "clk_null", 1, 1), + FACTOR(CLK_TOP_USB_SYSPLL_125M, "usb_syspll_125m", "clk_null", 1, 1), + FACTOR(CLK_TOP_HDMITX_DIG_CTS, "hdmitx_dig_cts", "clk_null", 1, 1), +}; + +static const struct mtk_fixed_factor top_divs[] __initconst = { + FACTOR(CLK_TOP_ARMCA7PLL_754M, "armca7pll_754m", "armca7pll", 1, 2), + FACTOR(CLK_TOP_ARMCA7PLL_502M, "armca7pll_502m", "armca7pll", 1, 3), + + FACTOR(CLK_TOP_MAIN_H546M, "main_h546m", "mainpll", 1, 2), + FACTOR(CLK_TOP_MAIN_H364M, "main_h364m", "mainpll", 1, 3), + FACTOR(CLK_TOP_MAIN_H218P4M, "main_h218p4m", "mainpll", 1, 5), + FACTOR(CLK_TOP_MAIN_H156M, "main_h156m", "mainpll", 1, 7), + + FACTOR(CLK_TOP_TVDPLL_445P5M, "tvdpll_445p5m", "tvdpll", 1, 4), + FACTOR(CLK_TOP_TVDPLL_594M, "tvdpll_594m", "tvdpll", 1, 3), + + FACTOR(CLK_TOP_UNIV_624M, "univ_624m", "univpll", 1, 2), + FACTOR(CLK_TOP_UNIV_416M, "univ_416m", "univpll", 1, 3), + FACTOR(CLK_TOP_UNIV_249P6M, "univ_249p6m", "univpll", 1, 5), + FACTOR(CLK_TOP_UNIV_178P3M, "univ_178p3m", "univpll", 1, 7), + FACTOR(CLK_TOP_UNIV_48M, "univ_48m", "univpll", 1, 26), + + FACTOR(CLK_TOP_CLKRTC_EXT, "clkrtc_ext", "clk32k", 1, 1), + FACTOR(CLK_TOP_CLKRTC_INT, "clkrtc_int", "clk26m", 1, 793), + FACTOR(CLK_TOP_FPC, "fpc_ck", "clk26m", 1, 1), + + FACTOR(CLK_TOP_HDMITXPLL_D2, "hdmitxpll_d2", "hdmitx_dig_cts", 1, 2), + FACTOR(CLK_TOP_HDMITXPLL_D3, "hdmitxpll_d3", "hdmitx_dig_cts", 1, 3), + + FACTOR(CLK_TOP_ARMCA7PLL_D2, "armca7pll_d2", "armca7pll_754m", 1, 1), + FACTOR(CLK_TOP_ARMCA7PLL_D3, "armca7pll_d3", "armca7pll_502m", 1, 1), + + FACTOR(CLK_TOP_APLL1, "apll1_ck", "apll1", 1, 1), + FACTOR(CLK_TOP_APLL2, "apll2_ck", "apll2", 1, 1), + + FACTOR(CLK_TOP_DMPLL, "dmpll_ck", "clkph_mck_o", 1, 1), + FACTOR(CLK_TOP_DMPLL_D2, "dmpll_d2", "clkph_mck_o", 1, 2), + FACTOR(CLK_TOP_DMPLL_D4, "dmpll_d4", "clkph_mck_o", 1, 4), + FACTOR(CLK_TOP_DMPLL_D8, "dmpll_d8", "clkph_mck_o", 1, 8), + FACTOR(CLK_TOP_DMPLL_D16, "dmpll_d16", "clkph_mck_o", 1, 16), + + FACTOR(CLK_TOP_LVDSPLL_D2, "lvdspll_d2", "lvdspll", 1, 2), + FACTOR(CLK_TOP_LVDSPLL_D4, "lvdspll_d4", "lvdspll", 1, 4), + FACTOR(CLK_TOP_LVDSPLL_D8, "lvdspll_d8", "lvdspll", 1, 8), + + FACTOR(CLK_TOP_MMPLL, "mmpll_ck", "mmpll", 1, 1), + FACTOR(CLK_TOP_MMPLL_D2, "mmpll_d2", "mmpll", 1, 2), + + FACTOR(CLK_TOP_MSDCPLL, "msdcpll_ck", "msdcpll", 1, 1), + FACTOR(CLK_TOP_MSDCPLL_D2, "msdcpll_d2", "msdcpll", 1, 2), + FACTOR(CLK_TOP_MSDCPLL_D4, "msdcpll_d4", "msdcpll", 1, 4), + FACTOR(CLK_TOP_MSDCPLL2, "msdcpll2_ck", "msdcpll2", 1, 1), + FACTOR(CLK_TOP_MSDCPLL2_D2, "msdcpll2_d2", "msdcpll2", 1, 2), + FACTOR(CLK_TOP_MSDCPLL2_D4, "msdcpll2_d4", "msdcpll2", 1, 4), + + FACTOR(CLK_TOP_SYSPLL_D2, "syspll_d2", "main_h546m", 1, 1), + FACTOR(CLK_TOP_SYSPLL1_D2, "syspll1_d2", "main_h546m", 1, 2), + FACTOR(CLK_TOP_SYSPLL1_D4, "syspll1_d4", "main_h546m", 1, 4), + FACTOR(CLK_TOP_SYSPLL1_D8, "syspll1_d8", "main_h546m", 1, 8), + FACTOR(CLK_TOP_SYSPLL1_D16, "syspll1_d16", "main_h546m", 1, 16), + FACTOR(CLK_TOP_SYSPLL_D3, "syspll_d3", "main_h364m", 1, 1), + FACTOR(CLK_TOP_SYSPLL2_D2, "syspll2_d2", "main_h364m", 1, 2), + FACTOR(CLK_TOP_SYSPLL2_D4, "syspll2_d4", "main_h364m", 1, 4), + FACTOR(CLK_TOP_SYSPLL_D5, "syspll_d5", "main_h218p4m", 1, 1), + FACTOR(CLK_TOP_SYSPLL3_D2, "syspll3_d2", "main_h218p4m", 1, 2), + FACTOR(CLK_TOP_SYSPLL3_D4, "syspll3_d4", "main_h218p4m", 1, 4), + FACTOR(CLK_TOP_SYSPLL_D7, "syspll_d7", "main_h156m", 1, 1), + FACTOR(CLK_TOP_SYSPLL4_D2, "syspll4_d2", "main_h156m", 1, 2), + FACTOR(CLK_TOP_SYSPLL4_D4, "syspll4_d4", "main_h156m", 1, 4), + + FACTOR(CLK_TOP_TVDPLL, "tvdpll_ck", "tvdpll_594m", 1, 1), + FACTOR(CLK_TOP_TVDPLL_D2, "tvdpll_d2", "tvdpll_594m", 1, 2), + FACTOR(CLK_TOP_TVDPLL_D4, "tvdpll_d4", "tvdpll_594m", 1, 4), + FACTOR(CLK_TOP_TVDPLL_D8, "tvdpll_d8", "tvdpll_594m", 1, 8), + FACTOR(CLK_TOP_TVDPLL_D16, "tvdpll_d16", "tvdpll_594m", 1, 16), + + FACTOR(CLK_TOP_UNIVPLL_D2, "univpll_d2", "univ_624m", 1, 1), + FACTOR(CLK_TOP_UNIVPLL1_D2, "univpll1_d2", "univ_624m", 1, 2), + FACTOR(CLK_TOP_UNIVPLL1_D4, "univpll1_d4", "univ_624m", 1, 4), + FACTOR(CLK_TOP_UNIVPLL1_D8, "univpll1_d8", "univ_624m", 1, 8), + FACTOR(CLK_TOP_UNIVPLL_D3, "univpll_d3", "univ_416m", 1, 1), + FACTOR(CLK_TOP_UNIVPLL2_D2, "univpll2_d2", "univ_416m", 1, 2), + FACTOR(CLK_TOP_UNIVPLL2_D4, "univpll2_d4", "univ_416m", 1, 4), + FACTOR(CLK_TOP_UNIVPLL2_D8, "univpll2_d8", "univ_416m", 1, 8), + FACTOR(CLK_TOP_UNIVPLL_D5, "univpll_d5", "univ_249p6m", 1, 1), + FACTOR(CLK_TOP_UNIVPLL3_D2, "univpll3_d2", "univ_249p6m", 1, 2), + FACTOR(CLK_TOP_UNIVPLL3_D4, "univpll3_d4", "univ_249p6m", 1, 4), + FACTOR(CLK_TOP_UNIVPLL3_D8, "univpll3_d8", "univ_249p6m", 1, 8), + FACTOR(CLK_TOP_UNIVPLL_D7, "univpll_d7", "univ_178p3m", 1, 1), + FACTOR(CLK_TOP_UNIVPLL_D26, "univpll_d26", "univ_48m", 1, 1), + FACTOR(CLK_TOP_UNIVPLL_D52, "univpll_d52", "univ_48m", 1, 2), + + FACTOR(CLK_TOP_VCODECPLL, "vcodecpll_ck", "vcodecpll", 1, 3), + FACTOR(CLK_TOP_VCODECPLL_370P5, "vcodecpll_370p5", "vcodecpll", 1, 4), + + FACTOR(CLK_TOP_VENCPLL, "vencpll_ck", "vencpll", 1, 1), + FACTOR(CLK_TOP_VENCPLL_D2, "vencpll_d2", "vencpll", 1, 2), + FACTOR(CLK_TOP_VENCPLL_D4, "vencpll_d4", "vencpll", 1, 4), +}; + +static const char * const axi_parents[] __initconst = { + "clk26m", + "syspll1_d2", + "syspll_d5", + "syspll1_d4", + "univpll_d5", + "univpll2_d2", + "dmpll_d2", + "dmpll_d4" +}; + +static const char * const mem_parents[] __initconst = { + "clk26m", + "dmpll_ck" +}; + +static const char * const ddrphycfg_parents[] __initconst = { + "clk26m", + "syspll1_d8" +}; + +static const char * const mm_parents[] __initconst = { + "clk26m", + "vencpll_d2", + "main_h364m", + "syspll1_d2", + "syspll_d5", + "syspll1_d4", + "univpll1_d2", + "univpll2_d2", + "dmpll_d2" +}; + +static const char * const pwm_parents[] __initconst = { + "clk26m", + "univpll2_d4", + "univpll3_d2", + "univpll1_d4" +}; + +static const char * const vdec_parents[] __initconst = { + "clk26m", + "vcodecpll_ck", + "tvdpll_445p5m", + "univpll_d3", + "vencpll_d2", + "syspll_d3", + "univpll1_d2", + "mmpll_d2", + "dmpll_d2", + "dmpll_d4" +}; + +static const char * const venc_parents[] __initconst = { + "clk26m", + "vcodecpll_ck", + "tvdpll_445p5m", + "univpll_d3", + "vencpll_d2", + "syspll_d3", + "univpll1_d2", + "univpll2_d2", + "dmpll_d2", + "dmpll_d4" +}; + +static const char * const mfg_parents[] __initconst = { + "clk26m", + "mmpll_ck", + "dmpll_ck", + "clk26m", + "clk26m", + "clk26m", + "clk26m", + "clk26m", + "clk26m", + "syspll_d3", + "syspll1_d2", + "syspll_d5", + "univpll_d3", + "univpll1_d2", + "univpll_d5", + "univpll2_d2" +}; + +static const char * const camtg_parents[] __initconst = { + "clk26m", + "univpll_d26", + "univpll2_d2", + "syspll3_d2", + "syspll3_d4", + "univpll1_d4" +}; + +static const char * const uart_parents[] __initconst = { + "clk26m", + "univpll2_d8" +}; + +static const char * const spi_parents[] __initconst = { + "clk26m", + "syspll3_d2", + "syspll1_d4", + "syspll4_d2", + "univpll3_d2", + "univpll2_d4", + "univpll1_d8" +}; + +static const char * const usb20_parents[] __initconst = { + "clk26m", + "univpll1_d8", + "univpll3_d4" +}; + +static const char * const usb30_parents[] __initconst = { + "clk26m", + "univpll3_d2", + "usb_syspll_125m", + "univpll2_d4" +}; + +static const char * const msdc50_0_h_parents[] __initconst = { + "clk26m", + "syspll1_d2", + "syspll2_d2", + "syspll4_d2", + "univpll_d5", + "univpll1_d4" +}; + +static const char * const msdc50_0_parents[] __initconst = { + "clk26m", + "msdcpll_ck", + "msdcpll_d2", + "univpll1_d4", + "syspll2_d2", + "syspll_d7", + "msdcpll_d4", + "vencpll_d4", + "tvdpll_ck", + "univpll_d2", + "univpll1_d2", + "mmpll_ck", + "msdcpll2_ck", + "msdcpll2_d2", + "msdcpll2_d4" +}; + +static const char * const msdc30_1_parents[] __initconst = { + "clk26m", + "univpll2_d2", + "msdcpll_d4", + "univpll1_d4", + "syspll2_d2", + "syspll_d7", + "univpll_d7", + "vencpll_d4" +}; + +static const char * const msdc30_2_parents[] __initconst = { + "clk26m", + "univpll2_d2", + "msdcpll_d4", + "univpll1_d4", + "syspll2_d2", + "syspll_d7", + "univpll_d7", + "vencpll_d2" +}; + +static const char * const msdc30_3_parents[] __initconst = { + "clk26m", + "msdcpll2_ck", + "msdcpll2_d2", + "univpll2_d2", + "msdcpll2_d4", + "msdcpll_d4", + "univpll1_d4", + "syspll2_d2", + "syspll_d7", + "univpll_d7", + "vencpll_d4", + "msdcpll_ck", + "msdcpll_d2", + "msdcpll_d4" +}; + +static const char * const audio_parents[] __initconst = { + "clk26m", + "syspll3_d4", + "syspll4_d4", + "syspll1_d16" +}; + +static const char * const aud_intbus_parents[] __initconst = { + "clk26m", + "syspll1_d4", + "syspll4_d2", + "univpll3_d2", + "univpll2_d8", + "dmpll_d4", + "dmpll_d8" +}; + +static const char * const pmicspi_parents[] __initconst = { + "clk26m", + "syspll1_d8", + "syspll3_d4", + "syspll1_d16", + "univpll3_d4", + "univpll_d26", + "dmpll_d8", + "dmpll_d16" +}; + +static const char * const scp_parents[] __initconst = { + "clk26m", + "syspll1_d2", + "univpll_d5", + "syspll_d5", + "dmpll_d2", + "dmpll_d4" +}; + +static const char * const atb_parents[] __initconst = { + "clk26m", + "syspll1_d2", + "univpll_d5", + "dmpll_d2" +}; + +static const char * const venc_lt_parents[] __initconst = { + "clk26m", + "univpll_d3", + "vcodecpll_ck", + "tvdpll_445p5m", + "vencpll_d2", + "syspll_d3", + "univpll1_d2", + "univpll2_d2", + "syspll1_d2", + "univpll_d5", + "vcodecpll_370p5", + "dmpll_ck" +}; + +static const char * const dpi0_parents[] __initconst = { + "clk26m", + "tvdpll_d2", + "tvdpll_d4", + "clk26m", + "clk26m", + "tvdpll_d8", + "tvdpll_d16" +}; + +static const char * const irda_parents[] __initconst = { + "clk26m", + "univpll2_d4", + "syspll2_d4" +}; + +static const char * const cci400_parents[] __initconst = { + "clk26m", + "vencpll_ck", + "armca7pll_754m", + "armca7pll_502m", + "univpll_d2", + "syspll_d2", + "msdcpll_ck", + "dmpll_ck" +}; + +static const char * const aud_1_parents[] __initconst = { + "clk26m", + "apll1_ck", + "univpll2_d4", + "univpll2_d8" +}; + +static const char * const aud_2_parents[] __initconst = { + "clk26m", + "apll2_ck", + "univpll2_d4", + "univpll2_d8" +}; + +static const char * const mem_mfg_in_parents[] __initconst = { + "clk26m", + "mmpll_ck", + "dmpll_ck", + "clk26m" +}; + +static const char * const axi_mfg_in_parents[] __initconst = { + "clk26m", + "axi_sel", + "dmpll_d2" +}; + +static const char * const scam_parents[] __initconst = { + "clk26m", + "syspll3_d2", + "univpll2_d4", + "dmpll_d4" +}; + +static const char * const spinfi_ifr_parents[] __initconst = { + "clk26m", + "univpll2_d8", + "univpll3_d4", + "syspll4_d2", + "univpll2_d4", + "univpll3_d2", + "syspll1_d4", + "univpll1_d4" +}; + +static const char * const hdmi_parents[] __initconst = { + "clk26m", + "hdmitx_dig_cts", + "hdmitxpll_d2", + "hdmitxpll_d3" +}; + +static const char * const dpilvds_parents[] __initconst = { + "clk26m", + "lvdspll", + "lvdspll_d2", + "lvdspll_d4", + "lvdspll_d8", + "fpc_ck" +}; + +static const char * const msdc50_2_h_parents[] __initconst = { + "clk26m", + "syspll1_d2", + "syspll2_d2", + "syspll4_d2", + "univpll_d5", + "univpll1_d4" +}; + +static const char * const hdcp_parents[] __initconst = { + "clk26m", + "syspll4_d2", + "syspll3_d4", + "univpll2_d4" +}; + +static const char * const hdcp_24m_parents[] __initconst = { + "clk26m", + "univpll_d26", + "univpll_d52", + "univpll2_d8" +}; + +static const char * const rtc_parents[] __initconst = { + "clkrtc_int", + "clkrtc_ext", + "clk26m", + "univpll3_d8" +}; + +static const char * const i2s0_m_ck_parents[] __initconst = { + "apll1_div1", + "apll2_div1" +}; + +static const char * const i2s1_m_ck_parents[] __initconst = { + "apll1_div2", + "apll2_div2" +}; + +static const char * const i2s2_m_ck_parents[] __initconst = { + "apll1_div3", + "apll2_div3" +}; + +static const char * const i2s3_m_ck_parents[] __initconst = { + "apll1_div4", + "apll2_div4" +}; + +static const char * const i2s3_b_ck_parents[] __initconst = { + "apll1_div5", + "apll2_div5" +}; + +static const struct mtk_composite top_muxes[] __initconst = { + /* CLK_CFG_0 */ + MUX(CLK_TOP_AXI_SEL, "axi_sel", axi_parents, 0x0040, 0, 3), + MUX(CLK_TOP_MEM_SEL, "mem_sel", mem_parents, 0x0040, 8, 1), + MUX_GATE(CLK_TOP_DDRPHYCFG_SEL, "ddrphycfg_sel", ddrphycfg_parents, 0x0040, 16, 1, 23), + MUX_GATE(CLK_TOP_MM_SEL, "mm_sel", mm_parents, 0x0040, 24, 4, 31), + /* CLK_CFG_1 */ + MUX_GATE(CLK_TOP_PWM_SEL, "pwm_sel", pwm_parents, 0x0050, 0, 2, 7), + MUX_GATE(CLK_TOP_VDEC_SEL, "vdec_sel", vdec_parents, 0x0050, 8, 4, 15), + MUX_GATE(CLK_TOP_VENC_SEL, "venc_sel", venc_parents, 0x0050, 16, 4, 23), + MUX_GATE(CLK_TOP_MFG_SEL, "mfg_sel", mfg_parents, 0x0050, 24, 4, 31), + /* CLK_CFG_2 */ + MUX_GATE(CLK_TOP_CAMTG_SEL, "camtg_sel", camtg_parents, 0x0060, 0, 3, 7), + MUX_GATE(CLK_TOP_UART_SEL, "uart_sel", uart_parents, 0x0060, 8, 1, 15), + MUX_GATE(CLK_TOP_SPI_SEL, "spi_sel", spi_parents, 0x0060, 16, 3, 23), + MUX_GATE(CLK_TOP_USB20_SEL, "usb20_sel", usb20_parents, 0x0060, 24, 2, 31), + /* CLK_CFG_3 */ + MUX_GATE(CLK_TOP_USB30_SEL, "usb30_sel", usb30_parents, 0x0070, 0, 2, 7), + MUX_GATE(CLK_TOP_MSDC50_0_H_SEL, "msdc50_0_h_sel", msdc50_0_h_parents, 0x0070, 8, 3, 15), + MUX_GATE(CLK_TOP_MSDC50_0_SEL, "msdc50_0_sel", msdc50_0_parents, 0x0070, 16, 4, 23), + MUX_GATE(CLK_TOP_MSDC30_1_SEL, "msdc30_1_sel", msdc30_1_parents, 0x0070, 24, 3, 31), + /* CLK_CFG_4 */ + MUX_GATE(CLK_TOP_MSDC30_2_SEL, "msdc30_2_sel", msdc30_2_parents, 0x0080, 0, 3, 7), + MUX_GATE(CLK_TOP_MSDC30_3_SEL, "msdc30_3_sel", msdc30_3_parents, 0x0080, 8, 4, 15), + MUX_GATE(CLK_TOP_AUDIO_SEL, "audio_sel", audio_parents, 0x0080, 16, 2, 23), + MUX_GATE(CLK_TOP_AUD_INTBUS_SEL, "aud_intbus_sel", aud_intbus_parents, 0x0080, 24, 3, 31), + /* CLK_CFG_5 */ + MUX_GATE(CLK_TOP_PMICSPI_SEL, "pmicspi_sel", pmicspi_parents, 0x0090, 0, 3, 7 /* 7:5 */), + MUX_GATE(CLK_TOP_SCP_SEL, "scp_sel", scp_parents, 0x0090, 8, 3, 15), + MUX_GATE(CLK_TOP_ATB_SEL, "atb_sel", atb_parents, 0x0090, 16, 2, 23), + MUX_GATE(CLK_TOP_VENC_LT_SEL, "venclt_sel", venc_lt_parents, 0x0090, 24, 4, 31), + /* CLK_CFG_6 */ + MUX_GATE(CLK_TOP_DPI0_SEL, "dpi0_sel", dpi0_parents, 0x00a0, 0, 3, 7), + MUX_GATE(CLK_TOP_IRDA_SEL, "irda_sel", irda_parents, 0x00a0, 8, 2, 15), + MUX_GATE(CLK_TOP_CCI400_SEL, "cci400_sel", cci400_parents, 0x00a0, 16, 3, 23), + MUX_GATE(CLK_TOP_AUD_1_SEL, "aud_1_sel", aud_1_parents, 0x00a0, 24, 2, 31), + /* CLK_CFG_7 */ + MUX_GATE(CLK_TOP_AUD_2_SEL, "aud_2_sel", aud_2_parents, 0x00b0, 0, 2, 7), + MUX_GATE(CLK_TOP_MEM_MFG_IN_SEL, "mem_mfg_in_sel", mem_mfg_in_parents, 0x00b0, 8, 2, 15), + MUX_GATE(CLK_TOP_AXI_MFG_IN_SEL, "axi_mfg_in_sel", axi_mfg_in_parents, 0x00b0, 16, 2, 23), + MUX_GATE(CLK_TOP_SCAM_SEL, "scam_sel", scam_parents, 0x00b0, 24, 2, 31), + /* CLK_CFG_12 */ + MUX_GATE(CLK_TOP_SPINFI_IFR_SEL, "spinfi_ifr_sel", spinfi_ifr_parents, 0x00c0, 0, 3, 7), + MUX_GATE(CLK_TOP_HDMI_SEL, "hdmi_sel", hdmi_parents, 0x00c0, 8, 2, 15), + MUX_GATE(CLK_TOP_DPILVDS_SEL, "dpilvds_sel", dpilvds_parents, 0x00c0, 24, 3, 31), + /* CLK_CFG_13 */ + MUX_GATE(CLK_TOP_MSDC50_2_H_SEL, "msdc50_2_h_sel", msdc50_2_h_parents, 0x00d0, 0, 3, 7), + MUX_GATE(CLK_TOP_HDCP_SEL, "hdcp_sel", hdcp_parents, 0x00d0, 8, 2, 15), + MUX_GATE(CLK_TOP_HDCP_24M_SEL, "hdcp_24m_sel", hdcp_24m_parents, 0x00d0, 16, 2, 23), + MUX(CLK_TOP_RTC_SEL, "rtc_sel", rtc_parents, 0x00d0, 24, 2), + + DIV_GATE(CLK_TOP_APLL1_DIV0, "apll1_div0", "aud_1_sel", 0x12c, 8, 0x120, 4, 24), + DIV_GATE(CLK_TOP_APLL1_DIV1, "apll1_div1", "aud_1_sel", 0x12c, 9, 0x124, 8, 0), + DIV_GATE(CLK_TOP_APLL1_DIV2, "apll1_div2", "aud_1_sel", 0x12c, 10, 0x124, 8, 8), + DIV_GATE(CLK_TOP_APLL1_DIV3, "apll1_div3", "aud_1_sel", 0x12c, 11, 0x124, 8, 16), + DIV_GATE(CLK_TOP_APLL1_DIV4, "apll1_div4", "aud_1_sel", 0x12c, 12, 0x124, 8, 24), + DIV_GATE(CLK_TOP_APLL1_DIV5, "apll1_div5", "apll1_div4", 0x12c, 13, 0x12c, 4, 0), + + DIV_GATE(CLK_TOP_APLL2_DIV0, "apll2_div0", "aud_2_sel", 0x12c, 16, 0x120, 4, 28), + DIV_GATE(CLK_TOP_APLL2_DIV1, "apll2_div1", "aud_2_sel", 0x12c, 17, 0x128, 8, 0), + DIV_GATE(CLK_TOP_APLL2_DIV2, "apll2_div2", "aud_2_sel", 0x12c, 18, 0x128, 8, 8), + DIV_GATE(CLK_TOP_APLL2_DIV3, "apll2_div3", "aud_2_sel", 0x12c, 19, 0x128, 8, 16), + DIV_GATE(CLK_TOP_APLL2_DIV4, "apll2_div4", "aud_2_sel", 0x12c, 20, 0x128, 8, 24), + DIV_GATE(CLK_TOP_APLL2_DIV5, "apll2_div5", "apll2_div4", 0x12c, 21, 0x12c, 4, 4), + + MUX(CLK_TOP_I2S0_M_SEL, "i2s0_m_ck_sel", i2s0_m_ck_parents, 0x120, 4, 1), + MUX(CLK_TOP_I2S1_M_SEL, "i2s1_m_ck_sel", i2s1_m_ck_parents, 0x120, 5, 1), + MUX(CLK_TOP_I2S2_M_SEL, "i2s2_m_ck_sel", i2s2_m_ck_parents, 0x120, 6, 1), + MUX(CLK_TOP_I2S3_M_SEL, "i2s3_m_ck_sel", i2s3_m_ck_parents, 0x120, 7, 1), + MUX(CLK_TOP_I2S3_B_SEL, "i2s3_b_ck_sel", i2s3_b_ck_parents, 0x120, 8, 1), +}; + +static const struct mtk_gate_regs infra_cg_regs = { + .set_ofs = 0x0040, + .clr_ofs = 0x0044, + .sta_ofs = 0x0048, +}; + +#define GATE_ICG(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &infra_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +static const struct mtk_gate infra_clks[] __initconst = { + GATE_ICG(CLK_INFRA_DBGCLK, "infra_dbgclk", "axi_sel", 0), + GATE_ICG(CLK_INFRA_SMI, "infra_smi", "mm_sel", 1), + GATE_ICG(CLK_INFRA_AUDIO, "infra_audio", "aud_intbus_sel", 5), + GATE_ICG(CLK_INFRA_GCE, "infra_gce", "axi_sel", 6), + GATE_ICG(CLK_INFRA_L2C_SRAM, "infra_l2c_sram", "axi_sel", 7), + GATE_ICG(CLK_INFRA_M4U, "infra_m4u", "mem_sel", 8), + GATE_ICG(CLK_INFRA_CPUM, "infra_cpum", "clk_null", 15), + GATE_ICG(CLK_INFRA_KP, "infra_kp", "axi_sel", 16), + GATE_ICG(CLK_INFRA_CEC, "infra_cec", "clk26m", 18), + GATE_ICG(CLK_INFRA_PMICSPI, "infra_pmicspi", "pmicspi_sel", 22), + GATE_ICG(CLK_INFRA_PMICWRAP, "infra_pmicwrap", "axi_sel", 23), +}; + +static const struct mtk_gate_regs peri0_cg_regs = { + .set_ofs = 0x0008, + .clr_ofs = 0x0010, + .sta_ofs = 0x0018, +}; + +static const struct mtk_gate_regs peri1_cg_regs = { + .set_ofs = 0x000c, + .clr_ofs = 0x0014, + .sta_ofs = 0x001c, +}; + +#define GATE_PERI0(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &peri0_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +#define GATE_PERI1(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &peri1_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +static const struct mtk_gate peri_gates[] __initconst = { + /* PERI0 */ + GATE_PERI0(CLK_PERI_NFI, "peri_nfi", "axi_sel", 0), + GATE_PERI0(CLK_PERI_THERM, "peri_therm", "axi_sel", 1), + GATE_PERI0(CLK_PERI_PWM1, "peri_pwm1", "axi_sel", 2), + GATE_PERI0(CLK_PERI_PWM2, "peri_pwm2", "axi_sel", 3), + GATE_PERI0(CLK_PERI_PWM3, "peri_pwm3", "axi_sel", 4), + GATE_PERI0(CLK_PERI_PWM4, "peri_pwm4", "axi_sel", 5), + GATE_PERI0(CLK_PERI_PWM5, "peri_pwm5", "axi_sel", 6), + GATE_PERI0(CLK_PERI_PWM6, "peri_pwm6", "axi_sel", 7), + GATE_PERI0(CLK_PERI_PWM7, "peri_pwm7", "axi_sel", 8), + GATE_PERI0(CLK_PERI_PWM, "peri_pwm", "axi_sel", 9), + GATE_PERI0(CLK_PERI_USB0, "peri_usb0", "usb20_sel", 10), + GATE_PERI0(CLK_PERI_USB1, "peri_usb1", "usb20_sel", 11), + GATE_PERI0(CLK_PERI_AP_DMA, "peri_ap_dma", "axi_sel", 12), + GATE_PERI0(CLK_PERI_MSDC30_0, "peri_msdc30_0", "msdc50_0_sel", 13), + GATE_PERI0(CLK_PERI_MSDC30_1, "peri_msdc30_1", "msdc30_1_sel", 14), + GATE_PERI0(CLK_PERI_MSDC30_2, "peri_msdc30_2", "msdc30_2_sel", 15), + GATE_PERI0(CLK_PERI_MSDC30_3, "peri_msdc30_3", "msdc30_3_sel", 16), + GATE_PERI0(CLK_PERI_NLI_ARB, "peri_nli_arb", "axi_sel", 17), + GATE_PERI0(CLK_PERI_IRDA, "peri_irda", "irda_sel", 18), + GATE_PERI0(CLK_PERI_UART0, "peri_uart0", "axi_sel", 19), + GATE_PERI0(CLK_PERI_UART1, "peri_uart1", "axi_sel", 20), + GATE_PERI0(CLK_PERI_UART2, "peri_uart2", "axi_sel", 21), + GATE_PERI0(CLK_PERI_UART3, "peri_uart3", "axi_sel", 22), + GATE_PERI0(CLK_PERI_I2C0, "peri_i2c0", "axi_sel", 23), + GATE_PERI0(CLK_PERI_I2C1, "peri_i2c1", "axi_sel", 24), + GATE_PERI0(CLK_PERI_I2C2, "peri_i2c2", "axi_sel", 25), + GATE_PERI0(CLK_PERI_I2C3, "peri_i2c3", "axi_sel", 26), + GATE_PERI0(CLK_PERI_I2C4, "peri_i2c4", "axi_sel", 27), + GATE_PERI0(CLK_PERI_AUXADC, "peri_auxadc", "clk26m", 28), + GATE_PERI0(CLK_PERI_SPI0, "peri_spi0", "spi_sel", 29), + GATE_PERI0(CLK_PERI_I2C5, "peri_i2c5", "axi_sel", 30), + GATE_PERI0(CLK_PERI_NFIECC, "peri_nfiecc", "axi_sel", 31), + /* PERI1 */ + GATE_PERI1(CLK_PERI_SPI, "peri_spi", "spi_sel", 0), + GATE_PERI1(CLK_PERI_IRRX, "peri_irrx", "spi_sel", 1), + GATE_PERI1(CLK_PERI_I2C6, "peri_i2c6", "axi_sel", 2), +}; + +static const char * const uart_ck_sel_parents[] __initconst = { + "clk26m", + "uart_sel", +}; + +static const struct mtk_composite peri_clks[] __initconst = { + MUX(CLK_PERI_UART0_SEL, "uart0_ck_sel", uart_ck_sel_parents, 0x40c, 0, 1), + MUX(CLK_PERI_UART1_SEL, "uart1_ck_sel", uart_ck_sel_parents, 0x40c, 1, 1), + MUX(CLK_PERI_UART2_SEL, "uart2_ck_sel", uart_ck_sel_parents, 0x40c, 2, 1), + MUX(CLK_PERI_UART3_SEL, "uart3_ck_sel", uart_ck_sel_parents, 0x40c, 3, 1), +}; + +static void __init mtk_topckgen_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + void __iomem *base; + int r; + + base = of_iomap(node, 0); + if (!base) { + pr_err("%s(): ioremap failed\n", __func__); + return; + } + + clk_data = mtk_alloc_clk_data(CLK_TOP_NR_CLK); + + mtk_clk_register_factors(root_clk_alias, ARRAY_SIZE(root_clk_alias), clk_data); + mtk_clk_register_factors(top_divs, ARRAY_SIZE(top_divs), clk_data); + mtk_clk_register_composites(top_muxes, ARRAY_SIZE(top_muxes), base, + &mt8173_clk_lock, clk_data); + + clk_prepare_enable(clk_data->clks[CLK_TOP_CCI400_SEL]); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); +} +CLK_OF_DECLARE(mtk_topckgen, "mediatek,mt8173-topckgen", mtk_topckgen_init); + +static void __init mtk_infrasys_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + int r; + + clk_data = mtk_alloc_clk_data(CLK_INFRA_NR_CLK); + + mtk_clk_register_gates(node, infra_clks, ARRAY_SIZE(infra_clks), + clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); + + mtk_register_reset_controller(node, 2, 0x30); +} +CLK_OF_DECLARE(mtk_infrasys, "mediatek,mt8173-infracfg", mtk_infrasys_init); + +static void __init mtk_pericfg_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + int r; + void __iomem *base; + + base = of_iomap(node, 0); + if (!base) { + pr_err("%s(): ioremap failed\n", __func__); + return; + } + + clk_data = mtk_alloc_clk_data(CLK_PERI_NR_CLK); + + mtk_clk_register_gates(node, peri_gates, ARRAY_SIZE(peri_gates), + clk_data); + mtk_clk_register_composites(peri_clks, ARRAY_SIZE(peri_clks), base, + &mt8173_clk_lock, clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); + + mtk_register_reset_controller(node, 2, 0); +} +CLK_OF_DECLARE(mtk_pericfg, "mediatek,mt8173-pericfg", mtk_pericfg_init); + +#define MT8173_PLL_FMAX (3000UL * MHZ) + +#define CON0_MT8173_RST_BAR BIT(24) + +#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, _pd_reg, _pd_shift, \ + _tuner_reg, _pcw_reg, _pcw_shift) { \ + .id = _id, \ + .name = _name, \ + .reg = _reg, \ + .pwr_reg = _pwr_reg, \ + .en_mask = _en_mask, \ + .flags = _flags, \ + .rst_bar_mask = CON0_MT8173_RST_BAR, \ + .fmax = MT8173_PLL_FMAX, \ + .pcwbits = _pcwbits, \ + .pd_reg = _pd_reg, \ + .pd_shift = _pd_shift, \ + .tuner_reg = _tuner_reg, \ + .pcw_reg = _pcw_reg, \ + .pcw_shift = _pcw_shift, \ + } + +static const struct mtk_pll_data plls[] = { + PLL(CLK_APMIXED_ARMCA15PLL, "armca15pll", 0x200, 0x20c, 0x00000001, 0, 21, 0x204, 24, 0x0, 0x204, 0), + PLL(CLK_APMIXED_ARMCA7PLL, "armca7pll", 0x210, 0x21c, 0x00000001, 0, 21, 0x214, 24, 0x0, 0x214, 0), + PLL(CLK_APMIXED_MAINPLL, "mainpll", 0x220, 0x22c, 0xf0000101, HAVE_RST_BAR, 21, 0x220, 4, 0x0, 0x224, 0), + PLL(CLK_APMIXED_UNIVPLL, "univpll", 0x230, 0x23c, 0xfe000001, HAVE_RST_BAR, 7, 0x230, 4, 0x0, 0x234, 14), + PLL(CLK_APMIXED_MMPLL, "mmpll", 0x240, 0x24c, 0x00000001, 0, 21, 0x244, 24, 0x0, 0x244, 0), + PLL(CLK_APMIXED_MSDCPLL, "msdcpll", 0x250, 0x25c, 0x00000001, 0, 21, 0x250, 4, 0x0, 0x254, 0), + PLL(CLK_APMIXED_VENCPLL, "vencpll", 0x260, 0x26c, 0x00000001, 0, 21, 0x260, 4, 0x0, 0x264, 0), + PLL(CLK_APMIXED_TVDPLL, "tvdpll", 0x270, 0x27c, 0x00000001, 0, 21, 0x270, 4, 0x0, 0x274, 0), + PLL(CLK_APMIXED_MPLL, "mpll", 0x280, 0x28c, 0x00000001, 0, 21, 0x280, 4, 0x0, 0x284, 0), + PLL(CLK_APMIXED_VCODECPLL, "vcodecpll", 0x290, 0x29c, 0x00000001, 0, 21, 0x290, 4, 0x0, 0x294, 0), + PLL(CLK_APMIXED_APLL1, "apll1", 0x2a0, 0x2b0, 0x00000001, 0, 31, 0x2a0, 4, 0x2a4, 0x2a4, 0), + PLL(CLK_APMIXED_APLL2, "apll2", 0x2b4, 0x2c4, 0x00000001, 0, 31, 0x2b4, 4, 0x2b8, 0x2b8, 0), + PLL(CLK_APMIXED_LVDSPLL, "lvdspll", 0x2d0, 0x2dc, 0x00000001, 0, 21, 0x2d0, 4, 0x0, 0x2d4, 0), + PLL(CLK_APMIXED_MSDCPLL2, "msdcpll2", 0x2f0, 0x2fc, 0x00000001, 0, 21, 0x2f0, 4, 0x0, 0x2f4, 0), +}; + +static void __init mtk_apmixedsys_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + + clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR_CLK); + if (!clk_data) + return; + + mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data); + + clk_prepare_enable(clk_data->clks[CLK_APMIXED_ARMCA15PLL]); +} +CLK_OF_DECLARE(mtk_apmixedsys, "mediatek,mt8173-apmixedsys", + mtk_apmixedsys_init); diff --git a/drivers/clk/mediatek/clk-mtk.c b/drivers/clk/mediatek/clk-mtk.c new file mode 100644 index 000000000000..18444aea63c9 --- /dev/null +++ b/drivers/clk/mediatek/clk-mtk.c @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clk-mtk.h" +#include "clk-gate.h" + +struct clk_onecell_data *mtk_alloc_clk_data(unsigned int clk_num) +{ + int i; + struct clk_onecell_data *clk_data; + + clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); + if (!clk_data) + return NULL; + + clk_data->clks = kcalloc(clk_num, sizeof(*clk_data->clks), GFP_KERNEL); + if (!clk_data->clks) + goto err_out; + + clk_data->clk_num = clk_num; + + for (i = 0; i < clk_num; i++) + clk_data->clks[i] = ERR_PTR(-ENOENT); + + return clk_data; +err_out: + kfree(clk_data); + + return NULL; +} + +void mtk_clk_register_factors(const struct mtk_fixed_factor *clks, int num, + struct clk_onecell_data *clk_data) +{ + int i; + struct clk *clk; + + for (i = 0; i < num; i++) { + const struct mtk_fixed_factor *ff = &clks[i]; + + clk = clk_register_fixed_factor(NULL, ff->name, ff->parent_name, + CLK_SET_RATE_PARENT, ff->mult, ff->div); + + if (IS_ERR(clk)) { + pr_err("Failed to register clk %s: %ld\n", + ff->name, PTR_ERR(clk)); + continue; + } + + if (clk_data) + clk_data->clks[ff->id] = clk; + } +} + +int mtk_clk_register_gates(struct device_node *node, const struct mtk_gate *clks, + int num, struct clk_onecell_data *clk_data) +{ + int i; + struct clk *clk; + struct regmap *regmap; + + if (!clk_data) + return -ENOMEM; + + regmap = syscon_node_to_regmap(node); + if (IS_ERR(regmap)) { + pr_err("Cannot find regmap for %s: %ld\n", node->full_name, + PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + + for (i = 0; i < num; i++) { + const struct mtk_gate *gate = &clks[i]; + + clk = mtk_clk_register_gate(gate->name, gate->parent_name, + regmap, + gate->regs->set_ofs, + gate->regs->clr_ofs, + gate->regs->sta_ofs, + gate->shift, gate->ops); + + if (IS_ERR(clk)) { + pr_err("Failed to register clk %s: %ld\n", + gate->name, PTR_ERR(clk)); + continue; + } + + clk_data->clks[gate->id] = clk; + } + + return 0; +} + +struct clk *mtk_clk_register_composite(const struct mtk_composite *mc, + void __iomem *base, spinlock_t *lock) +{ + struct clk *clk; + struct clk_mux *mux = NULL; + struct clk_gate *gate = NULL; + struct clk_divider *div = NULL; + struct clk_hw *mux_hw = NULL, *gate_hw = NULL, *div_hw = NULL; + const struct clk_ops *mux_ops = NULL, *gate_ops = NULL, *div_ops = NULL; + const char * const *parent_names; + const char *parent; + int num_parents; + int ret; + + if (mc->mux_shift >= 0) { + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) + return ERR_PTR(-ENOMEM); + + mux->reg = base + mc->mux_reg; + mux->mask = BIT(mc->mux_width) - 1; + mux->shift = mc->mux_shift; + mux->lock = lock; + + mux_hw = &mux->hw; + mux_ops = &clk_mux_ops; + + parent_names = mc->parent_names; + num_parents = mc->num_parents; + } else { + parent = mc->parent; + parent_names = &parent; + num_parents = 1; + } + + if (mc->gate_shift >= 0) { + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) { + ret = -ENOMEM; + goto err_out; + } + + gate->reg = base + mc->gate_reg; + gate->bit_idx = mc->gate_shift; + gate->flags = CLK_GATE_SET_TO_DISABLE; + gate->lock = lock; + + gate_hw = &gate->hw; + gate_ops = &clk_gate_ops; + } + + if (mc->divider_shift >= 0) { + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) { + ret = -ENOMEM; + goto err_out; + } + + div->reg = base + mc->divider_reg; + div->shift = mc->divider_shift; + div->width = mc->divider_width; + div->lock = lock; + + div_hw = &div->hw; + div_ops = &clk_divider_ops; + } + + clk = clk_register_composite(NULL, mc->name, parent_names, num_parents, + mux_hw, mux_ops, + div_hw, div_ops, + gate_hw, gate_ops, + mc->flags); + + if (IS_ERR(clk)) { + kfree(gate); + kfree(mux); + } + + return clk; +err_out: + kfree(mux); + + return ERR_PTR(ret); +} + +void mtk_clk_register_composites(const struct mtk_composite *mcs, + int num, void __iomem *base, spinlock_t *lock, + struct clk_onecell_data *clk_data) +{ + struct clk *clk; + int i; + + for (i = 0; i < num; i++) { + const struct mtk_composite *mc = &mcs[i]; + + clk = mtk_clk_register_composite(mc, base, lock); + + if (IS_ERR(clk)) { + pr_err("Failed to register clk %s: %ld\n", + mc->name, PTR_ERR(clk)); + continue; + } + + if (clk_data) + clk_data->clks[mc->id] = clk; + } +} diff --git a/drivers/clk/mediatek/clk-mtk.h b/drivers/clk/mediatek/clk-mtk.h new file mode 100644 index 000000000000..9dda9d8ad10b --- /dev/null +++ b/drivers/clk/mediatek/clk-mtk.h @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao + * + * 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. + * + * 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 __DRV_CLK_MTK_H +#define __DRV_CLK_MTK_H + +#include +#include +#include +#include + +#define MAX_MUX_GATE_BIT 31 +#define INVALID_MUX_GATE_BIT (MAX_MUX_GATE_BIT + 1) + +#define MHZ (1000 * 1000) + +struct mtk_fixed_factor { + int id; + const char *name; + const char *parent_name; + int mult; + int div; +}; + +#define FACTOR(_id, _name, _parent, _mult, _div) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .mult = _mult, \ + .div = _div, \ + } + +extern void mtk_clk_register_factors(const struct mtk_fixed_factor *clks, + int num, struct clk_onecell_data *clk_data); + +struct mtk_composite { + int id; + const char *name; + const char * const *parent_names; + const char *parent; + unsigned flags; + + uint32_t mux_reg; + uint32_t divider_reg; + uint32_t gate_reg; + + signed char mux_shift; + signed char mux_width; + signed char gate_shift; + + signed char divider_shift; + signed char divider_width; + + signed char num_parents; +}; + +#define MUX_GATE(_id, _name, _parents, _reg, _shift, _width, _gate) { \ + .id = _id, \ + .name = _name, \ + .mux_reg = _reg, \ + .mux_shift = _shift, \ + .mux_width = _width, \ + .gate_reg = _reg, \ + .gate_shift = _gate, \ + .divider_shift = -1, \ + .parent_names = _parents, \ + .num_parents = ARRAY_SIZE(_parents), \ + .flags = CLK_SET_RATE_PARENT, \ + } + +#define MUX(_id, _name, _parents, _reg, _shift, _width) { \ + .id = _id, \ + .name = _name, \ + .mux_reg = _reg, \ + .mux_shift = _shift, \ + .mux_width = _width, \ + .gate_shift = -1, \ + .divider_shift = -1, \ + .parent_names = _parents, \ + .num_parents = ARRAY_SIZE(_parents), \ + .flags = CLK_SET_RATE_PARENT, \ + } + +#define DIV_GATE(_id, _name, _parent, _gate_reg, _gate_shift, _div_reg, _div_width, _div_shift) { \ + .id = _id, \ + .parent = _parent, \ + .name = _name, \ + .divider_reg = _div_reg, \ + .divider_shift = _div_shift, \ + .divider_width = _div_width, \ + .gate_reg = _gate_reg, \ + .gate_shift = _gate_shift, \ + .mux_shift = -1, \ + .flags = 0, \ + } + +struct clk *mtk_clk_register_composite(const struct mtk_composite *mc, + void __iomem *base, spinlock_t *lock); + +void mtk_clk_register_composites(const struct mtk_composite *mcs, + int num, void __iomem *base, spinlock_t *lock, + struct clk_onecell_data *clk_data); + +struct mtk_gate_regs { + u32 sta_ofs; + u32 clr_ofs; + u32 set_ofs; +}; + +struct mtk_gate { + int id; + const char *name; + const char *parent_name; + const struct mtk_gate_regs *regs; + int shift; + const struct clk_ops *ops; +}; + +int mtk_clk_register_gates(struct device_node *node, const struct mtk_gate *clks, + int num, struct clk_onecell_data *clk_data); + +struct clk_onecell_data *mtk_alloc_clk_data(unsigned int clk_num); + +#define HAVE_RST_BAR BIT(0) + +struct mtk_pll_data { + int id; + const char *name; + uint32_t reg; + uint32_t pwr_reg; + uint32_t en_mask; + uint32_t pd_reg; + uint32_t tuner_reg; + int pd_shift; + unsigned int flags; + const struct clk_ops *ops; + u32 rst_bar_mask; + unsigned long fmax; + int pcwbits; + uint32_t pcw_reg; + int pcw_shift; +}; + +void __init mtk_clk_register_plls(struct device_node *node, + const struct mtk_pll_data *plls, int num_plls, + struct clk_onecell_data *clk_data); + +#ifdef CONFIG_RESET_CONTROLLER +void mtk_register_reset_controller(struct device_node *np, + unsigned int num_regs, int regofs); +#else +static inline void mtk_register_reset_controller(struct device_node *np, + unsigned int num_regs, int regofs) +{ +} +#endif + +#endif /* __DRV_CLK_MTK_H */ diff --git a/drivers/clk/mediatek/clk-pll.c b/drivers/clk/mediatek/clk-pll.c new file mode 100644 index 000000000000..44409e98c52f --- /dev/null +++ b/drivers/clk/mediatek/clk-pll.c @@ -0,0 +1,332 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include "clk-mtk.h" + +#define REG_CON0 0 +#define REG_CON1 4 + +#define CON0_BASE_EN BIT(0) +#define CON0_PWR_ON BIT(0) +#define CON0_ISO_EN BIT(1) +#define CON0_PCW_CHG BIT(31) + +#define AUDPLL_TUNER_EN BIT(31) + +#define POSTDIV_MASK 0x7 +#define INTEGER_BITS 7 + +/* + * MediaTek PLLs are configured through their pcw value. The pcw value describes + * a divider in the PLL feedback loop which consists of 7 bits for the integer + * part and the remaining bits (if present) for the fractional part. Also they + * have a 3 bit power-of-two post divider. + */ + +struct mtk_clk_pll { + struct clk_hw hw; + void __iomem *base_addr; + void __iomem *pd_addr; + void __iomem *pwr_addr; + void __iomem *tuner_addr; + void __iomem *pcw_addr; + const struct mtk_pll_data *data; +}; + +static inline struct mtk_clk_pll *to_mtk_clk_pll(struct clk_hw *hw) +{ + return container_of(hw, struct mtk_clk_pll, hw); +} + +static int mtk_pll_is_prepared(struct clk_hw *hw) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + + return (readl(pll->base_addr + REG_CON0) & CON0_BASE_EN) != 0; +} + +static unsigned long __mtk_pll_recalc_rate(struct mtk_clk_pll *pll, u32 fin, + u32 pcw, int postdiv) +{ + int pcwbits = pll->data->pcwbits; + int pcwfbits; + u64 vco; + u8 c = 0; + + /* The fractional part of the PLL divider. */ + pcwfbits = pcwbits > INTEGER_BITS ? pcwbits - INTEGER_BITS : 0; + + vco = (u64)fin * pcw; + + if (pcwfbits && (vco & GENMASK(pcwfbits - 1, 0))) + c = 1; + + vco >>= pcwfbits; + + if (c) + vco++; + + return ((unsigned long)vco + postdiv - 1) / postdiv; +} + +static void mtk_pll_set_rate_regs(struct mtk_clk_pll *pll, u32 pcw, + int postdiv) +{ + u32 con1, pd, val; + int pll_en; + + /* set postdiv */ + pd = readl(pll->pd_addr); + pd &= ~(POSTDIV_MASK << pll->data->pd_shift); + pd |= (ffs(postdiv) - 1) << pll->data->pd_shift; + writel(pd, pll->pd_addr); + + pll_en = readl(pll->base_addr + REG_CON0) & CON0_BASE_EN; + + /* set pcw */ + val = readl(pll->pcw_addr); + + val &= ~GENMASK(pll->data->pcw_shift + pll->data->pcwbits - 1, + pll->data->pcw_shift); + val |= pcw << pll->data->pcw_shift; + writel(val, pll->pcw_addr); + + con1 = readl(pll->base_addr + REG_CON1); + + if (pll_en) + con1 |= CON0_PCW_CHG; + + writel(con1, pll->base_addr + REG_CON1); + if (pll->tuner_addr) + writel(con1 + 1, pll->tuner_addr); + + if (pll_en) + udelay(20); +} + +/* + * mtk_pll_calc_values - calculate good values for a given input frequency. + * @pll: The pll + * @pcw: The pcw value (output) + * @postdiv: The post divider (output) + * @freq: The desired target frequency + * @fin: The input frequency + * + */ +static void mtk_pll_calc_values(struct mtk_clk_pll *pll, u32 *pcw, u32 *postdiv, + u32 freq, u32 fin) +{ + unsigned long fmin = 1000 * MHZ; + u64 _pcw; + u32 val; + + if (freq > pll->data->fmax) + freq = pll->data->fmax; + + for (val = 0; val < 4; val++) { + *postdiv = 1 << val; + if (freq * *postdiv >= fmin) + break; + } + + /* _pcw = freq * postdiv / fin * 2^pcwfbits */ + _pcw = ((u64)freq << val) << (pll->data->pcwbits - INTEGER_BITS); + do_div(_pcw, fin); + + *pcw = (u32)_pcw; +} + +static int mtk_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 pcw = 0; + u32 postdiv; + + mtk_pll_calc_values(pll, &pcw, &postdiv, rate, parent_rate); + mtk_pll_set_rate_regs(pll, pcw, postdiv); + + return 0; +} + +static unsigned long mtk_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 postdiv; + u32 pcw; + + postdiv = (readl(pll->pd_addr) >> pll->data->pd_shift) & POSTDIV_MASK; + postdiv = 1 << postdiv; + + pcw = readl(pll->pcw_addr) >> pll->data->pcw_shift; + pcw &= GENMASK(pll->data->pcwbits - 1, 0); + + return __mtk_pll_recalc_rate(pll, parent_rate, pcw, postdiv); +} + +static long mtk_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 pcw = 0; + int postdiv; + + mtk_pll_calc_values(pll, &pcw, &postdiv, rate, *prate); + + return __mtk_pll_recalc_rate(pll, *prate, pcw, postdiv); +} + +static int mtk_pll_prepare(struct clk_hw *hw) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 r; + + r = readl(pll->pwr_addr) | CON0_PWR_ON; + writel(r, pll->pwr_addr); + udelay(1); + + r = readl(pll->pwr_addr) & ~CON0_ISO_EN; + writel(r, pll->pwr_addr); + udelay(1); + + r = readl(pll->base_addr + REG_CON0); + r |= pll->data->en_mask; + writel(r, pll->base_addr + REG_CON0); + + if (pll->tuner_addr) { + r = readl(pll->tuner_addr) | AUDPLL_TUNER_EN; + writel(r, pll->tuner_addr); + } + + udelay(20); + + if (pll->data->flags & HAVE_RST_BAR) { + r = readl(pll->base_addr + REG_CON0); + r |= pll->data->rst_bar_mask; + writel(r, pll->base_addr + REG_CON0); + } + + return 0; +} + +static void mtk_pll_unprepare(struct clk_hw *hw) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 r; + + if (pll->data->flags & HAVE_RST_BAR) { + r = readl(pll->base_addr + REG_CON0); + r &= ~pll->data->rst_bar_mask; + writel(r, pll->base_addr + REG_CON0); + } + + if (pll->tuner_addr) { + r = readl(pll->tuner_addr) & ~AUDPLL_TUNER_EN; + writel(r, pll->tuner_addr); + } + + r = readl(pll->base_addr + REG_CON0); + r &= ~CON0_BASE_EN; + writel(r, pll->base_addr + REG_CON0); + + r = readl(pll->pwr_addr) | CON0_ISO_EN; + writel(r, pll->pwr_addr); + + r = readl(pll->pwr_addr) & ~CON0_PWR_ON; + writel(r, pll->pwr_addr); +} + +static const struct clk_ops mtk_pll_ops = { + .is_prepared = mtk_pll_is_prepared, + .prepare = mtk_pll_prepare, + .unprepare = mtk_pll_unprepare, + .recalc_rate = mtk_pll_recalc_rate, + .round_rate = mtk_pll_round_rate, + .set_rate = mtk_pll_set_rate, +}; + +static struct clk *mtk_clk_register_pll(const struct mtk_pll_data *data, + void __iomem *base) +{ + struct mtk_clk_pll *pll; + struct clk_init_data init = {}; + struct clk *clk; + const char *parent_name = "clk26m"; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + pll->base_addr = base + data->reg; + pll->pwr_addr = base + data->pwr_reg; + pll->pd_addr = base + data->pd_reg; + pll->pcw_addr = base + data->pcw_reg; + if (data->tuner_reg) + pll->tuner_addr = base + data->tuner_reg; + pll->hw.init = &init; + pll->data = data; + + init.name = data->name; + init.ops = &mtk_pll_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + + clk = clk_register(NULL, &pll->hw); + + if (IS_ERR(clk)) + kfree(pll); + + return clk; +} + +void __init mtk_clk_register_plls(struct device_node *node, + const struct mtk_pll_data *plls, int num_plls, struct clk_onecell_data *clk_data) +{ + void __iomem *base; + int r, i; + struct clk *clk; + + base = of_iomap(node, 0); + if (!base) { + pr_err("%s(): ioremap failed\n", __func__); + return; + } + + for (i = 0; i < num_plls; i++) { + const struct mtk_pll_data *pll = &plls[i]; + + clk = mtk_clk_register_pll(pll, base); + + if (IS_ERR(clk)) { + pr_err("Failed to register clk %s: %ld\n", + pll->name, PTR_ERR(clk)); + continue; + } + + clk_data->clks[pll->id] = clk; + } + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); +} diff --git a/drivers/clk/mediatek/reset.c b/drivers/clk/mediatek/reset.c new file mode 100644 index 000000000000..9e9fe4b19ac4 --- /dev/null +++ b/drivers/clk/mediatek/reset.c @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2014 MediaTek 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "clk-mtk.h" + +struct mtk_reset { + struct regmap *regmap; + int regofs; + struct reset_controller_dev rcdev; +}; + +static int mtk_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct mtk_reset *data = container_of(rcdev, struct mtk_reset, rcdev); + + return regmap_update_bits(data->regmap, data->regofs + ((id / 32) << 2), + BIT(id % 32), ~0); +} + +static int mtk_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct mtk_reset *data = container_of(rcdev, struct mtk_reset, rcdev); + + return regmap_update_bits(data->regmap, data->regofs + ((id / 32) << 2), + BIT(id % 32), 0); +} + +static int mtk_reset(struct reset_controller_dev *rcdev, + unsigned long id) +{ + int ret; + + ret = mtk_reset_assert(rcdev, id); + if (ret) + return ret; + + return mtk_reset_deassert(rcdev, id); +} + +static struct reset_control_ops mtk_reset_ops = { + .assert = mtk_reset_assert, + .deassert = mtk_reset_deassert, + .reset = mtk_reset, +}; + +void mtk_register_reset_controller(struct device_node *np, + unsigned int num_regs, int regofs) +{ + struct mtk_reset *data; + int ret; + struct regmap *regmap; + + regmap = syscon_node_to_regmap(np); + if (IS_ERR(regmap)) { + pr_err("Cannot find regmap for %s: %ld\n", np->full_name, + PTR_ERR(regmap)); + return; + } + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return; + + data->regmap = regmap; + data->regofs = regofs; + data->rcdev.owner = THIS_MODULE; + data->rcdev.nr_resets = num_regs * 32; + data->rcdev.ops = &mtk_reset_ops; + data->rcdev.of_node = np; + + ret = reset_controller_register(&data->rcdev); + if (ret) { + pr_err("could not register reset controller: %d\n", ret); + kfree(data); + return; + } +} diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile new file mode 100644 index 000000000000..6d45531df9ab --- /dev/null +++ b/drivers/clk/meson/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for Meson specific clk +# + +obj-y += clkc.o clk-pll.o clk-cpu.o +obj-y += meson8b-clkc.o diff --git a/drivers/clk/meson/clk-cpu.c b/drivers/clk/meson/clk-cpu.c new file mode 100644 index 000000000000..71ad493b94df --- /dev/null +++ b/drivers/clk/meson/clk-cpu.c @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2015 Endless Mobile, Inc. + * Author: Carlo Caione + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/* + * CPU clock path: + * + * +-[/N]-----|3| + * MUX2 +--[/3]-+----------|2| MUX1 + * [sys_pll]---|1| |--[/2]------------|1|-|1| + * | |---+------------------|0| | |----- [a5_clk] + * +--|0| | | + * [xtal]---+-------------------------------|0| + * + * + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MESON_CPU_CLK_CNTL1 0x00 +#define MESON_CPU_CLK_CNTL 0x40 + +#define MESON_CPU_CLK_MUX1 BIT(7) +#define MESON_CPU_CLK_MUX2 BIT(0) + +#define MESON_N_WIDTH 9 +#define MESON_N_SHIFT 20 +#define MESON_SEL_WIDTH 2 +#define MESON_SEL_SHIFT 2 + +#include "clkc.h" + +struct meson_clk_cpu { + struct notifier_block clk_nb; + const struct clk_div_table *div_table; + struct clk_hw hw; + void __iomem *base; + u16 reg_off; +}; +#define to_meson_clk_cpu_hw(_hw) container_of(_hw, struct meson_clk_cpu, hw) +#define to_meson_clk_cpu_nb(_nb) container_of(_nb, struct meson_clk_cpu, clk_nb) + +static long meson_clk_cpu_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct meson_clk_cpu *clk_cpu = to_meson_clk_cpu_hw(hw); + + return divider_round_rate(hw, rate, prate, clk_cpu->div_table, + MESON_N_WIDTH, CLK_DIVIDER_ROUND_CLOSEST); +} + +static int meson_clk_cpu_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct meson_clk_cpu *clk_cpu = to_meson_clk_cpu_hw(hw); + unsigned int div, sel, N = 0; + u32 reg; + + div = DIV_ROUND_UP(parent_rate, rate); + + if (div <= 3) { + sel = div - 1; + } else { + sel = 3; + N = div / 2; + } + + reg = readl(clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL1); + reg = PARM_SET(MESON_N_WIDTH, MESON_N_SHIFT, reg, N); + writel(reg, clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL1); + + reg = readl(clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL); + reg = PARM_SET(MESON_SEL_WIDTH, MESON_SEL_SHIFT, reg, sel); + writel(reg, clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL); + + return 0; +} + +static unsigned long meson_clk_cpu_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct meson_clk_cpu *clk_cpu = to_meson_clk_cpu_hw(hw); + unsigned int N, sel; + unsigned int div = 1; + u32 reg; + + reg = readl(clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL1); + N = PARM_GET(MESON_N_WIDTH, MESON_N_SHIFT, reg); + + reg = readl(clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL); + sel = PARM_GET(MESON_SEL_WIDTH, MESON_SEL_SHIFT, reg); + + if (sel < 3) + div = sel + 1; + else + div = 2 * N; + + return parent_rate / div; +} + +static int meson_clk_cpu_pre_rate_change(struct meson_clk_cpu *clk_cpu, + struct clk_notifier_data *ndata) +{ + u32 cpu_clk_cntl; + + /* switch MUX1 to xtal */ + cpu_clk_cntl = readl(clk_cpu->base + clk_cpu->reg_off + + MESON_CPU_CLK_CNTL); + cpu_clk_cntl &= ~MESON_CPU_CLK_MUX1; + writel(cpu_clk_cntl, clk_cpu->base + clk_cpu->reg_off + + MESON_CPU_CLK_CNTL); + udelay(100); + + /* switch MUX2 to sys-pll */ + cpu_clk_cntl |= MESON_CPU_CLK_MUX2; + writel(cpu_clk_cntl, clk_cpu->base + clk_cpu->reg_off + + MESON_CPU_CLK_CNTL); + + return 0; +} + +static int meson_clk_cpu_post_rate_change(struct meson_clk_cpu *clk_cpu, + struct clk_notifier_data *ndata) +{ + u32 cpu_clk_cntl; + + /* switch MUX1 to divisors' output */ + cpu_clk_cntl = readl(clk_cpu->base + clk_cpu->reg_off + + MESON_CPU_CLK_CNTL); + cpu_clk_cntl |= MESON_CPU_CLK_MUX1; + writel(cpu_clk_cntl, clk_cpu->base + clk_cpu->reg_off + + MESON_CPU_CLK_CNTL); + udelay(100); + + return 0; +} + +/* + * This clock notifier is called when the frequency of the of the parent + * PLL clock is to be changed. We use the xtal input as temporary parent + * while the PLL frequency is stabilized. + */ +static int meson_clk_cpu_notifier_cb(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct clk_notifier_data *ndata = data; + struct meson_clk_cpu *clk_cpu = to_meson_clk_cpu_nb(nb); + int ret = 0; + + if (event == PRE_RATE_CHANGE) + ret = meson_clk_cpu_pre_rate_change(clk_cpu, ndata); + else if (event == POST_RATE_CHANGE) + ret = meson_clk_cpu_post_rate_change(clk_cpu, ndata); + + return notifier_from_errno(ret); +} + +static const struct clk_ops meson_clk_cpu_ops = { + .recalc_rate = meson_clk_cpu_recalc_rate, + .round_rate = meson_clk_cpu_round_rate, + .set_rate = meson_clk_cpu_set_rate, +}; + +struct clk *meson_clk_register_cpu(const struct clk_conf *clk_conf, + void __iomem *reg_base, + spinlock_t *lock) +{ + struct clk *clk; + struct clk *pclk; + struct meson_clk_cpu *clk_cpu; + struct clk_init_data init; + int ret; + + clk_cpu = kzalloc(sizeof(*clk_cpu), GFP_KERNEL); + if (!clk_cpu) + return ERR_PTR(-ENOMEM); + + clk_cpu->base = reg_base; + clk_cpu->reg_off = clk_conf->reg_off; + clk_cpu->div_table = clk_conf->conf.div_table; + clk_cpu->clk_nb.notifier_call = meson_clk_cpu_notifier_cb; + + init.name = clk_conf->clk_name; + init.ops = &meson_clk_cpu_ops; + init.flags = clk_conf->flags | CLK_GET_RATE_NOCACHE; + init.flags |= CLK_SET_RATE_PARENT; + init.parent_names = clk_conf->clks_parent; + init.num_parents = 1; + + clk_cpu->hw.init = &init; + + pclk = __clk_lookup(clk_conf->clks_parent[0]); + if (!pclk) { + pr_err("%s: could not lookup parent clock %s\n", + __func__, clk_conf->clks_parent[0]); + ret = -EINVAL; + goto free_clk; + } + + ret = clk_notifier_register(pclk, &clk_cpu->clk_nb); + if (ret) { + pr_err("%s: failed to register clock notifier for %s\n", + __func__, clk_conf->clk_name); + goto free_clk; + } + + clk = clk_register(NULL, &clk_cpu->hw); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + goto unregister_clk_nb; + } + + return clk; + +unregister_clk_nb: + clk_notifier_unregister(pclk, &clk_cpu->clk_nb); +free_clk: + kfree(clk_cpu); + + return ERR_PTR(ret); +} + diff --git a/drivers/clk/meson/clk-pll.c b/drivers/clk/meson/clk-pll.c new file mode 100644 index 000000000000..664edf0708ea --- /dev/null +++ b/drivers/clk/meson/clk-pll.c @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2015 Endless Mobile, Inc. + * Author: Carlo Caione + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/* + * In the most basic form, a Meson PLL is composed as follows: + * + * PLL + * +------------------------------+ + * | | + * in -----[ /N ]---[ *M ]---[ >>OD ]----->> out + * | ^ ^ | + * +------------------------------+ + * | | + * FREF VCO + * + * out = (in * M / N) >> OD + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clkc.h" + +#define MESON_PLL_RESET BIT(29) +#define MESON_PLL_LOCK BIT(31) + +struct meson_clk_pll { + struct clk_hw hw; + void __iomem *base; + struct pll_conf *conf; + unsigned int rate_count; + spinlock_t *lock; +}; +#define to_meson_clk_pll(_hw) container_of(_hw, struct meson_clk_pll, hw) + +static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct meson_clk_pll *pll = to_meson_clk_pll(hw); + struct parm *p; + unsigned long parent_rate_mhz = parent_rate / 1000000; + unsigned long rate_mhz; + u16 n, m, od; + u32 reg; + + p = &pll->conf->n; + reg = readl(pll->base + p->reg_off); + n = PARM_GET(p->width, p->shift, reg); + + p = &pll->conf->m; + reg = readl(pll->base + p->reg_off); + m = PARM_GET(p->width, p->shift, reg); + + p = &pll->conf->od; + reg = readl(pll->base + p->reg_off); + od = PARM_GET(p->width, p->shift, reg); + + rate_mhz = (parent_rate_mhz * m / n) >> od; + + return rate_mhz * 1000000; +} + +static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct meson_clk_pll *pll = to_meson_clk_pll(hw); + const struct pll_rate_table *rate_table = pll->conf->rate_table; + int i; + + for (i = 0; i < pll->rate_count; i++) { + if (rate <= rate_table[i].rate) + return rate_table[i].rate; + } + + /* else return the smallest value */ + return rate_table[0].rate; +} + +static const struct pll_rate_table *meson_clk_get_pll_settings(struct meson_clk_pll *pll, + unsigned long rate) +{ + const struct pll_rate_table *rate_table = pll->conf->rate_table; + int i; + + for (i = 0; i < pll->rate_count; i++) { + if (rate == rate_table[i].rate) + return &rate_table[i]; + } + return NULL; +} + +static int meson_clk_pll_wait_lock(struct meson_clk_pll *pll, + struct parm *p_n) +{ + int delay = 24000000; + u32 reg; + + while (delay > 0) { + reg = readl(pll->base + p_n->reg_off); + + if (reg & MESON_PLL_LOCK) + return 0; + delay--; + } + return -ETIMEDOUT; +} + +static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct meson_clk_pll *pll = to_meson_clk_pll(hw); + struct parm *p; + const struct pll_rate_table *rate_set; + unsigned long old_rate; + int ret = 0; + u32 reg; + + if (parent_rate == 0 || rate == 0) + return -EINVAL; + + old_rate = rate; + + rate_set = meson_clk_get_pll_settings(pll, rate); + if (!rate_set) + return -EINVAL; + + /* PLL reset */ + p = &pll->conf->n; + reg = readl(pll->base + p->reg_off); + writel(reg | MESON_PLL_RESET, pll->base + p->reg_off); + + reg = PARM_SET(p->width, p->shift, reg, rate_set->n); + writel(reg, pll->base + p->reg_off); + + p = &pll->conf->m; + reg = readl(pll->base + p->reg_off); + reg = PARM_SET(p->width, p->shift, reg, rate_set->m); + writel(reg, pll->base + p->reg_off); + + p = &pll->conf->od; + reg = readl(pll->base + p->reg_off); + reg = PARM_SET(p->width, p->shift, reg, rate_set->od); + writel(reg, pll->base + p->reg_off); + + p = &pll->conf->n; + ret = meson_clk_pll_wait_lock(pll, p); + if (ret) { + pr_warn("%s: pll did not lock, trying to restore old rate %lu\n", + __func__, old_rate); + meson_clk_pll_set_rate(hw, old_rate, parent_rate); + } + + return ret; +} + +static const struct clk_ops meson_clk_pll_ops = { + .recalc_rate = meson_clk_pll_recalc_rate, + .round_rate = meson_clk_pll_round_rate, + .set_rate = meson_clk_pll_set_rate, +}; + +static const struct clk_ops meson_clk_pll_ro_ops = { + .recalc_rate = meson_clk_pll_recalc_rate, +}; + +struct clk *meson_clk_register_pll(const struct clk_conf *clk_conf, + void __iomem *reg_base, + spinlock_t *lock) +{ + struct clk *clk; + struct meson_clk_pll *clk_pll; + struct clk_init_data init; + + clk_pll = kzalloc(sizeof(*clk_pll), GFP_KERNEL); + if (!clk_pll) + return ERR_PTR(-ENOMEM); + + clk_pll->base = reg_base + clk_conf->reg_off; + clk_pll->lock = lock; + clk_pll->conf = clk_conf->conf.pll; + + init.name = clk_conf->clk_name; + init.flags = clk_conf->flags | CLK_GET_RATE_NOCACHE; + + init.parent_names = &clk_conf->clks_parent[0]; + init.num_parents = 1; + init.ops = &meson_clk_pll_ro_ops; + + /* If no rate_table is specified we assume the PLL is read-only */ + if (clk_pll->conf->rate_table) { + int len; + + for (len = 0; clk_pll->conf->rate_table[len].rate != 0; ) + len++; + + clk_pll->rate_count = len; + init.ops = &meson_clk_pll_ops; + } + + clk_pll->hw.init = &init; + + clk = clk_register(NULL, &clk_pll->hw); + if (IS_ERR(clk)) + kfree(clk_pll); + + return clk; +} diff --git a/drivers/clk/meson/clkc.c b/drivers/clk/meson/clkc.c new file mode 100644 index 000000000000..b8c511c5e7a7 --- /dev/null +++ b/drivers/clk/meson/clkc.c @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2015 Endless Mobile, Inc. + * Author: Carlo Caione + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include +#include +#include +#include + +#include "clkc.h" + +static DEFINE_SPINLOCK(clk_lock); + +static struct clk **clks; +static struct clk_onecell_data clk_data; + +struct clk ** __init meson_clk_init(struct device_node *np, + unsigned long nr_clks) +{ + clks = kcalloc(nr_clks, sizeof(*clks), GFP_KERNEL); + if (!clks) + return ERR_PTR(-ENOMEM); + + clk_data.clks = clks; + clk_data.clk_num = nr_clks; + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + + return clks; +} + +static void meson_clk_add_lookup(struct clk *clk, unsigned int id) +{ + if (clks && id) + clks[id] = clk; +} + +static struct clk * __init +meson_clk_register_composite(const struct clk_conf *clk_conf, + void __iomem *clk_base) +{ + struct clk *clk; + struct clk_mux *mux = NULL; + struct clk_divider *div = NULL; + struct clk_gate *gate = NULL; + const struct clk_ops *mux_ops = NULL; + const struct composite_conf *composite_conf; + + composite_conf = clk_conf->conf.composite; + + if (clk_conf->num_parents > 1) { + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) + return ERR_PTR(-ENOMEM); + + mux->reg = clk_base + clk_conf->reg_off + + composite_conf->mux_parm.reg_off; + mux->shift = composite_conf->mux_parm.shift; + mux->mask = BIT(composite_conf->mux_parm.width) - 1; + mux->flags = composite_conf->mux_flags; + mux->lock = &clk_lock; + mux->table = composite_conf->mux_table; + mux_ops = (composite_conf->mux_flags & CLK_MUX_READ_ONLY) ? + &clk_mux_ro_ops : &clk_mux_ops; + } + + if (MESON_PARM_APPLICABLE(&composite_conf->div_parm)) { + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) { + clk = ERR_PTR(-ENOMEM); + goto error; + } + + div->reg = clk_base + clk_conf->reg_off + + composite_conf->div_parm.reg_off; + div->shift = composite_conf->div_parm.shift; + div->width = composite_conf->div_parm.width; + div->lock = &clk_lock; + div->flags = composite_conf->div_flags; + div->table = composite_conf->div_table; + } + + if (MESON_PARM_APPLICABLE(&composite_conf->gate_parm)) { + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) { + clk = ERR_PTR(-ENOMEM); + goto error; + } + + gate->reg = clk_base + clk_conf->reg_off + + composite_conf->div_parm.reg_off; + gate->bit_idx = composite_conf->gate_parm.shift; + gate->flags = composite_conf->gate_flags; + gate->lock = &clk_lock; + } + + clk = clk_register_composite(NULL, clk_conf->clk_name, + clk_conf->clks_parent, + clk_conf->num_parents, + mux ? &mux->hw : NULL, mux_ops, + div ? &div->hw : NULL, &clk_divider_ops, + gate ? &gate->hw : NULL, &clk_gate_ops, + clk_conf->flags); + if (IS_ERR(clk)) + goto error; + + return clk; + +error: + kfree(gate); + kfree(div); + kfree(mux); + + return clk; +} + +static struct clk * __init +meson_clk_register_fixed_factor(const struct clk_conf *clk_conf, + void __iomem *clk_base) +{ + struct clk *clk; + const struct fixed_fact_conf *fixed_fact_conf; + const struct parm *p; + unsigned int mult, div; + u32 reg; + + fixed_fact_conf = &clk_conf->conf.fixed_fact; + + mult = clk_conf->conf.fixed_fact.mult; + div = clk_conf->conf.fixed_fact.div; + + if (!mult) { + mult = 1; + p = &fixed_fact_conf->mult_parm; + if (MESON_PARM_APPLICABLE(p)) { + reg = readl(clk_base + clk_conf->reg_off + p->reg_off); + mult = PARM_GET(p->width, p->shift, reg); + } + } + + if (!div) { + div = 1; + p = &fixed_fact_conf->div_parm; + if (MESON_PARM_APPLICABLE(p)) { + reg = readl(clk_base + clk_conf->reg_off + p->reg_off); + mult = PARM_GET(p->width, p->shift, reg); + } + } + + clk = clk_register_fixed_factor(NULL, + clk_conf->clk_name, + clk_conf->clks_parent[0], + clk_conf->flags, + mult, div); + + return clk; +} + +static struct clk * __init +meson_clk_register_fixed_rate(const struct clk_conf *clk_conf, + void __iomem *clk_base) +{ + struct clk *clk; + const struct fixed_rate_conf *fixed_rate_conf; + const struct parm *r; + unsigned long rate; + u32 reg; + + fixed_rate_conf = &clk_conf->conf.fixed_rate; + rate = fixed_rate_conf->rate; + + if (!rate) { + r = &fixed_rate_conf->rate_parm; + reg = readl(clk_base + clk_conf->reg_off + r->reg_off); + rate = PARM_GET(r->width, r->shift, reg); + } + + rate *= 1000000; + + clk = clk_register_fixed_rate(NULL, + clk_conf->clk_name, + clk_conf->num_parents + ? clk_conf->clks_parent[0] : NULL, + clk_conf->flags, rate); + + return clk; +} + +void __init meson_clk_register_clks(const struct clk_conf *clk_confs, + size_t nr_confs, + void __iomem *clk_base) +{ + unsigned int i; + struct clk *clk = NULL; + + for (i = 0; i < nr_confs; i++) { + const struct clk_conf *clk_conf = &clk_confs[i]; + + switch (clk_conf->clk_type) { + case CLK_FIXED_RATE: + clk = meson_clk_register_fixed_rate(clk_conf, + clk_base); + break; + case CLK_FIXED_FACTOR: + clk = meson_clk_register_fixed_factor(clk_conf, + clk_base); + break; + case CLK_COMPOSITE: + clk = meson_clk_register_composite(clk_conf, + clk_base); + break; + case CLK_CPU: + clk = meson_clk_register_cpu(clk_conf, clk_base, + &clk_lock); + break; + case CLK_PLL: + clk = meson_clk_register_pll(clk_conf, clk_base, + &clk_lock); + break; + default: + clk = NULL; + } + + if (!clk) { + pr_err("%s: unknown clock type %d\n", __func__, + clk_conf->clk_type); + continue; + } + + if (IS_ERR(clk)) { + pr_warn("%s: Unable to create %s clock\n", __func__, + clk_conf->clk_name); + continue; + } + + meson_clk_add_lookup(clk, clk_conf->clk_id); + } +} diff --git a/drivers/clk/meson/clkc.h b/drivers/clk/meson/clkc.h new file mode 100644 index 000000000000..609ae92cc13f --- /dev/null +++ b/drivers/clk/meson/clkc.h @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2015 Endless Mobile, Inc. + * Author: Carlo Caione + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef __CLKC_H +#define __CLKC_H + +#define PMASK(width) GENMASK(width - 1, 0) +#define SETPMASK(width, shift) GENMASK(shift + width - 1, shift) +#define CLRPMASK(width, shift) (~SETPMASK(width, shift)) + +#define PARM_GET(width, shift, reg) \ + (((reg) & SETPMASK(width, shift)) >> (shift)) +#define PARM_SET(width, shift, reg, val) \ + (((reg) & CLRPMASK(width, shift)) | (val << (shift))) + +#define MESON_PARM_APPLICABLE(p) (!!((p)->width)) + +struct parm { + u16 reg_off; + u8 shift; + u8 width; +}; +#define PARM(_r, _s, _w) \ + { \ + .reg_off = (_r), \ + .shift = (_s), \ + .width = (_w), \ + } \ + +struct pll_rate_table { + unsigned long rate; + u16 m; + u16 n; + u16 od; +}; +#define PLL_RATE(_r, _m, _n, _od) \ + { \ + .rate = (_r), \ + .m = (_m), \ + .n = (_n), \ + .od = (_od), \ + } \ + +struct pll_conf { + const struct pll_rate_table *rate_table; + struct parm m; + struct parm n; + struct parm od; +}; + +struct fixed_fact_conf { + unsigned int div; + unsigned int mult; + struct parm div_parm; + struct parm mult_parm; +}; + +struct fixed_rate_conf { + unsigned long rate; + struct parm rate_parm; +}; + +struct composite_conf { + struct parm mux_parm; + struct parm div_parm; + struct parm gate_parm; + struct clk_div_table *div_table; + u32 *mux_table; + u8 mux_flags; + u8 div_flags; + u8 gate_flags; +}; + +#define PNAME(x) static const char *x[] + +enum clk_type { + CLK_FIXED_FACTOR, + CLK_FIXED_RATE, + CLK_COMPOSITE, + CLK_CPU, + CLK_PLL, +}; + +struct clk_conf { + u16 reg_off; + enum clk_type clk_type; + unsigned int clk_id; + const char *clk_name; + const char **clks_parent; + int num_parents; + unsigned long flags; + union { + struct fixed_fact_conf fixed_fact; + struct fixed_rate_conf fixed_rate; + const struct composite_conf *composite; + struct pll_conf *pll; + const struct clk_div_table *div_table; + } conf; +}; + +#define FIXED_RATE_P(_ro, _ci, _cn, _f, _c) \ + { \ + .reg_off = (_ro), \ + .clk_type = CLK_FIXED_RATE, \ + .clk_id = (_ci), \ + .clk_name = (_cn), \ + .flags = (_f), \ + .conf.fixed_rate.rate_parm = _c, \ + } \ + +#define FIXED_RATE(_ci, _cn, _f, _r) \ + { \ + .clk_type = CLK_FIXED_RATE, \ + .clk_id = (_ci), \ + .clk_name = (_cn), \ + .flags = (_f), \ + .conf.fixed_rate.rate = (_r), \ + } \ + +#define PLL(_ro, _ci, _cn, _cp, _f, _c) \ + { \ + .reg_off = (_ro), \ + .clk_type = CLK_PLL, \ + .clk_id = (_ci), \ + .clk_name = (_cn), \ + .clks_parent = (_cp), \ + .num_parents = ARRAY_SIZE(_cp), \ + .flags = (_f), \ + .conf.pll = (_c), \ + } \ + +#define FIXED_FACTOR_DIV(_ci, _cn, _cp, _f, _d) \ + { \ + .clk_type = CLK_FIXED_FACTOR, \ + .clk_id = (_ci), \ + .clk_name = (_cn), \ + .clks_parent = (_cp), \ + .num_parents = ARRAY_SIZE(_cp), \ + .conf.fixed_fact.div = (_d), \ + } \ + +#define CPU(_ro, _ci, _cn, _cp, _dt) \ + { \ + .reg_off = (_ro), \ + .clk_type = CLK_CPU, \ + .clk_id = (_ci), \ + .clk_name = (_cn), \ + .clks_parent = (_cp), \ + .num_parents = ARRAY_SIZE(_cp), \ + .conf.div_table = (_dt), \ + } \ + +#define COMPOSITE(_ro, _ci, _cn, _cp, _f, _c) \ + { \ + .reg_off = (_ro), \ + .clk_type = CLK_COMPOSITE, \ + .clk_id = (_ci), \ + .clk_name = (_cn), \ + .clks_parent = (_cp), \ + .num_parents = ARRAY_SIZE(_cp), \ + .flags = (_f), \ + .conf.composite = (_c), \ + } \ + +struct clk **meson_clk_init(struct device_node *np, unsigned long nr_clks); +void meson_clk_register_clks(const struct clk_conf *clk_confs, + unsigned int nr_confs, void __iomem *clk_base); +struct clk *meson_clk_register_cpu(const struct clk_conf *clk_conf, + void __iomem *reg_base, spinlock_t *lock); +struct clk *meson_clk_register_pll(const struct clk_conf *clk_conf, + void __iomem *reg_base, spinlock_t *lock); + +#endif /* __CLKC_H */ diff --git a/drivers/clk/meson/meson8b-clkc.c b/drivers/clk/meson/meson8b-clkc.c new file mode 100644 index 000000000000..61f6d55c4ac7 --- /dev/null +++ b/drivers/clk/meson/meson8b-clkc.c @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2015 Endless Mobile, Inc. + * Author: Carlo Caione + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include "clkc.h" + +#define MESON8B_REG_CTL0_ADDR 0x0000 +#define MESON8B_REG_SYS_CPU_CNTL1 0x015c +#define MESON8B_REG_HHI_MPEG 0x0174 +#define MESON8B_REG_MALI 0x01b0 +#define MESON8B_REG_PLL_FIXED 0x0280 +#define MESON8B_REG_PLL_SYS 0x0300 +#define MESON8B_REG_PLL_VID 0x0320 + +static const struct pll_rate_table sys_pll_rate_table[] = { + PLL_RATE(312000000, 52, 1, 2), + PLL_RATE(336000000, 56, 1, 2), + PLL_RATE(360000000, 60, 1, 2), + PLL_RATE(384000000, 64, 1, 2), + PLL_RATE(408000000, 68, 1, 2), + PLL_RATE(432000000, 72, 1, 2), + PLL_RATE(456000000, 76, 1, 2), + PLL_RATE(480000000, 80, 1, 2), + PLL_RATE(504000000, 84, 1, 2), + PLL_RATE(528000000, 88, 1, 2), + PLL_RATE(552000000, 92, 1, 2), + PLL_RATE(576000000, 96, 1, 2), + PLL_RATE(600000000, 50, 1, 1), + PLL_RATE(624000000, 52, 1, 1), + PLL_RATE(648000000, 54, 1, 1), + PLL_RATE(672000000, 56, 1, 1), + PLL_RATE(696000000, 58, 1, 1), + PLL_RATE(720000000, 60, 1, 1), + PLL_RATE(744000000, 62, 1, 1), + PLL_RATE(768000000, 64, 1, 1), + PLL_RATE(792000000, 66, 1, 1), + PLL_RATE(816000000, 68, 1, 1), + PLL_RATE(840000000, 70, 1, 1), + PLL_RATE(864000000, 72, 1, 1), + PLL_RATE(888000000, 74, 1, 1), + PLL_RATE(912000000, 76, 1, 1), + PLL_RATE(936000000, 78, 1, 1), + PLL_RATE(960000000, 80, 1, 1), + PLL_RATE(984000000, 82, 1, 1), + PLL_RATE(1008000000, 84, 1, 1), + PLL_RATE(1032000000, 86, 1, 1), + PLL_RATE(1056000000, 88, 1, 1), + PLL_RATE(1080000000, 90, 1, 1), + PLL_RATE(1104000000, 92, 1, 1), + PLL_RATE(1128000000, 94, 1, 1), + PLL_RATE(1152000000, 96, 1, 1), + PLL_RATE(1176000000, 98, 1, 1), + PLL_RATE(1200000000, 50, 1, 0), + PLL_RATE(1224000000, 51, 1, 0), + PLL_RATE(1248000000, 52, 1, 0), + PLL_RATE(1272000000, 53, 1, 0), + PLL_RATE(1296000000, 54, 1, 0), + PLL_RATE(1320000000, 55, 1, 0), + PLL_RATE(1344000000, 56, 1, 0), + PLL_RATE(1368000000, 57, 1, 0), + PLL_RATE(1392000000, 58, 1, 0), + PLL_RATE(1416000000, 59, 1, 0), + PLL_RATE(1440000000, 60, 1, 0), + PLL_RATE(1464000000, 61, 1, 0), + PLL_RATE(1488000000, 62, 1, 0), + PLL_RATE(1512000000, 63, 1, 0), + PLL_RATE(1536000000, 64, 1, 0), + { /* sentinel */ }, +}; + +static const struct clk_div_table cpu_div_table[] = { + { .val = 1, .div = 1 }, + { .val = 2, .div = 2 }, + { .val = 3, .div = 3 }, + { .val = 2, .div = 4 }, + { .val = 3, .div = 6 }, + { .val = 4, .div = 8 }, + { .val = 5, .div = 10 }, + { .val = 6, .div = 12 }, + { .val = 7, .div = 14 }, + { .val = 8, .div = 16 }, + { /* sentinel */ }, +}; + +PNAME(p_xtal) = { "xtal" }; +PNAME(p_fclk_div) = { "fixed_pll" }; +PNAME(p_cpu_clk) = { "sys_pll" }; +PNAME(p_clk81) = { "fclk_div3", "fclk_div4", "fclk_div5" }; +PNAME(p_mali) = { "fclk_div3", "fclk_div4", "fclk_div5", + "fclk_div7", "zero" }; + +static u32 mux_table_clk81[] = { 6, 5, 7 }; +static u32 mux_table_mali[] = { 6, 5, 7, 4, 0 }; + +static struct pll_conf pll_confs = { + .m = PARM(0x00, 0, 9), + .n = PARM(0x00, 9, 5), + .od = PARM(0x00, 16, 2), +}; + +static struct pll_conf sys_pll_conf = { + .m = PARM(0x00, 0, 9), + .n = PARM(0x00, 9, 5), + .od = PARM(0x00, 16, 2), + .rate_table = sys_pll_rate_table, +}; + +static const struct composite_conf clk81_conf __initconst = { + .mux_table = mux_table_clk81, + .mux_flags = CLK_MUX_READ_ONLY, + .mux_parm = PARM(0x00, 12, 3), + .div_parm = PARM(0x00, 0, 7), + .gate_parm = PARM(0x00, 7, 1), +}; + +static const struct composite_conf mali_conf __initconst = { + .mux_table = mux_table_mali, + .mux_parm = PARM(0x00, 9, 3), + .div_parm = PARM(0x00, 0, 7), + .gate_parm = PARM(0x00, 8, 1), +}; + +static const struct clk_conf meson8b_xtal_conf __initconst = + FIXED_RATE_P(MESON8B_REG_CTL0_ADDR, CLKID_XTAL, "xtal", + CLK_IS_ROOT, PARM(0x00, 4, 7)); + +static const struct clk_conf meson8b_clk_confs[] __initconst = { + FIXED_RATE(CLKID_ZERO, "zero", CLK_IS_ROOT, 0), + PLL(MESON8B_REG_PLL_FIXED, CLKID_PLL_FIXED, "fixed_pll", + p_xtal, 0, &pll_confs), + PLL(MESON8B_REG_PLL_VID, CLKID_PLL_VID, "vid_pll", + p_xtal, 0, &pll_confs), + PLL(MESON8B_REG_PLL_SYS, CLKID_PLL_SYS, "sys_pll", + p_xtal, 0, &sys_pll_conf), + FIXED_FACTOR_DIV(CLKID_FCLK_DIV2, "fclk_div2", p_fclk_div, 0, 2), + FIXED_FACTOR_DIV(CLKID_FCLK_DIV3, "fclk_div3", p_fclk_div, 0, 3), + FIXED_FACTOR_DIV(CLKID_FCLK_DIV4, "fclk_div4", p_fclk_div, 0, 4), + FIXED_FACTOR_DIV(CLKID_FCLK_DIV5, "fclk_div5", p_fclk_div, 0, 5), + FIXED_FACTOR_DIV(CLKID_FCLK_DIV7, "fclk_div7", p_fclk_div, 0, 7), + CPU(MESON8B_REG_SYS_CPU_CNTL1, CLKID_CPUCLK, "a5_clk", p_cpu_clk, + cpu_div_table), + COMPOSITE(MESON8B_REG_HHI_MPEG, CLKID_CLK81, "clk81", p_clk81, + CLK_SET_RATE_NO_REPARENT | CLK_IGNORE_UNUSED, &clk81_conf), + COMPOSITE(MESON8B_REG_MALI, CLKID_MALI, "mali", p_mali, + CLK_IGNORE_UNUSED, &mali_conf), +}; + +static void __init meson8b_clkc_init(struct device_node *np) +{ + void __iomem *clk_base; + + if (!meson_clk_init(np, CLK_NR_CLKS)) + return; + + /* XTAL */ + clk_base = of_iomap(np, 0); + if (!clk_base) { + pr_err("%s: Unable to map xtal base\n", __func__); + return; + } + + meson_clk_register_clks(&meson8b_xtal_conf, 1, clk_base); + iounmap(clk_base); + + /* Generic clocks and PLLs */ + clk_base = of_iomap(np, 1); + if (!clk_base) { + pr_err("%s: Unable to map clk base\n", __func__); + return; + } + + meson_clk_register_clks(meson8b_clk_confs, + ARRAY_SIZE(meson8b_clk_confs), + clk_base); +} +CLK_OF_DECLARE(meson8b_clock, "amlogic,meson8b-clkc", meson8b_clkc_init); diff --git a/drivers/clk/mmp/Makefile b/drivers/clk/mmp/Makefile index 3caaf7cc169c..9d4bc41e4239 100644 --- a/drivers/clk/mmp/Makefile +++ b/drivers/clk/mmp/Makefile @@ -12,3 +12,5 @@ obj-$(CONFIG_MACH_MMP2_DT) += clk-of-mmp2.o obj-$(CONFIG_CPU_PXA168) += clk-pxa168.o obj-$(CONFIG_CPU_PXA910) += clk-pxa910.o obj-$(CONFIG_CPU_MMP2) += clk-mmp2.o + +obj-y += clk-of-pxa1928.o diff --git a/drivers/clk/mmp/clk-apbc.c b/drivers/clk/mmp/clk-apbc.c index d14120eaa71f..09d41c717c52 100644 --- a/drivers/clk/mmp/clk-apbc.c +++ b/drivers/clk/mmp/clk-apbc.c @@ -115,7 +115,7 @@ static void clk_apbc_unprepare(struct clk_hw *hw) spin_unlock_irqrestore(apbc->lock, flags); } -struct clk_ops clk_apbc_ops = { +static struct clk_ops clk_apbc_ops = { .prepare = clk_apbc_prepare, .unprepare = clk_apbc_unprepare, }; diff --git a/drivers/clk/mmp/clk-apmu.c b/drivers/clk/mmp/clk-apmu.c index abe182b2377f..cdcf2d7f321e 100644 --- a/drivers/clk/mmp/clk-apmu.c +++ b/drivers/clk/mmp/clk-apmu.c @@ -61,7 +61,7 @@ static void clk_apmu_disable(struct clk_hw *hw) spin_unlock_irqrestore(apmu->lock, flags); } -struct clk_ops clk_apmu_ops = { +static struct clk_ops clk_apmu_ops = { .enable = clk_apmu_enable, .disable = clk_apmu_disable, }; diff --git a/drivers/clk/mmp/clk-mmp2.c b/drivers/clk/mmp/clk-mmp2.c index 5c90a4230fa3..09d2832fbd78 100644 --- a/drivers/clk/mmp/clk-mmp2.c +++ b/drivers/clk/mmp/clk-mmp2.c @@ -63,10 +63,8 @@ static struct mmp_clk_factor_masks uart_factor_masks = { }; static struct mmp_clk_factor_tbl uart_factor_tbl[] = { - {.num = 14634, .den = 2165}, /*14.745MHZ */ + {.num = 8125, .den = 1536}, /*14.745MHZ */ {.num = 3521, .den = 689}, /*19.23MHZ */ - {.num = 9679, .den = 5728}, /*58.9824MHZ */ - {.num = 15850, .den = 9451}, /*59.429MHZ */ }; static const char *uart_parent[] = {"uart_pll", "vctcxo"}; diff --git a/drivers/clk/mmp/clk-of-mmp2.c b/drivers/clk/mmp/clk-of-mmp2.c index 2cbc2b43ae52..251533d87c65 100644 --- a/drivers/clk/mmp/clk-of-mmp2.c +++ b/drivers/clk/mmp/clk-of-mmp2.c @@ -30,6 +30,7 @@ #define APBC_TWSI4 0x7c #define APBC_TWSI5 0x80 #define APBC_KPC 0x18 +#define APBC_TIMER 0x24 #define APBC_UART0 0x2c #define APBC_UART1 0x30 #define APBC_UART2 0x34 @@ -98,10 +99,8 @@ static struct mmp_clk_factor_masks uart_factor_masks = { }; static struct mmp_clk_factor_tbl uart_factor_tbl[] = { - {.num = 14634, .den = 2165}, /*14.745MHZ */ + {.num = 8125, .den = 1536}, /*14.745MHZ */ {.num = 3521, .den = 689}, /*19.23MHZ */ - {.num = 9679, .den = 5728}, /*58.9824MHZ */ - {.num = 15850, .den = 9451}, /*59.429MHZ */ }; static void mmp2_pll_init(struct mmp2_clk_unit *pxa_unit) @@ -134,6 +133,9 @@ static DEFINE_SPINLOCK(ssp2_lock); static DEFINE_SPINLOCK(ssp3_lock); static const char *ssp_parent_names[] = {"vctcxo_4", "vctcxo_2", "vctcxo", "pll1_16"}; +static DEFINE_SPINLOCK(timer_lock); +static const char *timer_parent_names[] = {"clk32", "vctcxo_2", "vctcxo_4", "vctcxo"}; + static DEFINE_SPINLOCK(reset_lock); static struct mmp_param_mux_clk apbc_mux_clks[] = { @@ -145,6 +147,7 @@ static struct mmp_param_mux_clk apbc_mux_clks[] = { {0, "ssp1_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP1, 4, 3, 0, &ssp1_lock}, {0, "ssp2_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP2, 4, 3, 0, &ssp2_lock}, {0, "ssp3_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP3, 4, 3, 0, &ssp3_lock}, + {0, "timer_mux", timer_parent_names, ARRAY_SIZE(timer_parent_names), CLK_SET_RATE_PARENT, APBC_TIMER, 4, 3, 0, &timer_lock}, }; static struct mmp_param_gate_clk apbc_gate_clks[] = { @@ -170,6 +173,7 @@ static struct mmp_param_gate_clk apbc_gate_clks[] = { {MMP2_CLK_SSP1, "ssp1_clk", "ssp1_mux", CLK_SET_RATE_PARENT, APBC_SSP1, 0x7, 0x3, 0x0, 0, &ssp1_lock}, {MMP2_CLK_SSP2, "ssp2_clk", "ssp2_mux", CLK_SET_RATE_PARENT, APBC_SSP2, 0x7, 0x3, 0x0, 0, &ssp2_lock}, {MMP2_CLK_SSP3, "ssp3_clk", "ssp3_mux", CLK_SET_RATE_PARENT, APBC_SSP3, 0x7, 0x3, 0x0, 0, &ssp3_lock}, + {MMP2_CLK_TIMER, "timer_clk", "timer_mux", CLK_SET_RATE_PARENT, APBC_TIMER, 0x7, 0x3, 0x0, 0, &timer_lock}, }; static void mmp2_apb_periph_clk_init(struct mmp2_clk_unit *pxa_unit) diff --git a/drivers/clk/mmp/clk-of-pxa168.c b/drivers/clk/mmp/clk-of-pxa168.c index 5b1810dc4bd2..64eaf4141c69 100644 --- a/drivers/clk/mmp/clk-of-pxa168.c +++ b/drivers/clk/mmp/clk-of-pxa168.c @@ -32,6 +32,7 @@ #define APBC_PWM1 0x10 #define APBC_PWM2 0x14 #define APBC_PWM3 0x18 +#define APBC_TIMER 0x34 #define APBC_SSP0 0x81c #define APBC_SSP1 0x820 #define APBC_SSP2 0x84c @@ -58,6 +59,7 @@ static struct mmp_param_fixed_rate_clk fixed_rate_clks[] = { {PXA168_CLK_CLK32, "clk32", NULL, CLK_IS_ROOT, 32768}, {PXA168_CLK_VCTCXO, "vctcxo", NULL, CLK_IS_ROOT, 26000000}, {PXA168_CLK_PLL1, "pll1", NULL, CLK_IS_ROOT, 624000000}, + {PXA168_CLK_USB_PLL, "usb_pll", NULL, CLK_IS_ROOT, 480000000}, }; static struct mmp_param_fixed_factor_clk fixed_factor_clks[] = { @@ -70,6 +72,7 @@ static struct mmp_param_fixed_factor_clk fixed_factor_clks[] = { {PXA168_CLK_PLL1_24, "pll1_24", "pll1_12", 1, 2, 0}, {PXA168_CLK_PLL1_48, "pll1_48", "pll1_24", 1, 2, 0}, {PXA168_CLK_PLL1_96, "pll1_96", "pll1_48", 1, 2, 0}, + {PXA168_CLK_PLL1_192, "pll1_192", "pll1_96", 1, 2, 0}, {PXA168_CLK_PLL1_13, "pll1_13", "pll1", 1, 13, 0}, {PXA168_CLK_PLL1_13_1_5, "pll1_13_1_5", "pll1_13", 2, 3, 0}, {PXA168_CLK_PLL1_2_1_5, "pll1_2_1_5", "pll1_2", 2, 3, 0}, @@ -119,6 +122,9 @@ static DEFINE_SPINLOCK(ssp3_lock); static DEFINE_SPINLOCK(ssp4_lock); static const char *ssp_parent_names[] = {"pll1_96", "pll1_48", "pll1_24", "pll1_12"}; +static DEFINE_SPINLOCK(timer_lock); +static const char *timer_parent_names[] = {"pll1_48", "clk32", "pll1_96", "pll1_192"}; + static DEFINE_SPINLOCK(reset_lock); static struct mmp_param_mux_clk apbc_mux_clks[] = { @@ -130,6 +136,7 @@ static struct mmp_param_mux_clk apbc_mux_clks[] = { {0, "ssp2_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP2, 4, 3, 0, &ssp2_lock}, {0, "ssp3_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP3, 4, 3, 0, &ssp3_lock}, {0, "ssp4_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP4, 4, 3, 0, &ssp4_lock}, + {0, "timer_mux", timer_parent_names, ARRAY_SIZE(timer_parent_names), CLK_SET_RATE_PARENT, APBC_TIMER, 4, 3, 0, &timer_lock}, }; static struct mmp_param_gate_clk apbc_gate_clks[] = { @@ -151,6 +158,7 @@ static struct mmp_param_gate_clk apbc_gate_clks[] = { {PXA168_CLK_SSP2, "ssp2_clk", "ssp2_mux", CLK_SET_RATE_PARENT, APBC_SSP2, 0x3, 0x3, 0x0, 0, &ssp2_lock}, {PXA168_CLK_SSP3, "ssp3_clk", "ssp3_mux", CLK_SET_RATE_PARENT, APBC_SSP3, 0x3, 0x3, 0x0, 0, &ssp3_lock}, {PXA168_CLK_SSP4, "ssp4_clk", "ssp4_mux", CLK_SET_RATE_PARENT, APBC_SSP4, 0x3, 0x3, 0x0, 0, &ssp4_lock}, + {PXA168_CLK_TIMER, "timer_clk", "timer_mux", CLK_SET_RATE_PARENT, APBC_TIMER, 0x3, 0x3, 0x0, 0, &timer_lock}, }; static void pxa168_apb_periph_clk_init(struct pxa168_clk_unit *pxa_unit) diff --git a/drivers/clk/mmp/clk-of-pxa1928.c b/drivers/clk/mmp/clk-of-pxa1928.c new file mode 100644 index 000000000000..433a5ae1eae0 --- /dev/null +++ b/drivers/clk/mmp/clk-of-pxa1928.c @@ -0,0 +1,265 @@ +/* + * pxa1928 clock framework source file + * + * Copyright (C) 2015 Linaro, Ltd. + * Rob Herring + * + * Based on drivers/clk/mmp/clk-of-mmp2.c: + * Copyright (C) 2012 Marvell + * Chao Xie + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ +#include +#include +#include +#include +#include + +#include + +#include "clk.h" +#include "reset.h" + +#define MPMU_UART_PLL 0x14 + +struct pxa1928_clk_unit { + struct mmp_clk_unit unit; + void __iomem *mpmu_base; + void __iomem *apmu_base; + void __iomem *apbc_base; + void __iomem *apbcp_base; +}; + +static struct mmp_param_fixed_rate_clk fixed_rate_clks[] = { + {0, "clk32", NULL, CLK_IS_ROOT, 32768}, + {0, "vctcxo", NULL, CLK_IS_ROOT, 26000000}, + {0, "pll1_624", NULL, CLK_IS_ROOT, 624000000}, + {0, "pll5p", NULL, CLK_IS_ROOT, 832000000}, + {0, "pll5", NULL, CLK_IS_ROOT, 1248000000}, + {0, "usb_pll", NULL, CLK_IS_ROOT, 480000000}, +}; + +static struct mmp_param_fixed_factor_clk fixed_factor_clks[] = { + {0, "pll1_d2", "pll1_624", 1, 2, 0}, + {0, "pll1_d9", "pll1_624", 1, 9, 0}, + {0, "pll1_d12", "pll1_624", 1, 12, 0}, + {0, "pll1_d16", "pll1_624", 1, 16, 0}, + {0, "pll1_d20", "pll1_624", 1, 20, 0}, + {0, "pll1_416", "pll1_624", 2, 3, 0}, + {0, "vctcxo_d2", "vctcxo", 1, 2, 0}, + {0, "vctcxo_d4", "vctcxo", 1, 4, 0}, +}; + +static struct mmp_clk_factor_masks uart_factor_masks = { + .factor = 2, + .num_mask = 0x1fff, + .den_mask = 0x1fff, + .num_shift = 16, + .den_shift = 0, +}; + +static struct mmp_clk_factor_tbl uart_factor_tbl[] = { + {.num = 832, .den = 234}, /*58.5MHZ */ + {.num = 1, .den = 1}, /*26MHZ */ +}; + +static void pxa1928_pll_init(struct pxa1928_clk_unit *pxa_unit) +{ + struct clk *clk; + struct mmp_clk_unit *unit = &pxa_unit->unit; + + mmp_register_fixed_rate_clks(unit, fixed_rate_clks, + ARRAY_SIZE(fixed_rate_clks)); + + mmp_register_fixed_factor_clks(unit, fixed_factor_clks, + ARRAY_SIZE(fixed_factor_clks)); + + clk = mmp_clk_register_factor("uart_pll", "pll1_416", + CLK_SET_RATE_PARENT, + pxa_unit->mpmu_base + MPMU_UART_PLL, + &uart_factor_masks, uart_factor_tbl, + ARRAY_SIZE(uart_factor_tbl), NULL); +} + +static DEFINE_SPINLOCK(uart0_lock); +static DEFINE_SPINLOCK(uart1_lock); +static DEFINE_SPINLOCK(uart2_lock); +static DEFINE_SPINLOCK(uart3_lock); +static const char *uart_parent_names[] = {"uart_pll", "vctcxo"}; + +static DEFINE_SPINLOCK(ssp0_lock); +static DEFINE_SPINLOCK(ssp1_lock); +static const char *ssp_parent_names[] = {"vctcxo_d4", "vctcxo_d2", "vctcxo", "pll1_d12"}; + +static DEFINE_SPINLOCK(reset_lock); + +static struct mmp_param_mux_clk apbc_mux_clks[] = { + {0, "uart0_mux", uart_parent_names, ARRAY_SIZE(uart_parent_names), CLK_SET_RATE_PARENT, PXA1928_CLK_UART0 * 4, 4, 3, 0, &uart0_lock}, + {0, "uart1_mux", uart_parent_names, ARRAY_SIZE(uart_parent_names), CLK_SET_RATE_PARENT, PXA1928_CLK_UART1 * 4, 4, 3, 0, &uart1_lock}, + {0, "uart2_mux", uart_parent_names, ARRAY_SIZE(uart_parent_names), CLK_SET_RATE_PARENT, PXA1928_CLK_UART2 * 4, 4, 3, 0, &uart2_lock}, + {0, "uart3_mux", uart_parent_names, ARRAY_SIZE(uart_parent_names), CLK_SET_RATE_PARENT, PXA1928_CLK_UART3 * 4, 4, 3, 0, &uart3_lock}, + {0, "ssp0_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, PXA1928_CLK_SSP0 * 4, 4, 3, 0, &ssp0_lock}, + {0, "ssp1_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, PXA1928_CLK_SSP1 * 4, 4, 3, 0, &ssp1_lock}, +}; + +static struct mmp_param_gate_clk apbc_gate_clks[] = { + {PXA1928_CLK_TWSI0, "twsi0_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_TWSI0 * 4, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA1928_CLK_TWSI1, "twsi1_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_TWSI1 * 4, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA1928_CLK_TWSI2, "twsi2_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_TWSI2 * 4, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA1928_CLK_TWSI3, "twsi3_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_TWSI3 * 4, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA1928_CLK_TWSI4, "twsi4_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_TWSI4 * 4, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA1928_CLK_TWSI5, "twsi5_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_TWSI5 * 4, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA1928_CLK_GPIO, "gpio_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_GPIO * 4, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA1928_CLK_KPC, "kpc_clk", "clk32", CLK_SET_RATE_PARENT, PXA1928_CLK_KPC * 4, 0x3, 0x3, 0x0, MMP_CLK_GATE_NEED_DELAY, NULL}, + {PXA1928_CLK_RTC, "rtc_clk", "clk32", CLK_SET_RATE_PARENT, PXA1928_CLK_RTC * 4, 0x83, 0x83, 0x0, MMP_CLK_GATE_NEED_DELAY, NULL}, + {PXA1928_CLK_PWM0, "pwm0_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_PWM0 * 4, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA1928_CLK_PWM1, "pwm1_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_PWM1 * 4, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA1928_CLK_PWM2, "pwm2_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_PWM2 * 4, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA1928_CLK_PWM3, "pwm3_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_PWM3 * 4, 0x3, 0x3, 0x0, 0, &reset_lock}, + /* The gate clocks has mux parent. */ + {PXA1928_CLK_UART0, "uart0_clk", "uart0_mux", CLK_SET_RATE_PARENT, PXA1928_CLK_UART0 * 4, 0x3, 0x3, 0x0, 0, &uart0_lock}, + {PXA1928_CLK_UART1, "uart1_clk", "uart1_mux", CLK_SET_RATE_PARENT, PXA1928_CLK_UART1 * 4, 0x3, 0x3, 0x0, 0, &uart1_lock}, + {PXA1928_CLK_UART2, "uart2_clk", "uart2_mux", CLK_SET_RATE_PARENT, PXA1928_CLK_UART2 * 4, 0x3, 0x3, 0x0, 0, &uart2_lock}, + {PXA1928_CLK_UART3, "uart3_clk", "uart3_mux", CLK_SET_RATE_PARENT, PXA1928_CLK_UART3 * 4, 0x3, 0x3, 0x0, 0, &uart3_lock}, + {PXA1928_CLK_SSP0, "ssp0_clk", "ssp0_mux", CLK_SET_RATE_PARENT, PXA1928_CLK_SSP0 * 4, 0x3, 0x3, 0x0, 0, &ssp0_lock}, + {PXA1928_CLK_SSP1, "ssp1_clk", "ssp1_mux", CLK_SET_RATE_PARENT, PXA1928_CLK_SSP1 * 4, 0x3, 0x3, 0x0, 0, &ssp1_lock}, +}; + +static void pxa1928_apb_periph_clk_init(struct pxa1928_clk_unit *pxa_unit) +{ + struct mmp_clk_unit *unit = &pxa_unit->unit; + + mmp_register_mux_clks(unit, apbc_mux_clks, pxa_unit->apbc_base, + ARRAY_SIZE(apbc_mux_clks)); + + mmp_register_gate_clks(unit, apbc_gate_clks, pxa_unit->apbc_base, + ARRAY_SIZE(apbc_gate_clks)); +} + +static DEFINE_SPINLOCK(sdh0_lock); +static DEFINE_SPINLOCK(sdh1_lock); +static DEFINE_SPINLOCK(sdh2_lock); +static DEFINE_SPINLOCK(sdh3_lock); +static DEFINE_SPINLOCK(sdh4_lock); +static const char *sdh_parent_names[] = {"pll1_624", "pll5p", "pll5", "pll1_416"}; + +static DEFINE_SPINLOCK(usb_lock); + +static struct mmp_param_mux_clk apmu_mux_clks[] = { + {0, "sdh_mux", sdh_parent_names, ARRAY_SIZE(sdh_parent_names), CLK_SET_RATE_PARENT, PXA1928_CLK_SDH0 * 4, 8, 2, 0, &sdh0_lock}, +}; + +static struct mmp_param_div_clk apmu_div_clks[] = { + {0, "sdh_div", "sdh_mux", 0, PXA1928_CLK_SDH0 * 4, 10, 4, CLK_DIVIDER_ONE_BASED, &sdh0_lock}, +}; + +static struct mmp_param_gate_clk apmu_gate_clks[] = { + {PXA1928_CLK_USB, "usb_clk", "usb_pll", 0, PXA1928_CLK_USB * 4, 0x9, 0x9, 0x0, 0, &usb_lock}, + {PXA1928_CLK_HSIC, "hsic_clk", "usb_pll", 0, PXA1928_CLK_HSIC * 4, 0x9, 0x9, 0x0, 0, &usb_lock}, + /* The gate clocks has mux parent. */ + {PXA1928_CLK_SDH0, "sdh0_clk", "sdh_div", CLK_SET_RATE_PARENT, PXA1928_CLK_SDH0 * 4, 0x1b, 0x1b, 0x0, 0, &sdh0_lock}, + {PXA1928_CLK_SDH1, "sdh1_clk", "sdh_div", CLK_SET_RATE_PARENT, PXA1928_CLK_SDH1 * 4, 0x1b, 0x1b, 0x0, 0, &sdh1_lock}, + {PXA1928_CLK_SDH2, "sdh2_clk", "sdh_div", CLK_SET_RATE_PARENT, PXA1928_CLK_SDH2 * 4, 0x1b, 0x1b, 0x0, 0, &sdh2_lock}, + {PXA1928_CLK_SDH3, "sdh3_clk", "sdh_div", CLK_SET_RATE_PARENT, PXA1928_CLK_SDH3 * 4, 0x1b, 0x1b, 0x0, 0, &sdh3_lock}, + {PXA1928_CLK_SDH4, "sdh4_clk", "sdh_div", CLK_SET_RATE_PARENT, PXA1928_CLK_SDH4 * 4, 0x1b, 0x1b, 0x0, 0, &sdh4_lock}, +}; + +static void pxa1928_axi_periph_clk_init(struct pxa1928_clk_unit *pxa_unit) +{ + struct mmp_clk_unit *unit = &pxa_unit->unit; + + mmp_register_mux_clks(unit, apmu_mux_clks, pxa_unit->apmu_base, + ARRAY_SIZE(apmu_mux_clks)); + + mmp_register_div_clks(unit, apmu_div_clks, pxa_unit->apmu_base, + ARRAY_SIZE(apmu_div_clks)); + + mmp_register_gate_clks(unit, apmu_gate_clks, pxa_unit->apmu_base, + ARRAY_SIZE(apmu_gate_clks)); +} + +static void pxa1928_clk_reset_init(struct device_node *np, + struct pxa1928_clk_unit *pxa_unit) +{ + struct mmp_clk_reset_cell *cells; + int i, base, nr_resets; + + nr_resets = ARRAY_SIZE(apbc_gate_clks); + cells = kcalloc(nr_resets, sizeof(*cells), GFP_KERNEL); + if (!cells) + return; + + base = 0; + for (i = 0; i < nr_resets; i++) { + cells[base + i].clk_id = apbc_gate_clks[i].id; + cells[base + i].reg = + pxa_unit->apbc_base + apbc_gate_clks[i].offset; + cells[base + i].flags = 0; + cells[base + i].lock = apbc_gate_clks[i].lock; + cells[base + i].bits = 0x4; + } + + mmp_clk_reset_register(np, cells, nr_resets); +} + +static void __init pxa1928_mpmu_clk_init(struct device_node *np) +{ + struct pxa1928_clk_unit *pxa_unit; + + pxa_unit = kzalloc(sizeof(*pxa_unit), GFP_KERNEL); + if (!pxa_unit) + return; + + pxa_unit->mpmu_base = of_iomap(np, 0); + if (!pxa_unit->mpmu_base) { + pr_err("failed to map mpmu registers\n"); + return; + } + + pxa1928_pll_init(pxa_unit); +} +CLK_OF_DECLARE(pxa1928_mpmu_clk, "marvell,pxa1928-mpmu", pxa1928_mpmu_clk_init); + +static void __init pxa1928_apmu_clk_init(struct device_node *np) +{ + struct pxa1928_clk_unit *pxa_unit; + + pxa_unit = kzalloc(sizeof(*pxa_unit), GFP_KERNEL); + if (!pxa_unit) + return; + + pxa_unit->apmu_base = of_iomap(np, 0); + if (!pxa_unit->apmu_base) { + pr_err("failed to map apmu registers\n"); + return; + } + + mmp_clk_init(np, &pxa_unit->unit, PXA1928_APMU_NR_CLKS); + + pxa1928_axi_periph_clk_init(pxa_unit); +} +CLK_OF_DECLARE(pxa1928_apmu_clk, "marvell,pxa1928-apmu", pxa1928_apmu_clk_init); + +static void __init pxa1928_apbc_clk_init(struct device_node *np) +{ + struct pxa1928_clk_unit *pxa_unit; + + pxa_unit = kzalloc(sizeof(*pxa_unit), GFP_KERNEL); + if (!pxa_unit) + return; + + pxa_unit->apbc_base = of_iomap(np, 0); + if (!pxa_unit->apbc_base) { + pr_err("failed to map apbc registers\n"); + return; + } + + mmp_clk_init(np, &pxa_unit->unit, PXA1928_APBC_NR_CLKS); + + pxa1928_apb_periph_clk_init(pxa_unit); + pxa1928_clk_reset_init(np, pxa_unit); +} +CLK_OF_DECLARE(pxa1928_apbc_clk, "marvell,pxa1928-apbc", pxa1928_apbc_clk_init); diff --git a/drivers/clk/mmp/clk-of-pxa910.c b/drivers/clk/mmp/clk-of-pxa910.c index 5e3c80dad336..13d6173326a4 100644 --- a/drivers/clk/mmp/clk-of-pxa910.c +++ b/drivers/clk/mmp/clk-of-pxa910.c @@ -35,6 +35,8 @@ #define APBC_SSP0 0x1c #define APBC_SSP1 0x20 #define APBC_SSP2 0x4c +#define APBC_TIMER0 0x30 +#define APBC_TIMER1 0x44 #define APBCP_TWSI1 0x28 #define APBCP_UART2 0x1c #define APMU_SDH0 0x54 @@ -57,6 +59,7 @@ static struct mmp_param_fixed_rate_clk fixed_rate_clks[] = { {PXA910_CLK_CLK32, "clk32", NULL, CLK_IS_ROOT, 32768}, {PXA910_CLK_VCTCXO, "vctcxo", NULL, CLK_IS_ROOT, 26000000}, {PXA910_CLK_PLL1, "pll1", NULL, CLK_IS_ROOT, 624000000}, + {PXA910_CLK_USB_PLL, "usb_pll", NULL, CLK_IS_ROOT, 480000000}, }; static struct mmp_param_fixed_factor_clk fixed_factor_clks[] = { @@ -69,6 +72,7 @@ static struct mmp_param_fixed_factor_clk fixed_factor_clks[] = { {PXA910_CLK_PLL1_24, "pll1_24", "pll1_12", 1, 2, 0}, {PXA910_CLK_PLL1_48, "pll1_48", "pll1_24", 1, 2, 0}, {PXA910_CLK_PLL1_96, "pll1_96", "pll1_48", 1, 2, 0}, + {PXA910_CLK_PLL1_192, "pll1_192", "pll1_96", 1, 2, 0}, {PXA910_CLK_PLL1_13, "pll1_13", "pll1", 1, 13, 0}, {PXA910_CLK_PLL1_13_1_5, "pll1_13_1_5", "pll1_13", 2, 3, 0}, {PXA910_CLK_PLL1_2_1_5, "pll1_2_1_5", "pll1_2", 2, 3, 0}, @@ -115,6 +119,10 @@ static DEFINE_SPINLOCK(ssp0_lock); static DEFINE_SPINLOCK(ssp1_lock); static const char *ssp_parent_names[] = {"pll1_96", "pll1_48", "pll1_24", "pll1_12"}; +static DEFINE_SPINLOCK(timer0_lock); +static DEFINE_SPINLOCK(timer1_lock); +static const char *timer_parent_names[] = {"pll1_48", "clk32", "pll1_96"}; + static DEFINE_SPINLOCK(reset_lock); static struct mmp_param_mux_clk apbc_mux_clks[] = { @@ -122,6 +130,8 @@ static struct mmp_param_mux_clk apbc_mux_clks[] = { {0, "uart1_mux", uart_parent_names, ARRAY_SIZE(uart_parent_names), CLK_SET_RATE_PARENT, APBC_UART1, 4, 3, 0, &uart1_lock}, {0, "ssp0_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP0, 4, 3, 0, &ssp0_lock}, {0, "ssp1_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP1, 4, 3, 0, &ssp1_lock}, + {0, "timer0_mux", timer_parent_names, ARRAY_SIZE(timer_parent_names), CLK_SET_RATE_PARENT, APBC_TIMER0, 4, 3, 0, &timer0_lock}, + {0, "timer1_mux", timer_parent_names, ARRAY_SIZE(timer_parent_names), CLK_SET_RATE_PARENT, APBC_TIMER1, 4, 3, 0, &timer1_lock}, }; static struct mmp_param_mux_clk apbcp_mux_clks[] = { @@ -142,6 +152,8 @@ static struct mmp_param_gate_clk apbc_gate_clks[] = { {PXA910_CLK_UART1, "uart1_clk", "uart1_mux", CLK_SET_RATE_PARENT, APBC_UART1, 0x3, 0x3, 0x0, 0, &uart1_lock}, {PXA910_CLK_SSP0, "ssp0_clk", "ssp0_mux", CLK_SET_RATE_PARENT, APBC_SSP0, 0x3, 0x3, 0x0, 0, &ssp0_lock}, {PXA910_CLK_SSP1, "ssp1_clk", "ssp1_mux", CLK_SET_RATE_PARENT, APBC_SSP1, 0x3, 0x3, 0x0, 0, &ssp1_lock}, + {PXA910_CLK_TIMER0, "timer0_clk", "timer0_mux", CLK_SET_RATE_PARENT, APBC_TIMER0, 0x3, 0x3, 0x0, 0, &timer0_lock}, + {PXA910_CLK_TIMER1, "timer1_clk", "timer1_mux", CLK_SET_RATE_PARENT, APBC_TIMER1, 0x3, 0x3, 0x0, 0, &timer1_lock}, }; static struct mmp_param_gate_clk apbcp_gate_clks[] = { diff --git a/drivers/clk/mvebu/armada-370.c b/drivers/clk/mvebu/armada-370.c index 756f0f39d6a3..2c7c1085f883 100644 --- a/drivers/clk/mvebu/armada-370.c +++ b/drivers/clk/mvebu/armada-370.c @@ -163,6 +163,7 @@ static const struct clk_gating_soc_desc a370_gating_desc[] __initconst = { { "pex1", "pex1_en", 9, 0 }, { "sata0", NULL, 15, 0 }, { "sdio", NULL, 17, 0 }, + { "crypto", NULL, 23, CLK_IGNORE_UNUSED }, { "tdm", NULL, 25, 0 }, { "ddr", NULL, 28, CLK_IGNORE_UNUSED }, { "sata1", NULL, 30, 0 }, diff --git a/drivers/clk/mxs/clk-imx23.c b/drivers/clk/mxs/clk-imx23.c index 22d136aa699f..32216f9b7f03 100644 --- a/drivers/clk/mxs/clk-imx23.c +++ b/drivers/clk/mxs/clk-imx23.c @@ -77,12 +77,12 @@ static void __init clk_misc_init(void) writel_relaxed(30 << BP_FRAC_IOFRAC, FRAC + SET); } -static const char *sel_pll[] __initdata = { "pll", "ref_xtal", }; -static const char *sel_cpu[] __initdata = { "ref_cpu", "ref_xtal", }; -static const char *sel_pix[] __initdata = { "ref_pix", "ref_xtal", }; -static const char *sel_io[] __initdata = { "ref_io", "ref_xtal", }; -static const char *cpu_sels[] __initdata = { "cpu_pll", "cpu_xtal", }; -static const char *emi_sels[] __initdata = { "emi_pll", "emi_xtal", }; +static const char *const sel_pll[] __initconst = { "pll", "ref_xtal", }; +static const char *const sel_cpu[] __initconst = { "ref_cpu", "ref_xtal", }; +static const char *const sel_pix[] __initconst = { "ref_pix", "ref_xtal", }; +static const char *const sel_io[] __initconst = { "ref_io", "ref_xtal", }; +static const char *const cpu_sels[] __initconst = { "cpu_pll", "cpu_xtal", }; +static const char *const emi_sels[] __initconst = { "emi_pll", "emi_xtal", }; enum imx23_clk { ref_xtal, pll, ref_cpu, ref_emi, ref_pix, ref_io, saif_sel, diff --git a/drivers/clk/mxs/clk-imx28.c b/drivers/clk/mxs/clk-imx28.c index b1be3746ce95..a68670868baa 100644 --- a/drivers/clk/mxs/clk-imx28.c +++ b/drivers/clk/mxs/clk-imx28.c @@ -125,15 +125,15 @@ static void __init clk_misc_init(void) writel_relaxed(val, FRAC0); } -static const char *sel_cpu[] __initdata = { "ref_cpu", "ref_xtal", }; -static const char *sel_io0[] __initdata = { "ref_io0", "ref_xtal", }; -static const char *sel_io1[] __initdata = { "ref_io1", "ref_xtal", }; -static const char *sel_pix[] __initdata = { "ref_pix", "ref_xtal", }; -static const char *sel_gpmi[] __initdata = { "ref_gpmi", "ref_xtal", }; -static const char *sel_pll0[] __initdata = { "pll0", "ref_xtal", }; -static const char *cpu_sels[] __initdata = { "cpu_pll", "cpu_xtal", }; -static const char *emi_sels[] __initdata = { "emi_pll", "emi_xtal", }; -static const char *ptp_sels[] __initdata = { "ref_xtal", "pll0", }; +static const char *const sel_cpu[] __initconst = { "ref_cpu", "ref_xtal", }; +static const char *const sel_io0[] __initconst = { "ref_io0", "ref_xtal", }; +static const char *const sel_io1[] __initconst = { "ref_io1", "ref_xtal", }; +static const char *const sel_pix[] __initconst = { "ref_pix", "ref_xtal", }; +static const char *const sel_gpmi[] __initconst = { "ref_gpmi", "ref_xtal", }; +static const char *const sel_pll0[] __initconst = { "pll0", "ref_xtal", }; +static const char *const cpu_sels[] __initconst = { "cpu_pll", "cpu_xtal", }; +static const char *const emi_sels[] __initconst = { "emi_pll", "emi_xtal", }; +static const char *const ptp_sels[] __initconst = { "ref_xtal", "pll0", }; enum imx28_clk { ref_xtal, pll0, pll1, pll2, ref_cpu, ref_emi, ref_io0, ref_io1, diff --git a/drivers/clk/mxs/clk.h b/drivers/clk/mxs/clk.h index ef10ad9b5daa..f07d821dd75d 100644 --- a/drivers/clk/mxs/clk.h +++ b/drivers/clk/mxs/clk.h @@ -49,7 +49,7 @@ static inline struct clk *mxs_clk_gate(const char *name, } static inline struct clk *mxs_clk_mux(const char *name, void __iomem *reg, - u8 shift, u8 width, const char **parent_names, int num_parents) + u8 shift, u8 width, const char *const *parent_names, int num_parents) { return clk_register_mux(NULL, name, parent_names, num_parents, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, diff --git a/drivers/clk/nxp/Makefile b/drivers/clk/nxp/Makefile new file mode 100644 index 000000000000..7f608b0ad7b4 --- /dev/null +++ b/drivers/clk/nxp/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_ARCH_LPC18XX) += clk-lpc18xx-cgu.o +obj-$(CONFIG_ARCH_LPC18XX) += clk-lpc18xx-ccu.o diff --git a/drivers/clk/nxp/clk-lpc18xx-ccu.c b/drivers/clk/nxp/clk-lpc18xx-ccu.c new file mode 100644 index 000000000000..eeaee97da110 --- /dev/null +++ b/drivers/clk/nxp/clk-lpc18xx-ccu.c @@ -0,0 +1,293 @@ +/* + * Clk driver for NXP LPC18xx/LPC43xx Clock Control Unit (CCU) + * + * Copyright (C) 2015 Joachim Eastwood + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +/* Bit defines for CCU branch configuration register */ +#define LPC18XX_CCU_RUN BIT(0) +#define LPC18XX_CCU_AUTO BIT(1) +#define LPC18XX_CCU_DIV BIT(5) +#define LPC18XX_CCU_DIVSTAT BIT(27) + +/* CCU branch feature bits */ +#define CCU_BRANCH_IS_BUS BIT(0) +#define CCU_BRANCH_HAVE_DIV2 BIT(1) + +#define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw) + +struct lpc18xx_branch_clk_data { + const char **name; + int num; +}; + +struct lpc18xx_clk_branch { + const char *base_name; + const char *name; + u16 offset; + u16 flags; + struct clk *clk; + struct clk_gate gate; +}; + +static struct lpc18xx_clk_branch clk_branches[] = { + {"base_apb3_clk", "apb3_bus", CLK_APB3_BUS, CCU_BRANCH_IS_BUS}, + {"base_apb3_clk", "apb3_i2c1", CLK_APB3_I2C1, 0}, + {"base_apb3_clk", "apb3_dac", CLK_APB3_DAC, 0}, + {"base_apb3_clk", "apb3_adc0", CLK_APB3_ADC0, 0}, + {"base_apb3_clk", "apb3_adc1", CLK_APB3_ADC1, 0}, + {"base_apb3_clk", "apb3_can0", CLK_APB3_CAN0, 0}, + + {"base_apb1_clk", "apb1_bus", CLK_APB1_BUS, CCU_BRANCH_IS_BUS}, + {"base_apb1_clk", "apb1_mc_pwm", CLK_APB1_MOTOCON_PWM, 0}, + {"base_apb1_clk", "apb1_i2c0", CLK_APB1_I2C0, 0}, + {"base_apb1_clk", "apb1_i2s", CLK_APB1_I2S, 0}, + {"base_apb1_clk", "apb1_can1", CLK_APB1_CAN1, 0}, + + {"base_spifi_clk", "spifi", CLK_SPIFI, 0}, + + {"base_cpu_clk", "cpu_bus", CLK_CPU_BUS, CCU_BRANCH_IS_BUS}, + {"base_cpu_clk", "cpu_spifi", CLK_CPU_SPIFI, 0}, + {"base_cpu_clk", "cpu_gpio", CLK_CPU_GPIO, 0}, + {"base_cpu_clk", "cpu_lcd", CLK_CPU_LCD, 0}, + {"base_cpu_clk", "cpu_ethernet", CLK_CPU_ETHERNET, 0}, + {"base_cpu_clk", "cpu_usb0", CLK_CPU_USB0, 0}, + {"base_cpu_clk", "cpu_emc", CLK_CPU_EMC, 0}, + {"base_cpu_clk", "cpu_sdio", CLK_CPU_SDIO, 0}, + {"base_cpu_clk", "cpu_dma", CLK_CPU_DMA, 0}, + {"base_cpu_clk", "cpu_core", CLK_CPU_CORE, 0}, + {"base_cpu_clk", "cpu_sct", CLK_CPU_SCT, 0}, + {"base_cpu_clk", "cpu_usb1", CLK_CPU_USB1, 0}, + {"base_cpu_clk", "cpu_emcdiv", CLK_CPU_EMCDIV, CCU_BRANCH_HAVE_DIV2}, + {"base_cpu_clk", "cpu_flasha", CLK_CPU_FLASHA, CCU_BRANCH_HAVE_DIV2}, + {"base_cpu_clk", "cpu_flashb", CLK_CPU_FLASHB, CCU_BRANCH_HAVE_DIV2}, + {"base_cpu_clk", "cpu_m0app", CLK_CPU_M0APP, CCU_BRANCH_HAVE_DIV2}, + {"base_cpu_clk", "cpu_adchs", CLK_CPU_ADCHS, CCU_BRANCH_HAVE_DIV2}, + {"base_cpu_clk", "cpu_eeprom", CLK_CPU_EEPROM, CCU_BRANCH_HAVE_DIV2}, + {"base_cpu_clk", "cpu_wwdt", CLK_CPU_WWDT, 0}, + {"base_cpu_clk", "cpu_uart0", CLK_CPU_UART0, 0}, + {"base_cpu_clk", "cpu_uart1", CLK_CPU_UART1, 0}, + {"base_cpu_clk", "cpu_ssp0", CLK_CPU_SSP0, 0}, + {"base_cpu_clk", "cpu_timer0", CLK_CPU_TIMER0, 0}, + {"base_cpu_clk", "cpu_timer1", CLK_CPU_TIMER1, 0}, + {"base_cpu_clk", "cpu_scu", CLK_CPU_SCU, 0}, + {"base_cpu_clk", "cpu_creg", CLK_CPU_CREG, 0}, + {"base_cpu_clk", "cpu_ritimer", CLK_CPU_RITIMER, 0}, + {"base_cpu_clk", "cpu_uart2", CLK_CPU_UART2, 0}, + {"base_cpu_clk", "cpu_uart3", CLK_CPU_UART3, 0}, + {"base_cpu_clk", "cpu_timer2", CLK_CPU_TIMER2, 0}, + {"base_cpu_clk", "cpu_timer3", CLK_CPU_TIMER3, 0}, + {"base_cpu_clk", "cpu_ssp1", CLK_CPU_SSP1, 0}, + {"base_cpu_clk", "cpu_qei", CLK_CPU_QEI, 0}, + + {"base_periph_clk", "periph_bus", CLK_PERIPH_BUS, CCU_BRANCH_IS_BUS}, + {"base_periph_clk", "periph_core", CLK_PERIPH_CORE, 0}, + {"base_periph_clk", "periph_sgpio", CLK_PERIPH_SGPIO, 0}, + + {"base_usb0_clk", "usb0", CLK_USB0, 0}, + {"base_usb1_clk", "usb1", CLK_USB1, 0}, + {"base_spi_clk", "spi", CLK_SPI, 0}, + {"base_adchs_clk", "adchs", CLK_ADCHS, 0}, + + {"base_audio_clk", "audio", CLK_AUDIO, 0}, + {"base_uart3_clk", "apb2_uart3", CLK_APB2_UART3, 0}, + {"base_uart2_clk", "apb2_uart2", CLK_APB2_UART2, 0}, + {"base_uart1_clk", "apb0_uart1", CLK_APB0_UART1, 0}, + {"base_uart0_clk", "apb0_uart0", CLK_APB0_UART0, 0}, + {"base_ssp1_clk", "apb2_ssp1", CLK_APB2_SSP1, 0}, + {"base_ssp0_clk", "apb0_ssp0", CLK_APB0_SSP0, 0}, + {"base_sdio_clk", "sdio", CLK_SDIO, 0}, +}; + +static struct clk *lpc18xx_ccu_branch_clk_get(struct of_phandle_args *clkspec, + void *data) +{ + struct lpc18xx_branch_clk_data *clk_data = data; + unsigned int offset = clkspec->args[0]; + int i, j; + + for (i = 0; i < ARRAY_SIZE(clk_branches); i++) { + if (clk_branches[i].offset != offset) + continue; + + for (j = 0; j < clk_data->num; j++) { + if (!strcmp(clk_branches[i].base_name, clk_data->name[j])) + return clk_branches[i].clk; + } + } + + pr_err("%s: invalid clock offset %d\n", __func__, offset); + + return ERR_PTR(-EINVAL); +} + +static int lpc18xx_ccu_gate_endisable(struct clk_hw *hw, bool enable) +{ + struct clk_gate *gate = to_clk_gate(hw); + u32 val; + + /* + * Divider field is write only, so divider stat field must + * be read so divider field can be set accordingly. + */ + val = clk_readl(gate->reg); + if (val & LPC18XX_CCU_DIVSTAT) + val |= LPC18XX_CCU_DIV; + + if (enable) { + val |= LPC18XX_CCU_RUN; + } else { + /* + * To safely disable a branch clock a squence of two separate + * writes must be used. First write should set the AUTO bit + * and the next write should clear the RUN bit. + */ + val |= LPC18XX_CCU_AUTO; + clk_writel(val, gate->reg); + + val &= ~LPC18XX_CCU_RUN; + } + + clk_writel(val, gate->reg); + + return 0; +} + +static int lpc18xx_ccu_gate_enable(struct clk_hw *hw) +{ + return lpc18xx_ccu_gate_endisable(hw, true); +} + +static void lpc18xx_ccu_gate_disable(struct clk_hw *hw) +{ + lpc18xx_ccu_gate_endisable(hw, false); +} + +static int lpc18xx_ccu_gate_is_enabled(struct clk_hw *hw) +{ + struct clk_gate *gate = to_clk_gate(hw); + + return clk_readl(gate->reg) & LPC18XX_CCU_RUN; +} + +static const struct clk_ops lpc18xx_ccu_gate_ops = { + .enable = lpc18xx_ccu_gate_enable, + .disable = lpc18xx_ccu_gate_disable, + .is_enabled = lpc18xx_ccu_gate_is_enabled, +}; + +static void lpc18xx_ccu_register_branch_gate_div(struct lpc18xx_clk_branch *branch, + void __iomem *reg_base, + const char *parent) +{ + const struct clk_ops *div_ops = NULL; + struct clk_divider *div = NULL; + struct clk_hw *div_hw = NULL; + + if (branch->flags & CCU_BRANCH_HAVE_DIV2) { + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) + return; + + div->reg = branch->offset + reg_base; + div->flags = CLK_DIVIDER_READ_ONLY; + div->shift = 27; + div->width = 1; + + div_hw = &div->hw; + div_ops = &clk_divider_ops; + } + + branch->gate.reg = branch->offset + reg_base; + branch->gate.bit_idx = 0; + + branch->clk = clk_register_composite(NULL, branch->name, &parent, 1, + NULL, NULL, + div_hw, div_ops, + &branch->gate.hw, &lpc18xx_ccu_gate_ops, 0); + if (IS_ERR(branch->clk)) { + kfree(div); + pr_warn("%s: failed to register %s\n", __func__, branch->name); + return; + } + + /* Grab essential branch clocks for CPU and SDRAM */ + switch (branch->offset) { + case CLK_CPU_EMC: + case CLK_CPU_CORE: + case CLK_CPU_CREG: + case CLK_CPU_EMCDIV: + clk_prepare_enable(branch->clk); + } +} + +static void lpc18xx_ccu_register_branch_clks(void __iomem *reg_base, + const char *base_name) +{ + const char *parent = base_name; + int i; + + for (i = 0; i < ARRAY_SIZE(clk_branches); i++) { + if (strcmp(clk_branches[i].base_name, base_name)) + continue; + + lpc18xx_ccu_register_branch_gate_div(&clk_branches[i], reg_base, + parent); + + if (clk_branches[i].flags & CCU_BRANCH_IS_BUS) + parent = clk_branches[i].name; + } +} + +static void __init lpc18xx_ccu_init(struct device_node *np) +{ + struct lpc18xx_branch_clk_data *clk_data; + void __iomem *reg_base; + int i, ret; + + reg_base = of_iomap(np, 0); + if (!reg_base) { + pr_warn("%s: failed to map address range\n", __func__); + return; + } + + clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); + if (!clk_data) + return; + + clk_data->num = of_property_count_strings(np, "clock-names"); + clk_data->name = kcalloc(clk_data->num, sizeof(char *), GFP_KERNEL); + if (!clk_data->name) { + kfree(clk_data); + return; + } + + for (i = 0; i < clk_data->num; i++) { + ret = of_property_read_string_index(np, "clock-names", i, + &clk_data->name[i]); + if (ret) { + pr_warn("%s: failed to get clock name at idx %d\n", + __func__, i); + continue; + } + + lpc18xx_ccu_register_branch_clks(reg_base, clk_data->name[i]); + } + + of_clk_add_provider(np, lpc18xx_ccu_branch_clk_get, clk_data); +} +CLK_OF_DECLARE(lpc18xx_ccu, "nxp,lpc1850-ccu", lpc18xx_ccu_init); diff --git a/drivers/clk/nxp/clk-lpc18xx-cgu.c b/drivers/clk/nxp/clk-lpc18xx-cgu.c new file mode 100644 index 000000000000..81e9e1c788f4 --- /dev/null +++ b/drivers/clk/nxp/clk-lpc18xx-cgu.c @@ -0,0 +1,635 @@ +/* + * Clk driver for NXP LPC18xx/LPC43xx Clock Generation Unit (CGU) + * + * Copyright (C) 2015 Joachim Eastwood + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include + +#include + +/* Clock Generation Unit (CGU) registers */ +#define LPC18XX_CGU_XTAL_OSC_CTRL 0x018 +#define LPC18XX_CGU_PLL0USB_STAT 0x01c +#define LPC18XX_CGU_PLL0USB_CTRL 0x020 +#define LPC18XX_CGU_PLL0USB_MDIV 0x024 +#define LPC18XX_CGU_PLL0USB_NP_DIV 0x028 +#define LPC18XX_CGU_PLL0AUDIO_STAT 0x02c +#define LPC18XX_CGU_PLL0AUDIO_CTRL 0x030 +#define LPC18XX_CGU_PLL0AUDIO_MDIV 0x034 +#define LPC18XX_CGU_PLL0AUDIO_NP_DIV 0x038 +#define LPC18XX_CGU_PLL0AUDIO_FRAC 0x03c +#define LPC18XX_CGU_PLL1_STAT 0x040 +#define LPC18XX_CGU_PLL1_CTRL 0x044 +#define LPC18XX_PLL1_CTRL_FBSEL BIT(6) +#define LPC18XX_PLL1_CTRL_DIRECT BIT(7) +#define LPC18XX_CGU_IDIV_CTRL(n) (0x048 + (n) * sizeof(u32)) +#define LPC18XX_CGU_BASE_CLK(id) (0x05c + (id) * sizeof(u32)) +#define LPC18XX_CGU_PLL_CTRL_OFFSET 0x4 + +/* PLL0 bits common to both audio and USB PLL */ +#define LPC18XX_PLL0_STAT_LOCK BIT(0) +#define LPC18XX_PLL0_CTRL_PD BIT(0) +#define LPC18XX_PLL0_CTRL_BYPASS BIT(1) +#define LPC18XX_PLL0_CTRL_DIRECTI BIT(2) +#define LPC18XX_PLL0_CTRL_DIRECTO BIT(3) +#define LPC18XX_PLL0_CTRL_CLKEN BIT(4) +#define LPC18XX_PLL0_MDIV_MDEC_MASK 0x1ffff +#define LPC18XX_PLL0_MDIV_SELP_SHIFT 17 +#define LPC18XX_PLL0_MDIV_SELI_SHIFT 22 +#define LPC18XX_PLL0_MSEL_MAX BIT(15) + +/* Register value that gives PLL0 post/pre dividers equal to 1 */ +#define LPC18XX_PLL0_NP_DIVS_1 0x00302062 + +enum { + CLK_SRC_OSC32, + CLK_SRC_IRC, + CLK_SRC_ENET_RX_CLK, + CLK_SRC_ENET_TX_CLK, + CLK_SRC_GP_CLKIN, + CLK_SRC_RESERVED1, + CLK_SRC_OSC, + CLK_SRC_PLL0USB, + CLK_SRC_PLL0AUDIO, + CLK_SRC_PLL1, + CLK_SRC_RESERVED2, + CLK_SRC_RESERVED3, + CLK_SRC_IDIVA, + CLK_SRC_IDIVB, + CLK_SRC_IDIVC, + CLK_SRC_IDIVD, + CLK_SRC_IDIVE, + CLK_SRC_MAX +}; + +static const char *clk_src_names[CLK_SRC_MAX] = { + [CLK_SRC_OSC32] = "osc32", + [CLK_SRC_IRC] = "irc", + [CLK_SRC_ENET_RX_CLK] = "enet_rx_clk", + [CLK_SRC_ENET_TX_CLK] = "enet_tx_clk", + [CLK_SRC_GP_CLKIN] = "gp_clkin", + [CLK_SRC_OSC] = "osc", + [CLK_SRC_PLL0USB] = "pll0usb", + [CLK_SRC_PLL0AUDIO] = "pll0audio", + [CLK_SRC_PLL1] = "pll1", + [CLK_SRC_IDIVA] = "idiva", + [CLK_SRC_IDIVB] = "idivb", + [CLK_SRC_IDIVC] = "idivc", + [CLK_SRC_IDIVD] = "idivd", + [CLK_SRC_IDIVE] = "idive", +}; + +static const char *clk_base_names[BASE_CLK_MAX] = { + [BASE_SAFE_CLK] = "base_safe_clk", + [BASE_USB0_CLK] = "base_usb0_clk", + [BASE_PERIPH_CLK] = "base_periph_clk", + [BASE_USB1_CLK] = "base_usb1_clk", + [BASE_CPU_CLK] = "base_cpu_clk", + [BASE_SPIFI_CLK] = "base_spifi_clk", + [BASE_SPI_CLK] = "base_spi_clk", + [BASE_PHY_RX_CLK] = "base_phy_rx_clk", + [BASE_PHY_TX_CLK] = "base_phy_tx_clk", + [BASE_APB1_CLK] = "base_apb1_clk", + [BASE_APB3_CLK] = "base_apb3_clk", + [BASE_LCD_CLK] = "base_lcd_clk", + [BASE_ADCHS_CLK] = "base_adchs_clk", + [BASE_SDIO_CLK] = "base_sdio_clk", + [BASE_SSP0_CLK] = "base_ssp0_clk", + [BASE_SSP1_CLK] = "base_ssp1_clk", + [BASE_UART0_CLK] = "base_uart0_clk", + [BASE_UART1_CLK] = "base_uart1_clk", + [BASE_UART2_CLK] = "base_uart2_clk", + [BASE_UART3_CLK] = "base_uart3_clk", + [BASE_OUT_CLK] = "base_out_clk", + [BASE_AUDIO_CLK] = "base_audio_clk", + [BASE_CGU_OUT0_CLK] = "base_cgu_out0_clk", + [BASE_CGU_OUT1_CLK] = "base_cgu_out1_clk", +}; + +static u32 lpc18xx_cgu_pll0_src_ids[] = { + CLK_SRC_OSC32, CLK_SRC_IRC, CLK_SRC_ENET_RX_CLK, + CLK_SRC_ENET_TX_CLK, CLK_SRC_GP_CLKIN, CLK_SRC_OSC, + CLK_SRC_PLL1, CLK_SRC_IDIVA, CLK_SRC_IDIVB, CLK_SRC_IDIVC, + CLK_SRC_IDIVD, CLK_SRC_IDIVE, +}; + +static u32 lpc18xx_cgu_pll1_src_ids[] = { + CLK_SRC_OSC32, CLK_SRC_IRC, CLK_SRC_ENET_RX_CLK, + CLK_SRC_ENET_TX_CLK, CLK_SRC_GP_CLKIN, CLK_SRC_OSC, + CLK_SRC_PLL0USB, CLK_SRC_PLL0AUDIO, CLK_SRC_IDIVA, + CLK_SRC_IDIVB, CLK_SRC_IDIVC, CLK_SRC_IDIVD, CLK_SRC_IDIVE, +}; + +static u32 lpc18xx_cgu_idiva_src_ids[] = { + CLK_SRC_OSC32, CLK_SRC_IRC, CLK_SRC_ENET_RX_CLK, + CLK_SRC_ENET_TX_CLK, CLK_SRC_GP_CLKIN, CLK_SRC_OSC, + CLK_SRC_PLL0USB, CLK_SRC_PLL0AUDIO, CLK_SRC_PLL1 +}; + +static u32 lpc18xx_cgu_idivbcde_src_ids[] = { + CLK_SRC_OSC32, CLK_SRC_IRC, CLK_SRC_ENET_RX_CLK, + CLK_SRC_ENET_TX_CLK, CLK_SRC_GP_CLKIN, CLK_SRC_OSC, + CLK_SRC_PLL0AUDIO, CLK_SRC_PLL1, CLK_SRC_IDIVA, +}; + +static u32 lpc18xx_cgu_base_irc_src_ids[] = {CLK_SRC_IRC}; + +static u32 lpc18xx_cgu_base_usb0_src_ids[] = {CLK_SRC_PLL0USB}; + +static u32 lpc18xx_cgu_base_common_src_ids[] = { + CLK_SRC_OSC32, CLK_SRC_IRC, CLK_SRC_ENET_RX_CLK, + CLK_SRC_ENET_TX_CLK, CLK_SRC_GP_CLKIN, CLK_SRC_OSC, + CLK_SRC_PLL0AUDIO, CLK_SRC_PLL1, CLK_SRC_IDIVA, + CLK_SRC_IDIVB, CLK_SRC_IDIVC, CLK_SRC_IDIVD, CLK_SRC_IDIVE, +}; + +static u32 lpc18xx_cgu_base_all_src_ids[] = { + CLK_SRC_OSC32, CLK_SRC_IRC, CLK_SRC_ENET_RX_CLK, + CLK_SRC_ENET_TX_CLK, CLK_SRC_GP_CLKIN, CLK_SRC_OSC, + CLK_SRC_PLL0USB, CLK_SRC_PLL0AUDIO, CLK_SRC_PLL1, + CLK_SRC_IDIVA, CLK_SRC_IDIVB, CLK_SRC_IDIVC, + CLK_SRC_IDIVD, CLK_SRC_IDIVE, +}; + +struct lpc18xx_cgu_src_clk_div { + u8 clk_id; + u8 n_parents; + struct clk_divider div; + struct clk_mux mux; + struct clk_gate gate; +}; + +#define LPC1XX_CGU_SRC_CLK_DIV(_id, _width, _table) \ +{ \ + .clk_id = CLK_SRC_ ##_id, \ + .n_parents = ARRAY_SIZE(lpc18xx_cgu_ ##_table), \ + .div = { \ + .shift = 2, \ + .width = _width, \ + }, \ + .mux = { \ + .mask = 0x1f, \ + .shift = 24, \ + .table = lpc18xx_cgu_ ##_table, \ + }, \ + .gate = { \ + .bit_idx = 0, \ + .flags = CLK_GATE_SET_TO_DISABLE, \ + }, \ +} + +static struct lpc18xx_cgu_src_clk_div lpc18xx_cgu_src_clk_divs[] = { + LPC1XX_CGU_SRC_CLK_DIV(IDIVA, 2, idiva_src_ids), + LPC1XX_CGU_SRC_CLK_DIV(IDIVB, 4, idivbcde_src_ids), + LPC1XX_CGU_SRC_CLK_DIV(IDIVC, 4, idivbcde_src_ids), + LPC1XX_CGU_SRC_CLK_DIV(IDIVD, 4, idivbcde_src_ids), + LPC1XX_CGU_SRC_CLK_DIV(IDIVE, 8, idivbcde_src_ids), +}; + +struct lpc18xx_cgu_base_clk { + u8 clk_id; + u8 n_parents; + struct clk_mux mux; + struct clk_gate gate; +}; + +#define LPC1XX_CGU_BASE_CLK(_id, _table, _flags) \ +{ \ + .clk_id = BASE_ ##_id ##_CLK, \ + .n_parents = ARRAY_SIZE(lpc18xx_cgu_ ##_table), \ + .mux = { \ + .mask = 0x1f, \ + .shift = 24, \ + .table = lpc18xx_cgu_ ##_table, \ + .flags = _flags, \ + }, \ + .gate = { \ + .bit_idx = 0, \ + .flags = CLK_GATE_SET_TO_DISABLE, \ + }, \ +} + +static struct lpc18xx_cgu_base_clk lpc18xx_cgu_base_clks[] = { + LPC1XX_CGU_BASE_CLK(SAFE, base_irc_src_ids, CLK_MUX_READ_ONLY), + LPC1XX_CGU_BASE_CLK(USB0, base_usb0_src_ids, 0), + LPC1XX_CGU_BASE_CLK(PERIPH, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(USB1, base_all_src_ids, 0), + LPC1XX_CGU_BASE_CLK(CPU, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(SPIFI, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(SPI, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(PHY_RX, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(PHY_TX, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(APB1, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(APB3, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(LCD, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(ADCHS, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(SDIO, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(SSP0, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(SSP1, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(UART0, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(UART1, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(UART2, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(UART3, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(OUT, base_all_src_ids, 0), + { /* 21 reserved */ }, + { /* 22 reserved */ }, + { /* 23 reserved */ }, + { /* 24 reserved */ }, + LPC1XX_CGU_BASE_CLK(AUDIO, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(CGU_OUT0, base_all_src_ids, 0), + LPC1XX_CGU_BASE_CLK(CGU_OUT1, base_all_src_ids, 0), +}; + +struct lpc18xx_pll { + struct clk_hw hw; + void __iomem *reg; + spinlock_t *lock; + u8 flags; +}; + +#define to_lpc_pll(hw) container_of(hw, struct lpc18xx_pll, hw) + +struct lpc18xx_cgu_pll_clk { + u8 clk_id; + u8 n_parents; + u8 reg_offset; + struct clk_mux mux; + struct clk_gate gate; + struct lpc18xx_pll pll; + const struct clk_ops *pll_ops; +}; + +#define LPC1XX_CGU_CLK_PLL(_id, _table, _pll_ops) \ +{ \ + .clk_id = CLK_SRC_ ##_id, \ + .n_parents = ARRAY_SIZE(lpc18xx_cgu_ ##_table), \ + .reg_offset = LPC18XX_CGU_ ##_id ##_STAT, \ + .mux = { \ + .mask = 0x1f, \ + .shift = 24, \ + .table = lpc18xx_cgu_ ##_table, \ + }, \ + .gate = { \ + .bit_idx = 0, \ + .flags = CLK_GATE_SET_TO_DISABLE, \ + }, \ + .pll_ops = &lpc18xx_ ##_pll_ops, \ +} + +/* + * PLL0 uses a special register value encoding. The compute functions below + * are taken or derived from the LPC1850 user manual (section 12.6.3.3). + */ + +/* Compute PLL0 multiplier from decoded version */ +static u32 lpc18xx_pll0_mdec2msel(u32 x) +{ + int i; + + switch (x) { + case 0x18003: return 1; + case 0x10003: return 2; + default: + for (i = LPC18XX_PLL0_MSEL_MAX + 1; x != 0x4000 && i > 0; i--) + x = ((x ^ x >> 14) & 1) | (x << 1 & 0x7fff); + return i; + } +} +/* Compute PLL0 decoded multiplier from binary version */ +static u32 lpc18xx_pll0_msel2mdec(u32 msel) +{ + u32 i, x = 0x4000; + + switch (msel) { + case 0: return 0; + case 1: return 0x18003; + case 2: return 0x10003; + default: + for (i = msel; i <= LPC18XX_PLL0_MSEL_MAX; i++) + x = ((x ^ x >> 1) & 1) << 14 | (x >> 1 & 0xffff); + return x; + } +} + +/* Compute PLL0 bandwidth SELI reg from multiplier */ +static u32 lpc18xx_pll0_msel2seli(u32 msel) +{ + u32 tmp; + + if (msel > 16384) return 1; + if (msel > 8192) return 2; + if (msel > 2048) return 4; + if (msel >= 501) return 8; + if (msel >= 60) { + tmp = 1024 / (msel + 9); + return ((1024 == (tmp * (msel + 9))) == 0) ? tmp * 4 : (tmp + 1) * 4; + } + + return (msel & 0x3c) + 4; +} + +/* Compute PLL0 bandwidth SELP reg from multiplier */ +static u32 lpc18xx_pll0_msel2selp(u32 msel) +{ + if (msel < 60) + return (msel >> 1) + 1; + + return 31; +} + +static unsigned long lpc18xx_pll0_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct lpc18xx_pll *pll = to_lpc_pll(hw); + u32 ctrl, mdiv, msel, npdiv; + + ctrl = clk_readl(pll->reg + LPC18XX_CGU_PLL0USB_CTRL); + mdiv = clk_readl(pll->reg + LPC18XX_CGU_PLL0USB_MDIV); + npdiv = clk_readl(pll->reg + LPC18XX_CGU_PLL0USB_NP_DIV); + + if (ctrl & LPC18XX_PLL0_CTRL_BYPASS) + return parent_rate; + + if (npdiv != LPC18XX_PLL0_NP_DIVS_1) { + pr_warn("%s: pre/post dividers not supported\n", __func__); + return 0; + } + + msel = lpc18xx_pll0_mdec2msel(mdiv & LPC18XX_PLL0_MDIV_MDEC_MASK); + if (msel) + return 2 * msel * parent_rate; + + pr_warn("%s: unable to calculate rate\n", __func__); + + return 0; +} + +static long lpc18xx_pll0_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + unsigned long m; + + if (*prate < rate) { + pr_warn("%s: pll dividers not supported\n", __func__); + return -EINVAL; + } + + m = DIV_ROUND_UP_ULL(*prate, rate * 2); + if (m <= 0 && m > LPC18XX_PLL0_MSEL_MAX) { + pr_warn("%s: unable to support rate %lu\n", __func__, rate); + return -EINVAL; + } + + return 2 * *prate * m; +} + +static int lpc18xx_pll0_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct lpc18xx_pll *pll = to_lpc_pll(hw); + u32 ctrl, stat, m; + int retry = 3; + + if (parent_rate < rate) { + pr_warn("%s: pll dividers not supported\n", __func__); + return -EINVAL; + } + + m = DIV_ROUND_UP_ULL(parent_rate, rate * 2); + if (m <= 0 && m > LPC18XX_PLL0_MSEL_MAX) { + pr_warn("%s: unable to support rate %lu\n", __func__, rate); + return -EINVAL; + } + + m = lpc18xx_pll0_msel2mdec(m); + m |= lpc18xx_pll0_msel2selp(m) << LPC18XX_PLL0_MDIV_SELP_SHIFT; + m |= lpc18xx_pll0_msel2seli(m) << LPC18XX_PLL0_MDIV_SELI_SHIFT; + + /* Power down PLL, disable clk output and dividers */ + ctrl = clk_readl(pll->reg + LPC18XX_CGU_PLL0USB_CTRL); + ctrl |= LPC18XX_PLL0_CTRL_PD; + ctrl &= ~(LPC18XX_PLL0_CTRL_BYPASS | LPC18XX_PLL0_CTRL_DIRECTI | + LPC18XX_PLL0_CTRL_DIRECTO | LPC18XX_PLL0_CTRL_CLKEN); + clk_writel(ctrl, pll->reg + LPC18XX_CGU_PLL0USB_CTRL); + + /* Configure new PLL settings */ + clk_writel(m, pll->reg + LPC18XX_CGU_PLL0USB_MDIV); + clk_writel(LPC18XX_PLL0_NP_DIVS_1, pll->reg + LPC18XX_CGU_PLL0USB_NP_DIV); + + /* Power up PLL and wait for lock */ + ctrl &= ~LPC18XX_PLL0_CTRL_PD; + clk_writel(ctrl, pll->reg + LPC18XX_CGU_PLL0USB_CTRL); + do { + udelay(10); + stat = clk_readl(pll->reg + LPC18XX_CGU_PLL0USB_STAT); + if (stat & LPC18XX_PLL0_STAT_LOCK) { + ctrl |= LPC18XX_PLL0_CTRL_CLKEN; + clk_writel(ctrl, pll->reg + LPC18XX_CGU_PLL0USB_CTRL); + + return 0; + } + } while (retry--); + + pr_warn("%s: unable to lock pll\n", __func__); + + return -EINVAL; +} + +static const struct clk_ops lpc18xx_pll0_ops = { + .recalc_rate = lpc18xx_pll0_recalc_rate, + .round_rate = lpc18xx_pll0_round_rate, + .set_rate = lpc18xx_pll0_set_rate, +}; + +static unsigned long lpc18xx_pll1_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct lpc18xx_pll *pll = to_lpc_pll(hw); + u16 msel, nsel, psel; + bool direct, fbsel; + u32 stat, ctrl; + + stat = clk_readl(pll->reg + LPC18XX_CGU_PLL1_STAT); + ctrl = clk_readl(pll->reg + LPC18XX_CGU_PLL1_CTRL); + + direct = (ctrl & LPC18XX_PLL1_CTRL_DIRECT) ? true : false; + fbsel = (ctrl & LPC18XX_PLL1_CTRL_FBSEL) ? true : false; + + msel = ((ctrl >> 16) & 0xff) + 1; + nsel = ((ctrl >> 12) & 0x3) + 1; + + if (direct || fbsel) + return msel * (parent_rate / nsel); + + psel = (ctrl >> 8) & 0x3; + psel = 1 << psel; + + return (msel / (2 * psel)) * (parent_rate / nsel); +} + +static const struct clk_ops lpc18xx_pll1_ops = { + .recalc_rate = lpc18xx_pll1_recalc_rate, +}; + +static struct lpc18xx_cgu_pll_clk lpc18xx_cgu_src_clk_plls[] = { + LPC1XX_CGU_CLK_PLL(PLL0USB, pll0_src_ids, pll0_ops), + LPC1XX_CGU_CLK_PLL(PLL0AUDIO, pll0_src_ids, pll0_ops), + LPC1XX_CGU_CLK_PLL(PLL1, pll1_src_ids, pll1_ops), +}; + +static void lpc18xx_fill_parent_names(const char **parent, u32 *id, int size) +{ + int i; + + for (i = 0; i < size; i++) + parent[i] = clk_src_names[id[i]]; +} + +static struct clk *lpc18xx_cgu_register_div(struct lpc18xx_cgu_src_clk_div *clk, + void __iomem *base, int n) +{ + void __iomem *reg = base + LPC18XX_CGU_IDIV_CTRL(n); + const char *name = clk_src_names[clk->clk_id]; + const char *parents[CLK_SRC_MAX]; + + clk->div.reg = reg; + clk->mux.reg = reg; + clk->gate.reg = reg; + + lpc18xx_fill_parent_names(parents, clk->mux.table, clk->n_parents); + + return clk_register_composite(NULL, name, parents, clk->n_parents, + &clk->mux.hw, &clk_mux_ops, + &clk->div.hw, &clk_divider_ops, + &clk->gate.hw, &clk_gate_ops, 0); +} + + +static struct clk *lpc18xx_register_base_clk(struct lpc18xx_cgu_base_clk *clk, + void __iomem *reg_base, int n) +{ + void __iomem *reg = reg_base + LPC18XX_CGU_BASE_CLK(n); + const char *name = clk_base_names[clk->clk_id]; + const char *parents[CLK_SRC_MAX]; + + if (clk->n_parents == 0) + return ERR_PTR(-ENOENT); + + clk->mux.reg = reg; + clk->gate.reg = reg; + + lpc18xx_fill_parent_names(parents, clk->mux.table, clk->n_parents); + + /* SAFE_CLK can not be turned off */ + if (n == BASE_SAFE_CLK) + return clk_register_composite(NULL, name, parents, clk->n_parents, + &clk->mux.hw, &clk_mux_ops, + NULL, NULL, NULL, NULL, 0); + + return clk_register_composite(NULL, name, parents, clk->n_parents, + &clk->mux.hw, &clk_mux_ops, + NULL, NULL, + &clk->gate.hw, &clk_gate_ops, 0); +} + + +static struct clk *lpc18xx_cgu_register_pll(struct lpc18xx_cgu_pll_clk *clk, + void __iomem *base) +{ + const char *name = clk_src_names[clk->clk_id]; + const char *parents[CLK_SRC_MAX]; + + clk->pll.reg = base; + clk->mux.reg = base + clk->reg_offset + LPC18XX_CGU_PLL_CTRL_OFFSET; + clk->gate.reg = base + clk->reg_offset + LPC18XX_CGU_PLL_CTRL_OFFSET; + + lpc18xx_fill_parent_names(parents, clk->mux.table, clk->n_parents); + + return clk_register_composite(NULL, name, parents, clk->n_parents, + &clk->mux.hw, &clk_mux_ops, + &clk->pll.hw, clk->pll_ops, + &clk->gate.hw, &clk_gate_ops, 0); +} + +static void __init lpc18xx_cgu_register_source_clks(struct device_node *np, + void __iomem *base) +{ + const char *parents[CLK_SRC_MAX]; + struct clk *clk; + int i; + + /* Register the internal 12 MHz RC oscillator (IRC) */ + clk = clk_register_fixed_rate(NULL, clk_src_names[CLK_SRC_IRC], + NULL, CLK_IS_ROOT, 12000000); + if (IS_ERR(clk)) + pr_warn("%s: failed to register irc clk\n", __func__); + + /* Register crystal oscillator controlller */ + parents[0] = of_clk_get_parent_name(np, 0); + clk = clk_register_gate(NULL, clk_src_names[CLK_SRC_OSC], parents[0], + 0, base + LPC18XX_CGU_XTAL_OSC_CTRL, + 0, CLK_GATE_SET_TO_DISABLE, NULL); + if (IS_ERR(clk)) + pr_warn("%s: failed to register osc clk\n", __func__); + + /* Register all PLLs */ + for (i = 0; i < ARRAY_SIZE(lpc18xx_cgu_src_clk_plls); i++) { + clk = lpc18xx_cgu_register_pll(&lpc18xx_cgu_src_clk_plls[i], + base); + if (IS_ERR(clk)) + pr_warn("%s: failed to register pll (%d)\n", __func__, i); + } + + /* Register all clock dividers A-E */ + for (i = 0; i < ARRAY_SIZE(lpc18xx_cgu_src_clk_divs); i++) { + clk = lpc18xx_cgu_register_div(&lpc18xx_cgu_src_clk_divs[i], + base, i); + if (IS_ERR(clk)) + pr_warn("%s: failed to register div %d\n", __func__, i); + } +} + +static struct clk *clk_base[BASE_CLK_MAX]; +static struct clk_onecell_data clk_base_data = { + .clks = clk_base, + .clk_num = BASE_CLK_MAX, +}; + +static void __init lpc18xx_cgu_register_base_clks(void __iomem *reg_base) +{ + int i; + + for (i = BASE_SAFE_CLK; i < BASE_CLK_MAX; i++) { + clk_base[i] = lpc18xx_register_base_clk(&lpc18xx_cgu_base_clks[i], + reg_base, i); + if (IS_ERR(clk_base[i]) && PTR_ERR(clk_base[i]) != -ENOENT) + pr_warn("%s: register base clk %d failed\n", __func__, i); + } +} + +static void __init lpc18xx_cgu_init(struct device_node *np) +{ + void __iomem *reg_base; + + reg_base = of_iomap(np, 0); + if (!reg_base) { + pr_warn("%s: failed to map address range\n", __func__); + return; + } + + lpc18xx_cgu_register_source_clks(np, reg_base); + lpc18xx_cgu_register_base_clks(reg_base); + + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_base_data); +} +CLK_OF_DECLARE(lpc18xx_cgu, "nxp,lpc1850-cgu", lpc18xx_cgu_init); diff --git a/drivers/clk/pistachio/clk-pll.c b/drivers/clk/pistachio/clk-pll.c index de537560bf70..e17dada0dd21 100644 --- a/drivers/clk/pistachio/clk-pll.c +++ b/drivers/clk/pistachio/clk-pll.c @@ -6,9 +6,12 @@ * version 2, as published by the Free Software Foundation. */ +#define pr_fmt(fmt) "%s: " fmt, __func__ + #include #include #include +#include #include #include "clk.h" @@ -50,6 +53,18 @@ #define PLL_CTRL4 0x10 #define PLL_FRAC_CTRL4_BYPASS BIT(28) +#define MIN_PFD 9600000UL +#define MIN_VCO_LA 400000000UL +#define MAX_VCO_LA 1600000000UL +#define MIN_VCO_FRAC_INT 600000000UL +#define MAX_VCO_FRAC_INT 1600000000UL +#define MIN_VCO_FRAC_FRAC 600000000UL +#define MAX_VCO_FRAC_FRAC 2400000000UL +#define MIN_OUTPUT_LA 8000000UL +#define MAX_OUTPUT_LA 1600000000UL +#define MIN_OUTPUT_FRAC 12000000UL +#define MAX_OUTPUT_FRAC 1600000000UL + struct pistachio_clk_pll { struct clk_hw hw; void __iomem *base; @@ -67,6 +82,12 @@ static inline void pll_writel(struct pistachio_clk_pll *pll, u32 val, u32 reg) writel(val, pll->base + reg); } +static inline void pll_lock(struct pistachio_clk_pll *pll) +{ + while (!(pll_readl(pll, PLL_STATUS) & PLL_STATUS_LOCK)) + cpu_relax(); +} + static inline u32 do_div_round_closest(u64 dividend, u32 divisor) { dividend += divisor / 2; @@ -124,6 +145,8 @@ static int pll_gf40lp_frac_enable(struct clk_hw *hw) val &= ~PLL_FRAC_CTRL4_BYPASS; pll_writel(pll, val, PLL_CTRL4); + pll_lock(pll); + return 0; } @@ -149,16 +172,29 @@ static int pll_gf40lp_frac_set_rate(struct clk_hw *hw, unsigned long rate, { struct pistachio_clk_pll *pll = to_pistachio_pll(hw); struct pistachio_pll_rate_table *params; - bool was_enabled; - u32 val; + int enabled = pll_gf40lp_frac_is_enabled(hw); + u32 val, vco, old_postdiv1, old_postdiv2; + const char *name = __clk_get_name(hw->clk); - params = pll_get_params(pll, parent_rate, rate); - if (!params) + if (rate < MIN_OUTPUT_FRAC || rate > MAX_OUTPUT_FRAC) return -EINVAL; - was_enabled = pll_gf40lp_frac_is_enabled(hw); - if (!was_enabled) - pll_gf40lp_frac_enable(hw); + params = pll_get_params(pll, parent_rate, rate); + if (!params || !params->refdiv) + return -EINVAL; + + vco = params->fref * params->fbdiv / params->refdiv; + if (vco < MIN_VCO_FRAC_FRAC || vco > MAX_VCO_FRAC_FRAC) + pr_warn("%s: VCO %u is out of range %lu..%lu\n", name, vco, + MIN_VCO_FRAC_FRAC, MAX_VCO_FRAC_FRAC); + + val = params->fref / params->refdiv; + if (val < MIN_PFD) + pr_warn("%s: PFD %u is too low (min %lu)\n", + name, val, MIN_PFD); + if (val > vco / 16) + pr_warn("%s: PFD %u is too high (max %u)\n", + name, val, vco / 16); val = pll_readl(pll, PLL_CTRL1); val &= ~((PLL_CTRL1_REFDIV_MASK << PLL_CTRL1_REFDIV_SHIFT) | @@ -168,6 +204,19 @@ static int pll_gf40lp_frac_set_rate(struct clk_hw *hw, unsigned long rate, pll_writel(pll, val, PLL_CTRL1); val = pll_readl(pll, PLL_CTRL2); + + old_postdiv1 = (val >> PLL_FRAC_CTRL2_POSTDIV1_SHIFT) & + PLL_FRAC_CTRL2_POSTDIV1_MASK; + old_postdiv2 = (val >> PLL_FRAC_CTRL2_POSTDIV2_SHIFT) & + PLL_FRAC_CTRL2_POSTDIV2_MASK; + if (enabled && + (params->postdiv1 != old_postdiv1 || + params->postdiv2 != old_postdiv2)) + pr_warn("%s: changing postdiv while PLL is enabled\n", name); + + if (params->postdiv2 > params->postdiv1) + pr_warn("%s: postdiv2 should not exceed postdiv1\n", name); + val &= ~((PLL_FRAC_CTRL2_FRAC_MASK << PLL_FRAC_CTRL2_FRAC_SHIFT) | (PLL_FRAC_CTRL2_POSTDIV1_MASK << PLL_FRAC_CTRL2_POSTDIV1_SHIFT) | @@ -178,11 +227,8 @@ static int pll_gf40lp_frac_set_rate(struct clk_hw *hw, unsigned long rate, (params->postdiv2 << PLL_FRAC_CTRL2_POSTDIV2_SHIFT); pll_writel(pll, val, PLL_CTRL2); - while (!(pll_readl(pll, PLL_STATUS) & PLL_STATUS_LOCK)) - cpu_relax(); - - if (!was_enabled) - pll_gf40lp_frac_disable(hw); + if (enabled) + pll_lock(pll); return 0; } @@ -241,6 +287,8 @@ static int pll_gf40lp_laint_enable(struct clk_hw *hw) val &= ~PLL_INT_CTRL2_BYPASS; pll_writel(pll, val, PLL_CTRL2); + pll_lock(pll); + return 0; } @@ -266,18 +314,44 @@ static int pll_gf40lp_laint_set_rate(struct clk_hw *hw, unsigned long rate, { struct pistachio_clk_pll *pll = to_pistachio_pll(hw); struct pistachio_pll_rate_table *params; - bool was_enabled; - u32 val; + int enabled = pll_gf40lp_laint_is_enabled(hw); + u32 val, vco, old_postdiv1, old_postdiv2; + const char *name = __clk_get_name(hw->clk); - params = pll_get_params(pll, parent_rate, rate); - if (!params) + if (rate < MIN_OUTPUT_LA || rate > MAX_OUTPUT_LA) return -EINVAL; - was_enabled = pll_gf40lp_laint_is_enabled(hw); - if (!was_enabled) - pll_gf40lp_laint_enable(hw); + params = pll_get_params(pll, parent_rate, rate); + if (!params || !params->refdiv) + return -EINVAL; + + vco = params->fref * params->fbdiv / params->refdiv; + if (vco < MIN_VCO_LA || vco > MAX_VCO_LA) + pr_warn("%s: VCO %u is out of range %lu..%lu\n", name, vco, + MIN_VCO_LA, MAX_VCO_LA); + + val = params->fref / params->refdiv; + if (val < MIN_PFD) + pr_warn("%s: PFD %u is too low (min %lu)\n", + name, val, MIN_PFD); + if (val > vco / 16) + pr_warn("%s: PFD %u is too high (max %u)\n", + name, val, vco / 16); val = pll_readl(pll, PLL_CTRL1); + + old_postdiv1 = (val >> PLL_INT_CTRL1_POSTDIV1_SHIFT) & + PLL_INT_CTRL1_POSTDIV1_MASK; + old_postdiv2 = (val >> PLL_INT_CTRL1_POSTDIV2_SHIFT) & + PLL_INT_CTRL1_POSTDIV2_MASK; + if (enabled && + (params->postdiv1 != old_postdiv1 || + params->postdiv2 != old_postdiv2)) + pr_warn("%s: changing postdiv while PLL is enabled\n", name); + + if (params->postdiv2 > params->postdiv1) + pr_warn("%s: postdiv2 should not exceed postdiv1\n", name); + val &= ~((PLL_CTRL1_REFDIV_MASK << PLL_CTRL1_REFDIV_SHIFT) | (PLL_CTRL1_FBDIV_MASK << PLL_CTRL1_FBDIV_SHIFT) | (PLL_INT_CTRL1_POSTDIV1_MASK << PLL_INT_CTRL1_POSTDIV1_SHIFT) | @@ -288,11 +362,8 @@ static int pll_gf40lp_laint_set_rate(struct clk_hw *hw, unsigned long rate, (params->postdiv2 << PLL_INT_CTRL1_POSTDIV2_SHIFT); pll_writel(pll, val, PLL_CTRL1); - while (!(pll_readl(pll, PLL_STATUS) & PLL_STATUS_LOCK)) - cpu_relax(); - - if (!was_enabled) - pll_gf40lp_laint_disable(hw); + if (enabled) + pll_lock(pll); return 0; } diff --git a/drivers/clk/pxa/clk-pxa.h b/drivers/clk/pxa/clk-pxa.h index b04c5b9c0ea8..d1de805df867 100644 --- a/drivers/clk/pxa/clk-pxa.h +++ b/drivers/clk/pxa/clk-pxa.h @@ -14,7 +14,7 @@ #define _CLK_PXA_ #define PARENTS(name) \ - static const char *name ## _parents[] __initdata + static const char *const 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; \ @@ -72,7 +72,7 @@ struct desc_clk_cken { const char *name; const char *dev_id; const char *con_id; - const char **parent_names; + const char * const *parent_names; struct clk_fixed_factor lp; struct clk_fixed_factor hp; struct clk_gate gate; diff --git a/drivers/clk/rockchip/clk-cpu.c b/drivers/clk/rockchip/clk-cpu.c index 8539c4fd34cc..fb7721bd37e6 100644 --- a/drivers/clk/rockchip/clk-cpu.c +++ b/drivers/clk/rockchip/clk-cpu.c @@ -231,7 +231,7 @@ static int rockchip_cpuclk_notifier_cb(struct notifier_block *nb, } struct clk *rockchip_clk_register_cpuclk(const char *name, - const char **parent_names, u8 num_parents, + const char *const *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) diff --git a/drivers/clk/rockchip/clk-mmc-phase.c b/drivers/clk/rockchip/clk-mmc-phase.c index c842e3b60f21..e9f8df324e7c 100644 --- a/drivers/clk/rockchip/clk-mmc-phase.c +++ b/drivers/clk/rockchip/clk-mmc-phase.c @@ -120,7 +120,7 @@ static const struct clk_ops rockchip_mmc_clk_ops = { }; struct clk *rockchip_clk_register_mmc(const char *name, - const char **parent_names, u8 num_parents, + const char *const *parent_names, u8 num_parents, void __iomem *reg, int shift) { struct clk_init_data init; diff --git a/drivers/clk/rockchip/clk-pll.c b/drivers/clk/rockchip/clk-pll.c index f8d3baf275b2..76027261f7ed 100644 --- a/drivers/clk/rockchip/clk-pll.c +++ b/drivers/clk/rockchip/clk-pll.c @@ -329,10 +329,10 @@ static const struct clk_ops rockchip_rk3066_pll_clk_ops = { */ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type, - const char *name, const char **parent_names, u8 num_parents, - void __iomem *base, int con_offset, int grf_lock_offset, - int lock_shift, int mode_offset, int mode_shift, - struct rockchip_pll_rate_table *rate_table, + const char *name, const char *const *parent_names, + u8 num_parents, void __iomem *base, int con_offset, + int grf_lock_offset, int lock_shift, int mode_offset, + int mode_shift, struct rockchip_pll_rate_table *rate_table, u8 clk_pll_flags, spinlock_t *lock) { const char *pll_parents[3]; diff --git a/drivers/clk/rockchip/clk-rk3188.c b/drivers/clk/rockchip/clk-rk3188.c index 556ce041d371..e4f9d472f1ff 100644 --- a/drivers/clk/rockchip/clk-rk3188.c +++ b/drivers/clk/rockchip/clk-rk3188.c @@ -26,7 +26,7 @@ enum rk3188_plls { apll, cpll, dpll, gpll, }; -struct rockchip_pll_rate_table rk3188_pll_rates[] = { +static struct rockchip_pll_rate_table rk3188_pll_rates[] = { RK3066_PLL_RATE(2208000000, 1, 92, 1), RK3066_PLL_RATE(2184000000, 1, 91, 1), RK3066_PLL_RATE(2160000000, 1, 90, 1), diff --git a/drivers/clk/rockchip/clk-rk3288.c b/drivers/clk/rockchip/clk-rk3288.c index d17eb4528a28..4f817ed9e6ee 100644 --- a/drivers/clk/rockchip/clk-rk3288.c +++ b/drivers/clk/rockchip/clk-rk3288.c @@ -27,7 +27,7 @@ enum rk3288_plls { apll, dpll, cpll, gpll, npll, }; -struct rockchip_pll_rate_table rk3288_pll_rates[] = { +static struct rockchip_pll_rate_table rk3288_pll_rates[] = { RK3066_PLL_RATE(2208000000, 1, 92, 1), RK3066_PLL_RATE(2184000000, 1, 91, 1), RK3066_PLL_RATE(2160000000, 1, 90, 1), diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c index edb5d489ae61..052b94db0ff9 100644 --- a/drivers/clk/rockchip/clk.c +++ b/drivers/clk/rockchip/clk.c @@ -39,7 +39,7 @@ * sometimes without one of those components. */ static struct clk *rockchip_clk_register_branch(const char *name, - const char **parent_names, u8 num_parents, void __iomem *base, + const char *const *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, struct clk_div_table *div_table, int gate_offset, @@ -103,8 +103,8 @@ static struct clk *rockchip_clk_register_branch(const char *name, } 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, + const char *const *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) { @@ -297,7 +297,7 @@ void __init rockchip_clk_register_branches( } void __init rockchip_clk_register_armclk(unsigned int lookup_id, - const char *name, const char **parent_names, + const char *name, const char *const *parent_names, u8 num_parents, const struct rockchip_cpuclk_reg_data *reg_data, const struct rockchip_cpuclk_rate_table *rates, diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h index e63cafe893e1..6b092673048a 100644 --- a/drivers/clk/rockchip/clk.h +++ b/drivers/clk/rockchip/clk.h @@ -108,7 +108,7 @@ struct rockchip_pll_rate_table { struct rockchip_pll_clock { unsigned int id; const char *name; - const char **parent_names; + const char *const *parent_names; u8 num_parents; unsigned long flags; int con_offset; @@ -140,10 +140,10 @@ struct rockchip_pll_clock { } struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type, - const char *name, const char **parent_names, u8 num_parents, - void __iomem *base, int con_offset, int grf_lock_offset, - int lock_shift, int reg_mode, int mode_shift, - struct rockchip_pll_rate_table *rate_table, + const char *name, const char *const *parent_names, + u8 num_parents, void __iomem *base, int con_offset, + int grf_lock_offset, int lock_shift, int reg_mode, + int mode_shift, struct rockchip_pll_rate_table *rate_table, u8 clk_pll_flags, spinlock_t *lock); struct rockchip_cpuclk_clksel { @@ -173,16 +173,16 @@ struct rockchip_cpuclk_reg_data { }; struct clk *rockchip_clk_register_cpuclk(const char *name, - const char **parent_names, u8 num_parents, + const char *const *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 clk *rockchip_clk_register_mmc(const char *name, - const char **parent_names, u8 num_parents, + const char *const *parent_names, u8 num_parents, void __iomem *reg, int shift); -#define PNAME(x) static const char *x[] __initdata +#define PNAME(x) static const char *const x[] __initconst enum rockchip_clk_branch_type { branch_composite, @@ -197,7 +197,7 @@ struct rockchip_clk_branch { unsigned int id; enum rockchip_clk_branch_type branch_type; const char *name; - const char **parent_names; + const char *const *parent_names; u8 num_parents; unsigned long flags; int muxdiv_offset; @@ -403,7 +403,7 @@ void rockchip_clk_register_branches(struct rockchip_clk_branch *clk_list, 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 char *const *parent_names, u8 num_parents, const struct rockchip_cpuclk_reg_data *reg_data, const struct rockchip_cpuclk_rate_table *rates, int nrates); diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile index a17683b2cf27..5f6833ea355d 100644 --- a/drivers/clk/samsung/Makefile +++ b/drivers/clk/samsung/Makefile @@ -2,7 +2,7 @@ # Samsung Clock specific Makefile # -obj-$(CONFIG_COMMON_CLK) += clk.o clk-pll.o +obj-$(CONFIG_COMMON_CLK) += clk.o clk-pll.o clk-cpu.o obj-$(CONFIG_SOC_EXYNOS3250) += clk-exynos3250.o obj-$(CONFIG_ARCH_EXYNOS4) += clk-exynos4.o obj-$(CONFIG_SOC_EXYNOS4415) += clk-exynos4415.o diff --git a/drivers/clk/samsung/clk-cpu.c b/drivers/clk/samsung/clk-cpu.c new file mode 100644 index 000000000000..3a1fe07cfe9e --- /dev/null +++ b/drivers/clk/samsung/clk-cpu.c @@ -0,0 +1,349 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * Author: Thomas Abraham + * + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * Bartlomiej Zolnierkiewicz + * + * 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. + * + * This file contains the utility function to register CPU clock for Samsung + * Exynos platforms. 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 +#include "clk-cpu.h" + +#define E4210_SRC_CPU 0x0 +#define E4210_STAT_CPU 0x200 +#define E4210_DIV_CPU0 0x300 +#define E4210_DIV_CPU1 0x304 +#define E4210_DIV_STAT_CPU0 0x400 +#define E4210_DIV_STAT_CPU1 0x404 + +#define E4210_DIV0_RATIO0_MASK 0x7 +#define E4210_DIV1_HPM_MASK (0x7 << 4) +#define E4210_DIV1_COPY_MASK (0x7 << 0) +#define E4210_MUX_HPM_MASK (1 << 20) +#define E4210_DIV0_ATB_SHIFT 16 +#define E4210_DIV0_ATB_MASK (DIV_MASK << E4210_DIV0_ATB_SHIFT) + +#define MAX_DIV 8 +#define DIV_MASK 7 +#define DIV_MASK_ALL 0xffffffff +#define MUX_MASK 7 + +/* + * Helper function to wait until divider(s) have stabilized after the divider + * value has changed. + */ +static void wait_until_divider_stable(void __iomem *div_reg, unsigned long mask) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(10); + + do { + if (!(readl(div_reg) & mask)) + return; + } while (time_before(jiffies, timeout)); + + if (!(readl(div_reg) & mask)) + return; + + pr_err("%s: timeout in divider stablization\n", __func__); +} + +/* + * Helper function to wait until mux has stabilized after the mux selection + * value was changed. + */ +static void wait_until_mux_stable(void __iomem *mux_reg, u32 mux_pos, + unsigned long mux_value) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(10); + + do { + if (((readl(mux_reg) >> mux_pos) & MUX_MASK) == mux_value) + return; + } while (time_before(jiffies, timeout)); + + if (((readl(mux_reg) >> mux_pos) & MUX_MASK) == mux_value) + return; + + pr_err("%s: re-parenting mux timed-out\n", __func__); +} + +/* common round rate callback useable for all types of CPU clocks */ +static long exynos_cpuclk_round_rate(struct clk_hw *hw, + unsigned long drate, unsigned long *prate) +{ + struct clk *parent = __clk_get_parent(hw->clk); + *prate = __clk_round_rate(parent, drate); + return *prate; +} + +/* common recalc rate callback useable for all types of CPU clocks */ +static unsigned long exynos_cpuclk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + /* + * The CPU clock output (armclk) rate is the same as its parent + * rate. Although there exist certain dividers inside the CPU + * clock block that could be used to divide the parent clock, + * the driver does not make use of them currently, except during + * frequency transitions. + */ + return parent_rate; +} + +static const struct clk_ops exynos_cpuclk_clk_ops = { + .recalc_rate = exynos_cpuclk_recalc_rate, + .round_rate = exynos_cpuclk_round_rate, +}; + +/* + * Helper function to set the 'safe' dividers for the CPU clock. The parameters + * div and mask contain the divider value and the register bit mask of the + * dividers to be programmed. + */ +static void exynos_set_safe_div(void __iomem *base, unsigned long div, + unsigned long mask) +{ + unsigned long div0; + + div0 = readl(base + E4210_DIV_CPU0); + div0 = (div0 & ~mask) | (div & mask); + writel(div0, base + E4210_DIV_CPU0); + wait_until_divider_stable(base + E4210_DIV_STAT_CPU0, mask); +} + +/* handler for pre-rate change notification from parent clock */ +static int exynos_cpuclk_pre_rate_change(struct clk_notifier_data *ndata, + struct exynos_cpuclk *cpuclk, void __iomem *base) +{ + const struct exynos_cpuclk_cfg_data *cfg_data = cpuclk->cfg; + unsigned long alt_prate = clk_get_rate(cpuclk->alt_parent); + unsigned long alt_div = 0, alt_div_mask = DIV_MASK; + unsigned long div0, div1 = 0, mux_reg; + + /* find out the divider values to use for clock data */ + while ((cfg_data->prate * 1000) != ndata->new_rate) { + if (cfg_data->prate == 0) + return -EINVAL; + cfg_data++; + } + + spin_lock(cpuclk->lock); + + /* + * For the selected PLL clock frequency, get the pre-defined divider + * values. If the clock for sclk_hpm is not sourced from apll, then + * the values for DIV_COPY and DIV_HPM dividers need not be set. + */ + div0 = cfg_data->div0; + if (test_bit(CLK_CPU_HAS_DIV1, &cpuclk->flags)) { + div1 = cfg_data->div1; + if (readl(base + E4210_SRC_CPU) & E4210_MUX_HPM_MASK) + div1 = readl(base + E4210_DIV_CPU1) & + (E4210_DIV1_HPM_MASK | E4210_DIV1_COPY_MASK); + } + + /* + * 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_prate until the dividers are + * set. Also workaround the issue of the dividers being set to lower + * values before the parent clock speed is set to new lower speed + * (this can result in too high speed of armclk output clocks). + */ + if (alt_prate > ndata->old_rate || ndata->old_rate > ndata->new_rate) { + unsigned long tmp_rate = min(ndata->old_rate, ndata->new_rate); + + alt_div = DIV_ROUND_UP(alt_prate, tmp_rate) - 1; + WARN_ON(alt_div >= MAX_DIV); + + if (test_bit(CLK_CPU_NEEDS_DEBUG_ALT_DIV, &cpuclk->flags)) { + /* + * In Exynos4210, ATB clock parent is also mout_core. So + * ATB clock also needs to be mantained at safe speed. + */ + alt_div |= E4210_DIV0_ATB_MASK; + alt_div_mask |= E4210_DIV0_ATB_MASK; + } + exynos_set_safe_div(base, alt_div, alt_div_mask); + div0 |= alt_div; + } + + /* select sclk_mpll as the alternate parent */ + mux_reg = readl(base + E4210_SRC_CPU); + writel(mux_reg | (1 << 16), base + E4210_SRC_CPU); + wait_until_mux_stable(base + E4210_STAT_CPU, 16, 2); + + /* alternate parent is active now. set the dividers */ + writel(div0, base + E4210_DIV_CPU0); + wait_until_divider_stable(base + E4210_DIV_STAT_CPU0, DIV_MASK_ALL); + + if (test_bit(CLK_CPU_HAS_DIV1, &cpuclk->flags)) { + writel(div1, base + E4210_DIV_CPU1); + wait_until_divider_stable(base + E4210_DIV_STAT_CPU1, + DIV_MASK_ALL); + } + + spin_unlock(cpuclk->lock); + return 0; +} + +/* handler for post-rate change notification from parent clock */ +static int exynos_cpuclk_post_rate_change(struct clk_notifier_data *ndata, + struct exynos_cpuclk *cpuclk, void __iomem *base) +{ + const struct exynos_cpuclk_cfg_data *cfg_data = cpuclk->cfg; + unsigned long div = 0, div_mask = DIV_MASK; + unsigned long mux_reg; + + /* find out the divider values to use for clock data */ + if (test_bit(CLK_CPU_NEEDS_DEBUG_ALT_DIV, &cpuclk->flags)) { + while ((cfg_data->prate * 1000) != ndata->new_rate) { + if (cfg_data->prate == 0) + return -EINVAL; + cfg_data++; + } + } + + spin_lock(cpuclk->lock); + + /* select mout_apll as the alternate parent */ + mux_reg = readl(base + E4210_SRC_CPU); + writel(mux_reg & ~(1 << 16), base + E4210_SRC_CPU); + wait_until_mux_stable(base + E4210_STAT_CPU, 16, 1); + + if (test_bit(CLK_CPU_NEEDS_DEBUG_ALT_DIV, &cpuclk->flags)) { + div |= (cfg_data->div0 & E4210_DIV0_ATB_MASK); + div_mask |= E4210_DIV0_ATB_MASK; + } + + exynos_set_safe_div(base, div, div_mask); + spin_unlock(cpuclk->lock); + return 0; +} + +/* + * This notifier function is called for the pre-rate and post-rate change + * notifications of the parent clock of cpuclk. + */ +static int exynos_cpuclk_notifier_cb(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct clk_notifier_data *ndata = data; + struct exynos_cpuclk *cpuclk; + void __iomem *base; + int err = 0; + + cpuclk = container_of(nb, struct exynos_cpuclk, clk_nb); + base = cpuclk->ctrl_base; + + if (event == PRE_RATE_CHANGE) + err = exynos_cpuclk_pre_rate_change(ndata, cpuclk, base); + else if (event == POST_RATE_CHANGE) + err = exynos_cpuclk_post_rate_change(ndata, cpuclk, base); + + return notifier_from_errno(err); +} + +/* helper function to register a CPU clock */ +int __init exynos_register_cpu_clock(struct samsung_clk_provider *ctx, + unsigned int lookup_id, const char *name, const char *parent, + const char *alt_parent, unsigned long offset, + const struct exynos_cpuclk_cfg_data *cfg, + unsigned long num_cfgs, unsigned long flags) +{ + struct exynos_cpuclk *cpuclk; + struct clk_init_data init; + struct clk *clk; + int ret = 0; + + cpuclk = kzalloc(sizeof(*cpuclk), GFP_KERNEL); + if (!cpuclk) + return -ENOMEM; + + init.name = name; + init.flags = CLK_SET_RATE_PARENT; + init.parent_names = &parent; + init.num_parents = 1; + init.ops = &exynos_cpuclk_clk_ops; + + cpuclk->hw.init = &init; + cpuclk->ctrl_base = ctx->reg_base + offset; + cpuclk->lock = &ctx->lock; + cpuclk->flags = flags; + cpuclk->clk_nb.notifier_call = exynos_cpuclk_notifier_cb; + + cpuclk->alt_parent = __clk_lookup(alt_parent); + if (!cpuclk->alt_parent) { + pr_err("%s: could not lookup alternate parent %s\n", + __func__, alt_parent); + ret = -EINVAL; + goto free_cpuclk; + } + + clk = __clk_lookup(parent); + if (!clk) { + pr_err("%s: could not lookup parent clock %s\n", + __func__, parent); + 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; + } + + cpuclk->cfg = kmemdup(cfg, sizeof(*cfg) * num_cfgs, GFP_KERNEL); + if (!cpuclk->cfg) { + pr_err("%s: could not allocate memory for cpuclk data\n", + __func__); + ret = -ENOMEM; + goto unregister_clk_nb; + } + + clk = 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_cpuclk_data; + } + + samsung_clk_add_lookup(ctx, clk, lookup_id); + return 0; + +free_cpuclk_data: + kfree(cpuclk->cfg); +unregister_clk_nb: + clk_notifier_unregister(__clk_lookup(parent), &cpuclk->clk_nb); +free_cpuclk: + kfree(cpuclk); + return ret; +} diff --git a/drivers/clk/samsung/clk-cpu.h b/drivers/clk/samsung/clk-cpu.h new file mode 100644 index 000000000000..37874d3c3165 --- /dev/null +++ b/drivers/clk/samsung/clk-cpu.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * 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. + * + * Common Clock Framework support for all PLL's in Samsung platforms +*/ + +#ifndef __SAMSUNG_CLK_CPU_H +#define __SAMSUNG_CLK_CPU_H + +#include "clk.h" + +/** + * struct exynos_cpuclk_data: config data to setup cpu clocks. + * @prate: frequency of the primary parent clock (in KHz). + * @div0: value to be programmed in the div_cpu0 register. + * @div1: value to be programmed in the div_cpu1 register. + * + * This structure holds the divider configuration data for dividers in the CPU + * clock domain. The parent frequency at which these divider values are valid is + * specified in @prate. The @prate is the frequency of the primary parent clock. + * For CPU clock domains that do not have a DIV1 register, the @div1 member + * value is not used. + */ +struct exynos_cpuclk_cfg_data { + unsigned long prate; + unsigned long div0; + unsigned long div1; +}; + +/** + * struct exynos_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. + * @ctrl_base: base address of the clock controller. + * @lock: cpu clock domain register access lock. + * @cfg: cpu clock rate configuration data. + * @num_cfgs: number of array elements in @cfg array. + * @clk_nb: clock notifier registered for changes in clock speed of the + * primary parent clock. + * @flags: configuration flags for the CPU clock. + * + * This structure holds information required for programming the CPU clock for + * various clock speeds. + */ +struct exynos_cpuclk { + struct clk_hw hw; + struct clk *alt_parent; + void __iomem *ctrl_base; + spinlock_t *lock; + const struct exynos_cpuclk_cfg_data *cfg; + const unsigned long num_cfgs; + struct notifier_block clk_nb; + unsigned long flags; + +/* The CPU clock registers has DIV1 configuration register */ +#define CLK_CPU_HAS_DIV1 (1 << 0) +/* When ALT parent is active, debug clocks need safe divider values */ +#define CLK_CPU_NEEDS_DEBUG_ALT_DIV (1 << 1) +}; + +extern int __init exynos_register_cpu_clock(struct samsung_clk_provider *ctx, + unsigned int lookup_id, const char *name, + const char *parent, const char *alt_parent, + unsigned long offset, + const struct exynos_cpuclk_cfg_data *cfg, + unsigned long num_cfgs, unsigned long flags); + +#endif /* __SAMSUNG_CLK_CPU_H */ diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index 714d6ba782c8..cae2c048488d 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -19,6 +19,7 @@ #include #include "clk.h" +#include "clk-cpu.h" /* Exynos4 clock controller register offsets */ #define SRC_LEFTBUS 0x4200 @@ -534,7 +535,8 @@ static struct samsung_fixed_factor_clock exynos4x12_fixed_factor_clks[] __initda /* list of mux clocks supported in all exynos4 soc's */ 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"), + CLK_SET_RATE_PARENT | CLK_RECALC_NEW_RATES, 0, + "mout_apll"), 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), @@ -1378,6 +1380,22 @@ static void __init exynos4x12_core_down_clock(void) __raw_writel(0x0, reg_base + E4X12_PWR_CTRL2); } +#define E4210_CPU_DIV0(apll, pclk_dbg, atb, periph, corem1, corem0) \ + (((apll) << 24) | ((pclk_dbg) << 20) | ((atb) << 16) | \ + ((periph) << 12) | ((corem1) << 8) | ((corem0) << 4)) +#define E4210_CPU_DIV1(hpm, copy) \ + (((hpm) << 4) | ((copy) << 0)) + +static const struct exynos_cpuclk_cfg_data e4210_armclk_d[] __initconst = { + { 1200000, E4210_CPU_DIV0(7, 1, 4, 3, 7, 3), E4210_CPU_DIV1(0, 5), }, + { 1000000, E4210_CPU_DIV0(7, 1, 4, 3, 7, 3), E4210_CPU_DIV1(0, 4), }, + { 800000, E4210_CPU_DIV0(7, 1, 3, 3, 7, 3), E4210_CPU_DIV1(0, 3), }, + { 500000, E4210_CPU_DIV0(7, 1, 3, 3, 7, 3), E4210_CPU_DIV1(0, 3), }, + { 400000, E4210_CPU_DIV0(7, 1, 3, 3, 7, 3), E4210_CPU_DIV1(0, 3), }, + { 200000, E4210_CPU_DIV0(0, 1, 1, 1, 3, 1), E4210_CPU_DIV1(0, 3), }, + { 0 }, +}; + /* register exynos4 clocks */ static void __init exynos4_clk_init(struct device_node *np, enum exynos4_soc soc) @@ -1455,6 +1473,10 @@ static void __init exynos4_clk_init(struct device_node *np, samsung_clk_register_fixed_factor(ctx, exynos4210_fixed_factor_clks, ARRAY_SIZE(exynos4210_fixed_factor_clks)); + exynos_register_cpu_clock(ctx, CLK_ARM_CLK, "armclk", + mout_core_p4210[0], mout_core_p4210[1], 0x14200, + e4210_armclk_d, ARRAY_SIZE(e4210_armclk_d), + CLK_CPU_NEEDS_DEBUG_ALT_DIV | CLK_CPU_HAS_DIV1); } else { samsung_clk_register_mux(ctx, exynos4x12_mux_clks, ARRAY_SIZE(exynos4x12_mux_clks)); diff --git a/drivers/clk/samsung/clk-exynos5260.c b/drivers/clk/samsung/clk-exynos5260.c index e2e5193d1049..06f96eb7cf93 100644 --- a/drivers/clk/samsung/clk-exynos5260.c +++ b/drivers/clk/samsung/clk-exynos5260.c @@ -94,7 +94,7 @@ PNAME(mout_aud_pll_user_p) = {"fin_pll", "fout_aud_pll"}; PNAME(mout_sclk_aud_i2s_p) = {"mout_aud_pll_user", "ioclk_i2s_cdclk"}; PNAME(mout_sclk_aud_pcm_p) = {"mout_aud_pll_user", "ioclk_pcm_extclk"}; -struct samsung_mux_clock aud_mux_clks[] __initdata = { +static struct samsung_mux_clock aud_mux_clks[] __initdata = { MUX(AUD_MOUT_AUD_PLL_USER, "mout_aud_pll_user", mout_aud_pll_user_p, MUX_SEL_AUD, 0, 1), MUX(AUD_MOUT_SCLK_AUD_I2S, "mout_sclk_aud_i2s", mout_sclk_aud_i2s_p, @@ -103,7 +103,7 @@ struct samsung_mux_clock aud_mux_clks[] __initdata = { MUX_SEL_AUD, 8, 1), }; -struct samsung_div_clock aud_div_clks[] __initdata = { +static struct samsung_div_clock aud_div_clks[] __initdata = { DIV(AUD_DOUT_ACLK_AUD_131, "dout_aclk_aud_131", "mout_aud_pll_user", DIV_AUD0, 0, 4), @@ -115,7 +115,7 @@ struct samsung_div_clock aud_div_clks[] __initdata = { DIV_AUD1, 12, 4), }; -struct samsung_gate_clock aud_gate_clks[] __initdata = { +static struct samsung_gate_clock aud_gate_clks[] __initdata = { GATE(AUD_SCLK_I2S, "sclk_aud_i2s", "dout_sclk_aud_i2s", EN_SCLK_AUD, 0, CLK_SET_RATE_PARENT, 0), GATE(AUD_SCLK_PCM, "sclk_aud_pcm", "dout_sclk_aud_pcm", @@ -135,7 +135,7 @@ struct samsung_gate_clock aud_gate_clks[] __initdata = { static void __init exynos5260_clk_aud_init(struct device_node *np) { - struct samsung_cmu_info cmu = {0}; + struct samsung_cmu_info cmu = { NULL }; cmu.mux_clks = aud_mux_clks; cmu.nr_mux_clks = ARRAY_SIZE(aud_mux_clks); @@ -203,7 +203,7 @@ PNAME(mout_phyclk_mipi_dphy_4lmrxclk_esc0_user_p) = {"fin_pll", PNAME(mout_sclk_hdmi_spdif_p) = {"fin_pll", "ioclk_spdif_extclk", "dout_aclk_peri_aud", "phyclk_hdmi_phy_ref_cko"}; -struct samsung_mux_clock disp_mux_clks[] __initdata = { +static struct samsung_mux_clock disp_mux_clks[] __initdata = { MUX(DISP_MOUT_ACLK_DISP_333_USER, "mout_aclk_disp_333_user", mout_aclk_disp_333_user_p, MUX_SEL_DISP0, 0, 1), @@ -272,7 +272,7 @@ struct samsung_mux_clock disp_mux_clks[] __initdata = { MUX_SEL_DISP4, 4, 2), }; -struct samsung_div_clock disp_div_clks[] __initdata = { +static struct samsung_div_clock disp_div_clks[] __initdata = { DIV(DISP_DOUT_PCLK_DISP_111, "dout_pclk_disp_111", "mout_aclk_disp_222_user", DIV_DISP, 8, 4), @@ -285,7 +285,7 @@ struct samsung_div_clock disp_div_clks[] __initdata = { DIV_DISP, 16, 4), }; -struct samsung_gate_clock disp_gate_clks[] __initdata = { +static struct samsung_gate_clock disp_gate_clks[] __initdata = { GATE(DISP_MOUT_HDMI_PHY_PIXEL_USER, "sclk_hdmi_link_i_pixel", "mout_phyclk_hdmi_phy_pixel_clko_user", EN_SCLK_DISP0, 26, CLK_SET_RATE_PARENT, 0), @@ -325,7 +325,7 @@ struct samsung_gate_clock disp_gate_clks[] __initdata = { static void __init exynos5260_clk_disp_init(struct device_node *np) { - struct samsung_cmu_info cmu = {0}; + struct samsung_cmu_info cmu = { NULL }; cmu.mux_clks = disp_mux_clks; cmu.nr_mux_clks = ARRAY_SIZE(disp_mux_clks); @@ -363,13 +363,13 @@ static unsigned long egl_clk_regs[] __initdata = { PNAME(mout_egl_b_p) = {"mout_egl_pll", "dout_bus_pll"}; PNAME(mout_egl_pll_p) = {"fin_pll", "fout_egl_pll"}; -struct samsung_mux_clock egl_mux_clks[] __initdata = { +static struct samsung_mux_clock egl_mux_clks[] __initdata = { MUX(EGL_MOUT_EGL_PLL, "mout_egl_pll", mout_egl_pll_p, MUX_SEL_EGL, 4, 1), MUX(EGL_MOUT_EGL_B, "mout_egl_b", mout_egl_b_p, MUX_SEL_EGL, 16, 1), }; -struct samsung_div_clock egl_div_clks[] __initdata = { +static struct samsung_div_clock egl_div_clks[] __initdata = { DIV(EGL_DOUT_EGL1, "dout_egl1", "mout_egl_b", DIV_EGL, 0, 3), DIV(EGL_DOUT_EGL2, "dout_egl2", "dout_egl1", DIV_EGL, 4, 3), DIV(EGL_DOUT_ACLK_EGL, "dout_aclk_egl", "dout_egl2", DIV_EGL, 8, 3), @@ -389,7 +389,7 @@ static struct samsung_pll_clock egl_pll_clks[] __initdata = { static void __init exynos5260_clk_egl_init(struct device_node *np) { - struct samsung_cmu_info cmu = {0}; + struct samsung_cmu_info cmu = { NULL }; cmu.pll_clks = egl_pll_clks; cmu.nr_pll_clks = ARRAY_SIZE(egl_pll_clks); @@ -433,7 +433,7 @@ PNAME(mout_phyclk_usbdrd30_pipe_pclk_user_p) = {"fin_pll", PNAME(mout_phyclk_usbdrd30_phyclock_user_p) = {"fin_pll", "phyclk_usbdrd30_udrd30_phyclock"}; -struct samsung_mux_clock fsys_mux_clks[] __initdata = { +static struct samsung_mux_clock fsys_mux_clks[] __initdata = { MUX(FSYS_MOUT_PHYCLK_USBDRD30_PHYCLOCK_USER, "mout_phyclk_usbdrd30_phyclock_user", mout_phyclk_usbdrd30_phyclock_user_p, @@ -456,7 +456,7 @@ struct samsung_mux_clock fsys_mux_clks[] __initdata = { MUX_SEL_FSYS1, 16, 1), }; -struct samsung_gate_clock fsys_gate_clks[] __initdata = { +static struct samsung_gate_clock fsys_gate_clks[] __initdata = { GATE(FSYS_PHYCLK_USBHOST20, "phyclk_usbhost20_phyclock", "mout_phyclk_usbdrd30_phyclock_user", EN_SCLK_FSYS, 1, 0, 0), @@ -491,7 +491,7 @@ struct samsung_gate_clock fsys_gate_clks[] __initdata = { static void __init exynos5260_clk_fsys_init(struct device_node *np) { - struct samsung_cmu_info cmu = {0}; + struct samsung_cmu_info cmu = { NULL }; cmu.mux_clks = fsys_mux_clks; cmu.nr_mux_clks = ARRAY_SIZE(fsys_mux_clks); @@ -537,18 +537,18 @@ static unsigned long g2d_clk_regs[] __initdata = { PNAME(mout_aclk_g2d_333_user_p) = {"fin_pll", "dout_aclk_g2d_333"}; -struct samsung_mux_clock g2d_mux_clks[] __initdata = { +static struct samsung_mux_clock g2d_mux_clks[] __initdata = { MUX(G2D_MOUT_ACLK_G2D_333_USER, "mout_aclk_g2d_333_user", mout_aclk_g2d_333_user_p, MUX_SEL_G2D, 0, 1), }; -struct samsung_div_clock g2d_div_clks[] __initdata = { +static struct samsung_div_clock g2d_div_clks[] __initdata = { DIV(G2D_DOUT_PCLK_G2D_83, "dout_pclk_g2d_83", "mout_aclk_g2d_333_user", DIV_G2D, 0, 3), }; -struct samsung_gate_clock g2d_gate_clks[] __initdata = { +static struct samsung_gate_clock g2d_gate_clks[] __initdata = { GATE(G2D_CLK_G2D, "clk_g2d", "mout_aclk_g2d_333_user", EN_IP_G2D, 4, 0, 0), GATE(G2D_CLK_JPEG, "clk_jpeg", "mout_aclk_g2d_333_user", @@ -580,7 +580,7 @@ struct samsung_gate_clock g2d_gate_clks[] __initdata = { static void __init exynos5260_clk_g2d_init(struct device_node *np) { - struct samsung_cmu_info cmu = {0}; + struct samsung_cmu_info cmu = { NULL }; cmu.mux_clks = g2d_mux_clks; cmu.nr_mux_clks = ARRAY_SIZE(g2d_mux_clks); @@ -617,17 +617,17 @@ static unsigned long g3d_clk_regs[] __initdata = { PNAME(mout_g3d_pll_p) = {"fin_pll", "fout_g3d_pll"}; -struct samsung_mux_clock g3d_mux_clks[] __initdata = { +static struct samsung_mux_clock g3d_mux_clks[] __initdata = { MUX(G3D_MOUT_G3D_PLL, "mout_g3d_pll", mout_g3d_pll_p, MUX_SEL_G3D, 0, 1), }; -struct samsung_div_clock g3d_div_clks[] __initdata = { +static struct samsung_div_clock g3d_div_clks[] __initdata = { DIV(G3D_DOUT_PCLK_G3D, "dout_pclk_g3d", "dout_aclk_g3d", DIV_G3D, 0, 3), DIV(G3D_DOUT_ACLK_G3D, "dout_aclk_g3d", "mout_g3d_pll", DIV_G3D, 4, 3), }; -struct samsung_gate_clock g3d_gate_clks[] __initdata = { +static struct samsung_gate_clock g3d_gate_clks[] __initdata = { GATE(G3D_CLK_G3D, "clk_g3d", "dout_aclk_g3d", EN_IP_G3D, 2, 0, 0), GATE(G3D_CLK_G3D_HPM, "clk_g3d_hpm", "dout_aclk_g3d", EN_IP_G3D, 3, 0, 0), @@ -641,7 +641,7 @@ static struct samsung_pll_clock g3d_pll_clks[] __initdata = { static void __init exynos5260_clk_g3d_init(struct device_node *np) { - struct samsung_cmu_info cmu = {0}; + struct samsung_cmu_info cmu = { NULL }; cmu.pll_clks = g3d_pll_clks; cmu.nr_pll_clks = ARRAY_SIZE(g3d_pll_clks); @@ -694,7 +694,7 @@ PNAME(mout_aclk_m2m_400_user_p) = {"fin_pll", "dout_aclk_gscl_400"}; PNAME(mout_aclk_gscl_fimc_user_p) = {"fin_pll", "dout_aclk_gscl_400"}; PNAME(mout_aclk_csis_p) = {"dout_aclk_csis_200", "mout_aclk_gscl_fimc_user"}; -struct samsung_mux_clock gscl_mux_clks[] __initdata = { +static struct samsung_mux_clock gscl_mux_clks[] __initdata = { MUX(GSCL_MOUT_ACLK_GSCL_333_USER, "mout_aclk_gscl_333_user", mout_aclk_gscl_333_user_p, MUX_SEL_GSCL, 0, 1), @@ -708,7 +708,7 @@ struct samsung_mux_clock gscl_mux_clks[] __initdata = { MUX_SEL_GSCL, 24, 1), }; -struct samsung_div_clock gscl_div_clks[] __initdata = { +static struct samsung_div_clock gscl_div_clks[] __initdata = { DIV(GSCL_DOUT_PCLK_M2M_100, "dout_pclk_m2m_100", "mout_aclk_m2m_400_user", DIV_GSCL, 0, 3), @@ -717,7 +717,7 @@ struct samsung_div_clock gscl_div_clks[] __initdata = { DIV_GSCL, 4, 3), }; -struct samsung_gate_clock gscl_gate_clks[] __initdata = { +static struct samsung_gate_clock gscl_gate_clks[] __initdata = { GATE(GSCL_SCLK_CSIS0_WRAP, "sclk_csis0_wrap", "dout_aclk_csis_200", EN_SCLK_GSCL_FIMC, 0, CLK_SET_RATE_PARENT, 0), GATE(GSCL_SCLK_CSIS1_WRAP, "sclk_csis1_wrap", "dout_aclk_csis_200", @@ -776,7 +776,7 @@ struct samsung_gate_clock gscl_gate_clks[] __initdata = { static void __init exynos5260_clk_gscl_init(struct device_node *np) { - struct samsung_cmu_info cmu = {0}; + struct samsung_cmu_info cmu = { NULL }; cmu.mux_clks = gscl_mux_clks; cmu.nr_mux_clks = ARRAY_SIZE(gscl_mux_clks); @@ -813,14 +813,14 @@ static unsigned long isp_clk_regs[] __initdata = { PNAME(mout_isp_400_user_p) = {"fin_pll", "dout_aclk_isp1_400"}; PNAME(mout_isp_266_user_p) = {"fin_pll", "dout_aclk_isp1_266"}; -struct samsung_mux_clock isp_mux_clks[] __initdata = { +static struct samsung_mux_clock isp_mux_clks[] __initdata = { MUX(ISP_MOUT_ISP_266_USER, "mout_isp_266_user", mout_isp_266_user_p, MUX_SEL_ISP0, 0, 1), MUX(ISP_MOUT_ISP_400_USER, "mout_isp_400_user", mout_isp_400_user_p, MUX_SEL_ISP0, 4, 1), }; -struct samsung_div_clock isp_div_clks[] __initdata = { +static struct samsung_div_clock isp_div_clks[] __initdata = { DIV(ISP_DOUT_PCLK_ISP_66, "dout_pclk_isp_66", "mout_kfc", DIV_ISP, 0, 3), DIV(ISP_DOUT_PCLK_ISP_133, "dout_pclk_isp_133", "mout_kfc", @@ -832,7 +832,7 @@ struct samsung_div_clock isp_div_clks[] __initdata = { DIV(ISP_DOUT_SCLK_MPWM, "dout_sclk_mpwm", "mout_kfc", DIV_ISP, 20, 2), }; -struct samsung_gate_clock isp_gate_clks[] __initdata = { +static struct samsung_gate_clock isp_gate_clks[] __initdata = { GATE(ISP_CLK_GIC, "clk_isp_gic", "mout_aclk_isp1_266", EN_IP_ISP0, 15, 0, 0), @@ -895,7 +895,7 @@ struct samsung_gate_clock isp_gate_clks[] __initdata = { static void __init exynos5260_clk_isp_init(struct device_node *np) { - struct samsung_cmu_info cmu = {0}; + struct samsung_cmu_info cmu = { NULL }; cmu.mux_clks = isp_mux_clks; cmu.nr_mux_clks = ARRAY_SIZE(isp_mux_clks); @@ -934,13 +934,13 @@ static unsigned long kfc_clk_regs[] __initdata = { PNAME(mout_kfc_pll_p) = {"fin_pll", "fout_kfc_pll"}; PNAME(mout_kfc_p) = {"mout_kfc_pll", "dout_media_pll"}; -struct samsung_mux_clock kfc_mux_clks[] __initdata = { +static struct samsung_mux_clock kfc_mux_clks[] __initdata = { MUX(KFC_MOUT_KFC_PLL, "mout_kfc_pll", mout_kfc_pll_p, MUX_SEL_KFC0, 0, 1), MUX(KFC_MOUT_KFC, "mout_kfc", mout_kfc_p, MUX_SEL_KFC2, 0, 1), }; -struct samsung_div_clock kfc_div_clks[] __initdata = { +static struct samsung_div_clock kfc_div_clks[] __initdata = { DIV(KFC_DOUT_KFC1, "dout_kfc1", "mout_kfc", DIV_KFC, 0, 3), DIV(KFC_DOUT_KFC2, "dout_kfc2", "dout_kfc1", DIV_KFC, 4, 3), DIV(KFC_DOUT_KFC_ATCLK, "dout_kfc_atclk", "dout_kfc2", DIV_KFC, 8, 3), @@ -959,7 +959,7 @@ static struct samsung_pll_clock kfc_pll_clks[] __initdata = { static void __init exynos5260_clk_kfc_init(struct device_node *np) { - struct samsung_cmu_info cmu = {0}; + struct samsung_cmu_info cmu = { NULL }; cmu.pll_clks = kfc_pll_clks; cmu.nr_pll_clks = ARRAY_SIZE(kfc_pll_clks); @@ -993,18 +993,18 @@ static unsigned long mfc_clk_regs[] __initdata = { PNAME(mout_aclk_mfc_333_user_p) = {"fin_pll", "dout_aclk_mfc_333"}; -struct samsung_mux_clock mfc_mux_clks[] __initdata = { +static struct samsung_mux_clock mfc_mux_clks[] __initdata = { MUX(MFC_MOUT_ACLK_MFC_333_USER, "mout_aclk_mfc_333_user", mout_aclk_mfc_333_user_p, MUX_SEL_MFC, 0, 1), }; -struct samsung_div_clock mfc_div_clks[] __initdata = { +static struct samsung_div_clock mfc_div_clks[] __initdata = { DIV(MFC_DOUT_PCLK_MFC_83, "dout_pclk_mfc_83", "mout_aclk_mfc_333_user", DIV_MFC, 0, 3), }; -struct samsung_gate_clock mfc_gate_clks[] __initdata = { +static struct samsung_gate_clock mfc_gate_clks[] __initdata = { GATE(MFC_CLK_MFC, "clk_mfc", "mout_aclk_mfc_333_user", EN_IP_MFC, 1, 0, 0), GATE(MFC_CLK_SMMU2_MFCM0, "clk_smmu2_mfcm0", "mout_aclk_mfc_333_user", @@ -1015,7 +1015,7 @@ struct samsung_gate_clock mfc_gate_clks[] __initdata = { static void __init exynos5260_clk_mfc_init(struct device_node *np) { - struct samsung_cmu_info cmu = {0}; + struct samsung_cmu_info cmu = { NULL }; cmu.mux_clks = mfc_mux_clks; cmu.nr_mux_clks = ARRAY_SIZE(mfc_mux_clks); @@ -1078,7 +1078,7 @@ PNAME(mout_mif_drex2x_p) = {"dout_mem_pll", "dout_bus_pll"}; PNAME(mout_clkm_phy_p) = {"mout_mif_drex", "dout_media_pll"}; PNAME(mout_clk2x_phy_p) = {"mout_mif_drex2x", "dout_media_pll"}; -struct samsung_mux_clock mif_mux_clks[] __initdata = { +static struct samsung_mux_clock mif_mux_clks[] __initdata = { MUX(MIF_MOUT_MEM_PLL, "mout_mem_pll", mout_mem_pll_p, MUX_SEL_MIF, 0, 1), MUX(MIF_MOUT_BUS_PLL, "mout_bus_pll", mout_bus_pll_p, @@ -1095,7 +1095,7 @@ struct samsung_mux_clock mif_mux_clks[] __initdata = { MUX_SEL_MIF, 24, 1), }; -struct samsung_div_clock mif_div_clks[] __initdata = { +static struct samsung_div_clock mif_div_clks[] __initdata = { DIV(MIF_DOUT_MEDIA_PLL, "dout_media_pll", "mout_media_pll", DIV_MIF, 0, 3), DIV(MIF_DOUT_MEM_PLL, "dout_mem_pll", "mout_mem_pll", @@ -1114,7 +1114,7 @@ struct samsung_div_clock mif_div_clks[] __initdata = { DIV_MIF, 28, 4), }; -struct samsung_gate_clock mif_gate_clks[] __initdata = { +static struct samsung_gate_clock mif_gate_clks[] __initdata = { GATE(MIF_CLK_LPDDR3PHY_WRAP0, "clk_lpddr3phy_wrap0", "dout_clk2x_phy", EN_IP_MIF, 12, CLK_IGNORE_UNUSED, 0), GATE(MIF_CLK_LPDDR3PHY_WRAP1, "clk_lpddr3phy_wrap1", "dout_clk2x_phy", @@ -1162,7 +1162,7 @@ static struct samsung_pll_clock mif_pll_clks[] __initdata = { static void __init exynos5260_clk_mif_init(struct device_node *np) { - struct samsung_cmu_info cmu = {0}; + struct samsung_cmu_info cmu = { NULL }; cmu.pll_clks = mif_pll_clks; cmu.nr_pll_clks = ARRAY_SIZE(mif_pll_clks); @@ -1221,7 +1221,7 @@ PNAME(mout_sclk_i2scod_p) = {"ioclk_i2s_cdclk", "fin_pll", "dout_aclk_peri_aud", PNAME(mout_sclk_spdif_p) = {"ioclk_spdif_extclk", "fin_pll", "dout_aclk_peri_aud", "phyclk_hdmi_phy_ref_cko"}; -struct samsung_mux_clock peri_mux_clks[] __initdata = { +static struct samsung_mux_clock peri_mux_clks[] __initdata = { MUX(PERI_MOUT_SCLK_PCM, "mout_sclk_pcm", mout_sclk_pcm_p, MUX_SEL_PERI1, 4, 2), MUX(PERI_MOUT_SCLK_I2SCOD, "mout_sclk_i2scod", mout_sclk_i2scod_p, @@ -1230,12 +1230,12 @@ struct samsung_mux_clock peri_mux_clks[] __initdata = { MUX_SEL_PERI1, 20, 2), }; -struct samsung_div_clock peri_div_clks[] __initdata = { +static struct samsung_div_clock peri_div_clks[] __initdata = { DIV(PERI_DOUT_PCM, "dout_pcm", "mout_sclk_pcm", DIV_PERI, 0, 8), DIV(PERI_DOUT_I2S, "dout_i2s", "mout_sclk_i2scod", DIV_PERI, 8, 6), }; -struct samsung_gate_clock peri_gate_clks[] __initdata = { +static struct samsung_gate_clock peri_gate_clks[] __initdata = { GATE(PERI_SCLK_PCM1, "sclk_pcm1", "dout_pcm", EN_SCLK_PERI, 0, CLK_SET_RATE_PARENT, 0), GATE(PERI_SCLK_I2S, "sclk_i2s", "dout_i2s", EN_SCLK_PERI, 1, @@ -1370,7 +1370,7 @@ struct samsung_gate_clock peri_gate_clks[] __initdata = { static void __init exynos5260_clk_peri_init(struct device_node *np) { - struct samsung_cmu_info cmu = {0}; + struct samsung_cmu_info cmu = { NULL }; cmu.mux_clks = peri_mux_clks; cmu.nr_mux_clks = ARRAY_SIZE(peri_mux_clks); @@ -1432,7 +1432,7 @@ static unsigned long top_clk_regs[] __initdata = { }; /* fixed rate clocks generated inside the soc */ -struct samsung_fixed_rate_clock fixed_rate_clks[] __initdata = { +static struct samsung_fixed_rate_clock fixed_rate_clks[] __initdata = { FRATE(PHYCLK_DPTX_PHY_CH3_TXD_CLK, "phyclk_dptx_phy_ch3_txd_clk", NULL, CLK_IS_ROOT, 270000000), FRATE(PHYCLK_DPTX_PHY_CH2_TXD_CLK, "phyclk_dptx_phy_ch2_txd_clk", NULL, @@ -1519,7 +1519,7 @@ PNAME(mout_sclk_fsys_mmc1_sdclkin_b_p) = {"mout_sclk_fsys_mmc1_sdclkin_a", PNAME(mout_sclk_fsys_mmc2_sdclkin_b_p) = {"mout_sclk_fsys_mmc2_sdclkin_a", "mout_mediatop_pll_user"}; -struct samsung_mux_clock top_mux_clks[] __initdata = { +static struct samsung_mux_clock top_mux_clks[] __initdata = { MUX(TOP_MOUT_MEDIATOP_PLL_USER, "mout_mediatop_pll_user", mout_mediatop_pll_user_p, MUX_SEL_TOP_PLL0, 0, 1), @@ -1679,7 +1679,7 @@ struct samsung_mux_clock top_mux_clks[] __initdata = { MUX_SEL_TOP_GSCL, 20, 1), }; -struct samsung_div_clock top_div_clks[] __initdata = { +static struct samsung_div_clock top_div_clks[] __initdata = { DIV(TOP_DOUT_ACLK_G2D_333, "dout_aclk_g2d_333", "mout_aclk_g2d_333", DIV_TOP_G2D_MFC, 0, 3), DIV(TOP_DOUT_ACLK_MFC_333, "dout_aclk_mfc_333", "mout_aclk_mfc_333", @@ -1800,7 +1800,7 @@ struct samsung_div_clock top_div_clks[] __initdata = { }; -struct samsung_gate_clock top_gate_clks[] __initdata = { +static struct samsung_gate_clock top_gate_clks[] __initdata = { GATE(TOP_SCLK_MMC0, "sclk_fsys_mmc0_sdclkin", "dout_sclk_fsys_mmc0_sdclkin_b", EN_SCLK_TOP, 7, CLK_SET_RATE_PARENT, 0), @@ -1826,7 +1826,7 @@ static struct samsung_pll_clock top_pll_clks[] __initdata = { static void __init exynos5260_clk_top_init(struct device_node *np) { - struct samsung_cmu_info cmu = {0}; + struct samsung_cmu_info cmu = { NULL }; cmu.pll_clks = top_pll_clks; cmu.nr_pll_clks = ARRAY_SIZE(top_pll_clks); diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c index bea4a173eef5..a1d731ca8f48 100644 --- a/drivers/clk/samsung/clk-exynos5420.c +++ b/drivers/clk/samsung/clk-exynos5420.c @@ -504,7 +504,7 @@ static struct samsung_fixed_factor_clock FFACTOR(0, "ff_dout_spll2", "mout_sclk_spll", 1, 2, 0), }; -struct samsung_mux_clock exynos5800_mux_clks[] __initdata = { +static struct samsung_mux_clock exynos5800_mux_clks[] __initdata = { MUX(0, "mout_aclk400_isp", mout_group3_5800_p, SRC_TOP0, 0, 3), MUX(0, "mout_aclk400_mscl", mout_group3_5800_p, SRC_TOP0, 4, 3), MUX(0, "mout_aclk400_wcore", mout_group2_5800_p, SRC_TOP0, 16, 3), @@ -553,7 +553,7 @@ struct samsung_mux_clock exynos5800_mux_clks[] __initdata = { MUX(0, "mout_fimd1", mout_group2_p, SRC_DISP10, 4, 3), }; -struct samsung_div_clock exynos5800_div_clks[] __initdata = { +static struct samsung_div_clock exynos5800_div_clks[] __initdata = { DIV(0, "dout_aclk400_wcore", "mout_aclk400_wcore", DIV_TOP0, 16, 3), DIV(0, "dout_aclk550_cam", "mout_aclk550_cam", @@ -569,14 +569,14 @@ struct samsung_div_clock exynos5800_div_clks[] __initdata = { DIV(0, "dout_sclk_sw", "sclk_spll", DIV_TOP9, 24, 6), }; -struct samsung_gate_clock exynos5800_gate_clks[] __initdata = { +static struct samsung_gate_clock exynos5800_gate_clks[] __initdata = { GATE(CLK_ACLK550_CAM, "aclk550_cam", "mout_user_aclk550_cam", GATE_BUS_TOP, 24, 0, 0), GATE(CLK_ACLK432_SCALER, "aclk432_scaler", "mout_user_aclk432_scaler", GATE_BUS_TOP, 27, 0, 0), }; -struct samsung_mux_clock exynos5420_mux_clks[] __initdata = { +static struct samsung_mux_clock exynos5420_mux_clks[] __initdata = { MUX(0, "sclk_bpll", mout_bpll_p, TOP_SPARE2, 0, 1), MUX(0, "mout_aclk400_wcore_bpll", mout_aclk400_wcore_bpll_p, TOP_SPARE2, 4, 1), @@ -606,7 +606,7 @@ struct samsung_mux_clock exynos5420_mux_clks[] __initdata = { MUX(0, "mout_fimd1", mout_group3_p, SRC_DISP10, 4, 1), }; -struct samsung_div_clock exynos5420_div_clks[] __initdata = { +static struct samsung_div_clock exynos5420_div_clks[] __initdata = { DIV(0, "dout_aclk400_wcore", "mout_aclk400_wcore_bpll", DIV_TOP0, 16, 3), }; diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c index 9e04ae2bb4d7..39c95649d3d0 100644 --- a/drivers/clk/samsung/clk-exynos5433.c +++ b/drivers/clk/samsung/clk-exynos5433.c @@ -835,6 +835,7 @@ static unsigned long cpif_clk_regs[] __initdata = { MPHY_PLL_CON1, MPHY_PLL_FREQ_DET, MUX_SEL_CPIF0, + DIV_CPIF, ENABLE_SCLK_CPIF, }; @@ -1389,7 +1390,7 @@ static struct samsung_gate_clock mif_gate_clks[] __initdata = { /* ENABLE_ACLK_MIF2 */ GATE(CLK_ACLK_MIFND_266, "aclk_mifnd_266", "div_aclk_mif_266", - ENABLE_ACLK_MIF2, 20, 0, 0), + ENABLE_ACLK_MIF2, 20, CLK_IGNORE_UNUSED, 0), GATE(CLK_ACLK_PPMU_DREX1S3, "aclk_ppmu_drex1s3", "div_aclk_drex1", ENABLE_ACLK_MIF2, 17, CLK_IGNORE_UNUSED, 0), GATE(CLK_ACLK_PPMU_DREX1S1, "aclk_ppmu_drex1s1", "div_aclk_drex1", @@ -1832,39 +1833,39 @@ static struct samsung_gate_clock peris_gate_clks[] __initdata = { /* ENABLE_PCLK_PERIS_SECURE_TZPC */ GATE(CLK_PCLK_TZPC12, "pclk_tzpc12", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_TZPC, 12, 0, 0), + ENABLE_PCLK_PERIS_SECURE_TZPC, 12, CLK_IGNORE_UNUSED, 0), GATE(CLK_PCLK_TZPC11, "pclk_tzpc11", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_TZPC, 11, 0, 0), + ENABLE_PCLK_PERIS_SECURE_TZPC, 11, CLK_IGNORE_UNUSED, 0), GATE(CLK_PCLK_TZPC10, "pclk_tzpc10", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_TZPC, 10, 0, 0), + ENABLE_PCLK_PERIS_SECURE_TZPC, 10, CLK_IGNORE_UNUSED, 0), GATE(CLK_PCLK_TZPC9, "pclk_tzpc9", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_TZPC, 9, 0, 0), + ENABLE_PCLK_PERIS_SECURE_TZPC, 9, CLK_IGNORE_UNUSED, 0), GATE(CLK_PCLK_TZPC8, "pclk_tzpc8", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_TZPC, 8, 0, 0), + ENABLE_PCLK_PERIS_SECURE_TZPC, 8, CLK_IGNORE_UNUSED, 0), GATE(CLK_PCLK_TZPC7, "pclk_tzpc7", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_TZPC, 7, 0, 0), + ENABLE_PCLK_PERIS_SECURE_TZPC, 7, CLK_IGNORE_UNUSED, 0), GATE(CLK_PCLK_TZPC6, "pclk_tzpc6", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_TZPC, 6, 0, 0), + ENABLE_PCLK_PERIS_SECURE_TZPC, 6, CLK_IGNORE_UNUSED, 0), GATE(CLK_PCLK_TZPC5, "pclk_tzpc5", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_TZPC, 5, 0, 0), + ENABLE_PCLK_PERIS_SECURE_TZPC, 5, CLK_IGNORE_UNUSED, 0), GATE(CLK_PCLK_TZPC4, "pclk_tzpc4", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_TZPC, 4, 0, 0), + ENABLE_PCLK_PERIS_SECURE_TZPC, 4, CLK_IGNORE_UNUSED, 0), GATE(CLK_PCLK_TZPC3, "pclk_tzpc3", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_TZPC, 3, 0, 0), + ENABLE_PCLK_PERIS_SECURE_TZPC, 3, CLK_IGNORE_UNUSED, 0), GATE(CLK_PCLK_TZPC2, "pclk_tzpc2", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_TZPC, 2, 0, 0), + ENABLE_PCLK_PERIS_SECURE_TZPC, 2, CLK_IGNORE_UNUSED, 0), GATE(CLK_PCLK_TZPC1, "pclk_tzpc1", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_TZPC, 1, 0, 0), + ENABLE_PCLK_PERIS_SECURE_TZPC, 1, CLK_IGNORE_UNUSED, 0), GATE(CLK_PCLK_TZPC0, "pclk_tzpc0", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_TZPC, 0, 0, 0), + ENABLE_PCLK_PERIS_SECURE_TZPC, 0, CLK_IGNORE_UNUSED, 0), /* ENABLE_PCLK_PERIS_SECURE_SECKEY_APBIF */ GATE(CLK_PCLK_SECKEY_APBIF, "pclk_seckey_apbif", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_SECKEY_APBIF, 0, 0, 0), + ENABLE_PCLK_PERIS_SECURE_SECKEY_APBIF, 0, CLK_IGNORE_UNUSED, 0), /* ENABLE_PCLK_PERIS_SECURE_CHIPID_APBIF */ GATE(CLK_PCLK_CHIPID_APBIF, "pclk_chipid_apbif", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_CHIPID_APBIF, 0, 0, 0), + ENABLE_PCLK_PERIS_SECURE_CHIPID_APBIF, 0, CLK_IGNORE_UNUSED, 0), /* ENABLE_PCLK_PERIS_SECURE_TOPRTC */ GATE(CLK_PCLK_TOPRTC, "pclk_toprtc", "aclk_peris_66", @@ -1895,11 +1896,11 @@ static struct samsung_gate_clock peris_gate_clks[] __initdata = { /* ENABLE_SCLK_PERIS_SECURE_SECKEY */ GATE(CLK_SCLK_SECKEY, "sclk_seckey", "oscclk_efuse_common", - ENABLE_SCLK_PERIS_SECURE_SECKEY, 0, 0, 0), + ENABLE_SCLK_PERIS_SECURE_SECKEY, 0, CLK_IGNORE_UNUSED, 0), /* ENABLE_SCLK_PERIS_SECURE_CHIPID */ GATE(CLK_SCLK_CHIPID, "sclk_chipid", "oscclk_efuse_common", - ENABLE_SCLK_PERIS_SECURE_CHIPID, 0, 0, 0), + ENABLE_SCLK_PERIS_SECURE_CHIPID, 0, CLK_IGNORE_UNUSED, 0), /* ENABLE_SCLK_PERIS_SECURE_TOPRTC */ GATE(CLK_SCLK_TOPRTC, "sclk_toprtc", "oscclk_efuse_common", @@ -3286,10 +3287,10 @@ static struct samsung_pll_clock g3d_pll_clks[] __initdata = { static struct samsung_mux_clock g3d_mux_clks[] __initdata = { /* MUX_SEL_G3D */ - MUX(CLK_MOUT_ACLK_G3D_400, "mout_aclk_g3d_400", mout_aclk_g3d_400_p, - MUX_SEL_G3D, 8, 1), - MUX(CLK_MOUT_G3D_PLL, "mout_g3d_pll", mout_g3d_pll_p, - MUX_SEL_G3D, 0, 1), + MUX_F(CLK_MOUT_ACLK_G3D_400, "mout_aclk_g3d_400", mout_aclk_g3d_400_p, + MUX_SEL_G3D, 8, 1, CLK_SET_RATE_PARENT, 0), + MUX_F(CLK_MOUT_G3D_PLL, "mout_g3d_pll", mout_g3d_pll_p, + MUX_SEL_G3D, 0, 1, CLK_SET_RATE_PARENT, 0), }; static struct samsung_div_clock g3d_div_clks[] __initdata = { @@ -3298,8 +3299,8 @@ static struct samsung_div_clock g3d_div_clks[] __initdata = { 8, 2), DIV(CLK_DIV_PCLK_G3D, "div_pclk_g3d", "div_aclk_g3d", DIV_G3D, 4, 3), - DIV(CLK_DIV_ACLK_G3D, "div_aclk_g3d", "mout_aclk_g3d_400", DIV_G3D, - 0, 3), + DIV_F(CLK_DIV_ACLK_G3D, "div_aclk_g3d", "mout_aclk_g3d_400", DIV_G3D, + 0, 3, CLK_SET_RATE_PARENT, 0), }; static struct samsung_gate_clock g3d_gate_clks[] __initdata = { @@ -3309,9 +3310,9 @@ static struct samsung_gate_clock g3d_gate_clks[] __initdata = { GATE(CLK_ACLK_BTS_G3D0, "aclk_bts_g3d0", "div_aclk_g3d", ENABLE_ACLK_G3D, 6, 0, 0), GATE(CLK_ACLK_ASYNCAPBS_G3D, "aclk_asyncapbs_g3d", "div_pclk_g3d", - ENABLE_ACLK_G3D, 5, 0, 0), + ENABLE_ACLK_G3D, 5, CLK_IGNORE_UNUSED, 0), GATE(CLK_ACLK_ASYNCAPBM_G3D, "aclk_asyncapbm_g3d", "div_aclk_g3d", - ENABLE_ACLK_G3D, 4, 0, 0), + ENABLE_ACLK_G3D, 4, CLK_IGNORE_UNUSED, 0), GATE(CLK_ACLK_AHB2APB_G3DP, "aclk_ahb2apb_g3dp", "div_pclk_g3d", ENABLE_ACLK_G3D, 3, CLK_IGNORE_UNUSED, 0), GATE(CLK_ACLK_G3DNP_150, "aclk_g3dnp_150", "div_pclk_g3d", @@ -3319,7 +3320,7 @@ static struct samsung_gate_clock g3d_gate_clks[] __initdata = { GATE(CLK_ACLK_G3DND_600, "aclk_g3dnd_600", "div_aclk_g3d", ENABLE_ACLK_G3D, 1, CLK_IGNORE_UNUSED, 0), GATE(CLK_ACLK_G3D, "aclk_g3d", "div_aclk_g3d", - ENABLE_ACLK_G3D, 0, 0, 0), + ENABLE_ACLK_G3D, 0, CLK_SET_RATE_PARENT, 0), /* ENABLE_PCLK_G3D */ GATE(CLK_PCLK_BTS_G3D1, "pclk_bts_g3d1", "div_pclk_g3d", @@ -3582,7 +3583,7 @@ static struct samsung_pll_clock apollo_pll_clks[] __initdata = { static struct samsung_mux_clock apollo_mux_clks[] __initdata = { /* MUX_SEL_APOLLO0 */ MUX_F(CLK_MOUT_APOLLO_PLL, "mout_apollo_pll", mout_apollo_pll_p, - MUX_SEL_APOLLO0, 0, 1, 0, CLK_MUX_READ_ONLY), + MUX_SEL_APOLLO0, 0, 1, CLK_SET_RATE_PARENT, 0), /* MUX_SEL_APOLLO1 */ MUX(CLK_MOUT_BUS_PLL_APOLLO_USER, "mout_bus_pll_apollo_user", @@ -3590,7 +3591,7 @@ static struct samsung_mux_clock apollo_mux_clks[] __initdata = { /* MUX_SEL_APOLLO2 */ MUX_F(CLK_MOUT_APOLLO, "mout_apollo", mout_apollo_p, MUX_SEL_APOLLO2, - 0, 1, 0, CLK_MUX_READ_ONLY), + 0, 1, CLK_SET_RATE_PARENT, 0), }; static struct samsung_div_clock apollo_div_clks[] __initdata = { @@ -3611,11 +3612,9 @@ static struct samsung_div_clock apollo_div_clks[] __initdata = { DIV_APOLLO0, 8, 3, CLK_GET_RATE_NOCACHE, CLK_DIVIDER_READ_ONLY), DIV_F(CLK_DIV_APOLLO2, "div_apollo2", "div_apollo1", - DIV_APOLLO0, 4, 3, CLK_GET_RATE_NOCACHE, - CLK_DIVIDER_READ_ONLY), + DIV_APOLLO0, 4, 3, CLK_SET_RATE_PARENT, 0), DIV_F(CLK_DIV_APOLLO1, "div_apollo1", "mout_apollo", - DIV_APOLLO0, 0, 3, CLK_GET_RATE_NOCACHE, - CLK_DIVIDER_READ_ONLY), + DIV_APOLLO0, 0, 3, CLK_SET_RATE_PARENT, 0), /* DIV_APOLLO1 */ DIV_F(CLK_DIV_SCLK_HPM_APOLLO, "div_sclk_hpm_apollo", "mout_apollo", @@ -3666,7 +3665,8 @@ static struct samsung_gate_clock apollo_gate_clks[] __initdata = { GATE(CLK_SCLK_HPM_APOLLO, "sclk_hpm_apollo", "div_sclk_hpm_apollo", ENABLE_SCLK_APOLLO, 1, CLK_IGNORE_UNUSED, 0), GATE(CLK_SCLK_APOLLO, "sclk_apollo", "div_apollo2", - ENABLE_SCLK_APOLLO, 0, CLK_IGNORE_UNUSED, 0), + ENABLE_SCLK_APOLLO, 0, + CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0), }; static struct samsung_cmu_info apollo_cmu_info __initdata = { @@ -3775,7 +3775,7 @@ static struct samsung_pll_clock atlas_pll_clks[] __initdata = { static struct samsung_mux_clock atlas_mux_clks[] __initdata = { /* MUX_SEL_ATLAS0 */ MUX_F(CLK_MOUT_ATLAS_PLL, "mout_atlas_pll", mout_atlas_pll_p, - MUX_SEL_ATLAS0, 0, 1, 0, CLK_MUX_READ_ONLY), + MUX_SEL_ATLAS0, 0, 1, CLK_SET_RATE_PARENT, 0), /* MUX_SEL_ATLAS1 */ MUX(CLK_MOUT_BUS_PLL_ATLAS_USER, "mout_bus_pll_atlas_user", @@ -3783,7 +3783,7 @@ static struct samsung_mux_clock atlas_mux_clks[] __initdata = { /* MUX_SEL_ATLAS2 */ MUX_F(CLK_MOUT_ATLAS, "mout_atlas", mout_atlas_p, MUX_SEL_ATLAS2, - 0, 1, 0, CLK_MUX_READ_ONLY), + 0, 1, CLK_SET_RATE_PARENT, 0), }; static struct samsung_div_clock atlas_div_clks[] __initdata = { @@ -3804,11 +3804,9 @@ static struct samsung_div_clock atlas_div_clks[] __initdata = { DIV_ATLAS0, 8, 3, CLK_GET_RATE_NOCACHE, CLK_DIVIDER_READ_ONLY), DIV_F(CLK_DIV_ATLAS2, "div_atlas2", "div_atlas1", - DIV_ATLAS0, 4, 3, CLK_GET_RATE_NOCACHE, - CLK_DIVIDER_READ_ONLY), + DIV_ATLAS0, 4, 3, CLK_SET_RATE_PARENT, 0), DIV_F(CLK_DIV_ATLAS1, "div_atlas1", "mout_atlas", - DIV_ATLAS0, 0, 3, CLK_GET_RATE_NOCACHE, - CLK_DIVIDER_READ_ONLY), + DIV_ATLAS0, 0, 3, CLK_SET_RATE_PARENT, 0), /* DIV_ATLAS1 */ DIV_F(CLK_DIV_SCLK_HPM_ATLAS, "div_sclk_hpm_atlas", "mout_atlas", @@ -3885,7 +3883,8 @@ static struct samsung_gate_clock atlas_gate_clks[] __initdata = { GATE(CLK_ATCLK, "atclk", "div_atclk_atlas", ENABLE_SCLK_ATLAS, 1, CLK_IGNORE_UNUSED, 0), GATE(CLK_SCLK_ATLAS, "sclk_atlas", "div_atlas2", - ENABLE_SCLK_ATLAS, 0, CLK_IGNORE_UNUSED, 0), + ENABLE_SCLK_ATLAS, 0, + CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0), }; static struct samsung_cmu_info atlas_cmu_info __initdata = { diff --git a/drivers/clk/samsung/clk-pll.c b/drivers/clk/samsung/clk-pll.c index 9d70e5c03804..bebc61b5fce1 100644 --- a/drivers/clk/samsung/clk-pll.c +++ b/drivers/clk/samsung/clk-pll.c @@ -1156,7 +1156,7 @@ static const struct clk_ops samsung_pll2650xx_clk_min_ops = { }; static void __init _samsung_clk_register_pll(struct samsung_clk_provider *ctx, - struct samsung_pll_clock *pll_clk, + const struct samsung_pll_clock *pll_clk, void __iomem *base) { struct samsung_clk_pll *pll; @@ -1303,7 +1303,7 @@ static void __init _samsung_clk_register_pll(struct samsung_clk_provider *ctx, } void __init samsung_clk_register_pll(struct samsung_clk_provider *ctx, - struct samsung_pll_clock *pll_list, + const struct samsung_pll_clock *pll_list, unsigned int nr_pll, void __iomem *base) { int cnt; diff --git a/drivers/clk/samsung/clk-s3c2410-dclk.c b/drivers/clk/samsung/clk-s3c2410-dclk.c index f4f29ed6bd25..e56df5064889 100644 --- a/drivers/clk/samsung/clk-s3c2410-dclk.c +++ b/drivers/clk/samsung/clk-s3c2410-dclk.c @@ -81,13 +81,13 @@ static int s3c24xx_clkout_set_parent(struct clk_hw *hw, u8 index) return ret; } -const struct clk_ops s3c24xx_clkout_ops = { +static const struct clk_ops s3c24xx_clkout_ops = { .get_parent = s3c24xx_clkout_get_parent, .set_parent = s3c24xx_clkout_set_parent, .determine_rate = __clk_mux_determine_rate, }; -struct clk *s3c24xx_register_clkout(struct device *dev, const char *name, +static struct clk *s3c24xx_register_clkout(struct device *dev, const char *name, const char **parent_names, u8 num_parents, u8 shift, u32 mask) { @@ -404,7 +404,7 @@ static struct s3c24xx_dclk_drv_data dclk_variants[] = { }, }; -static struct platform_device_id s3c24xx_dclk_driver_ids[] = { +static const struct platform_device_id s3c24xx_dclk_driver_ids[] = { { .name = "s3c2410-dclk", .driver_data = (kernel_ulong_t)&dclk_variants[S3C2410], diff --git a/drivers/clk/samsung/clk-s5pv210.c b/drivers/clk/samsung/clk-s5pv210.c index e668e479a697..cf7e8fa7b624 100644 --- a/drivers/clk/samsung/clk-s5pv210.c +++ b/drivers/clk/samsung/clk-s5pv210.c @@ -169,44 +169,44 @@ static inline void s5pv210_clk_sleep_init(void) { } #endif /* Mux parent lists. */ -static const char *fin_pll_p[] __initdata = { +static const char *const fin_pll_p[] __initconst = { "xxti", "xusbxti" }; -static const char *mout_apll_p[] __initdata = { +static const char *const mout_apll_p[] __initconst = { "fin_pll", "fout_apll" }; -static const char *mout_mpll_p[] __initdata = { +static const char *const mout_mpll_p[] __initconst = { "fin_pll", "fout_mpll" }; -static const char *mout_epll_p[] __initdata = { +static const char *const mout_epll_p[] __initconst = { "fin_pll", "fout_epll" }; -static const char *mout_vpllsrc_p[] __initdata = { +static const char *const mout_vpllsrc_p[] __initconst = { "fin_pll", "sclk_hdmi27m" }; -static const char *mout_vpll_p[] __initdata = { +static const char *const mout_vpll_p[] __initconst = { "mout_vpllsrc", "fout_vpll" }; -static const char *mout_group1_p[] __initdata = { +static const char *const mout_group1_p[] __initconst = { "dout_a2m", "mout_mpll", "mout_epll", "mout_vpll" }; -static const char *mout_group2_p[] __initdata = { +static const char *const mout_group2_p[] __initconst = { "xxti", "xusbxti", "sclk_hdmi27m", @@ -218,7 +218,7 @@ static const char *mout_group2_p[] __initdata = { "mout_vpll", }; -static const char *mout_audio0_p[] __initdata = { +static const char *const mout_audio0_p[] __initconst = { "xxti", "pcmcdclk0", "sclk_hdmi27m", @@ -230,7 +230,7 @@ static const char *mout_audio0_p[] __initdata = { "mout_vpll", }; -static const char *mout_audio1_p[] __initdata = { +static const char *const mout_audio1_p[] __initconst = { "i2scdclk1", "pcmcdclk1", "sclk_hdmi27m", @@ -242,7 +242,7 @@ static const char *mout_audio1_p[] __initdata = { "mout_vpll", }; -static const char *mout_audio2_p[] __initdata = { +static const char *const mout_audio2_p[] __initconst = { "i2scdclk2", "pcmcdclk2", "sclk_hdmi27m", @@ -254,63 +254,63 @@ static const char *mout_audio2_p[] __initdata = { "mout_vpll", }; -static const char *mout_spdif_p[] __initdata = { +static const char *const mout_spdif_p[] __initconst = { "dout_audio0", "dout_audio1", "dout_audio3", }; -static const char *mout_group3_p[] __initdata = { +static const char *const mout_group3_p[] __initconst = { "mout_apll", "mout_mpll" }; -static const char *mout_group4_p[] __initdata = { +static const char *const mout_group4_p[] __initconst = { "mout_mpll", "dout_a2m" }; -static const char *mout_flash_p[] __initdata = { +static const char *const mout_flash_p[] __initconst = { "dout_hclkd", "dout_hclkp" }; -static const char *mout_dac_p[] __initdata = { +static const char *const mout_dac_p[] __initconst = { "mout_vpll", "sclk_hdmiphy" }; -static const char *mout_hdmi_p[] __initdata = { +static const char *const mout_hdmi_p[] __initconst = { "sclk_hdmiphy", "dout_tblk" }; -static const char *mout_mixer_p[] __initdata = { +static const char *const mout_mixer_p[] __initconst = { "mout_dac", "mout_hdmi" }; -static const char *mout_vpll_6442_p[] __initdata = { +static const char *const mout_vpll_6442_p[] __initconst = { "fin_pll", "fout_vpll" }; -static const char *mout_mixer_6442_p[] __initdata = { +static const char *const mout_mixer_6442_p[] __initconst = { "mout_vpll", "dout_mixer" }; -static const char *mout_d0sync_6442_p[] __initdata = { +static const char *const mout_d0sync_6442_p[] __initconst = { "mout_dsys", "div_apll" }; -static const char *mout_d1sync_6442_p[] __initdata = { +static const char *const mout_d1sync_6442_p[] __initconst = { "mout_psys", "div_apll" }; -static const char *mout_group2_6442_p[] __initdata = { +static const char *const mout_group2_6442_p[] __initconst = { "fin_pll", "none", "none", @@ -322,7 +322,7 @@ static const char *mout_group2_6442_p[] __initdata = { "mout_vpll", }; -static const char *mout_audio0_6442_p[] __initdata = { +static const char *const mout_audio0_6442_p[] __initconst = { "fin_pll", "pcmcdclk0", "none", @@ -334,7 +334,7 @@ static const char *mout_audio0_6442_p[] __initdata = { "mout_vpll", }; -static const char *mout_audio1_6442_p[] __initdata = { +static const char *const mout_audio1_6442_p[] __initconst = { "i2scdclk1", "pcmcdclk1", "none", @@ -347,7 +347,7 @@ static const char *mout_audio1_6442_p[] __initdata = { "fin_pll", }; -static const char *mout_clksel_p[] __initdata = { +static const char *const mout_clksel_p[] __initconst = { "fout_apll_clkout", "fout_mpll_clkout", "fout_epll", @@ -370,7 +370,7 @@ static const char *mout_clksel_p[] __initdata = { "div_dclk" }; -static const char *mout_clksel_6442_p[] __initdata = { +static const char *const mout_clksel_6442_p[] __initconst = { "fout_apll_clkout", "fout_mpll_clkout", "fout_epll", @@ -393,7 +393,7 @@ static const char *mout_clksel_6442_p[] __initdata = { "div_dclk" }; -static const char *mout_clkout_p[] __initdata = { +static const char *const mout_clkout_p[] __initconst = { "dout_clkout", "none", "xxti", @@ -401,20 +401,20 @@ static const char *mout_clkout_p[] __initdata = { }; /* Common fixed factor clocks. */ -static struct samsung_fixed_factor_clock ffactor_clks[] __initdata = { +static const struct samsung_fixed_factor_clock ffactor_clks[] __initconst = { FFACTOR(FOUT_APLL_CLKOUT, "fout_apll_clkout", "fout_apll", 1, 4, 0), FFACTOR(FOUT_MPLL_CLKOUT, "fout_mpll_clkout", "fout_mpll", 1, 2, 0), FFACTOR(DOUT_APLL_CLKOUT, "dout_apll_clkout", "dout_apll", 1, 4, 0), }; /* PLL input mux (fin_pll), which needs to be registered before PLLs. */ -static struct samsung_mux_clock early_mux_clks[] __initdata = { +static const struct samsung_mux_clock early_mux_clks[] __initconst = { MUX_F(FIN_PLL, "fin_pll", fin_pll_p, OM_STAT, 0, 1, CLK_MUX_READ_ONLY, 0), }; /* Common clock muxes. */ -static struct samsung_mux_clock mux_clks[] __initdata = { +static const struct samsung_mux_clock mux_clks[] __initconst = { MUX(MOUT_FLASH, "mout_flash", mout_flash_p, CLK_SRC0, 28, 1), MUX(MOUT_PSYS, "mout_psys", mout_group4_p, CLK_SRC0, 24, 1), MUX(MOUT_DSYS, "mout_dsys", mout_group4_p, CLK_SRC0, 20, 1), @@ -427,7 +427,7 @@ static struct samsung_mux_clock mux_clks[] __initdata = { }; /* S5PV210-specific clock muxes. */ -static struct samsung_mux_clock s5pv210_mux_clks[] __initdata = { +static const struct samsung_mux_clock s5pv210_mux_clks[] __initconst = { MUX(MOUT_VPLL, "mout_vpll", mout_vpll_p, CLK_SRC0, 12, 1), MUX(MOUT_VPLLSRC, "mout_vpllsrc", mout_vpllsrc_p, CLK_SRC1, 28, 1), @@ -472,7 +472,7 @@ static struct samsung_mux_clock s5pv210_mux_clks[] __initdata = { }; /* S5P6442-specific clock muxes. */ -static struct samsung_mux_clock s5p6442_mux_clks[] __initdata = { +static const struct samsung_mux_clock s5p6442_mux_clks[] __initconst = { MUX(MOUT_VPLL, "mout_vpll", mout_vpll_6442_p, CLK_SRC0, 12, 1), MUX(MOUT_FIMD, "mout_fimd", mout_group2_6442_p, CLK_SRC1, 20, 4), @@ -504,7 +504,7 @@ static struct samsung_mux_clock s5p6442_mux_clks[] __initdata = { }; /* S5PV210-specific fixed rate clocks generated inside the SoC. */ -static struct samsung_fixed_rate_clock s5pv210_frate_clks[] __initdata = { +static const struct samsung_fixed_rate_clock s5pv210_frate_clks[] __initconst = { FRATE(SCLK_HDMI27M, "sclk_hdmi27m", NULL, CLK_IS_ROOT, 27000000), FRATE(SCLK_HDMIPHY, "sclk_hdmiphy", NULL, CLK_IS_ROOT, 27000000), FRATE(SCLK_USBPHY0, "sclk_usbphy0", NULL, CLK_IS_ROOT, 48000000), @@ -512,12 +512,12 @@ static struct samsung_fixed_rate_clock s5pv210_frate_clks[] __initdata = { }; /* S5P6442-specific fixed rate clocks generated inside the SoC. */ -static struct samsung_fixed_rate_clock s5p6442_frate_clks[] __initdata = { +static const struct samsung_fixed_rate_clock s5p6442_frate_clks[] __initconst = { FRATE(SCLK_USBPHY0, "sclk_usbphy0", NULL, CLK_IS_ROOT, 30000000), }; /* Common clock dividers. */ -static struct samsung_div_clock div_clks[] __initdata = { +static const struct samsung_div_clock div_clks[] __initconst = { DIV(DOUT_PCLKP, "dout_pclkp", "dout_hclkp", CLK_DIV0, 28, 3), DIV(DOUT_PCLKD, "dout_pclkd", "dout_hclkd", CLK_DIV0, 20, 3), DIV(DOUT_A2M, "dout_a2m", "mout_apll", CLK_DIV0, 4, 3), @@ -549,7 +549,7 @@ static struct samsung_div_clock div_clks[] __initdata = { }; /* S5PV210-specific clock dividers. */ -static struct samsung_div_clock s5pv210_div_clks[] __initdata = { +static const struct samsung_div_clock s5pv210_div_clks[] __initconst = { DIV(DOUT_HCLKP, "dout_hclkp", "mout_psys", CLK_DIV0, 24, 4), DIV(DOUT_HCLKD, "dout_hclkd", "mout_dsys", CLK_DIV0, 16, 4), DIV(DOUT_PCLKM, "dout_pclkm", "dout_hclkm", CLK_DIV0, 12, 3), @@ -578,7 +578,7 @@ static struct samsung_div_clock s5pv210_div_clks[] __initdata = { }; /* S5P6442-specific clock dividers. */ -static struct samsung_div_clock s5p6442_div_clks[] __initdata = { +static const struct samsung_div_clock s5p6442_div_clks[] __initconst = { DIV(DOUT_HCLKP, "dout_hclkp", "mout_d1sync", CLK_DIV0, 24, 4), DIV(DOUT_HCLKD, "dout_hclkd", "mout_d0sync", CLK_DIV0, 16, 4), @@ -586,7 +586,7 @@ static struct samsung_div_clock s5p6442_div_clks[] __initdata = { }; /* Common clock gates. */ -static struct samsung_gate_clock gate_clks[] __initdata = { +static const struct samsung_gate_clock gate_clks[] __initconst = { GATE(CLK_ROTATOR, "rotator", "dout_hclkd", CLK_GATE_IP0, 29, 0, 0), GATE(CLK_FIMC2, "fimc2", "dout_hclkd", CLK_GATE_IP0, 26, 0, 0), GATE(CLK_FIMC1, "fimc1", "dout_hclkd", CLK_GATE_IP0, 25, 0, 0), @@ -666,7 +666,7 @@ static struct samsung_gate_clock gate_clks[] __initdata = { }; /* S5PV210-specific clock gates. */ -static struct samsung_gate_clock s5pv210_gate_clks[] __initdata = { +static const struct samsung_gate_clock s5pv210_gate_clks[] __initconst = { GATE(CLK_CSIS, "clk_csis", "dout_hclkd", CLK_GATE_IP0, 31, 0, 0), GATE(CLK_MFC, "mfc", "dout_hclkm", CLK_GATE_IP0, 16, 0, 0), GATE(CLK_G2D, "g2d", "dout_hclkd", CLK_GATE_IP0, 12, 0, 0), @@ -728,7 +728,7 @@ static struct samsung_gate_clock s5pv210_gate_clks[] __initdata = { }; /* S5P6442-specific clock gates. */ -static struct samsung_gate_clock s5p6442_gate_clks[] __initdata = { +static const struct samsung_gate_clock s5p6442_gate_clks[] __initconst = { GATE(CLK_JPEG, "jpeg", "dout_hclkd", CLK_GATE_IP0, 28, 0, 0), GATE(CLK_MFC, "mfc", "dout_hclkd", CLK_GATE_IP0, 16, 0, 0), GATE(CLK_G2D, "g2d", "dout_hclkd", CLK_GATE_IP0, 12, 0, 0), @@ -748,14 +748,14 @@ static struct samsung_gate_clock s5p6442_gate_clks[] __initdata = { * Clock aliases for legacy clkdev look-up. * NOTE: Needed only to support legacy board files. */ -static struct samsung_clock_alias s5pv210_aliases[] = { +static const struct samsung_clock_alias s5pv210_aliases[] __initconst = { ALIAS(DOUT_APLL, NULL, "armclk"), ALIAS(DOUT_HCLKM, NULL, "hclk_msys"), ALIAS(MOUT_DMC0, NULL, "sclk_dmc0"), }; /* S5PV210-specific PLLs. */ -static struct samsung_pll_clock s5pv210_pll_clks[] __initdata = { +static const struct samsung_pll_clock s5pv210_pll_clks[] __initconst = { [apll] = PLL(pll_4508, FOUT_APLL, "fout_apll", "fin_pll", APLL_LOCK, APLL_CON0, NULL), [mpll] = PLL(pll_4502, FOUT_MPLL, "fout_mpll", "fin_pll", @@ -767,7 +767,7 @@ static struct samsung_pll_clock s5pv210_pll_clks[] __initdata = { }; /* S5P6442-specific PLLs. */ -static struct samsung_pll_clock s5p6442_pll_clks[] __initdata = { +static const struct samsung_pll_clock s5p6442_pll_clks[] __initconst = { [apll] = PLL(pll_4502, FOUT_APLL, "fout_apll", "fin_pll", APLL_LOCK, APLL_CON0, NULL), [mpll] = PLL(pll_4502, FOUT_MPLL, "fout_mpll", "fin_pll", diff --git a/drivers/clk/samsung/clk.c b/drivers/clk/samsung/clk.c index 9e1f88c04fd4..0117238391d6 100644 --- a/drivers/clk/samsung/clk.c +++ b/drivers/clk/samsung/clk.c @@ -98,7 +98,7 @@ void samsung_clk_add_lookup(struct samsung_clk_provider *ctx, struct clk *clk, /* register a list of aliases */ void __init samsung_clk_register_alias(struct samsung_clk_provider *ctx, - struct samsung_clock_alias *list, + const struct samsung_clock_alias *list, unsigned int nr_clk) { struct clk *clk; @@ -132,7 +132,8 @@ void __init samsung_clk_register_alias(struct samsung_clk_provider *ctx, /* register a list of fixed clocks */ void __init samsung_clk_register_fixed_rate(struct samsung_clk_provider *ctx, - struct samsung_fixed_rate_clock *list, unsigned int nr_clk) + const struct samsung_fixed_rate_clock *list, + unsigned int nr_clk) { struct clk *clk; unsigned int idx, ret; @@ -161,7 +162,7 @@ void __init samsung_clk_register_fixed_rate(struct samsung_clk_provider *ctx, /* register a list of fixed factor clocks */ void __init samsung_clk_register_fixed_factor(struct samsung_clk_provider *ctx, - struct samsung_fixed_factor_clock *list, unsigned int nr_clk) + const struct samsung_fixed_factor_clock *list, unsigned int nr_clk) { struct clk *clk; unsigned int idx; @@ -181,7 +182,7 @@ void __init samsung_clk_register_fixed_factor(struct samsung_clk_provider *ctx, /* register a list of mux clocks */ void __init samsung_clk_register_mux(struct samsung_clk_provider *ctx, - struct samsung_mux_clock *list, + const struct samsung_mux_clock *list, unsigned int nr_clk) { struct clk *clk; @@ -213,7 +214,7 @@ void __init samsung_clk_register_mux(struct samsung_clk_provider *ctx, /* register a list of div clocks */ void __init samsung_clk_register_div(struct samsung_clk_provider *ctx, - struct samsung_div_clock *list, + const struct samsung_div_clock *list, unsigned int nr_clk) { struct clk *clk; @@ -252,7 +253,7 @@ void __init samsung_clk_register_div(struct samsung_clk_provider *ctx, /* register a list of gate clocks */ void __init samsung_clk_register_gate(struct samsung_clk_provider *ctx, - struct samsung_gate_clock *list, + const struct samsung_gate_clock *list, unsigned int nr_clk) { struct clk *clk; @@ -389,7 +390,7 @@ struct samsung_clk_provider * __init samsung_cmu_register_one( ctx = samsung_clk_init(np, reg_base, cmu->nr_clk_ids); if (!ctx) { - panic("%s: unable to alllocate ctx\n", __func__); + panic("%s: unable to allocate ctx\n", __func__); return ctx; } diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h index e4c75383cea7..b775fc29caa5 100644 --- a/drivers/clk/samsung/clk.h +++ b/drivers/clk/samsung/clk.h @@ -121,7 +121,7 @@ struct samsung_mux_clock { unsigned int id; const char *dev_name; const char *name; - const char **parent_names; + const char *const *parent_names; u8 num_parents; unsigned long flags; unsigned long offset; @@ -368,28 +368,28 @@ extern void __init samsung_clk_of_register_fixed_ext( extern void samsung_clk_add_lookup(struct samsung_clk_provider *ctx, struct clk *clk, unsigned int id); -extern void samsung_clk_register_alias(struct samsung_clk_provider *ctx, - struct samsung_clock_alias *list, +extern void __init samsung_clk_register_alias(struct samsung_clk_provider *ctx, + const struct samsung_clock_alias *list, unsigned int nr_clk); extern void __init samsung_clk_register_fixed_rate( struct samsung_clk_provider *ctx, - struct samsung_fixed_rate_clock *clk_list, + const struct samsung_fixed_rate_clock *clk_list, unsigned int nr_clk); extern void __init samsung_clk_register_fixed_factor( struct samsung_clk_provider *ctx, - struct samsung_fixed_factor_clock *list, + const struct samsung_fixed_factor_clock *list, unsigned int nr_clk); extern void __init samsung_clk_register_mux(struct samsung_clk_provider *ctx, - struct samsung_mux_clock *clk_list, + const struct samsung_mux_clock *clk_list, unsigned int nr_clk); extern void __init samsung_clk_register_div(struct samsung_clk_provider *ctx, - struct samsung_div_clock *clk_list, + const struct samsung_div_clock *clk_list, unsigned int nr_clk); extern void __init samsung_clk_register_gate(struct samsung_clk_provider *ctx, - struct samsung_gate_clock *clk_list, + const struct samsung_gate_clock *clk_list, unsigned int nr_clk); extern void __init samsung_clk_register_pll(struct samsung_clk_provider *ctx, - struct samsung_pll_clock *pll_list, + const struct samsung_pll_clock *pll_list, unsigned int nr_clk, void __iomem *base); extern struct samsung_clk_provider __init *samsung_cmu_register_one( diff --git a/drivers/clk/shmobile/clk-emev2.c b/drivers/clk/shmobile/clk-emev2.c index 6c7c929c7765..5b60beb7d0eb 100644 --- a/drivers/clk/shmobile/clk-emev2.c +++ b/drivers/clk/shmobile/clk-emev2.c @@ -34,7 +34,7 @@ static DEFINE_SPINLOCK(lock); /* not pretty, but hey */ -void __iomem *smu_base; +static void __iomem *smu_base; static void __init emev2_smu_write(unsigned long value, int offs) { diff --git a/drivers/clk/sirf/Makefile b/drivers/clk/sirf/Makefile index 36b8e203f6e7..09b4210d9124 100644 --- a/drivers/clk/sirf/Makefile +++ b/drivers/clk/sirf/Makefile @@ -2,4 +2,4 @@ # Makefile for sirf specific clk # -obj-$(CONFIG_ARCH_SIRF) += clk-prima2.o clk-atlas6.o +obj-$(CONFIG_ARCH_SIRF) += clk-prima2.o clk-atlas6.o clk-atlas7.o diff --git a/drivers/clk/sirf/clk-atlas7.c b/drivers/clk/sirf/clk-atlas7.c new file mode 100644 index 000000000000..db8ab691dbf6 --- /dev/null +++ b/drivers/clk/sirf/clk-atlas7.c @@ -0,0 +1,1632 @@ +/* + * Clock tree for CSR SiRFAtlas7 + * + * Copyright (c) 2014 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define SIRFSOC_CLKC_MEMPLL_AB_FREQ 0x0000 +#define SIRFSOC_CLKC_MEMPLL_AB_SSC 0x0004 +#define SIRFSOC_CLKC_MEMPLL_AB_CTRL0 0x0008 +#define SIRFSOC_CLKC_MEMPLL_AB_CTRL1 0x000c +#define SIRFSOC_CLKC_MEMPLL_AB_STATUS 0x0010 +#define SIRFSOC_CLKC_MEMPLL_AB_SSRAM_ADDR 0x0014 +#define SIRFSOC_CLKC_MEMPLL_AB_SSRAM_DATA 0x0018 + +#define SIRFSOC_CLKC_CPUPLL_AB_FREQ 0x001c +#define SIRFSOC_CLKC_CPUPLL_AB_SSC 0x0020 +#define SIRFSOC_CLKC_CPUPLL_AB_CTRL0 0x0024 +#define SIRFSOC_CLKC_CPUPLL_AB_CTRL1 0x0028 +#define SIRFSOC_CLKC_CPUPLL_AB_STATUS 0x002c + +#define SIRFSOC_CLKC_SYS0PLL_AB_FREQ 0x0030 +#define SIRFSOC_CLKC_SYS0PLL_AB_SSC 0x0034 +#define SIRFSOC_CLKC_SYS0PLL_AB_CTRL0 0x0038 +#define SIRFSOC_CLKC_SYS0PLL_AB_CTRL1 0x003c +#define SIRFSOC_CLKC_SYS0PLL_AB_STATUS 0x0040 + +#define SIRFSOC_CLKC_SYS1PLL_AB_FREQ 0x0044 +#define SIRFSOC_CLKC_SYS1PLL_AB_SSC 0x0048 +#define SIRFSOC_CLKC_SYS1PLL_AB_CTRL0 0x004c +#define SIRFSOC_CLKC_SYS1PLL_AB_CTRL1 0x0050 +#define SIRFSOC_CLKC_SYS1PLL_AB_STATUS 0x0054 + +#define SIRFSOC_CLKC_SYS2PLL_AB_FREQ 0x0058 +#define SIRFSOC_CLKC_SYS2PLL_AB_SSC 0x005c +#define SIRFSOC_CLKC_SYS2PLL_AB_CTRL0 0x0060 +#define SIRFSOC_CLKC_SYS2PLL_AB_CTRL1 0x0064 +#define SIRFSOC_CLKC_SYS2PLL_AB_STATUS 0x0068 + +#define SIRFSOC_CLKC_SYS3PLL_AB_FREQ 0x006c +#define SIRFSOC_CLKC_SYS3PLL_AB_SSC 0x0070 +#define SIRFSOC_CLKC_SYS3PLL_AB_CTRL0 0x0074 +#define SIRFSOC_CLKC_SYS3PLL_AB_CTRL1 0x0078 +#define SIRFSOC_CLKC_SYS3PLL_AB_STATUS 0x007c + +#define SIRFSOC_ABPLL_CTRL0_SSEN 0x00001000 +#define SIRFSOC_ABPLL_CTRL0_BYPASS 0x00000010 +#define SIRFSOC_ABPLL_CTRL0_RESET 0x00000001 + +#define SIRFSOC_CLKC_AUDIO_DTO_INC 0x0088 +#define SIRFSOC_CLKC_DISP0_DTO_INC 0x008c +#define SIRFSOC_CLKC_DISP1_DTO_INC 0x0090 + +#define SIRFSOC_CLKC_AUDIO_DTO_SRC 0x0094 +#define SIRFSOC_CLKC_AUDIO_DTO_ENA 0x0098 +#define SIRFSOC_CLKC_AUDIO_DTO_DROFF 0x009c + +#define SIRFSOC_CLKC_DISP0_DTO_SRC 0x00a0 +#define SIRFSOC_CLKC_DISP0_DTO_ENA 0x00a4 +#define SIRFSOC_CLKC_DISP0_DTO_DROFF 0x00a8 + +#define SIRFSOC_CLKC_DISP1_DTO_SRC 0x00ac +#define SIRFSOC_CLKC_DISP1_DTO_ENA 0x00b0 +#define SIRFSOC_CLKC_DISP1_DTO_DROFF 0x00b4 + +#define SIRFSOC_CLKC_I2S_CLK_SEL 0x00b8 +#define SIRFSOC_CLKC_I2S_SEL_STAT 0x00bc + +#define SIRFSOC_CLKC_USBPHY_CLKDIV_CFG 0x00c0 +#define SIRFSOC_CLKC_USBPHY_CLKDIV_ENA 0x00c4 +#define SIRFSOC_CLKC_USBPHY_CLK_SEL 0x00c8 +#define SIRFSOC_CLKC_USBPHY_CLK_SEL_STAT 0x00cc + +#define SIRFSOC_CLKC_BTSS_CLKDIV_CFG 0x00d0 +#define SIRFSOC_CLKC_BTSS_CLKDIV_ENA 0x00d4 +#define SIRFSOC_CLKC_BTSS_CLK_SEL 0x00d8 +#define SIRFSOC_CLKC_BTSS_CLK_SEL_STAT 0x00dc + +#define SIRFSOC_CLKC_RGMII_CLKDIV_CFG 0x00e0 +#define SIRFSOC_CLKC_RGMII_CLKDIV_ENA 0x00e4 +#define SIRFSOC_CLKC_RGMII_CLK_SEL 0x00e8 +#define SIRFSOC_CLKC_RGMII_CLK_SEL_STAT 0x00ec + +#define SIRFSOC_CLKC_CPU_CLKDIV_CFG 0x00f0 +#define SIRFSOC_CLKC_CPU_CLKDIV_ENA 0x00f4 +#define SIRFSOC_CLKC_CPU_CLK_SEL 0x00f8 +#define SIRFSOC_CLKC_CPU_CLK_SEL_STAT 0x00fc + +#define SIRFSOC_CLKC_SDPHY01_CLKDIV_CFG 0x0100 +#define SIRFSOC_CLKC_SDPHY01_CLKDIV_ENA 0x0104 +#define SIRFSOC_CLKC_SDPHY01_CLK_SEL 0x0108 +#define SIRFSOC_CLKC_SDPHY01_CLK_SEL_STAT 0x010c + +#define SIRFSOC_CLKC_SDPHY23_CLKDIV_CFG 0x0110 +#define SIRFSOC_CLKC_SDPHY23_CLKDIV_ENA 0x0114 +#define SIRFSOC_CLKC_SDPHY23_CLK_SEL 0x0118 +#define SIRFSOC_CLKC_SDPHY23_CLK_SEL_STAT 0x011c + +#define SIRFSOC_CLKC_SDPHY45_CLKDIV_CFG 0x0120 +#define SIRFSOC_CLKC_SDPHY45_CLKDIV_ENA 0x0124 +#define SIRFSOC_CLKC_SDPHY45_CLK_SEL 0x0128 +#define SIRFSOC_CLKC_SDPHY45_CLK_SEL_STAT 0x012c + +#define SIRFSOC_CLKC_SDPHY67_CLKDIV_CFG 0x0130 +#define SIRFSOC_CLKC_SDPHY67_CLKDIV_ENA 0x0134 +#define SIRFSOC_CLKC_SDPHY67_CLK_SEL 0x0138 +#define SIRFSOC_CLKC_SDPHY67_CLK_SEL_STAT 0x013c + +#define SIRFSOC_CLKC_CAN_CLKDIV_CFG 0x0140 +#define SIRFSOC_CLKC_CAN_CLKDIV_ENA 0x0144 +#define SIRFSOC_CLKC_CAN_CLK_SEL 0x0148 +#define SIRFSOC_CLKC_CAN_CLK_SEL_STAT 0x014c + +#define SIRFSOC_CLKC_DEINT_CLKDIV_CFG 0x0150 +#define SIRFSOC_CLKC_DEINT_CLKDIV_ENA 0x0154 +#define SIRFSOC_CLKC_DEINT_CLK_SEL 0x0158 +#define SIRFSOC_CLKC_DEINT_CLK_SEL_STAT 0x015c + +#define SIRFSOC_CLKC_NAND_CLKDIV_CFG 0x0160 +#define SIRFSOC_CLKC_NAND_CLKDIV_ENA 0x0164 +#define SIRFSOC_CLKC_NAND_CLK_SEL 0x0168 +#define SIRFSOC_CLKC_NAND_CLK_SEL_STAT 0x016c + +#define SIRFSOC_CLKC_DISP0_CLKDIV_CFG 0x0170 +#define SIRFSOC_CLKC_DISP0_CLKDIV_ENA 0x0174 +#define SIRFSOC_CLKC_DISP0_CLK_SEL 0x0178 +#define SIRFSOC_CLKC_DISP0_CLK_SEL_STAT 0x017c + +#define SIRFSOC_CLKC_DISP1_CLKDIV_CFG 0x0180 +#define SIRFSOC_CLKC_DISP1_CLKDIV_ENA 0x0184 +#define SIRFSOC_CLKC_DISP1_CLK_SEL 0x0188 +#define SIRFSOC_CLKC_DISP1_CLK_SEL_STAT 0x018c + +#define SIRFSOC_CLKC_GPU_CLKDIV_CFG 0x0190 +#define SIRFSOC_CLKC_GPU_CLKDIV_ENA 0x0194 +#define SIRFSOC_CLKC_GPU_CLK_SEL 0x0198 +#define SIRFSOC_CLKC_GPU_CLK_SEL_STAT 0x019c + +#define SIRFSOC_CLKC_GNSS_CLKDIV_CFG 0x01a0 +#define SIRFSOC_CLKC_GNSS_CLKDIV_ENA 0x01a4 +#define SIRFSOC_CLKC_GNSS_CLK_SEL 0x01a8 +#define SIRFSOC_CLKC_GNSS_CLK_SEL_STAT 0x01ac + +#define SIRFSOC_CLKC_SHARED_DIVIDER_CFG0 0x01b0 +#define SIRFSOC_CLKC_SHARED_DIVIDER_CFG1 0x01b4 +#define SIRFSOC_CLKC_SHARED_DIVIDER_ENA 0x01b8 + +#define SIRFSOC_CLKC_SYS_CLK_SEL 0x01bc +#define SIRFSOC_CLKC_SYS_CLK_SEL_STAT 0x01c0 +#define SIRFSOC_CLKC_IO_CLK_SEL 0x01c4 +#define SIRFSOC_CLKC_IO_CLK_SEL_STAT 0x01c8 +#define SIRFSOC_CLKC_G2D_CLK_SEL 0x01cc +#define SIRFSOC_CLKC_G2D_CLK_SEL_STAT 0x01d0 +#define SIRFSOC_CLKC_JPENC_CLK_SEL 0x01d4 +#define SIRFSOC_CLKC_JPENC_CLK_SEL_STAT 0x01d8 +#define SIRFSOC_CLKC_VDEC_CLK_SEL 0x01dc +#define SIRFSOC_CLKC_VDEC_CLK_SEL_STAT 0x01e0 +#define SIRFSOC_CLKC_GMAC_CLK_SEL 0x01e4 +#define SIRFSOC_CLKC_GMAC_CLK_SEL_STAT 0x01e8 +#define SIRFSOC_CLKC_USB_CLK_SEL 0x01ec +#define SIRFSOC_CLKC_USB_CLK_SEL_STAT 0x01f0 +#define SIRFSOC_CLKC_KAS_CLK_SEL 0x01f4 +#define SIRFSOC_CLKC_KAS_CLK_SEL_STAT 0x01f8 +#define SIRFSOC_CLKC_SEC_CLK_SEL 0x01fc +#define SIRFSOC_CLKC_SEC_CLK_SEL_STAT 0x0200 +#define SIRFSOC_CLKC_SDR_CLK_SEL 0x0204 +#define SIRFSOC_CLKC_SDR_CLK_SEL_STAT 0x0208 +#define SIRFSOC_CLKC_VIP_CLK_SEL 0x020c +#define SIRFSOC_CLKC_VIP_CLK_SEL_STAT 0x0210 +#define SIRFSOC_CLKC_NOCD_CLK_SEL 0x0214 +#define SIRFSOC_CLKC_NOCD_CLK_SEL_STAT 0x0218 +#define SIRFSOC_CLKC_NOCR_CLK_SEL 0x021c +#define SIRFSOC_CLKC_NOCR_CLK_SEL_STAT 0x0220 +#define SIRFSOC_CLKC_TPIU_CLK_SEL 0x0224 +#define SIRFSOC_CLKC_TPIU_CLK_SEL_STAT 0x0228 + +#define SIRFSOC_CLKC_ROOT_CLK_EN0_SET 0x022c +#define SIRFSOC_CLKC_ROOT_CLK_EN0_CLR 0x0230 +#define SIRFSOC_CLKC_ROOT_CLK_EN0_STAT 0x0234 +#define SIRFSOC_CLKC_ROOT_CLK_EN1_SET 0x0238 +#define SIRFSOC_CLKC_ROOT_CLK_EN1_CLR 0x023c +#define SIRFSOC_CLKC_ROOT_CLK_EN1_STAT 0x0240 + +#define SIRFSOC_CLKC_LEAF_CLK_EN0_SET 0x0244 +#define SIRFSOC_CLKC_LEAF_CLK_EN0_CLR 0x0248 +#define SIRFSOC_CLKC_LEAF_CLK_EN0_STAT 0x024c + +#define SIRFSOC_CLKC_RSTC_A7_SW_RST 0x0308 + +#define SIRFSOC_CLKC_LEAF_CLK_EN1_SET 0x04a0 +#define SIRFSOC_CLKC_LEAF_CLK_EN2_SET 0x04b8 +#define SIRFSOC_CLKC_LEAF_CLK_EN3_SET 0x04d0 +#define SIRFSOC_CLKC_LEAF_CLK_EN4_SET 0x04e8 +#define SIRFSOC_CLKC_LEAF_CLK_EN5_SET 0x0500 +#define SIRFSOC_CLKC_LEAF_CLK_EN6_SET 0x0518 +#define SIRFSOC_CLKC_LEAF_CLK_EN7_SET 0x0530 +#define SIRFSOC_CLKC_LEAF_CLK_EN8_SET 0x0548 + + +static void __iomem *sirfsoc_clk_vbase; +static struct clk_onecell_data clk_data; + +static const struct clk_div_table pll_div_table[] = { + { .val = 0, .div = 1 }, + { .val = 1, .div = 2 }, + { .val = 2, .div = 4 }, + { .val = 3, .div = 8 }, + { .val = 4, .div = 16 }, + { .val = 5, .div = 32 }, +}; + +struct clk_pll { + struct clk_hw hw; + u16 regofs; /* register offset */ +}; +#define to_pllclk(_hw) container_of(_hw, struct clk_pll, hw) + +struct clk_dto { + struct clk_hw hw; + u16 inc_offset; /* dto increment offset */ + u16 src_offset; /* dto src offset */ +}; +#define to_dtoclk(_hw) container_of(_hw, struct clk_dto, hw) + +struct clk_unit { + struct clk_hw hw; + u16 regofs; + u16 bit; + spinlock_t *lock; +}; +#define to_unitclk(_hw) container_of(_hw, struct clk_unit, hw) + +struct atlas7_div_init_data { + const char *div_name; + const char *parent_name; + const char *gate_name; + unsigned long flags; + u8 divider_flags; + u8 gate_flags; + u32 div_offset; + u8 shift; + u8 width; + u32 gate_offset; + u8 gate_bit; + spinlock_t *lock; +}; + +struct atlas7_mux_init_data { + const char *mux_name; + const char * const *parent_names; + u8 parent_num; + unsigned long flags; + u8 mux_flags; + u32 mux_offset; + u8 shift; + u8 width; +}; + +struct atlas7_unit_init_data { + u32 index; + const char *unit_name; + const char *parent_name; + unsigned long flags; + u32 regofs; + u8 bit; + spinlock_t *lock; +}; + +struct atlas7_reset_desc { + const char *name; + u32 clk_ofs; + u8 clk_bit; + u32 rst_ofs; + u8 rst_bit; + spinlock_t *lock; +}; + +static DEFINE_SPINLOCK(cpupll_ctrl1_lock); +static DEFINE_SPINLOCK(mempll_ctrl1_lock); +static DEFINE_SPINLOCK(sys0pll_ctrl1_lock); +static DEFINE_SPINLOCK(sys1pll_ctrl1_lock); +static DEFINE_SPINLOCK(sys2pll_ctrl1_lock); +static DEFINE_SPINLOCK(sys3pll_ctrl1_lock); +static DEFINE_SPINLOCK(usbphy_div_lock); +static DEFINE_SPINLOCK(btss_div_lock); +static DEFINE_SPINLOCK(rgmii_div_lock); +static DEFINE_SPINLOCK(cpu_div_lock); +static DEFINE_SPINLOCK(sdphy01_div_lock); +static DEFINE_SPINLOCK(sdphy23_div_lock); +static DEFINE_SPINLOCK(sdphy45_div_lock); +static DEFINE_SPINLOCK(sdphy67_div_lock); +static DEFINE_SPINLOCK(can_div_lock); +static DEFINE_SPINLOCK(deint_div_lock); +static DEFINE_SPINLOCK(nand_div_lock); +static DEFINE_SPINLOCK(disp0_div_lock); +static DEFINE_SPINLOCK(disp1_div_lock); +static DEFINE_SPINLOCK(gpu_div_lock); +static DEFINE_SPINLOCK(gnss_div_lock); +/* gate register shared */ +static DEFINE_SPINLOCK(share_div_lock); +static DEFINE_SPINLOCK(root0_gate_lock); +static DEFINE_SPINLOCK(root1_gate_lock); +static DEFINE_SPINLOCK(leaf0_gate_lock); +static DEFINE_SPINLOCK(leaf1_gate_lock); +static DEFINE_SPINLOCK(leaf2_gate_lock); +static DEFINE_SPINLOCK(leaf3_gate_lock); +static DEFINE_SPINLOCK(leaf4_gate_lock); +static DEFINE_SPINLOCK(leaf5_gate_lock); +static DEFINE_SPINLOCK(leaf6_gate_lock); +static DEFINE_SPINLOCK(leaf7_gate_lock); +static DEFINE_SPINLOCK(leaf8_gate_lock); + +static inline unsigned long clkc_readl(unsigned reg) +{ + return readl(sirfsoc_clk_vbase + reg); +} + +static inline void clkc_writel(u32 val, unsigned reg) +{ + writel(val, sirfsoc_clk_vbase + reg); +} + +/* +* ABPLL +* integer mode: Fvco = Fin * 2 * NF / NR +* Spread Spectrum mode: Fvco = Fin * SSN / NR +* SSN = 2^24 / (256 * ((ssdiv >> ssdepth) << ssdepth) + (ssmod << ssdepth)) +*/ +static unsigned long pll_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + unsigned long fin = parent_rate; + struct clk_pll *clk = to_pllclk(hw); + u64 rate; + u32 regctrl0 = clkc_readl(clk->regofs + SIRFSOC_CLKC_MEMPLL_AB_CTRL0 - + SIRFSOC_CLKC_MEMPLL_AB_FREQ); + u32 regfreq = clkc_readl(clk->regofs); + u32 regssc = clkc_readl(clk->regofs + SIRFSOC_CLKC_MEMPLL_AB_SSC - + SIRFSOC_CLKC_MEMPLL_AB_FREQ); + u32 nr = (regfreq >> 16 & (BIT(3) - 1)) + 1; + u32 nf = (regfreq & (BIT(9) - 1)) + 1; + u32 ssdiv = regssc >> 8 & (BIT(12) - 1); + u32 ssdepth = regssc >> 20 & (BIT(2) - 1); + u32 ssmod = regssc & (BIT(8) - 1); + + if (regctrl0 & SIRFSOC_ABPLL_CTRL0_BYPASS) + return fin; + + if (regctrl0 & SIRFSOC_ABPLL_CTRL0_SSEN) { + rate = fin; + rate *= 1 << 24; + do_div(rate, (256 * ((ssdiv >> ssdepth) << ssdepth) + + (ssmod << ssdepth))); + } else { + rate = 2 * fin; + rate *= nf; + do_div(rate, nr); + } + return rate; +} + +static const struct clk_ops ab_pll_ops = { + .recalc_rate = pll_clk_recalc_rate, +}; + +static const char * const pll_clk_parents[] = { + "xin", +}; + +static struct clk_init_data clk_cpupll_init = { + .name = "cpupll_vco", + .ops = &ab_pll_ops, + .parent_names = pll_clk_parents, + .num_parents = ARRAY_SIZE(pll_clk_parents), +}; + +static struct clk_pll clk_cpupll = { + .regofs = SIRFSOC_CLKC_CPUPLL_AB_FREQ, + .hw = { + .init = &clk_cpupll_init, + }, +}; + +static struct clk_init_data clk_mempll_init = { + .name = "mempll_vco", + .ops = &ab_pll_ops, + .parent_names = pll_clk_parents, + .num_parents = ARRAY_SIZE(pll_clk_parents), +}; + +static struct clk_pll clk_mempll = { + .regofs = SIRFSOC_CLKC_MEMPLL_AB_FREQ, + .hw = { + .init = &clk_mempll_init, + }, +}; + +static struct clk_init_data clk_sys0pll_init = { + .name = "sys0pll_vco", + .ops = &ab_pll_ops, + .parent_names = pll_clk_parents, + .num_parents = ARRAY_SIZE(pll_clk_parents), +}; + +static struct clk_pll clk_sys0pll = { + .regofs = SIRFSOC_CLKC_SYS0PLL_AB_FREQ, + .hw = { + .init = &clk_sys0pll_init, + }, +}; + +static struct clk_init_data clk_sys1pll_init = { + .name = "sys1pll_vco", + .ops = &ab_pll_ops, + .parent_names = pll_clk_parents, + .num_parents = ARRAY_SIZE(pll_clk_parents), +}; + +static struct clk_pll clk_sys1pll = { + .regofs = SIRFSOC_CLKC_SYS1PLL_AB_FREQ, + .hw = { + .init = &clk_sys1pll_init, + }, +}; + +static struct clk_init_data clk_sys2pll_init = { + .name = "sys2pll_vco", + .ops = &ab_pll_ops, + .parent_names = pll_clk_parents, + .num_parents = ARRAY_SIZE(pll_clk_parents), +}; + +static struct clk_pll clk_sys2pll = { + .regofs = SIRFSOC_CLKC_SYS2PLL_AB_FREQ, + .hw = { + .init = &clk_sys2pll_init, + }, +}; + +static struct clk_init_data clk_sys3pll_init = { + .name = "sys3pll_vco", + .ops = &ab_pll_ops, + .parent_names = pll_clk_parents, + .num_parents = ARRAY_SIZE(pll_clk_parents), +}; + +static struct clk_pll clk_sys3pll = { + .regofs = SIRFSOC_CLKC_SYS3PLL_AB_FREQ, + .hw = { + .init = &clk_sys3pll_init, + }, +}; + +/* + * DTO in clkc, default enable double resolution mode + * double resolution mode:fout = fin * finc / 2^29 + * normal mode:fout = fin * finc / 2^28 + */ +static int dto_clk_is_enabled(struct clk_hw *hw) +{ + struct clk_dto *clk = to_dtoclk(hw); + int reg; + + reg = clk->src_offset + SIRFSOC_CLKC_AUDIO_DTO_ENA - SIRFSOC_CLKC_AUDIO_DTO_SRC; + + return !!(clkc_readl(reg) & BIT(0)); +} + +static int dto_clk_enable(struct clk_hw *hw) +{ + u32 val, reg; + struct clk_dto *clk = to_dtoclk(hw); + + reg = clk->src_offset + SIRFSOC_CLKC_AUDIO_DTO_ENA - SIRFSOC_CLKC_AUDIO_DTO_SRC; + + val = clkc_readl(reg) | BIT(0); + clkc_writel(val, reg); + return 0; +} + +static void dto_clk_disable(struct clk_hw *hw) +{ + u32 val, reg; + struct clk_dto *clk = to_dtoclk(hw); + + reg = clk->src_offset + SIRFSOC_CLKC_AUDIO_DTO_ENA - SIRFSOC_CLKC_AUDIO_DTO_SRC; + + val = clkc_readl(reg) & ~BIT(0); + clkc_writel(val, reg); +} + +static unsigned long dto_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + u64 rate = parent_rate; + struct clk_dto *clk = to_dtoclk(hw); + u32 finc = clkc_readl(clk->inc_offset); + u32 droff = clkc_readl(clk->src_offset + SIRFSOC_CLKC_AUDIO_DTO_DROFF - SIRFSOC_CLKC_AUDIO_DTO_SRC); + + rate *= finc; + if (droff & BIT(0)) + /* Double resolution off */ + do_div(rate, 1 << 28); + else + do_div(rate, 1 << 29); + + return rate; +} + +static long dto_clk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + u64 dividend = rate * (1 << 29); + + do_div(dividend, *parent_rate); + dividend *= *parent_rate; + do_div(dividend, 1 << 29); + + return dividend; +} + +static int dto_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + u64 dividend = rate * (1 << 29); + struct clk_dto *clk = to_dtoclk(hw); + + do_div(dividend, parent_rate); + clkc_writel(0, clk->src_offset + SIRFSOC_CLKC_AUDIO_DTO_DROFF - SIRFSOC_CLKC_AUDIO_DTO_SRC); + clkc_writel(dividend, clk->inc_offset); + + return 0; +} + +static u8 dto_clk_get_parent(struct clk_hw *hw) +{ + struct clk_dto *clk = to_dtoclk(hw); + + return clkc_readl(clk->src_offset); +} + +/* + * dto need CLK_SET_PARENT_GATE + */ +static int dto_clk_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_dto *clk = to_dtoclk(hw); + + clkc_writel(index, clk->src_offset); + return 0; +} + +static const struct clk_ops dto_ops = { + .is_enabled = dto_clk_is_enabled, + .enable = dto_clk_enable, + .disable = dto_clk_disable, + .recalc_rate = dto_clk_recalc_rate, + .round_rate = dto_clk_round_rate, + .set_rate = dto_clk_set_rate, + .get_parent = dto_clk_get_parent, + .set_parent = dto_clk_set_parent, +}; + +/* dto parent clock as syspllvco/clk1 */ +static const char * const audiodto_clk_parents[] = { + "sys0pll_clk1", + "sys1pll_clk1", + "sys3pll_clk1", +}; + +static struct clk_init_data clk_audiodto_init = { + .name = "audio_dto", + .ops = &dto_ops, + .parent_names = audiodto_clk_parents, + .num_parents = ARRAY_SIZE(audiodto_clk_parents), +}; + +static struct clk_dto clk_audio_dto = { + .inc_offset = SIRFSOC_CLKC_AUDIO_DTO_INC, + .src_offset = SIRFSOC_CLKC_AUDIO_DTO_SRC, + .hw = { + .init = &clk_audiodto_init, + }, +}; + +static const char * const disp0dto_clk_parents[] = { + "sys0pll_clk1", + "sys1pll_clk1", + "sys3pll_clk1", +}; + +static struct clk_init_data clk_disp0dto_init = { + .name = "disp0_dto", + .ops = &dto_ops, + .parent_names = disp0dto_clk_parents, + .num_parents = ARRAY_SIZE(disp0dto_clk_parents), +}; + +static struct clk_dto clk_disp0_dto = { + .inc_offset = SIRFSOC_CLKC_DISP0_DTO_INC, + .src_offset = SIRFSOC_CLKC_DISP0_DTO_SRC, + .hw = { + .init = &clk_disp0dto_init, + }, +}; + +static const char * const disp1dto_clk_parents[] = { + "sys0pll_clk1", + "sys1pll_clk1", + "sys3pll_clk1", +}; + +static struct clk_init_data clk_disp1dto_init = { + .name = "disp1_dto", + .ops = &dto_ops, + .parent_names = disp1dto_clk_parents, + .num_parents = ARRAY_SIZE(disp1dto_clk_parents), +}; + +static struct clk_dto clk_disp1_dto = { + .inc_offset = SIRFSOC_CLKC_DISP1_DTO_INC, + .src_offset = SIRFSOC_CLKC_DISP1_DTO_SRC, + .hw = { + .init = &clk_disp1dto_init, + }, +}; + +static struct atlas7_div_init_data divider_list[] __initdata = { + /* div_name, parent_name, gate_name, clk_flag, divider_flag, gate_flag, div_offset, shift, wdith, gate_offset, bit_enable, lock */ + { "sys0pll_qa1", "sys0pll_fixdiv", "sys0pll_a1", 0, 0, 0, SIRFSOC_CLKC_USBPHY_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_USBPHY_CLKDIV_ENA, 0, &usbphy_div_lock }, + { "sys1pll_qa1", "sys1pll_fixdiv", "sys1pll_a1", 0, 0, 0, SIRFSOC_CLKC_USBPHY_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_USBPHY_CLKDIV_ENA, 4, &usbphy_div_lock }, + { "sys2pll_qa1", "sys2pll_fixdiv", "sys2pll_a1", 0, 0, 0, SIRFSOC_CLKC_USBPHY_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_USBPHY_CLKDIV_ENA, 8, &usbphy_div_lock }, + { "sys3pll_qa1", "sys3pll_fixdiv", "sys3pll_a1", 0, 0, 0, SIRFSOC_CLKC_USBPHY_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_USBPHY_CLKDIV_ENA, 12, &usbphy_div_lock }, + { "sys0pll_qa2", "sys0pll_fixdiv", "sys0pll_a2", 0, 0, 0, SIRFSOC_CLKC_BTSS_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_BTSS_CLKDIV_ENA, 0, &btss_div_lock }, + { "sys1pll_qa2", "sys1pll_fixdiv", "sys1pll_a2", 0, 0, 0, SIRFSOC_CLKC_BTSS_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_BTSS_CLKDIV_ENA, 4, &btss_div_lock }, + { "sys2pll_qa2", "sys2pll_fixdiv", "sys2pll_a2", 0, 0, 0, SIRFSOC_CLKC_BTSS_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_BTSS_CLKDIV_ENA, 8, &btss_div_lock }, + { "sys3pll_qa2", "sys3pll_fixdiv", "sys3pll_a2", 0, 0, 0, SIRFSOC_CLKC_BTSS_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_BTSS_CLKDIV_ENA, 12, &btss_div_lock }, + { "sys0pll_qa3", "sys0pll_fixdiv", "sys0pll_a3", 0, 0, 0, SIRFSOC_CLKC_RGMII_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_RGMII_CLKDIV_ENA, 0, &rgmii_div_lock }, + { "sys1pll_qa3", "sys1pll_fixdiv", "sys1pll_a3", 0, 0, 0, SIRFSOC_CLKC_RGMII_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_RGMII_CLKDIV_ENA, 4, &rgmii_div_lock }, + { "sys2pll_qa3", "sys2pll_fixdiv", "sys2pll_a3", 0, 0, 0, SIRFSOC_CLKC_RGMII_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_RGMII_CLKDIV_ENA, 8, &rgmii_div_lock }, + { "sys3pll_qa3", "sys3pll_fixdiv", "sys3pll_a3", 0, 0, 0, SIRFSOC_CLKC_RGMII_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_RGMII_CLKDIV_ENA, 12, &rgmii_div_lock }, + { "sys0pll_qa4", "sys0pll_fixdiv", "sys0pll_a4", 0, 0, 0, SIRFSOC_CLKC_CPU_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_CPU_CLKDIV_ENA, 0, &cpu_div_lock }, + { "sys1pll_qa4", "sys1pll_fixdiv", "sys1pll_a4", 0, 0, CLK_IGNORE_UNUSED, SIRFSOC_CLKC_CPU_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_CPU_CLKDIV_ENA, 4, &cpu_div_lock }, + { "sys0pll_qa5", "sys0pll_fixdiv", "sys0pll_a5", 0, 0, 0, SIRFSOC_CLKC_SDPHY01_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_SDPHY01_CLKDIV_ENA, 0, &sdphy01_div_lock }, + { "sys1pll_qa5", "sys1pll_fixdiv", "sys1pll_a5", 0, 0, 0, SIRFSOC_CLKC_SDPHY01_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_SDPHY01_CLKDIV_ENA, 4, &sdphy01_div_lock }, + { "sys2pll_qa5", "sys2pll_fixdiv", "sys2pll_a5", 0, 0, 0, SIRFSOC_CLKC_SDPHY01_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_SDPHY01_CLKDIV_ENA, 8, &sdphy01_div_lock }, + { "sys3pll_qa5", "sys3pll_fixdiv", "sys3pll_a5", 0, 0, 0, SIRFSOC_CLKC_SDPHY01_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_SDPHY01_CLKDIV_ENA, 12, &sdphy01_div_lock }, + { "sys0pll_qa6", "sys0pll_fixdiv", "sys0pll_a6", 0, 0, 0, SIRFSOC_CLKC_SDPHY23_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_SDPHY23_CLKDIV_ENA, 0, &sdphy23_div_lock }, + { "sys1pll_qa6", "sys1pll_fixdiv", "sys1pll_a6", 0, 0, 0, SIRFSOC_CLKC_SDPHY23_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_SDPHY23_CLKDIV_ENA, 4, &sdphy23_div_lock }, + { "sys2pll_qa6", "sys2pll_fixdiv", "sys2pll_a6", 0, 0, 0, SIRFSOC_CLKC_SDPHY23_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_SDPHY23_CLKDIV_ENA, 8, &sdphy23_div_lock }, + { "sys3pll_qa6", "sys3pll_fixdiv", "sys3pll_a6", 0, 0, 0, SIRFSOC_CLKC_SDPHY23_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_SDPHY23_CLKDIV_ENA, 12, &sdphy23_div_lock }, + { "sys0pll_qa7", "sys0pll_fixdiv", "sys0pll_a7", 0, 0, 0, SIRFSOC_CLKC_SDPHY45_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_SDPHY45_CLKDIV_ENA, 0, &sdphy45_div_lock }, + { "sys1pll_qa7", "sys1pll_fixdiv", "sys1pll_a7", 0, 0, 0, SIRFSOC_CLKC_SDPHY45_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_SDPHY45_CLKDIV_ENA, 4, &sdphy45_div_lock }, + { "sys2pll_qa7", "sys2pll_fixdiv", "sys2pll_a7", 0, 0, 0, SIRFSOC_CLKC_SDPHY45_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_SDPHY45_CLKDIV_ENA, 8, &sdphy45_div_lock }, + { "sys3pll_qa7", "sys3pll_fixdiv", "sys3pll_a7", 0, 0, 0, SIRFSOC_CLKC_SDPHY45_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_SDPHY45_CLKDIV_ENA, 12, &sdphy45_div_lock }, + { "sys0pll_qa8", "sys0pll_fixdiv", "sys0pll_a8", 0, 0, 0, SIRFSOC_CLKC_SDPHY67_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_SDPHY67_CLKDIV_ENA, 0, &sdphy67_div_lock }, + { "sys1pll_qa8", "sys1pll_fixdiv", "sys1pll_a8", 0, 0, 0, SIRFSOC_CLKC_SDPHY67_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_SDPHY67_CLKDIV_ENA, 4, &sdphy67_div_lock }, + { "sys2pll_qa8", "sys2pll_fixdiv", "sys2pll_a8", 0, 0, 0, SIRFSOC_CLKC_SDPHY67_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_SDPHY67_CLKDIV_ENA, 8, &sdphy67_div_lock }, + { "sys3pll_qa8", "sys3pll_fixdiv", "sys3pll_a8", 0, 0, 0, SIRFSOC_CLKC_SDPHY67_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_SDPHY67_CLKDIV_ENA, 12, &sdphy67_div_lock }, + { "sys0pll_qa9", "sys0pll_fixdiv", "sys0pll_a9", 0, 0, 0, SIRFSOC_CLKC_CAN_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_CAN_CLKDIV_ENA, 0, &can_div_lock }, + { "sys1pll_qa9", "sys1pll_fixdiv", "sys1pll_a9", 0, 0, 0, SIRFSOC_CLKC_CAN_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_CAN_CLKDIV_ENA, 4, &can_div_lock }, + { "sys2pll_qa9", "sys2pll_fixdiv", "sys2pll_a9", 0, 0, 0, SIRFSOC_CLKC_CAN_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_CAN_CLKDIV_ENA, 8, &can_div_lock }, + { "sys3pll_qa9", "sys3pll_fixdiv", "sys3pll_a9", 0, 0, 0, SIRFSOC_CLKC_CAN_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_CAN_CLKDIV_ENA, 12, &can_div_lock }, + { "sys0pll_qa10", "sys0pll_fixdiv", "sys0pll_a10", 0, 0, 0, SIRFSOC_CLKC_DEINT_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_DEINT_CLKDIV_ENA, 0, &deint_div_lock }, + { "sys1pll_qa10", "sys1pll_fixdiv", "sys1pll_a10", 0, 0, 0, SIRFSOC_CLKC_DEINT_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_DEINT_CLKDIV_ENA, 4, &deint_div_lock }, + { "sys2pll_qa10", "sys2pll_fixdiv", "sys2pll_a10", 0, 0, 0, SIRFSOC_CLKC_DEINT_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_DEINT_CLKDIV_ENA, 8, &deint_div_lock }, + { "sys3pll_qa10", "sys3pll_fixdiv", "sys3pll_a10", 0, 0, 0, SIRFSOC_CLKC_DEINT_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_DEINT_CLKDIV_ENA, 12, &deint_div_lock }, + { "sys0pll_qa11", "sys0pll_fixdiv", "sys0pll_a11", 0, 0, 0, SIRFSOC_CLKC_NAND_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_NAND_CLKDIV_ENA, 0, &nand_div_lock }, + { "sys1pll_qa11", "sys1pll_fixdiv", "sys1pll_a11", 0, 0, 0, SIRFSOC_CLKC_NAND_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_NAND_CLKDIV_ENA, 4, &nand_div_lock }, + { "sys2pll_qa11", "sys2pll_fixdiv", "sys2pll_a11", 0, 0, 0, SIRFSOC_CLKC_NAND_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_NAND_CLKDIV_ENA, 8, &nand_div_lock }, + { "sys3pll_qa11", "sys3pll_fixdiv", "sys3pll_a11", 0, 0, 0, SIRFSOC_CLKC_NAND_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_NAND_CLKDIV_ENA, 12, &nand_div_lock }, + { "sys0pll_qa12", "sys0pll_fixdiv", "sys0pll_a12", 0, 0, 0, SIRFSOC_CLKC_DISP0_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_DISP0_CLKDIV_ENA, 0, &disp0_div_lock }, + { "sys1pll_qa12", "sys1pll_fixdiv", "sys1pll_a12", 0, 0, 0, SIRFSOC_CLKC_DISP0_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_DISP0_CLKDIV_ENA, 4, &disp0_div_lock }, + { "sys2pll_qa12", "sys2pll_fixdiv", "sys2pll_a12", 0, 0, 0, SIRFSOC_CLKC_DISP0_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_DISP0_CLKDIV_ENA, 8, &disp0_div_lock }, + { "sys3pll_qa12", "sys3pll_fixdiv", "sys3pll_a12", 0, 0, 0, SIRFSOC_CLKC_DISP0_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_DISP0_CLKDIV_ENA, 12, &disp0_div_lock }, + { "sys0pll_qa13", "sys0pll_fixdiv", "sys0pll_a13", 0, 0, 0, SIRFSOC_CLKC_DISP1_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_DISP1_CLKDIV_ENA, 0, &disp1_div_lock }, + { "sys1pll_qa13", "sys1pll_fixdiv", "sys1pll_a13", 0, 0, 0, SIRFSOC_CLKC_DISP1_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_DISP1_CLKDIV_ENA, 4, &disp1_div_lock }, + { "sys2pll_qa13", "sys2pll_fixdiv", "sys2pll_a13", 0, 0, 0, SIRFSOC_CLKC_DISP1_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_DISP1_CLKDIV_ENA, 8, &disp1_div_lock }, + { "sys3pll_qa13", "sys3pll_fixdiv", "sys3pll_a13", 0, 0, 0, SIRFSOC_CLKC_DISP1_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_DISP1_CLKDIV_ENA, 12, &disp1_div_lock }, + { "sys0pll_qa14", "sys0pll_fixdiv", "sys0pll_a14", 0, 0, 0, SIRFSOC_CLKC_GPU_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_GPU_CLKDIV_ENA, 0, &gpu_div_lock }, + { "sys1pll_qa14", "sys1pll_fixdiv", "sys1pll_a14", 0, 0, 0, SIRFSOC_CLKC_GPU_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_GPU_CLKDIV_ENA, 4, &gpu_div_lock }, + { "sys2pll_qa14", "sys2pll_fixdiv", "sys2pll_a14", 0, 0, 0, SIRFSOC_CLKC_GPU_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_GPU_CLKDIV_ENA, 8, &gpu_div_lock }, + { "sys3pll_qa14", "sys3pll_fixdiv", "sys3pll_a14", 0, 0, 0, SIRFSOC_CLKC_GPU_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_GPU_CLKDIV_ENA, 12, &gpu_div_lock }, + { "sys0pll_qa15", "sys0pll_fixdiv", "sys0pll_a15", 0, 0, 0, SIRFSOC_CLKC_GNSS_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_GNSS_CLKDIV_ENA, 0, &gnss_div_lock }, + { "sys1pll_qa15", "sys1pll_fixdiv", "sys1pll_a15", 0, 0, 0, SIRFSOC_CLKC_GNSS_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_GNSS_CLKDIV_ENA, 4, &gnss_div_lock }, + { "sys2pll_qa15", "sys2pll_fixdiv", "sys2pll_a15", 0, 0, 0, SIRFSOC_CLKC_GNSS_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_GNSS_CLKDIV_ENA, 8, &gnss_div_lock }, + { "sys3pll_qa15", "sys3pll_fixdiv", "sys3pll_a15", 0, 0, 0, SIRFSOC_CLKC_GNSS_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_GNSS_CLKDIV_ENA, 12, &gnss_div_lock }, + { "sys1pll_qa18", "sys1pll_fixdiv", "sys1pll_a18", 0, 0, 0, SIRFSOC_CLKC_SHARED_DIVIDER_CFG0, 24, 6, SIRFSOC_CLKC_SHARED_DIVIDER_ENA, 12, &share_div_lock }, + { "sys1pll_qa19", "sys1pll_fixdiv", "sys1pll_a19", 0, 0, CLK_IGNORE_UNUSED, SIRFSOC_CLKC_SHARED_DIVIDER_CFG0, 16, 6, SIRFSOC_CLKC_SHARED_DIVIDER_ENA, 8, &share_div_lock }, + { "sys1pll_qa20", "sys1pll_fixdiv", "sys1pll_a20", 0, 0, 0, SIRFSOC_CLKC_SHARED_DIVIDER_CFG0, 8, 6, SIRFSOC_CLKC_SHARED_DIVIDER_ENA, 4, &share_div_lock }, + { "sys2pll_qa20", "sys2pll_fixdiv", "sys2pll_a20", 0, 0, 0, SIRFSOC_CLKC_SHARED_DIVIDER_CFG0, 0, 6, SIRFSOC_CLKC_SHARED_DIVIDER_ENA, 0, &share_div_lock }, + { "sys1pll_qa17", "sys1pll_fixdiv", "sys1pll_a17", 0, 0, CLK_IGNORE_UNUSED, SIRFSOC_CLKC_SHARED_DIVIDER_CFG1, 8, 6, SIRFSOC_CLKC_SHARED_DIVIDER_ENA, 20, &share_div_lock }, + { "sys0pll_qa20", "sys0pll_fixdiv", "sys0pll_a20", 0, 0, 0, SIRFSOC_CLKC_SHARED_DIVIDER_CFG1, 0, 6, SIRFSOC_CLKC_SHARED_DIVIDER_ENA, 16, &share_div_lock }, +}; + +static const char * const i2s_clk_parents[] = { + "xin", + "xinw", + "audio_dto", + /* "pwm_i2s01" */ +}; + +static const char * const usbphy_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a1", + "sys1pll_a1", + "sys2pll_a1", + "sys3pll_a1", +}; + +static const char * const btss_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a2", + "sys1pll_a2", + "sys2pll_a2", + "sys3pll_a2", +}; + +static const char * const rgmii_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a3", + "sys1pll_a3", + "sys2pll_a3", + "sys3pll_a3", +}; + +static const char * const cpu_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a4", + "sys1pll_a4", + "cpupll_clk1", +}; + +static const char * const sdphy01_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a5", + "sys1pll_a5", + "sys2pll_a5", + "sys3pll_a5", +}; + +static const char * const sdphy23_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a6", + "sys1pll_a6", + "sys2pll_a6", + "sys3pll_a6", +}; + +static const char * const sdphy45_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a7", + "sys1pll_a7", + "sys2pll_a7", + "sys3pll_a7", +}; + +static const char * const sdphy67_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a8", + "sys1pll_a8", + "sys2pll_a8", + "sys3pll_a8", +}; + +static const char * const can_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a9", + "sys1pll_a9", + "sys2pll_a9", + "sys3pll_a9", +}; + +static const char * const deint_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a10", + "sys1pll_a10", + "sys2pll_a10", + "sys3pll_a10", +}; + +static const char * const nand_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a11", + "sys1pll_a11", + "sys2pll_a11", + "sys3pll_a11", +}; + +static const char * const disp0_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a12", + "sys1pll_a12", + "sys2pll_a12", + "sys3pll_a12", + "disp0_dto", +}; + +static const char * const disp1_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a13", + "sys1pll_a13", + "sys2pll_a13", + "sys3pll_a13", + "disp1_dto", +}; + +static const char * const gpu_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a14", + "sys1pll_a14", + "sys2pll_a14", + "sys3pll_a14", +}; + +static const char * const gnss_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a15", + "sys1pll_a15", + "sys2pll_a15", + "sys3pll_a15", +}; + +static const char * const sys_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static const char * const io_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static const char * const g2d_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static const char * const jpenc_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static const char * const vdec_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static const char * const gmac_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static const char * const usb_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static const char * const kas_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static const char * const sec_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static const char * const sdr_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static const char * const vip_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static const char * const nocd_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static const char * const nocr_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static const char * const tpiu_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static struct atlas7_mux_init_data mux_list[] __initdata = { + /* mux_name, parent_names, parent_num, flags, mux_flags, mux_offset, shift, width */ + { "i2s_mux", i2s_clk_parents, ARRAY_SIZE(i2s_clk_parents), 0, 0, SIRFSOC_CLKC_I2S_CLK_SEL, 0, 2 }, + { "usbphy_mux", usbphy_clk_parents, ARRAY_SIZE(usbphy_clk_parents), 0, 0, SIRFSOC_CLKC_I2S_CLK_SEL, 0, 3 }, + { "btss_mux", btss_clk_parents, ARRAY_SIZE(btss_clk_parents), 0, 0, SIRFSOC_CLKC_BTSS_CLK_SEL, 0, 3 }, + { "rgmii_mux", rgmii_clk_parents, ARRAY_SIZE(rgmii_clk_parents), 0, 0, SIRFSOC_CLKC_RGMII_CLK_SEL, 0, 3 }, + { "cpu_mux", cpu_clk_parents, ARRAY_SIZE(cpu_clk_parents), 0, 0, SIRFSOC_CLKC_CPU_CLK_SEL, 0, 3 }, + { "sdphy01_mux", sdphy01_clk_parents, ARRAY_SIZE(sdphy01_clk_parents), 0, 0, SIRFSOC_CLKC_SDPHY01_CLK_SEL, 0, 3 }, + { "sdphy23_mux", sdphy23_clk_parents, ARRAY_SIZE(sdphy23_clk_parents), 0, 0, SIRFSOC_CLKC_SDPHY23_CLK_SEL, 0, 3 }, + { "sdphy45_mux", sdphy45_clk_parents, ARRAY_SIZE(sdphy45_clk_parents), 0, 0, SIRFSOC_CLKC_SDPHY45_CLK_SEL, 0, 3 }, + { "sdphy67_mux", sdphy67_clk_parents, ARRAY_SIZE(sdphy67_clk_parents), 0, 0, SIRFSOC_CLKC_SDPHY67_CLK_SEL, 0, 3 }, + { "can_mux", can_clk_parents, ARRAY_SIZE(can_clk_parents), 0, 0, SIRFSOC_CLKC_CAN_CLK_SEL, 0, 3 }, + { "deint_mux", deint_clk_parents, ARRAY_SIZE(deint_clk_parents), 0, 0, SIRFSOC_CLKC_DEINT_CLK_SEL, 0, 3 }, + { "nand_mux", nand_clk_parents, ARRAY_SIZE(nand_clk_parents), 0, 0, SIRFSOC_CLKC_NAND_CLK_SEL, 0, 3 }, + { "disp0_mux", disp0_clk_parents, ARRAY_SIZE(disp0_clk_parents), 0, 0, SIRFSOC_CLKC_DISP0_CLK_SEL, 0, 3 }, + { "disp1_mux", disp1_clk_parents, ARRAY_SIZE(disp1_clk_parents), 0, 0, SIRFSOC_CLKC_DISP1_CLK_SEL, 0, 3 }, + { "gpu_mux", gpu_clk_parents, ARRAY_SIZE(gpu_clk_parents), 0, 0, SIRFSOC_CLKC_GPU_CLK_SEL, 0, 3 }, + { "gnss_mux", gnss_clk_parents, ARRAY_SIZE(gnss_clk_parents), 0, 0, SIRFSOC_CLKC_GNSS_CLK_SEL, 0, 3 }, + { "sys_mux", sys_clk_parents, ARRAY_SIZE(sys_clk_parents), 0, 0, SIRFSOC_CLKC_SYS_CLK_SEL, 0, 3 }, + { "io_mux", io_clk_parents, ARRAY_SIZE(io_clk_parents), 0, 0, SIRFSOC_CLKC_IO_CLK_SEL, 0, 3 }, + { "g2d_mux", g2d_clk_parents, ARRAY_SIZE(g2d_clk_parents), 0, 0, SIRFSOC_CLKC_G2D_CLK_SEL, 0, 3 }, + { "jpenc_mux", jpenc_clk_parents, ARRAY_SIZE(jpenc_clk_parents), 0, 0, SIRFSOC_CLKC_JPENC_CLK_SEL, 0, 3 }, + { "vdec_mux", vdec_clk_parents, ARRAY_SIZE(vdec_clk_parents), 0, 0, SIRFSOC_CLKC_VDEC_CLK_SEL, 0, 3 }, + { "gmac_mux", gmac_clk_parents, ARRAY_SIZE(gmac_clk_parents), 0, 0, SIRFSOC_CLKC_GMAC_CLK_SEL, 0, 3 }, + { "usb_mux", usb_clk_parents, ARRAY_SIZE(usb_clk_parents), 0, 0, SIRFSOC_CLKC_USB_CLK_SEL, 0, 3 }, + { "kas_mux", kas_clk_parents, ARRAY_SIZE(kas_clk_parents), 0, 0, SIRFSOC_CLKC_KAS_CLK_SEL, 0, 3 }, + { "sec_mux", sec_clk_parents, ARRAY_SIZE(sec_clk_parents), 0, 0, SIRFSOC_CLKC_SEC_CLK_SEL, 0, 3 }, + { "sdr_mux", sdr_clk_parents, ARRAY_SIZE(sdr_clk_parents), 0, 0, SIRFSOC_CLKC_SDR_CLK_SEL, 0, 3 }, + { "vip_mux", vip_clk_parents, ARRAY_SIZE(vip_clk_parents), 0, 0, SIRFSOC_CLKC_VIP_CLK_SEL, 0, 3 }, + { "nocd_mux", nocd_clk_parents, ARRAY_SIZE(nocd_clk_parents), 0, 0, SIRFSOC_CLKC_NOCD_CLK_SEL, 0, 3 }, + { "nocr_mux", nocr_clk_parents, ARRAY_SIZE(nocr_clk_parents), 0, 0, SIRFSOC_CLKC_NOCR_CLK_SEL, 0, 3 }, + { "tpiu_mux", tpiu_clk_parents, ARRAY_SIZE(tpiu_clk_parents), 0, 0, SIRFSOC_CLKC_TPIU_CLK_SEL, 0, 3 }, +}; + + /* new unit should add start from the tail of list */ +static struct atlas7_unit_init_data unit_list[] __initdata = { + /* unit_name, parent_name, flags, regofs, bit, lock */ + { 0, "audmscm_kas", "kas_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 0, &root0_gate_lock }, + { 1, "gnssm_gnss", "gnss_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 1, &root0_gate_lock }, + { 2, "gpum_gpu", "gpu_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 2, &root0_gate_lock }, + { 3, "mediam_g2d", "g2d_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 3, &root0_gate_lock }, + { 4, "mediam_jpenc", "jpenc_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 4, &root0_gate_lock }, + { 5, "vdifm_disp0", "disp0_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 5, &root0_gate_lock }, + { 6, "vdifm_disp1", "disp1_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 6, &root0_gate_lock }, + { 7, "audmscm_i2s", "i2s_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 8, &root0_gate_lock }, + { 8, "audmscm_io", "io_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 11, &root0_gate_lock }, + { 9, "vdifm_io", "io_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 12, &root0_gate_lock }, + { 10, "gnssm_io", "io_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 13, &root0_gate_lock }, + { 11, "mediam_io", "io_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 14, &root0_gate_lock }, + { 12, "btm_io", "io_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 17, &root0_gate_lock }, + { 13, "mediam_sdphy01", "sdphy01_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 18, &root0_gate_lock }, + { 14, "vdifm_sdphy23", "sdphy23_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 19, &root0_gate_lock }, + { 15, "vdifm_sdphy45", "sdphy45_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 20, &root0_gate_lock }, + { 16, "vdifm_sdphy67", "sdphy67_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 21, &root0_gate_lock }, + { 17, "audmscm_xin", "xin", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 22, &root0_gate_lock }, + { 18, "mediam_nand", "nand_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 27, &root0_gate_lock }, + { 19, "gnssm_sec", "sec_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 28, &root0_gate_lock }, + { 20, "cpum_cpu", "cpu_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 29, &root0_gate_lock }, + { 21, "gnssm_xin", "xin", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 30, &root0_gate_lock }, + { 22, "vdifm_vip", "vip_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 31, &root0_gate_lock }, + { 23, "btm_btss", "btss_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 0, &root1_gate_lock }, + { 24, "mediam_usbphy", "usbphy_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 1, &root1_gate_lock }, + { 25, "rtcm_kas", "kas_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 2, &root1_gate_lock }, + { 26, "audmscm_nocd", "nocd_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 3, &root1_gate_lock }, + { 27, "vdifm_nocd", "nocd_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 4, &root1_gate_lock }, + { 28, "gnssm_nocd", "nocd_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 5, &root1_gate_lock }, + { 29, "mediam_nocd", "nocd_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 6, &root1_gate_lock }, + { 30, "cpum_nocd", "nocd_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 8, &root1_gate_lock }, + { 31, "gpum_nocd", "nocd_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 9, &root1_gate_lock }, + { 32, "audmscm_nocr", "nocr_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 11, &root1_gate_lock }, + { 33, "vdifm_nocr", "nocr_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 12, &root1_gate_lock }, + { 34, "gnssm_nocr", "nocr_mux", CLK_IGNORE_UNUSED, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 13, &root1_gate_lock }, + { 35, "mediam_nocr", "nocr_mux", CLK_IGNORE_UNUSED, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 14, &root1_gate_lock }, + { 36, "ddrm_nocr", "nocr_mux", CLK_IGNORE_UNUSED, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 15, &root1_gate_lock }, + { 37, "cpum_tpiu", "tpiu_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 16, &root1_gate_lock }, + { 38, "gpum_nocr", "nocr_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 17, &root1_gate_lock }, + { 39, "gnssm_rgmii", "rgmii_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 20, &root1_gate_lock }, + { 40, "mediam_vdec", "vdec_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 21, &root1_gate_lock }, + { 41, "gpum_sdr", "sdr_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 22, &root1_gate_lock }, + { 42, "vdifm_deint", "deint_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 23, &root1_gate_lock }, + { 43, "gnssm_can", "can_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 26, &root1_gate_lock }, + { 44, "mediam_usb", "usb_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 28, &root1_gate_lock }, + { 45, "gnssm_gmac", "gmac_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 29, &root1_gate_lock }, + { 46, "cvd_io", "audmscm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 0, &leaf1_gate_lock }, + { 47, "timer_io", "audmscm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 1, &leaf1_gate_lock }, + { 48, "pulse_io", "audmscm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 2, &leaf1_gate_lock }, + { 49, "tsc_io", "audmscm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 3, &leaf1_gate_lock }, + { 50, "tsc_xin", "audmscm_xin", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 21, &leaf1_gate_lock }, + { 51, "ioctop_io", "audmscm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 4, &leaf1_gate_lock }, + { 52, "rsc_io", "audmscm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 5, &leaf1_gate_lock }, + { 53, "dvm_io", "audmscm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 6, &leaf1_gate_lock }, + { 54, "lvds_xin", "audmscm_xin", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 7, &leaf1_gate_lock }, + { 55, "kas_kas", "audmscm_kas", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 8, &leaf1_gate_lock }, + { 56, "ac97_kas", "audmscm_kas", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 9, &leaf1_gate_lock }, + { 57, "usp0_kas", "audmscm_kas", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 10, &leaf1_gate_lock }, + { 58, "usp1_kas", "audmscm_kas", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 11, &leaf1_gate_lock }, + { 59, "usp2_kas", "audmscm_kas", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 12, &leaf1_gate_lock }, + { 60, "dmac2_kas", "audmscm_kas", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 13, &leaf1_gate_lock }, + { 61, "dmac3_kas", "audmscm_kas", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 14, &leaf1_gate_lock }, + { 62, "audioif_kas", "audmscm_kas", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 15, &leaf1_gate_lock }, + { 63, "i2s1_kas", "audmscm_kas", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 17, &leaf1_gate_lock }, + { 64, "thaudmscm_io", "audmscm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 22, &leaf1_gate_lock }, + { 65, "analogtest_xin", "audmscm_xin", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 23, &leaf1_gate_lock }, + { 66, "sys2pci_io", "vdifm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 0, &leaf2_gate_lock }, + { 67, "pciarb_io", "vdifm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 1, &leaf2_gate_lock }, + { 68, "pcicopy_io", "vdifm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 2, &leaf2_gate_lock }, + { 69, "rom_io", "vdifm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 3, &leaf2_gate_lock }, + { 70, "sdio23_io", "vdifm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 4, &leaf2_gate_lock }, + { 71, "sdio45_io", "vdifm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 5, &leaf2_gate_lock }, + { 72, "sdio67_io", "vdifm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 6, &leaf2_gate_lock }, + { 73, "vip1_io", "vdifm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 7, &leaf2_gate_lock }, + { 74, "vip1_vip", "vdifm_vip", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 16, &leaf2_gate_lock }, + { 75, "sdio23_sdphy23", "vdifm_sdphy23", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 8, &leaf2_gate_lock }, + { 76, "sdio45_sdphy45", "vdifm_sdphy45", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 9, &leaf2_gate_lock }, + { 77, "sdio67_sdphy67", "vdifm_sdphy67", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 10, &leaf2_gate_lock }, + { 78, "vpp0_disp0", "vdifm_disp0", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 11, &leaf2_gate_lock }, + { 79, "lcd0_disp0", "vdifm_disp0", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 12, &leaf2_gate_lock }, + { 80, "vpp1_disp1", "vdifm_disp1", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 13, &leaf2_gate_lock }, + { 81, "lcd1_disp1", "vdifm_disp1", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 14, &leaf2_gate_lock }, + { 82, "dcu_deint", "vdifm_deint", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 15, &leaf2_gate_lock }, + { 83, "vdifm_dapa_r_nocr", "vdifm_nocr", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 17, &leaf2_gate_lock }, + { 84, "gpio1_io", "vdifm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 18, &leaf2_gate_lock }, + { 85, "thvdifm_io", "vdifm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 19, &leaf2_gate_lock }, + { 86, "gmac_rgmii", "gnssm_rgmii", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 0, &leaf3_gate_lock }, + { 87, "gmac_gmac", "gnssm_gmac", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 1, &leaf3_gate_lock }, + { 88, "uart1_io", "gnssm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 2, &leaf3_gate_lock }, + { 89, "dmac0_io", "gnssm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 3, &leaf3_gate_lock }, + { 90, "uart0_io", "gnssm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 4, &leaf3_gate_lock }, + { 91, "uart2_io", "gnssm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 5, &leaf3_gate_lock }, + { 92, "uart3_io", "gnssm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 6, &leaf3_gate_lock }, + { 93, "uart4_io", "gnssm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 7, &leaf3_gate_lock }, + { 94, "uart5_io", "gnssm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 8, &leaf3_gate_lock }, + { 95, "spi1_io", "gnssm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 9, &leaf3_gate_lock }, + { 96, "gnss_gnss", "gnssm_gnss", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 10, &leaf3_gate_lock }, + { 97, "canbus1_can", "gnssm_can", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 12, &leaf3_gate_lock }, + { 98, "ccsec_sec", "gnssm_sec", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 15, &leaf3_gate_lock }, + { 99, "ccpub_sec", "gnssm_sec", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 16, &leaf3_gate_lock }, + { 100, "gnssm_dapa_r_nocr", "gnssm_nocr", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 13, &leaf3_gate_lock }, + { 101, "thgnssm_io", "gnssm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 14, &leaf3_gate_lock }, + { 102, "media_vdec", "mediam_vdec", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 0, &leaf4_gate_lock }, + { 103, "media_jpenc", "mediam_jpenc", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 1, &leaf4_gate_lock }, + { 104, "g2d_g2d", "mediam_g2d", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 2, &leaf4_gate_lock }, + { 105, "i2c0_io", "mediam_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 3, &leaf4_gate_lock }, + { 106, "i2c1_io", "mediam_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 4, &leaf4_gate_lock }, + { 107, "gpio0_io", "mediam_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 5, &leaf4_gate_lock }, + { 108, "nand_io", "mediam_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 6, &leaf4_gate_lock }, + { 109, "sdio01_io", "mediam_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 7, &leaf4_gate_lock }, + { 110, "sys2pci2_io", "mediam_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 8, &leaf4_gate_lock }, + { 111, "sdio01_sdphy01", "mediam_sdphy01", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 9, &leaf4_gate_lock }, + { 112, "nand_nand", "mediam_nand", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 10, &leaf4_gate_lock }, + { 113, "usb0_usb", "mediam_usb", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 11, &leaf4_gate_lock }, + { 114, "usb1_usb", "mediam_usb", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 12, &leaf4_gate_lock }, + { 115, "usbphy0_usbphy", "mediam_usbphy", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 13, &leaf4_gate_lock }, + { 116, "usbphy1_usbphy", "mediam_usbphy", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 14, &leaf4_gate_lock }, + { 117, "thmediam_io", "mediam_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 15, &leaf4_gate_lock }, + { 118, "memc_mem", "mempll_clk1", CLK_IGNORE_UNUSED, SIRFSOC_CLKC_LEAF_CLK_EN5_SET, 0, &leaf5_gate_lock }, + { 119, "dapa_mem", "mempll_clk1", 0, SIRFSOC_CLKC_LEAF_CLK_EN5_SET, 1, &leaf5_gate_lock }, + { 120, "nocddrm_nocr", "ddrm_nocr", 0, SIRFSOC_CLKC_LEAF_CLK_EN5_SET, 2, &leaf5_gate_lock }, + { 121, "thddrm_nocr", "ddrm_nocr", 0, SIRFSOC_CLKC_LEAF_CLK_EN5_SET, 3, &leaf5_gate_lock }, + { 122, "spram1_cpudiv2", "cpum_cpu", 0, SIRFSOC_CLKC_LEAF_CLK_EN6_SET, 0, &leaf6_gate_lock }, + { 123, "spram2_cpudiv2", "cpum_cpu", 0, SIRFSOC_CLKC_LEAF_CLK_EN6_SET, 1, &leaf6_gate_lock }, + { 124, "coresight_cpudiv2", "cpum_cpu", 0, SIRFSOC_CLKC_LEAF_CLK_EN6_SET, 2, &leaf6_gate_lock }, + { 125, "thcpum_cpudiv4", "cpum_cpu", 0, SIRFSOC_CLKC_LEAF_CLK_EN6_SET, 3, &leaf6_gate_lock }, + { 126, "graphic_gpu", "gpum_gpu", 0, SIRFSOC_CLKC_LEAF_CLK_EN7_SET, 0, &leaf7_gate_lock }, + { 127, "vss_sdr", "gpum_sdr", 0, SIRFSOC_CLKC_LEAF_CLK_EN7_SET, 1, &leaf7_gate_lock }, + { 128, "thgpum_nocr", "gpum_nocr", 0, SIRFSOC_CLKC_LEAF_CLK_EN7_SET, 2, &leaf7_gate_lock }, + { 129, "a7ca_btss", "btm_btss", 0, SIRFSOC_CLKC_LEAF_CLK_EN8_SET, 1, &leaf8_gate_lock }, + { 130, "dmac4_io", "btm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN8_SET, 2, &leaf8_gate_lock }, + { 131, "uart6_io", "btm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN8_SET, 3, &leaf8_gate_lock }, + { 132, "usp3_io", "btm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN8_SET, 4, &leaf8_gate_lock }, + { 133, "a7ca_io", "btm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN8_SET, 5, &leaf8_gate_lock }, + { 134, "noc_btm_io", "btm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN8_SET, 6, &leaf8_gate_lock }, + { 135, "thbtm_io", "btm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN8_SET, 7, &leaf8_gate_lock }, + { 136, "btslow", "xinw_fixdiv_btslow", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 25, &root1_gate_lock }, + { 137, "a7ca_btslow", "btslow", 0, SIRFSOC_CLKC_LEAF_CLK_EN8_SET, 0, &leaf8_gate_lock }, +}; + +static struct clk *atlas7_clks[ARRAY_SIZE(unit_list)]; + +static int unit_clk_is_enabled(struct clk_hw *hw) +{ + struct clk_unit *clk = to_unitclk(hw); + u32 reg; + + reg = clk->regofs + SIRFSOC_CLKC_ROOT_CLK_EN0_STAT - SIRFSOC_CLKC_ROOT_CLK_EN0_SET; + + return !!(clkc_readl(reg) & BIT(clk->bit)); +} + +static int unit_clk_enable(struct clk_hw *hw) +{ + u32 reg; + struct clk_unit *clk = to_unitclk(hw); + unsigned long flags; + + reg = clk->regofs; + + spin_lock_irqsave(clk->lock, flags); + clkc_writel(BIT(clk->bit), reg); + spin_unlock_irqrestore(clk->lock, flags); + return 0; +} + +static void unit_clk_disable(struct clk_hw *hw) +{ + u32 reg; + struct clk_unit *clk = to_unitclk(hw); + unsigned long flags; + + reg = clk->regofs + SIRFSOC_CLKC_ROOT_CLK_EN0_CLR - SIRFSOC_CLKC_ROOT_CLK_EN0_SET; + + spin_lock_irqsave(clk->lock, flags); + clkc_writel(BIT(clk->bit), reg); + spin_unlock_irqrestore(clk->lock, flags); +} + +static const struct clk_ops unit_clk_ops = { + .is_enabled = unit_clk_is_enabled, + .enable = unit_clk_enable, + .disable = unit_clk_disable, +}; + +static struct clk * __init +atlas7_unit_clk_register(struct device *dev, const char *name, + const char * const parent_name, unsigned long flags, + u32 regofs, u8 bit, spinlock_t *lock) +{ + struct clk *clk; + struct clk_unit *unit; + struct clk_init_data init; + + unit = kzalloc(sizeof(*unit), GFP_KERNEL); + if (!unit) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.parent_names = &parent_name; + init.num_parents = 1; + init.ops = &unit_clk_ops; + init.flags = flags; + + unit->hw.init = &init; + unit->regofs = regofs; + unit->bit = bit; + unit->lock = lock; + + clk = clk_register(dev, &unit->hw); + if (IS_ERR(clk)) + kfree(unit); + + return clk; +} + +static struct atlas7_reset_desc atlas7_reset_unit[] = { + { "PWM", 0x0244, 0, 0x0320, 0, &leaf0_gate_lock }, /* 0-5 */ + { "THCGUM", 0x0244, 3, 0x0320, 1, &leaf0_gate_lock }, + { "CVD", 0x04A0, 0, 0x032C, 0, &leaf1_gate_lock }, + { "TIMER", 0x04A0, 1, 0x032C, 1, &leaf1_gate_lock }, + { "PULSEC", 0x04A0, 2, 0x032C, 2, &leaf1_gate_lock }, + { "TSC", 0x04A0, 3, 0x032C, 3, &leaf1_gate_lock }, + { "IOCTOP", 0x04A0, 4, 0x032C, 4, &leaf1_gate_lock }, /* 6-10 */ + { "RSC", 0x04A0, 5, 0x032C, 5, &leaf1_gate_lock }, + { "DVM", 0x04A0, 6, 0x032C, 6, &leaf1_gate_lock }, + { "LVDS", 0x04A0, 7, 0x032C, 7, &leaf1_gate_lock }, + { "KAS", 0x04A0, 8, 0x032C, 8, &leaf1_gate_lock }, + { "AC97", 0x04A0, 9, 0x032C, 9, &leaf1_gate_lock }, /* 11-15 */ + { "USP0", 0x04A0, 10, 0x032C, 10, &leaf1_gate_lock }, + { "USP1", 0x04A0, 11, 0x032C, 11, &leaf1_gate_lock }, + { "USP2", 0x04A0, 12, 0x032C, 12, &leaf1_gate_lock }, + { "DMAC2", 0x04A0, 13, 0x032C, 13, &leaf1_gate_lock }, + { "DMAC3", 0x04A0, 14, 0x032C, 14, &leaf1_gate_lock }, /* 16-20 */ + { "AUDIO", 0x04A0, 15, 0x032C, 15, &leaf1_gate_lock }, + { "I2S1", 0x04A0, 17, 0x032C, 16, &leaf1_gate_lock }, + { "PMU_AUDIO", 0x04A0, 22, 0x032C, 17, &leaf1_gate_lock }, + { "THAUDMSCM", 0x04A0, 23, 0x032C, 18, &leaf1_gate_lock }, + { "SYS2PCI", 0x04B8, 0, 0x0338, 0, &leaf2_gate_lock }, /* 21-25 */ + { "PCIARB", 0x04B8, 1, 0x0338, 1, &leaf2_gate_lock }, + { "PCICOPY", 0x04B8, 2, 0x0338, 2, &leaf2_gate_lock }, + { "ROM", 0x04B8, 3, 0x0338, 3, &leaf2_gate_lock }, + { "SDIO23", 0x04B8, 4, 0x0338, 4, &leaf2_gate_lock }, + { "SDIO45", 0x04B8, 5, 0x0338, 5, &leaf2_gate_lock }, /* 26-30 */ + { "SDIO67", 0x04B8, 6, 0x0338, 6, &leaf2_gate_lock }, + { "VIP1", 0x04B8, 7, 0x0338, 7, &leaf2_gate_lock }, + { "VPP0", 0x04B8, 11, 0x0338, 8, &leaf2_gate_lock }, + { "LCD0", 0x04B8, 12, 0x0338, 9, &leaf2_gate_lock }, + { "VPP1", 0x04B8, 13, 0x0338, 10, &leaf2_gate_lock }, /* 31-35 */ + { "LCD1", 0x04B8, 14, 0x0338, 11, &leaf2_gate_lock }, + { "DCU", 0x04B8, 15, 0x0338, 12, &leaf2_gate_lock }, + { "GPIO", 0x04B8, 18, 0x0338, 13, &leaf2_gate_lock }, + { "DAPA_VDIFM", 0x04B8, 17, 0x0338, 15, &leaf2_gate_lock }, + { "THVDIFM", 0x04B8, 19, 0x0338, 16, &leaf2_gate_lock }, /* 36-40 */ + { "RGMII", 0x04D0, 0, 0x0344, 0, &leaf3_gate_lock }, + { "GMAC", 0x04D0, 1, 0x0344, 1, &leaf3_gate_lock }, + { "UART1", 0x04D0, 2, 0x0344, 2, &leaf3_gate_lock }, + { "DMAC0", 0x04D0, 3, 0x0344, 3, &leaf3_gate_lock }, + { "UART0", 0x04D0, 4, 0x0344, 4, &leaf3_gate_lock }, /* 41-45 */ + { "UART2", 0x04D0, 5, 0x0344, 5, &leaf3_gate_lock }, + { "UART3", 0x04D0, 6, 0x0344, 6, &leaf3_gate_lock }, + { "UART4", 0x04D0, 7, 0x0344, 7, &leaf3_gate_lock }, + { "UART5", 0x04D0, 8, 0x0344, 8, &leaf3_gate_lock }, + { "SPI1", 0x04D0, 9, 0x0344, 9, &leaf3_gate_lock }, /* 46-50 */ + { "GNSS_SYS_M0", 0x04D0, 10, 0x0344, 10, &leaf3_gate_lock }, + { "CANBUS1", 0x04D0, 12, 0x0344, 11, &leaf3_gate_lock }, + { "CCSEC", 0x04D0, 15, 0x0344, 12, &leaf3_gate_lock }, + { "CCPUB", 0x04D0, 16, 0x0344, 13, &leaf3_gate_lock }, + { "DAPA_GNSSM", 0x04D0, 13, 0x0344, 14, &leaf3_gate_lock }, /* 51-55 */ + { "THGNSSM", 0x04D0, 14, 0x0344, 15, &leaf3_gate_lock }, + { "VDEC", 0x04E8, 0, 0x0350, 0, &leaf4_gate_lock }, + { "JPENC", 0x04E8, 1, 0x0350, 1, &leaf4_gate_lock }, + { "G2D", 0x04E8, 2, 0x0350, 2, &leaf4_gate_lock }, + { "I2C0", 0x04E8, 3, 0x0350, 3, &leaf4_gate_lock }, /* 56-60 */ + { "I2C1", 0x04E8, 4, 0x0350, 4, &leaf4_gate_lock }, + { "GPIO0", 0x04E8, 5, 0x0350, 5, &leaf4_gate_lock }, + { "NAND", 0x04E8, 6, 0x0350, 6, &leaf4_gate_lock }, + { "SDIO01", 0x04E8, 7, 0x0350, 7, &leaf4_gate_lock }, + { "SYS2PCI2", 0x04E8, 8, 0x0350, 8, &leaf4_gate_lock }, /* 61-65 */ + { "USB0", 0x04E8, 11, 0x0350, 9, &leaf4_gate_lock }, + { "USB1", 0x04E8, 12, 0x0350, 10, &leaf4_gate_lock }, + { "THMEDIAM", 0x04E8, 15, 0x0350, 11, &leaf4_gate_lock }, + { "MEMC_DDRPHY", 0x0500, 0, 0x035C, 0, &leaf5_gate_lock }, + { "MEMC_UPCTL", 0x0500, 0, 0x035C, 1, &leaf5_gate_lock }, /* 66-70 */ + { "DAPA_MEM", 0x0500, 1, 0x035C, 2, &leaf5_gate_lock }, + { "MEMC_MEMDIV", 0x0500, 0, 0x035C, 3, &leaf5_gate_lock }, + { "THDDRM", 0x0500, 3, 0x035C, 4, &leaf5_gate_lock }, + { "CORESIGHT", 0x0518, 3, 0x0368, 13, &leaf6_gate_lock }, + { "THCPUM", 0x0518, 4, 0x0368, 17, &leaf6_gate_lock }, /* 71-75 */ + { "GRAPHIC", 0x0530, 0, 0x0374, 0, &leaf7_gate_lock }, + { "VSS_SDR", 0x0530, 1, 0x0374, 1, &leaf7_gate_lock }, + { "THGPUM", 0x0530, 2, 0x0374, 2, &leaf7_gate_lock }, + { "DMAC4", 0x0548, 2, 0x0380, 1, &leaf8_gate_lock }, + { "UART6", 0x0548, 3, 0x0380, 2, &leaf8_gate_lock }, /* 76- */ + { "USP3", 0x0548, 4, 0x0380, 3, &leaf8_gate_lock }, + { "THBTM", 0x0548, 5, 0x0380, 5, &leaf8_gate_lock }, + { "A7CA", 0x0548, 1, 0x0380, 0, &leaf8_gate_lock }, + { "A7CA_APB", 0x0548, 5, 0x0380, 4, &leaf8_gate_lock }, +}; + +static int atlas7_reset_module(struct reset_controller_dev *rcdev, + unsigned long reset_idx) +{ + struct atlas7_reset_desc *reset = &atlas7_reset_unit[reset_idx]; + unsigned long flags; + + /* + * HW suggest unit reset sequence: + * assert sw reset (0) + * setting sw clk_en to if the clock was disabled before reset + * delay 16 clocks + * disable clock (sw clk_en = 0) + * de-assert reset (1) + * after this sequence, restore clock or not is decided by SW + */ + + spin_lock_irqsave(reset->lock, flags); + /* clock enable or not */ + if (clkc_readl(reset->clk_ofs + 8) & (1 << reset->clk_bit)) { + clkc_writel(1 << reset->rst_bit, reset->rst_ofs + 4); + udelay(2); + clkc_writel(1 << reset->clk_bit, reset->clk_ofs + 4); + clkc_writel(1 << reset->rst_bit, reset->rst_ofs); + /* restore clock enable */ + clkc_writel(1 << reset->clk_bit, reset->clk_ofs); + } else { + clkc_writel(1 << reset->rst_bit, reset->rst_ofs + 4); + clkc_writel(1 << reset->clk_bit, reset->clk_ofs); + udelay(2); + clkc_writel(1 << reset->clk_bit, reset->clk_ofs + 4); + clkc_writel(1 << reset->rst_bit, reset->rst_ofs); + } + spin_unlock_irqrestore(reset->lock, flags); + + return 0; +} + +static struct reset_control_ops atlas7_rst_ops = { + .reset = atlas7_reset_module, +}; + +static struct reset_controller_dev atlas7_rst_ctlr = { + .ops = &atlas7_rst_ops, + .owner = THIS_MODULE, + .of_reset_n_cells = 1, +}; + +static void __init atlas7_clk_init(struct device_node *np) +{ + struct clk *clk; + struct atlas7_div_init_data *div; + struct atlas7_mux_init_data *mux; + struct atlas7_unit_init_data *unit; + int i; + int ret; + + sirfsoc_clk_vbase = of_iomap(np, 0); + if (!sirfsoc_clk_vbase) + panic("unable to map clkc registers\n"); + + of_node_put(np); + + clk = clk_register(NULL, &clk_cpupll.hw); + BUG_ON(!clk); + clk = clk_register(NULL, &clk_mempll.hw); + BUG_ON(!clk); + clk = clk_register(NULL, &clk_sys0pll.hw); + BUG_ON(!clk); + clk = clk_register(NULL, &clk_sys1pll.hw); + BUG_ON(!clk); + clk = clk_register(NULL, &clk_sys2pll.hw); + BUG_ON(!clk); + clk = clk_register(NULL, &clk_sys3pll.hw); + BUG_ON(!clk); + + clk = clk_register_divider_table(NULL, "cpupll_div1", "cpupll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_CPUPLL_AB_CTRL1, 0, 3, 0, + pll_div_table, &cpupll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_divider_table(NULL, "cpupll_div2", "cpupll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_CPUPLL_AB_CTRL1, 4, 3, 0, + pll_div_table, &cpupll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_divider_table(NULL, "cpupll_div3", "cpupll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_CPUPLL_AB_CTRL1, 8, 3, 0, + pll_div_table, &cpupll_ctrl1_lock); + BUG_ON(!clk); + + clk = clk_register_divider_table(NULL, "mempll_div1", "mempll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_MEMPLL_AB_CTRL1, 0, 3, 0, + pll_div_table, &mempll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_divider_table(NULL, "mempll_div2", "mempll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_MEMPLL_AB_CTRL1, 4, 3, 0, + pll_div_table, &mempll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_divider_table(NULL, "mempll_div3", "mempll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_MEMPLL_AB_CTRL1, 8, 3, 0, + pll_div_table, &mempll_ctrl1_lock); + BUG_ON(!clk); + + clk = clk_register_divider_table(NULL, "sys0pll_div1", "sys0pll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS0PLL_AB_CTRL1, 0, 3, 0, + pll_div_table, &sys0pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_divider_table(NULL, "sys0pll_div2", "sys0pll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS0PLL_AB_CTRL1, 4, 3, 0, + pll_div_table, &sys0pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_divider_table(NULL, "sys0pll_div3", "sys0pll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS0PLL_AB_CTRL1, 8, 3, 0, + pll_div_table, &sys0pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_fixed_factor(NULL, "sys0pll_fixdiv", "sys0pll_vco", + CLK_SET_RATE_PARENT, 1, 2); + + clk = clk_register_divider_table(NULL, "sys1pll_div1", "sys1pll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS1PLL_AB_CTRL1, 0, 3, 0, + pll_div_table, &sys1pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_divider_table(NULL, "sys1pll_div2", "sys1pll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS1PLL_AB_CTRL1, 4, 3, 0, + pll_div_table, &sys1pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_divider_table(NULL, "sys1pll_div3", "sys1pll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS1PLL_AB_CTRL1, 8, 3, 0, + pll_div_table, &sys1pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_fixed_factor(NULL, "sys1pll_fixdiv", "sys1pll_vco", + CLK_SET_RATE_PARENT, 1, 2); + + clk = clk_register_divider_table(NULL, "sys2pll_div1", "sys2pll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS2PLL_AB_CTRL1, 0, 3, 0, + pll_div_table, &sys2pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_divider_table(NULL, "sys2pll_div2", "sys2pll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS2PLL_AB_CTRL1, 4, 3, 0, + pll_div_table, &sys2pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_divider_table(NULL, "sys2pll_div3", "sys2pll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS2PLL_AB_CTRL1, 8, 3, 0, + pll_div_table, &sys2pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_fixed_factor(NULL, "sys2pll_fixdiv", "sys2pll_vco", + CLK_SET_RATE_PARENT, 1, 2); + + clk = clk_register_divider_table(NULL, "sys3pll_div1", "sys3pll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS3PLL_AB_CTRL1, 0, 3, 0, + pll_div_table, &sys3pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_divider_table(NULL, "sys3pll_div2", "sys3pll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS3PLL_AB_CTRL1, 4, 3, 0, + pll_div_table, &sys3pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_divider_table(NULL, "sys3pll_div3", "sys3pll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS3PLL_AB_CTRL1, 8, 3, 0, + pll_div_table, &sys3pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_fixed_factor(NULL, "sys3pll_fixdiv", "sys3pll_vco", + CLK_SET_RATE_PARENT, 1, 2); + + BUG_ON(!clk); + clk = clk_register_fixed_factor(NULL, "xinw_fixdiv_btslow", "xinw", + CLK_SET_RATE_PARENT, 1, 4); + + BUG_ON(!clk); + clk = clk_register_gate(NULL, "cpupll_clk1", "cpupll_div1", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_CPUPLL_AB_CTRL1, + 12, 0, &cpupll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_gate(NULL, "cpupll_clk2", "cpupll_div2", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_CPUPLL_AB_CTRL1, + 13, 0, &cpupll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_gate(NULL, "cpupll_clk3", "cpupll_div3", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_CPUPLL_AB_CTRL1, + 14, 0, &cpupll_ctrl1_lock); + BUG_ON(!clk); + + clk = clk_register_gate(NULL, "mempll_clk1", "mempll_div1", + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + sirfsoc_clk_vbase + SIRFSOC_CLKC_MEMPLL_AB_CTRL1, + 12, 0, &mempll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_gate(NULL, "mempll_clk2", "mempll_div2", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_MEMPLL_AB_CTRL1, + 13, 0, &mempll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_gate(NULL, "mempll_clk3", "mempll_div3", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_MEMPLL_AB_CTRL1, + 14, 0, &mempll_ctrl1_lock); + BUG_ON(!clk); + + clk = clk_register_gate(NULL, "sys0pll_clk1", "sys0pll_div1", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS0PLL_AB_CTRL1, + 12, 0, &sys0pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_gate(NULL, "sys0pll_clk2", "sys0pll_div2", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS0PLL_AB_CTRL1, + 13, 0, &sys0pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_gate(NULL, "sys0pll_clk3", "sys0pll_div3", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS0PLL_AB_CTRL1, + 14, 0, &sys0pll_ctrl1_lock); + BUG_ON(!clk); + + clk = clk_register_gate(NULL, "sys1pll_clk1", "sys1pll_div1", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS1PLL_AB_CTRL1, + 12, 0, &sys1pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_gate(NULL, "sys1pll_clk2", "sys1pll_div2", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS1PLL_AB_CTRL1, + 13, 0, &sys1pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_gate(NULL, "sys1pll_clk3", "sys1pll_div3", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS1PLL_AB_CTRL1, + 14, 0, &sys1pll_ctrl1_lock); + BUG_ON(!clk); + + clk = clk_register_gate(NULL, "sys2pll_clk1", "sys2pll_div1", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS2PLL_AB_CTRL1, + 12, 0, &sys2pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_gate(NULL, "sys2pll_clk2", "sys2pll_div2", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS2PLL_AB_CTRL1, + 13, 0, &sys2pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_gate(NULL, "sys2pll_clk3", "sys2pll_div3", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS2PLL_AB_CTRL1, + 14, 0, &sys2pll_ctrl1_lock); + BUG_ON(!clk); + + clk = clk_register_gate(NULL, "sys3pll_clk1", "sys3pll_div1", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS3PLL_AB_CTRL1, + 12, 0, &sys3pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_gate(NULL, "sys3pll_clk2", "sys3pll_div2", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS3PLL_AB_CTRL1, + 13, 0, &sys3pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_gate(NULL, "sys3pll_clk3", "sys3pll_div3", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS3PLL_AB_CTRL1, + 14, 0, &sys3pll_ctrl1_lock); + BUG_ON(!clk); + + clk = clk_register(NULL, &clk_audio_dto.hw); + BUG_ON(!clk); + + clk = clk_register(NULL, &clk_disp0_dto.hw); + BUG_ON(!clk); + + clk = clk_register(NULL, &clk_disp1_dto.hw); + BUG_ON(!clk); + + for (i = 0; i < ARRAY_SIZE(divider_list); i++) { + div = ÷r_list[i]; + clk = clk_register_divider(NULL, div->div_name, + div->parent_name, div->divider_flags, sirfsoc_clk_vbase + div->div_offset, + div->shift, div->width, 0, div->lock); + BUG_ON(!clk); + clk = clk_register_gate(NULL, div->gate_name, div->div_name, + div->gate_flags, sirfsoc_clk_vbase + div->gate_offset, + div->gate_bit, 0, div->lock); + BUG_ON(!clk); + } + /* ignore selector status register check */ + for (i = 0; i < ARRAY_SIZE(mux_list); i++) { + mux = &mux_list[i]; + clk = clk_register_mux(NULL, mux->mux_name, mux->parent_names, + mux->parent_num, mux->flags, + sirfsoc_clk_vbase + mux->mux_offset, + mux->shift, mux->width, + mux->mux_flags, NULL); + BUG_ON(!clk); + } + + for (i = 0; i < ARRAY_SIZE(unit_list); i++) { + unit = &unit_list[i]; + atlas7_clks[i] = atlas7_unit_clk_register(NULL, unit->unit_name, unit->parent_name, + unit->flags, unit->regofs, unit->bit, unit->lock); + BUG_ON(!atlas7_clks[i]); + } + + clk_data.clks = atlas7_clks; + clk_data.clk_num = ARRAY_SIZE(unit_list); + + ret = of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + BUG_ON(ret); + + atlas7_rst_ctlr.of_node = np; + atlas7_rst_ctlr.nr_resets = ARRAY_SIZE(atlas7_reset_unit); + reset_controller_register(&atlas7_rst_ctlr); +} +CLK_OF_DECLARE(atlas7_clk, "sirf,atlas7-car", atlas7_clk_init); diff --git a/drivers/clk/sirf/clk-common.c b/drivers/clk/sirf/clk-common.c index 37af51c5f213..9fc285d784d3 100644 --- a/drivers/clk/sirf/clk-common.c +++ b/drivers/clk/sirf/clk-common.c @@ -10,8 +10,8 @@ #define KHZ 1000 #define MHZ (KHZ * KHZ) -static void *sirfsoc_clk_vbase; -static void *sirfsoc_rsc_vbase; +static void __iomem *sirfsoc_clk_vbase; +static void __iomem *sirfsoc_rsc_vbase; static struct clk_onecell_data clk_data; /* @@ -188,7 +188,7 @@ static struct clk_ops std_pll_ops = { .set_rate = pll_clk_set_rate, }; -static const char *pll_clk_parents[] = { +static const char * const pll_clk_parents[] = { "osc", }; @@ -284,7 +284,7 @@ static struct clk_hw usb_pll_clk_hw = { * clock domains - cpu, mem, sys/io, dsp, gfx */ -static const char *dmn_clk_parents[] = { +static const char * const dmn_clk_parents[] = { "rtc", "osc", "pll1", @@ -673,7 +673,7 @@ static void std_clk_disable(struct clk_hw *hw) clkc_writel(val, reg); } -static const char *std_clk_io_parents[] = { +static const char * const std_clk_io_parents[] = { "io", }; @@ -949,7 +949,7 @@ static struct clk_std clk_pulse = { }, }; -static const char *std_clk_dsp_parents[] = { +static const char * const std_clk_dsp_parents[] = { "dsp", }; @@ -981,7 +981,7 @@ static struct clk_std clk_mf = { }, }; -static const char *std_clk_sys_parents[] = { +static const char * const std_clk_sys_parents[] = { "sys", }; @@ -999,7 +999,7 @@ static struct clk_std clk_security = { }, }; -static const char *std_clk_usb_parents[] = { +static const char * const std_clk_usb_parents[] = { "usb_pll", }; diff --git a/drivers/clk/socfpga/Makefile b/drivers/clk/socfpga/Makefile index 7e2d15a0c7b8..d8bb239753a4 100644 --- a/drivers/clk/socfpga/Makefile +++ b/drivers/clk/socfpga/Makefile @@ -2,3 +2,4 @@ obj-y += clk.o obj-y += clk-gate.o obj-y += clk-pll.o obj-y += clk-periph.o +obj-y += clk-pll-a10.o clk-periph-a10.o clk-gate-a10.o diff --git a/drivers/clk/socfpga/clk-gate-a10.c b/drivers/clk/socfpga/clk-gate-a10.c new file mode 100644 index 000000000000..83c6780ff4b2 --- /dev/null +++ b/drivers/clk/socfpga/clk-gate-a10.c @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2015 Altera Corporation. All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +#include +#include +#include +#include +#include + +#include "clk.h" + +#define streq(a, b) (strcmp((a), (b)) == 0) + +#define to_socfpga_gate_clk(p) container_of(p, struct socfpga_gate_clk, hw.hw) + +/* SDMMC Group for System Manager defines */ +#define SYSMGR_SDMMCGRP_CTRL_OFFSET 0x28 + +static unsigned long socfpga_gate_clk_recalc_rate(struct clk_hw *hwclk, + unsigned long parent_rate) +{ + struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk); + u32 div = 1, val; + + if (socfpgaclk->fixed_div) + div = socfpgaclk->fixed_div; + else if (socfpgaclk->div_reg) { + val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift; + val &= div_mask(socfpgaclk->width); + div = (1 << val); + } + + return parent_rate / div; +} + +static int socfpga_clk_prepare(struct clk_hw *hwclk) +{ + struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk); + int i; + u32 hs_timing; + u32 clk_phase[2]; + + if (socfpgaclk->clk_phase[0] || socfpgaclk->clk_phase[1]) { + for (i = 0; i < ARRAY_SIZE(clk_phase); i++) { + switch (socfpgaclk->clk_phase[i]) { + case 0: + clk_phase[i] = 0; + break; + case 45: + clk_phase[i] = 1; + break; + case 90: + clk_phase[i] = 2; + break; + case 135: + clk_phase[i] = 3; + break; + case 180: + clk_phase[i] = 4; + break; + case 225: + clk_phase[i] = 5; + break; + case 270: + clk_phase[i] = 6; + break; + case 315: + clk_phase[i] = 7; + break; + default: + clk_phase[i] = 0; + break; + } + } + + hs_timing = SYSMGR_SDMMC_CTRL_SET(clk_phase[0], clk_phase[1]); + if (!IS_ERR(socfpgaclk->sys_mgr_base_addr)) + regmap_write(socfpgaclk->sys_mgr_base_addr, + SYSMGR_SDMMCGRP_CTRL_OFFSET, hs_timing); + else + pr_err("%s: cannot set clk_phase because sys_mgr_base_addr is not available!\n", + __func__); + } + return 0; +} + +static struct clk_ops gateclk_ops = { + .prepare = socfpga_clk_prepare, + .recalc_rate = socfpga_gate_clk_recalc_rate, +}; + +static void __init __socfpga_gate_init(struct device_node *node, + const struct clk_ops *ops) +{ + u32 clk_gate[2]; + u32 div_reg[3]; + u32 clk_phase[2]; + u32 fixed_div; + struct clk *clk; + struct socfpga_gate_clk *socfpga_clk; + const char *clk_name = node->name; + const char *parent_name[SOCFPGA_MAX_PARENTS]; + struct clk_init_data init; + int rc; + int i = 0; + + socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL); + if (WARN_ON(!socfpga_clk)) + return; + + rc = of_property_read_u32_array(node, "clk-gate", clk_gate, 2); + if (rc) + clk_gate[0] = 0; + + if (clk_gate[0]) { + socfpga_clk->hw.reg = clk_mgr_a10_base_addr + clk_gate[0]; + socfpga_clk->hw.bit_idx = clk_gate[1]; + + gateclk_ops.enable = clk_gate_ops.enable; + gateclk_ops.disable = clk_gate_ops.disable; + } + + rc = of_property_read_u32(node, "fixed-divider", &fixed_div); + if (rc) + socfpga_clk->fixed_div = 0; + else + socfpga_clk->fixed_div = fixed_div; + + rc = of_property_read_u32_array(node, "div-reg", div_reg, 3); + if (!rc) { + socfpga_clk->div_reg = clk_mgr_a10_base_addr + div_reg[0]; + socfpga_clk->shift = div_reg[1]; + socfpga_clk->width = div_reg[2]; + } else { + socfpga_clk->div_reg = NULL; + } + + rc = of_property_read_u32_array(node, "clk-phase", clk_phase, 2); + if (!rc) { + socfpga_clk->clk_phase[0] = clk_phase[0]; + socfpga_clk->clk_phase[1] = clk_phase[1]; + + socfpga_clk->sys_mgr_base_addr = + syscon_regmap_lookup_by_compatible("altr,sys-mgr"); + if (IS_ERR(socfpga_clk->sys_mgr_base_addr)) { + pr_err("%s: failed to find altr,sys-mgr regmap!\n", + __func__); + return; + } + } + + of_property_read_string(node, "clock-output-names", &clk_name); + + init.name = clk_name; + init.ops = ops; + init.flags = 0; + while (i < SOCFPGA_MAX_PARENTS && (parent_name[i] = + of_clk_get_parent_name(node, i)) != NULL) + i++; + + init.parent_names = parent_name; + init.num_parents = i; + socfpga_clk->hw.hw.init = &init; + + clk = clk_register(NULL, &socfpga_clk->hw.hw); + if (WARN_ON(IS_ERR(clk))) { + kfree(socfpga_clk); + return; + } + rc = of_clk_add_provider(node, of_clk_src_simple_get, clk); + if (WARN_ON(rc)) + return; +} + +void __init socfpga_a10_gate_init(struct device_node *node) +{ + __socfpga_gate_init(node, &gateclk_ops); +} diff --git a/drivers/clk/socfpga/clk-gate.c b/drivers/clk/socfpga/clk-gate.c index dd3a78c64795..82449cd76fd7 100644 --- a/drivers/clk/socfpga/clk-gate.c +++ b/drivers/clk/socfpga/clk-gate.c @@ -32,14 +32,10 @@ #define SOCFPGA_MMC_CLK "sdmmc_clk" #define SOCFPGA_GPIO_DB_CLK_OFFSET 0xA8 -#define streq(a, b) (strcmp((a), (b)) == 0) - #define to_socfpga_gate_clk(p) container_of(p, struct socfpga_gate_clk, hw.hw) /* SDMMC Group for System Manager defines */ #define SYSMGR_SDMMCGRP_CTRL_OFFSET 0x108 -#define SYSMGR_SDMMC_CTRL_SET(smplsel, drvsel) \ - ((((smplsel) & 0x7) << 3) | (((drvsel) & 0x7) << 0)) static u8 socfpga_clk_get_parent(struct clk_hw *hwclk) { @@ -194,7 +190,6 @@ static void __init __socfpga_gate_init(struct device_node *node, const char *parent_name[SOCFPGA_MAX_PARENTS]; struct clk_init_data init; int rc; - int i = 0; socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL); if (WARN_ON(!socfpga_clk)) @@ -224,7 +219,7 @@ static void __init __socfpga_gate_init(struct device_node *node, socfpga_clk->shift = div_reg[1]; socfpga_clk->width = div_reg[2]; } else { - socfpga_clk->div_reg = 0; + socfpga_clk->div_reg = NULL; } rc = of_property_read_u32_array(node, "clk-phase", clk_phase, 2); @@ -238,12 +233,9 @@ static void __init __socfpga_gate_init(struct device_node *node, init.name = clk_name; init.ops = ops; init.flags = 0; - while (i < SOCFPGA_MAX_PARENTS && (parent_name[i] = - of_clk_get_parent_name(node, i)) != NULL) - i++; + init.num_parents = of_clk_parent_fill(node, parent_name, SOCFPGA_MAX_PARENTS); init.parent_names = parent_name; - init.num_parents = i; socfpga_clk->hw.hw.init = &init; clk = clk_register(NULL, &socfpga_clk->hw.hw); diff --git a/drivers/clk/socfpga/clk-periph-a10.c b/drivers/clk/socfpga/clk-periph-a10.c new file mode 100644 index 000000000000..9d0181b5a6a4 --- /dev/null +++ b/drivers/clk/socfpga/clk-periph-a10.c @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2015 Altera Corporation. All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +#include +#include +#include + +#include "clk.h" + +#define CLK_MGR_FREE_SHIFT 16 +#define CLK_MGR_FREE_MASK 0x7 + +#define SOCFPGA_MPU_FREE_CLK "mpu_free_clk" +#define SOCFPGA_NOC_FREE_CLK "noc_free_clk" +#define SOCFPGA_SDMMC_FREE_CLK "sdmmc_free_clk" +#define to_socfpga_periph_clk(p) container_of(p, struct socfpga_periph_clk, hw.hw) + +static unsigned long clk_periclk_recalc_rate(struct clk_hw *hwclk, + unsigned long parent_rate) +{ + struct socfpga_periph_clk *socfpgaclk = to_socfpga_periph_clk(hwclk); + u32 div; + + if (socfpgaclk->fixed_div) { + div = socfpgaclk->fixed_div; + } else if (socfpgaclk->div_reg) { + div = readl(socfpgaclk->div_reg) >> socfpgaclk->shift; + div &= div_mask(socfpgaclk->width); + div += 1; + } else { + div = ((readl(socfpgaclk->hw.reg) & 0x7ff) + 1); + } + + return parent_rate / div; +} + +static u8 clk_periclk_get_parent(struct clk_hw *hwclk) +{ + struct socfpga_periph_clk *socfpgaclk = to_socfpga_periph_clk(hwclk); + u32 clk_src; + + clk_src = readl(socfpgaclk->hw.reg); + if (streq(hwclk->init->name, SOCFPGA_MPU_FREE_CLK) || + streq(hwclk->init->name, SOCFPGA_NOC_FREE_CLK) || + streq(hwclk->init->name, SOCFPGA_SDMMC_FREE_CLK)) + return (clk_src >> CLK_MGR_FREE_SHIFT) & + CLK_MGR_FREE_MASK; + else + return 0; +} + +static const struct clk_ops periclk_ops = { + .recalc_rate = clk_periclk_recalc_rate, + .get_parent = clk_periclk_get_parent, +}; + +static __init void __socfpga_periph_init(struct device_node *node, + const struct clk_ops *ops) +{ + u32 reg; + struct clk *clk; + struct socfpga_periph_clk *periph_clk; + const char *clk_name = node->name; + const char *parent_name; + struct clk_init_data init; + int rc; + u32 fixed_div; + u32 div_reg[3]; + + of_property_read_u32(node, "reg", ®); + + periph_clk = kzalloc(sizeof(*periph_clk), GFP_KERNEL); + if (WARN_ON(!periph_clk)) + return; + + periph_clk->hw.reg = clk_mgr_a10_base_addr + reg; + + rc = of_property_read_u32_array(node, "div-reg", div_reg, 3); + if (!rc) { + periph_clk->div_reg = clk_mgr_a10_base_addr + div_reg[0]; + periph_clk->shift = div_reg[1]; + periph_clk->width = div_reg[2]; + } else { + periph_clk->div_reg = NULL; + } + + rc = of_property_read_u32(node, "fixed-divider", &fixed_div); + if (rc) + periph_clk->fixed_div = 0; + else + periph_clk->fixed_div = fixed_div; + + of_property_read_string(node, "clock-output-names", &clk_name); + + init.name = clk_name; + init.ops = ops; + init.flags = 0; + + parent_name = of_clk_get_parent_name(node, 0); + init.num_parents = 1; + init.parent_names = &parent_name; + + periph_clk->hw.hw.init = &init; + + clk = clk_register(NULL, &periph_clk->hw.hw); + if (WARN_ON(IS_ERR(clk))) { + kfree(periph_clk); + return; + } + rc = of_clk_add_provider(node, of_clk_src_simple_get, clk); + if (rc < 0) { + pr_err("Could not register clock provider for node:%s\n", + clk_name); + goto err_clk; + } + + return; + +err_clk: + clk_unregister(clk); +} + +void __init socfpga_a10_periph_init(struct device_node *node) +{ + __socfpga_periph_init(node, &periclk_ops); +} diff --git a/drivers/clk/socfpga/clk-periph.c b/drivers/clk/socfpga/clk-periph.c index 46531c34ec9b..83aeaa219d14 100644 --- a/drivers/clk/socfpga/clk-periph.c +++ b/drivers/clk/socfpga/clk-periph.c @@ -76,7 +76,7 @@ static __init void __socfpga_periph_init(struct device_node *node, periph_clk->shift = div_reg[1]; periph_clk->width = div_reg[2]; } else { - periph_clk->div_reg = 0; + periph_clk->div_reg = NULL; } rc = of_property_read_u32(node, "fixed-divider", &fixed_div); diff --git a/drivers/clk/socfpga/clk-pll-a10.c b/drivers/clk/socfpga/clk-pll-a10.c new file mode 100644 index 000000000000..1178b11babca --- /dev/null +++ b/drivers/clk/socfpga/clk-pll-a10.c @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2015 Altera Corporation. All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +#include +#include +#include +#include + +#include "clk.h" + +/* Clock Manager offsets */ +#define CLK_MGR_PLL_CLK_SRC_SHIFT 8 +#define CLK_MGR_PLL_CLK_SRC_MASK 0x3 + +/* Clock bypass bits */ +#define SOCFPGA_PLL_BG_PWRDWN 0 +#define SOCFPGA_PLL_PWR_DOWN 1 +#define SOCFPGA_PLL_EXT_ENA 2 +#define SOCFPGA_PLL_DIVF_MASK 0x00001FFF +#define SOCFPGA_PLL_DIVF_SHIFT 0 +#define SOCFPGA_PLL_DIVQ_MASK 0x003F0000 +#define SOCFPGA_PLL_DIVQ_SHIFT 16 +#define SOCFGPA_MAX_PARENTS 5 + +#define SOCFPGA_MAIN_PLL_CLK "main_pll" +#define SOCFPGA_PERIP_PLL_CLK "periph_pll" + +#define to_socfpga_clk(p) container_of(p, struct socfpga_pll, hw.hw) + +void __iomem *clk_mgr_a10_base_addr; + +static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk, + unsigned long parent_rate) +{ + struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk); + unsigned long divf, divq, reg; + unsigned long long vco_freq; + + /* read VCO1 reg for numerator and denominator */ + reg = readl(socfpgaclk->hw.reg + 0x4); + divf = (reg & SOCFPGA_PLL_DIVF_MASK) >> SOCFPGA_PLL_DIVF_SHIFT; + divq = (reg & SOCFPGA_PLL_DIVQ_MASK) >> SOCFPGA_PLL_DIVQ_SHIFT; + vco_freq = (unsigned long long)parent_rate * (divf + 1); + do_div(vco_freq, (1 + divq)); + return (unsigned long)vco_freq; +} + +static u8 clk_pll_get_parent(struct clk_hw *hwclk) +{ + struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk); + u32 pll_src; + + pll_src = readl(socfpgaclk->hw.reg); + + return (pll_src >> CLK_MGR_PLL_CLK_SRC_SHIFT) & + CLK_MGR_PLL_CLK_SRC_MASK; +} + +static struct clk_ops clk_pll_ops = { + .recalc_rate = clk_pll_recalc_rate, + .get_parent = clk_pll_get_parent, +}; + +static struct __init clk * __socfpga_pll_init(struct device_node *node, + const struct clk_ops *ops) +{ + u32 reg; + struct clk *clk; + struct socfpga_pll *pll_clk; + const char *clk_name = node->name; + const char *parent_name[SOCFGPA_MAX_PARENTS]; + struct clk_init_data init; + struct device_node *clkmgr_np; + int rc; + int i = 0; + + of_property_read_u32(node, "reg", ®); + + pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL); + if (WARN_ON(!pll_clk)) + return NULL; + + clkmgr_np = of_find_compatible_node(NULL, NULL, "altr,clk-mgr"); + clk_mgr_a10_base_addr = of_iomap(clkmgr_np, 0); + BUG_ON(!clk_mgr_a10_base_addr); + pll_clk->hw.reg = clk_mgr_a10_base_addr + reg; + + of_property_read_string(node, "clock-output-names", &clk_name); + + init.name = clk_name; + init.ops = ops; + init.flags = 0; + + while (i < SOCFGPA_MAX_PARENTS && (parent_name[i] = + of_clk_get_parent_name(node, i)) != NULL) + i++; + init.num_parents = i; + init.parent_names = parent_name; + pll_clk->hw.hw.init = &init; + + pll_clk->hw.bit_idx = SOCFPGA_PLL_EXT_ENA; + clk_pll_ops.enable = clk_gate_ops.enable; + clk_pll_ops.disable = clk_gate_ops.disable; + + clk = clk_register(NULL, &pll_clk->hw.hw); + if (WARN_ON(IS_ERR(clk))) { + kfree(pll_clk); + return NULL; + } + rc = of_clk_add_provider(node, of_clk_src_simple_get, clk); + return clk; +} + +void __init socfpga_a10_pll_init(struct device_node *node) +{ + __socfpga_pll_init(node, &clk_pll_ops); +} diff --git a/drivers/clk/socfpga/clk-pll.c b/drivers/clk/socfpga/clk-pll.c index de6da957a09d..8f26b5234947 100644 --- a/drivers/clk/socfpga/clk-pll.c +++ b/drivers/clk/socfpga/clk-pll.c @@ -92,7 +92,6 @@ static __init struct clk *__socfpga_pll_init(struct device_node *node, struct clk_init_data init; struct device_node *clkmgr_np; int rc; - int i = 0; of_property_read_u32(node, "reg", ®); @@ -111,11 +110,7 @@ static __init struct clk *__socfpga_pll_init(struct device_node *node, init.ops = ops; init.flags = 0; - while (i < SOCFPGA_MAX_PARENTS && (parent_name[i] = - of_clk_get_parent_name(node, i)) != NULL) - i++; - - init.num_parents = i; + init.num_parents = of_clk_parent_fill(node, parent_name, SOCFPGA_MAX_PARENTS); init.parent_names = parent_name; pll_clk->hw.hw.init = &init; diff --git a/drivers/clk/socfpga/clk.c b/drivers/clk/socfpga/clk.c index 43db947e5f0e..7564d2e35f32 100644 --- a/drivers/clk/socfpga/clk.c +++ b/drivers/clk/socfpga/clk.c @@ -24,4 +24,9 @@ CLK_OF_DECLARE(socfpga_pll_clk, "altr,socfpga-pll-clock", socfpga_pll_init); CLK_OF_DECLARE(socfpga_perip_clk, "altr,socfpga-perip-clk", socfpga_periph_init); CLK_OF_DECLARE(socfpga_gate_clk, "altr,socfpga-gate-clk", socfpga_gate_init); - +CLK_OF_DECLARE(socfpga_a10_pll_clk, "altr,socfpga-a10-pll-clock", + socfpga_a10_pll_init); +CLK_OF_DECLARE(socfpga_a10_perip_clk, "altr,socfpga-a10-perip-clk", + socfpga_a10_periph_init); +CLK_OF_DECLARE(socfpga_a10_gate_clk, "altr,socfpga-a10-gate-clk", + socfpga_a10_gate_init); diff --git a/drivers/clk/socfpga/clk.h b/drivers/clk/socfpga/clk.h index d291f60c46e1..603973ab7e29 100644 --- a/drivers/clk/socfpga/clk.h +++ b/drivers/clk/socfpga/clk.h @@ -26,14 +26,22 @@ #define CLKMGR_L4SRC 0x70 #define CLKMGR_PERPLL_SRC 0xAC -#define SOCFPGA_MAX_PARENTS 3 +#define SOCFPGA_MAX_PARENTS 5 #define div_mask(width) ((1 << (width)) - 1) +#define streq(a, b) (strcmp((a), (b)) == 0) +#define SYSMGR_SDMMC_CTRL_SET(smplsel, drvsel) \ + ((((smplsel) & 0x7) << 3) | (((drvsel) & 0x7) << 0)) + extern void __iomem *clk_mgr_base_addr; +extern void __iomem *clk_mgr_a10_base_addr; void __init socfpga_pll_init(struct device_node *node); void __init socfpga_periph_init(struct device_node *node); void __init socfpga_gate_init(struct device_node *node); +void socfpga_a10_pll_init(struct device_node *node); +void socfpga_a10_periph_init(struct device_node *node); +void socfpga_a10_gate_init(struct device_node *node); struct socfpga_pll { struct clk_gate hw; @@ -44,6 +52,7 @@ struct socfpga_gate_clk { char *parent_name; u32 fixed_div; void __iomem *div_reg; + struct regmap *sys_mgr_base_addr; u32 width; /* only valid if div_reg != 0 */ u32 shift; /* only valid if div_reg != 0 */ u32 clk_phase[2]; diff --git a/drivers/clk/st/clk-flexgen.c b/drivers/clk/st/clk-flexgen.c index bf12a25eb3a2..657ca14ba709 100644 --- a/drivers/clk/st/clk-flexgen.c +++ b/drivers/clk/st/clk-flexgen.c @@ -116,7 +116,7 @@ static long flexgen_round_rate(struct clk_hw *hw, unsigned long rate, return *prate / div; } -unsigned long flexgen_recalc_rate(struct clk_hw *hw, +static unsigned long flexgen_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct flexgen *flexgen = to_flexgen(hw); @@ -174,7 +174,7 @@ static const struct clk_ops flexgen_ops = { .set_rate = flexgen_set_rate, }; -struct clk *clk_register_flexgen(const char *name, +static struct clk *clk_register_flexgen(const char *name, const char **parent_names, u8 num_parents, void __iomem *reg, spinlock_t *lock, u32 idx, unsigned long flexgen_flags) { @@ -245,7 +245,7 @@ static const char ** __init flexgen_get_parents(struct device_node *np, const char **parents; int nparents, i; - nparents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); + nparents = of_clk_get_parent_count(np); if (WARN_ON(nparents <= 0)) return NULL; @@ -260,7 +260,7 @@ static const char ** __init flexgen_get_parents(struct device_node *np, return parents; } -void __init st_of_flexgen_setup(struct device_node *np) +static void __init st_of_flexgen_setup(struct device_node *np) { struct device_node *pnode; void __iomem *reg; diff --git a/drivers/clk/st/clkgen-fsyn.c b/drivers/clk/st/clkgen-fsyn.c index a917c4c7eaa9..e94197f04b0b 100644 --- a/drivers/clk/st/clkgen-fsyn.c +++ b/drivers/clk/st/clkgen-fsyn.c @@ -492,7 +492,7 @@ static int quadfs_pll_is_enabled(struct clk_hw *hw) return !!npda; } -int clk_fs660c32_vco_get_rate(unsigned long input, struct stm_fs *fs, +static int clk_fs660c32_vco_get_rate(unsigned long input, struct stm_fs *fs, unsigned long *rate) { unsigned long nd = fs->ndiv + 16; /* ndiv value */ @@ -519,7 +519,7 @@ static unsigned long quadfs_pll_fs660c32_recalc_rate(struct clk_hw *hw, return rate; } -int clk_fs660c32_vco_get_params(unsigned long input, +static int clk_fs660c32_vco_get_params(unsigned long input, unsigned long output, struct stm_fs *fs) { /* Formula diff --git a/drivers/clk/st/clkgen-mux.c b/drivers/clk/st/clkgen-mux.c index fdcff10f6d30..4fbe6e099587 100644 --- a/drivers/clk/st/clkgen-mux.c +++ b/drivers/clk/st/clkgen-mux.c @@ -26,7 +26,7 @@ static const char ** __init clkgen_mux_get_parents(struct device_node *np, const char **parents; int nparents, i; - nparents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); + nparents = of_clk_get_parent_count(np); if (WARN_ON(nparents <= 0)) return ERR_PTR(-EINVAL); @@ -131,7 +131,7 @@ static int clkgena_divmux_is_enabled(struct clk_hw *hw) return (s8)clk_mux_ops.get_parent(mux_hw) > 0; } -u8 clkgena_divmux_get_parent(struct clk_hw *hw) +static u8 clkgena_divmux_get_parent(struct clk_hw *hw) { struct clkgena_divmux *genamux = to_clkgena_divmux(hw); struct clk_hw *mux_hw = &genamux->mux.hw; @@ -168,7 +168,7 @@ static int clkgena_divmux_set_parent(struct clk_hw *hw, u8 index) return 0; } -unsigned long clkgena_divmux_recalc_rate(struct clk_hw *hw, +static unsigned long clkgena_divmux_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct clkgena_divmux *genamux = to_clkgena_divmux(hw); @@ -215,7 +215,7 @@ static const struct clk_ops clkgena_divmux_ops = { /** * clk_register_genamux - register a genamux clock with the clock framework */ -struct clk *clk_register_genamux(const char *name, +static struct clk *clk_register_genamux(const char *name, const char **parent_names, u8 num_parents, void __iomem *reg, const struct clkgena_divmux_data *muxdata, @@ -385,7 +385,7 @@ static void __iomem * __init clkgen_get_register_base( return reg; } -void __init st_of_clkgena_divmux_setup(struct device_node *np) +static void __init st_of_clkgena_divmux_setup(struct device_node *np) { const struct of_device_id *match; const struct clkgena_divmux_data *data; @@ -485,7 +485,7 @@ static const struct of_device_id clkgena_prediv_of_match[] = { {} }; -void __init st_of_clkgena_prediv_setup(struct device_node *np) +static void __init st_of_clkgena_prediv_setup(struct device_node *np) { const struct of_device_id *match; void __iomem *reg; @@ -622,7 +622,7 @@ static const struct of_device_id mux_of_match[] = { {} }; -void __init st_of_clkgen_mux_setup(struct device_node *np) +static void __init st_of_clkgen_mux_setup(struct device_node *np) { const struct of_device_id *match; struct clk *clk; @@ -699,7 +699,7 @@ static const struct of_device_id vcc_of_match[] = { {} }; -void __init st_of_clkgen_vcc_setup(struct device_node *np) +static void __init st_of_clkgen_vcc_setup(struct device_node *np) { const struct of_device_id *match; void __iomem *reg; diff --git a/drivers/clk/st/clkgen-pll.c b/drivers/clk/st/clkgen-pll.c index d204ba85db3a..106532207213 100644 --- a/drivers/clk/st/clkgen-pll.c +++ b/drivers/clk/st/clkgen-pll.c @@ -270,7 +270,7 @@ static int clkgen_pll_is_enabled(struct clk_hw *hw) return !poweroff; } -unsigned long recalc_stm_pll800c65(struct clk_hw *hw, +static unsigned long recalc_stm_pll800c65(struct clk_hw *hw, unsigned long parent_rate) { struct clkgen_pll *pll = to_clkgen_pll(hw); @@ -297,7 +297,7 @@ unsigned long recalc_stm_pll800c65(struct clk_hw *hw, } -unsigned long recalc_stm_pll1600c65(struct clk_hw *hw, +static unsigned long recalc_stm_pll1600c65(struct clk_hw *hw, unsigned long parent_rate) { struct clkgen_pll *pll = to_clkgen_pll(hw); @@ -321,7 +321,7 @@ unsigned long recalc_stm_pll1600c65(struct clk_hw *hw, return rate; } -unsigned long recalc_stm_pll3200c32(struct clk_hw *hw, +static unsigned long recalc_stm_pll3200c32(struct clk_hw *hw, unsigned long parent_rate) { struct clkgen_pll *pll = to_clkgen_pll(hw); @@ -343,7 +343,7 @@ unsigned long recalc_stm_pll3200c32(struct clk_hw *hw, return rate; } -unsigned long recalc_stm_pll1200c32(struct clk_hw *hw, +static unsigned long recalc_stm_pll1200c32(struct clk_hw *hw, unsigned long parent_rate) { struct clkgen_pll *pll = to_clkgen_pll(hw); @@ -544,7 +544,7 @@ CLK_OF_DECLARE(clkgena_c65_plls, "st,clkgena-plls-c65", clkgena_c65_pll_setup); static struct clk * __init clkgen_odf_register(const char *parent_name, - void * __iomem reg, + void __iomem *reg, struct clkgen_pll_data *pll_data, int odf, spinlock_t *odf_lock, diff --git a/drivers/clk/sunxi/clk-sun9i-core.c b/drivers/clk/sunxi/clk-sun9i-core.c index d8da77d72861..887f4ea161bb 100644 --- a/drivers/clk/sunxi/clk-sun9i-core.c +++ b/drivers/clk/sunxi/clk-sun9i-core.c @@ -93,7 +93,7 @@ static void __init sun9i_a80_pll4_setup(struct device_node *node) void __iomem *reg; reg = of_io_request_and_map(node, 0, of_node_full_name(node)); - if (!reg) { + if (IS_ERR(reg)) { pr_err("Could not get registers for a80-pll4-clk: %s\n", node->name); return; @@ -154,7 +154,7 @@ static void __init sun9i_a80_gt_setup(struct device_node *node) struct clk *gt; reg = of_io_request_and_map(node, 0, of_node_full_name(node)); - if (!reg) { + if (IS_ERR(reg)) { pr_err("Could not get registers for a80-gt-clk: %s\n", node->name); return; @@ -218,7 +218,7 @@ static void __init sun9i_a80_ahb_setup(struct device_node *node) void __iomem *reg; reg = of_io_request_and_map(node, 0, of_node_full_name(node)); - if (!reg) { + if (IS_ERR(reg)) { pr_err("Could not get registers for a80-ahb-clk: %s\n", node->name); return; @@ -244,7 +244,7 @@ static void __init sun9i_a80_apb0_setup(struct device_node *node) void __iomem *reg; reg = of_io_request_and_map(node, 0, of_node_full_name(node)); - if (!reg) { + if (IS_ERR(reg)) { pr_err("Could not get registers for a80-apb0-clk: %s\n", node->name); return; @@ -310,7 +310,7 @@ static void __init sun9i_a80_apb1_setup(struct device_node *node) void __iomem *reg; reg = of_io_request_and_map(node, 0, of_node_full_name(node)); - if (!reg) { + if (IS_ERR(reg)) { pr_err("Could not get registers for a80-apb1-clk: %s\n", node->name); return; diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 7e1e2bd189b6..9a82f17d2d73 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -198,6 +198,8 @@ static void __init sun6i_ahb1_clk_setup(struct device_node *node) int i = 0; reg = of_io_request_and_map(node, 0, of_node_full_name(node)); + if (IS_ERR(reg)) + return; /* we have a mux, we will have >1 parents */ while (i < SUN6I_AHB1_MAX_PARENTS && diff --git a/drivers/clk/sunxi/clk-usb.c b/drivers/clk/sunxi/clk-usb.c index a86ed2f8d7af..3a25f9588e67 100644 --- a/drivers/clk/sunxi/clk-usb.c +++ b/drivers/clk/sunxi/clk-usb.c @@ -204,6 +204,17 @@ static void __init sun6i_a31_usb_setup(struct device_node *node) } CLK_OF_DECLARE(sun6i_a31_usb, "allwinner,sun6i-a31-usb-clk", sun6i_a31_usb_setup); +static const struct usb_clk_data sun8i_a23_usb_clk_data __initconst = { + .clk_mask = BIT(16) | BIT(11) | BIT(10) | BIT(9) | BIT(8), + .reset_mask = BIT(2) | BIT(1) | BIT(0), +}; + +static void __init sun8i_a23_usb_setup(struct device_node *node) +{ + sunxi_usb_clk_setup(node, &sun8i_a23_usb_clk_data, &sun4i_a10_usb_lock); +} +CLK_OF_DECLARE(sun8i_a23_usb, "allwinner,sun8i-a23-usb-clk", sun8i_a23_usb_setup); + static const struct usb_clk_data sun9i_a80_usb_mod_data __initconst = { .clk_mask = BIT(6) | BIT(5) | BIT(4) | BIT(3) | BIT(2) | BIT(1), .reset_mask = BIT(19) | BIT(18) | BIT(17), diff --git a/drivers/clk/tegra/Kconfig b/drivers/clk/tegra/Kconfig new file mode 100644 index 000000000000..1ba30d1e14f2 --- /dev/null +++ b/drivers/clk/tegra/Kconfig @@ -0,0 +1,3 @@ +config TEGRA_CLK_EMC + def_bool y + depends on TEGRA124_EMC diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile index edb8358fa6ce..aec862ba7a17 100644 --- a/drivers/clk/tegra/Makefile +++ b/drivers/clk/tegra/Makefile @@ -11,6 +11,7 @@ obj-y += clk-tegra-periph.o obj-y += clk-tegra-pmc.o obj-y += clk-tegra-fixed.o obj-y += clk-tegra-super-gen4.o +obj-$(CONFIG_TEGRA_CLK_EMC) += clk-emc.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clk-tegra20.o obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += clk-tegra30.o obj-$(CONFIG_ARCH_TEGRA_114_SOC) += clk-tegra114.o diff --git a/drivers/clk/tegra/clk-emc.c b/drivers/clk/tegra/clk-emc.c new file mode 100644 index 000000000000..7649685c86bc --- /dev/null +++ b/drivers/clk/tegra/clk-emc.c @@ -0,0 +1,538 @@ +/* + * drivers/clk/tegra/clk-emc.c + * + * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. + * + * Author: + * Mikko Perttunen + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "clk.h" + +#define CLK_SOURCE_EMC 0x19c + +#define CLK_SOURCE_EMC_EMC_2X_CLK_DIVISOR_SHIFT 0 +#define CLK_SOURCE_EMC_EMC_2X_CLK_DIVISOR_MASK 0xff +#define CLK_SOURCE_EMC_EMC_2X_CLK_DIVISOR(x) (((x) & CLK_SOURCE_EMC_EMC_2X_CLK_DIVISOR_MASK) << \ + CLK_SOURCE_EMC_EMC_2X_CLK_DIVISOR_SHIFT) + +#define CLK_SOURCE_EMC_EMC_2X_CLK_SRC_SHIFT 29 +#define CLK_SOURCE_EMC_EMC_2X_CLK_SRC_MASK 0x7 +#define CLK_SOURCE_EMC_EMC_2X_CLK_SRC(x) (((x) & CLK_SOURCE_EMC_EMC_2X_CLK_SRC_MASK) << \ + CLK_SOURCE_EMC_EMC_2X_CLK_SRC_SHIFT) + +static const char * const emc_parent_clk_names[] = { + "pll_m", "pll_c", "pll_p", "clk_m", "pll_m_ud", + "pll_c2", "pll_c3", "pll_c_ud" +}; + +/* + * List of clock sources for various parents the EMC clock can have. + * When we change the timing to a timing with a parent that has the same + * clock source as the current parent, we must first change to a backup + * timing that has a different clock source. + */ + +#define EMC_SRC_PLL_M 0 +#define EMC_SRC_PLL_C 1 +#define EMC_SRC_PLL_P 2 +#define EMC_SRC_CLK_M 3 +#define EMC_SRC_PLL_C2 4 +#define EMC_SRC_PLL_C3 5 + +static const char emc_parent_clk_sources[] = { + EMC_SRC_PLL_M, EMC_SRC_PLL_C, EMC_SRC_PLL_P, EMC_SRC_CLK_M, + EMC_SRC_PLL_M, EMC_SRC_PLL_C2, EMC_SRC_PLL_C3, EMC_SRC_PLL_C +}; + +struct emc_timing { + unsigned long rate, parent_rate; + u8 parent_index; + struct clk *parent; + u32 ram_code; +}; + +struct tegra_clk_emc { + struct clk_hw hw; + void __iomem *clk_regs; + struct clk *prev_parent; + bool changing_timing; + + struct device_node *emc_node; + struct tegra_emc *emc; + + int num_timings; + struct emc_timing *timings; + spinlock_t *lock; +}; + +/* Common clock framework callback implementations */ + +static unsigned long emc_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct tegra_clk_emc *tegra; + u32 val, div; + + tegra = container_of(hw, struct tegra_clk_emc, hw); + + /* + * CCF wrongly assumes that the parent won't change during set_rate, + * so get the parent rate explicitly. + */ + parent_rate = __clk_get_rate(__clk_get_parent(hw->clk)); + + val = readl(tegra->clk_regs + CLK_SOURCE_EMC); + div = val & CLK_SOURCE_EMC_EMC_2X_CLK_DIVISOR_MASK; + + return parent_rate / (div + 2) * 2; +} + +/* + * Rounds up unless no higher rate exists, in which case down. This way is + * safer since things have EMC rate floors. Also don't touch parent_rate + * since we don't want the CCF to play with our parent clocks. + */ +static long emc_determine_rate(struct clk_hw *hw, unsigned long rate, + unsigned long min_rate, + unsigned long max_rate, + unsigned long *best_parent_rate, + struct clk_hw **best_parent_hw) +{ + struct tegra_clk_emc *tegra; + u8 ram_code = tegra_read_ram_code(); + struct emc_timing *timing = NULL; + int i; + + tegra = container_of(hw, struct tegra_clk_emc, hw); + + for (i = 0; i < tegra->num_timings; i++) { + if (tegra->timings[i].ram_code != ram_code) + continue; + + timing = tegra->timings + i; + + if (timing->rate > max_rate) { + i = min(i, 1); + return tegra->timings[i - 1].rate; + } + + if (timing->rate < min_rate) + continue; + + if (timing->rate >= rate) + return timing->rate; + } + + if (timing) + return timing->rate; + + return __clk_get_rate(hw->clk); +} + +static u8 emc_get_parent(struct clk_hw *hw) +{ + struct tegra_clk_emc *tegra; + u32 val; + + tegra = container_of(hw, struct tegra_clk_emc, hw); + + val = readl(tegra->clk_regs + CLK_SOURCE_EMC); + + return (val >> CLK_SOURCE_EMC_EMC_2X_CLK_SRC_SHIFT) + & CLK_SOURCE_EMC_EMC_2X_CLK_SRC_MASK; +} + +static struct tegra_emc *emc_ensure_emc_driver(struct tegra_clk_emc *tegra) +{ + struct platform_device *pdev; + + if (tegra->emc) + return tegra->emc; + + if (!tegra->emc_node) + return NULL; + + pdev = of_find_device_by_node(tegra->emc_node); + if (!pdev) { + pr_err("%s: could not get external memory controller\n", + __func__); + return NULL; + } + + of_node_put(tegra->emc_node); + tegra->emc_node = NULL; + + tegra->emc = platform_get_drvdata(pdev); + if (!tegra->emc) { + pr_err("%s: cannot find EMC driver\n", __func__); + return NULL; + } + + return tegra->emc; +} + +static int emc_set_timing(struct tegra_clk_emc *tegra, + struct emc_timing *timing) +{ + int err; + u8 div; + u32 car_value; + unsigned long flags = 0; + struct tegra_emc *emc = emc_ensure_emc_driver(tegra); + + if (!emc) + return -ENOENT; + + pr_debug("going to rate %ld prate %ld p %s\n", timing->rate, + timing->parent_rate, __clk_get_name(timing->parent)); + + if (emc_get_parent(&tegra->hw) == timing->parent_index && + clk_get_rate(timing->parent) != timing->parent_rate) { + BUG(); + return -EINVAL; + } + + tegra->changing_timing = true; + + err = clk_set_rate(timing->parent, timing->parent_rate); + if (err) { + pr_err("cannot change parent %s rate to %ld: %d\n", + __clk_get_name(timing->parent), timing->parent_rate, + err); + + return err; + } + + err = clk_prepare_enable(timing->parent); + if (err) { + pr_err("cannot enable parent clock: %d\n", err); + return err; + } + + div = timing->parent_rate / (timing->rate / 2) - 2; + + err = tegra_emc_prepare_timing_change(emc, timing->rate); + if (err) + return err; + + spin_lock_irqsave(tegra->lock, flags); + + car_value = readl(tegra->clk_regs + CLK_SOURCE_EMC); + + car_value &= ~CLK_SOURCE_EMC_EMC_2X_CLK_SRC(~0); + car_value |= CLK_SOURCE_EMC_EMC_2X_CLK_SRC(timing->parent_index); + + car_value &= ~CLK_SOURCE_EMC_EMC_2X_CLK_DIVISOR(~0); + car_value |= CLK_SOURCE_EMC_EMC_2X_CLK_DIVISOR(div); + + writel(car_value, tegra->clk_regs + CLK_SOURCE_EMC); + + spin_unlock_irqrestore(tegra->lock, flags); + + tegra_emc_complete_timing_change(emc, timing->rate); + + clk_hw_reparent(&tegra->hw, __clk_get_hw(timing->parent)); + clk_disable_unprepare(tegra->prev_parent); + + tegra->prev_parent = timing->parent; + tegra->changing_timing = false; + + return 0; +} + +/* + * Get backup timing to use as an intermediate step when a change between + * two timings with the same clock source has been requested. First try to + * find a timing with a higher clock rate to avoid a rate below any set rate + * floors. If that is not possible, find a lower rate. + */ +static struct emc_timing *get_backup_timing(struct tegra_clk_emc *tegra, + int timing_index) +{ + int i; + u32 ram_code = tegra_read_ram_code(); + struct emc_timing *timing; + + for (i = timing_index+1; i < tegra->num_timings; i++) { + timing = tegra->timings + i; + if (timing->ram_code != ram_code) + continue; + + if (emc_parent_clk_sources[timing->parent_index] != + emc_parent_clk_sources[ + tegra->timings[timing_index].parent_index]) + return timing; + } + + for (i = timing_index-1; i >= 0; --i) { + timing = tegra->timings + i; + if (timing->ram_code != ram_code) + continue; + + if (emc_parent_clk_sources[timing->parent_index] != + emc_parent_clk_sources[ + tegra->timings[timing_index].parent_index]) + return timing; + } + + return NULL; +} + +static int emc_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct tegra_clk_emc *tegra; + struct emc_timing *timing = NULL; + int i, err; + u32 ram_code = tegra_read_ram_code(); + + tegra = container_of(hw, struct tegra_clk_emc, hw); + + if (__clk_get_rate(hw->clk) == rate) + return 0; + + /* + * When emc_set_timing changes the parent rate, CCF will propagate + * that downward to us, so ignore any set_rate calls while a rate + * change is already going on. + */ + if (tegra->changing_timing) + return 0; + + for (i = 0; i < tegra->num_timings; i++) { + if (tegra->timings[i].rate == rate && + tegra->timings[i].ram_code == ram_code) { + timing = tegra->timings + i; + break; + } + } + + if (!timing) { + pr_err("cannot switch to rate %ld without emc table\n", rate); + return -EINVAL; + } + + if (emc_parent_clk_sources[emc_get_parent(hw)] == + emc_parent_clk_sources[timing->parent_index] && + clk_get_rate(timing->parent) != timing->parent_rate) { + /* + * Parent clock source not changed but parent rate has changed, + * need to temporarily switch to another parent + */ + + struct emc_timing *backup_timing; + + backup_timing = get_backup_timing(tegra, i); + if (!backup_timing) { + pr_err("cannot find backup timing\n"); + return -EINVAL; + } + + pr_debug("using %ld as backup rate when going to %ld\n", + backup_timing->rate, rate); + + err = emc_set_timing(tegra, backup_timing); + if (err) { + pr_err("cannot set backup timing: %d\n", err); + return err; + } + } + + return emc_set_timing(tegra, timing); +} + +/* Initialization and deinitialization */ + +static int load_one_timing_from_dt(struct tegra_clk_emc *tegra, + struct emc_timing *timing, + struct device_node *node) +{ + int err, i; + u32 tmp; + + err = of_property_read_u32(node, "clock-frequency", &tmp); + if (err) { + pr_err("timing %s: failed to read rate\n", node->full_name); + return err; + } + + timing->rate = tmp; + + err = of_property_read_u32(node, "nvidia,parent-clock-frequency", &tmp); + if (err) { + pr_err("timing %s: failed to read parent rate\n", + node->full_name); + return err; + } + + timing->parent_rate = tmp; + + timing->parent = of_clk_get_by_name(node, "emc-parent"); + if (IS_ERR(timing->parent)) { + pr_err("timing %s: failed to get parent clock\n", + node->full_name); + return PTR_ERR(timing->parent); + } + + timing->parent_index = 0xff; + for (i = 0; i < ARRAY_SIZE(emc_parent_clk_names); i++) { + if (!strcmp(emc_parent_clk_names[i], + __clk_get_name(timing->parent))) { + timing->parent_index = i; + break; + } + } + if (timing->parent_index == 0xff) { + pr_err("timing %s: %s is not a valid parent\n", + node->full_name, __clk_get_name(timing->parent)); + clk_put(timing->parent); + return -EINVAL; + } + + return 0; +} + +static int cmp_timings(const void *_a, const void *_b) +{ + const struct emc_timing *a = _a; + const struct emc_timing *b = _b; + + if (a->rate < b->rate) + return -1; + else if (a->rate == b->rate) + return 0; + else + return 1; +} + +static int load_timings_from_dt(struct tegra_clk_emc *tegra, + struct device_node *node, + u32 ram_code) +{ + struct device_node *child; + int child_count = of_get_child_count(node); + int i = 0, err; + + tegra->timings = kcalloc(child_count, sizeof(struct emc_timing), + GFP_KERNEL); + if (!tegra->timings) + return -ENOMEM; + + tegra->num_timings = child_count; + + for_each_child_of_node(node, child) { + struct emc_timing *timing = tegra->timings + (i++); + + err = load_one_timing_from_dt(tegra, timing, child); + if (err) + return err; + + timing->ram_code = ram_code; + } + + sort(tegra->timings, tegra->num_timings, sizeof(struct emc_timing), + cmp_timings, NULL); + + return 0; +} + +static const struct clk_ops tegra_clk_emc_ops = { + .recalc_rate = emc_recalc_rate, + .determine_rate = emc_determine_rate, + .set_rate = emc_set_rate, + .get_parent = emc_get_parent, +}; + +struct clk *tegra_clk_register_emc(void __iomem *base, struct device_node *np, + spinlock_t *lock) +{ + struct tegra_clk_emc *tegra; + struct clk_init_data init; + struct device_node *node; + u32 node_ram_code; + struct clk *clk; + int err; + + tegra = kcalloc(1, sizeof(*tegra), GFP_KERNEL); + if (!tegra) + return ERR_PTR(-ENOMEM); + + tegra->clk_regs = base; + tegra->lock = lock; + + tegra->num_timings = 0; + + for_each_child_of_node(np, node) { + err = of_property_read_u32(node, "nvidia,ram-code", + &node_ram_code); + if (err) { + of_node_put(node); + continue; + } + + /* + * Store timings for all ram codes as we cannot read the + * fuses until the apbmisc driver is loaded. + */ + err = load_timings_from_dt(tegra, node, node_ram_code); + if (err) + return ERR_PTR(err); + of_node_put(node); + break; + } + + if (tegra->num_timings == 0) + pr_warn("%s: no memory timings registered\n", __func__); + + tegra->emc_node = of_parse_phandle(np, + "nvidia,external-memory-controller", 0); + if (!tegra->emc_node) + pr_warn("%s: couldn't find node for EMC driver\n", __func__); + + init.name = "emc"; + init.ops = &tegra_clk_emc_ops; + init.flags = 0; + init.parent_names = emc_parent_clk_names; + init.num_parents = ARRAY_SIZE(emc_parent_clk_names); + + tegra->hw.init = &init; + + clk = clk_register(NULL, &tegra->hw); + if (IS_ERR(clk)) + return clk; + + tegra->prev_parent = clk_get_parent_by_index( + tegra->hw.clk, emc_get_parent(&tegra->hw)); + tegra->changing_timing = false; + + /* Allow debugging tools to see the EMC clock */ + clk_register_clkdev(clk, "emc", "tegra-clk-debug"); + + clk_prepare_enable(clk); + + return clk; +}; diff --git a/drivers/clk/tegra/clk-tegra124.c b/drivers/clk/tegra/clk-tegra124.c index 11f857cd5f6a..e8cca3eac007 100644 --- a/drivers/clk/tegra/clk-tegra124.c +++ b/drivers/clk/tegra/clk-tegra124.c @@ -152,11 +152,6 @@ static unsigned long tegra124_input_freq[] = { [12] = 260000000, }; -static const char *mux_pllmcp_clkm[] = { - "pll_m", "pll_c", "pll_p", "clk_m", "pll_m_ud", "pll_c2", "pll_c3", -}; -#define mux_pllmcp_clkm_idx NULL - static struct div_nmp pllxc_nmp = { .divm_shift = 0, .divm_width = 8, @@ -791,7 +786,6 @@ static struct tegra_clk tegra124_clks[tegra_clk_max] __initdata = { [tegra_clk_i2c2] = { .dt_id = TEGRA124_CLK_I2C2, .present = true }, [tegra_clk_uartc] = { .dt_id = TEGRA124_CLK_UARTC, .present = true }, [tegra_clk_mipi_cal] = { .dt_id = TEGRA124_CLK_MIPI_CAL, .present = true }, - [tegra_clk_emc] = { .dt_id = TEGRA124_CLK_EMC, .present = true }, [tegra_clk_usb2] = { .dt_id = TEGRA124_CLK_USB2, .present = true }, [tegra_clk_usb3] = { .dt_id = TEGRA124_CLK_USB3, .present = true }, [tegra_clk_vde_8] = { .dt_id = TEGRA124_CLK_VDE, .present = true }, @@ -1127,13 +1121,7 @@ static __init void tegra124_periph_clk_init(void __iomem *clk_base, periph_clk_enb_refcnt); clks[TEGRA124_CLK_DSIB] = clk; - /* emc mux */ - clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm, - ARRAY_SIZE(mux_pllmcp_clkm), 0, - clk_base + CLK_SOURCE_EMC, - 29, 3, 0, &emc_lock); - - clk = tegra_clk_register_mc("mc", "emc_mux", clk_base + CLK_SOURCE_EMC, + clk = tegra_clk_register_mc("mc", "emc", clk_base + CLK_SOURCE_EMC, &emc_lock); clks[TEGRA124_CLK_MC] = clk; @@ -1389,7 +1377,6 @@ static struct tegra_clk_init_table common_init_table[] __initdata = { {TEGRA124_CLK_XUSB_HOST_SRC, TEGRA124_CLK_PLL_RE_OUT, 112000000, 0}, {TEGRA124_CLK_SATA, TEGRA124_CLK_PLL_P, 104000000, 0}, {TEGRA124_CLK_SATA_OOB, TEGRA124_CLK_PLL_P, 204000000, 0}, - {TEGRA124_CLK_EMC, TEGRA124_CLK_CLK_MAX, 0, 1}, {TEGRA124_CLK_MSELECT, TEGRA124_CLK_CLK_MAX, 0, 1}, {TEGRA124_CLK_CSITE, TEGRA124_CLK_CLK_MAX, 0, 1}, {TEGRA124_CLK_TSENSOR, TEGRA124_CLK_CLK_M, 400000, 0}, @@ -1513,6 +1500,10 @@ static void __init tegra124_132_clock_init_post(struct device_node *np) tegra_super_clk_gen4_init(clk_base, pmc_base, tegra124_clks, &pll_x_params); tegra_add_of_provider(np); + + clks[TEGRA124_CLK_EMC] = tegra_clk_register_emc(clk_base, np, + &emc_lock); + tegra_register_devclks(devclks, ARRAY_SIZE(devclks)); tegra_cpu_car_ops = &tegra124_cpu_car_ops; diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c index 4b26509fc218..0af3e834dd24 100644 --- a/drivers/clk/tegra/clk-tegra30.c +++ b/drivers/clk/tegra/clk-tegra30.c @@ -679,7 +679,7 @@ static struct tegra_devclk devclks[] __initdata = { { .dev_id = "tegra30-dam.1", .dt_id = TEGRA30_CLK_DAM1 }, { .dev_id = "tegra30-dam.2", .dt_id = TEGRA30_CLK_DAM2 }, { .con_id = "hda", .dev_id = "tegra30-hda", .dt_id = TEGRA30_CLK_HDA }, - { .con_id = "hda2codec", .dev_id = "tegra30-hda", .dt_id = TEGRA30_CLK_HDA2CODEC_2X }, + { .con_id = "hda2codec_2x", .dev_id = "tegra30-hda", .dt_id = TEGRA30_CLK_HDA2CODEC_2X }, { .dev_id = "spi_tegra.0", .dt_id = TEGRA30_CLK_SBC1 }, { .dev_id = "spi_tegra.1", .dt_id = TEGRA30_CLK_SBC2 }, { .dev_id = "spi_tegra.2", .dt_id = TEGRA30_CLK_SBC3 }, diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index d6ac00647faf..75ddc8ff8bd4 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -623,6 +623,18 @@ void tegra_super_clk_gen4_init(void __iomem *clk_base, void __iomem *pmc_base, struct tegra_clk *tegra_clks, struct tegra_clk_pll_params *pll_params); +#ifdef CONFIG_TEGRA_CLK_EMC +struct clk *tegra_clk_register_emc(void __iomem *base, struct device_node *np, + spinlock_t *lock); +#else +static inline struct clk *tegra_clk_register_emc(void __iomem *base, + struct device_node *np, + spinlock_t *lock) +{ + return NULL; +} +#endif + void tegra114_clock_tune_cpu_trimmers_high(void); void tegra114_clock_tune_cpu_trimmers_low(void); void tegra114_clock_tune_cpu_trimmers_init(void); diff --git a/drivers/clk/ti/clk-dra7-atl.c b/drivers/clk/ti/clk-dra7-atl.c index d86bc46b93bd..19e543a32e2b 100644 --- a/drivers/clk/ti/clk-dra7-atl.c +++ b/drivers/clk/ti/clk-dra7-atl.c @@ -155,7 +155,7 @@ static int atl_clk_set_rate(struct clk_hw *hw, unsigned long rate, return 0; } -const struct clk_ops atl_clk_ops = { +static const struct clk_ops atl_clk_ops = { .enable = atl_clk_enable, .disable = atl_clk_disable, .is_enabled = atl_clk_is_enabled, @@ -167,7 +167,7 @@ const struct clk_ops atl_clk_ops = { static void __init of_dra7_atl_clock_setup(struct device_node *node) { struct dra7_atl_desc *clk_hw = NULL; - struct clk_init_data init = { 0 }; + struct clk_init_data init = { NULL }; const char **parent_names = NULL; struct clk *clk; @@ -252,6 +252,11 @@ static int of_dra7_atl_clk_probe(struct platform_device *pdev) } clk = of_clk_get_from_provider(&clkspec); + if (IS_ERR(clk)) { + pr_err("%s: failed to get atl clock %d from provider\n", + __func__, i); + return PTR_ERR(clk); + } cdesc = to_atl_desc(__clk_get_hw(clk)); cdesc->cinfo = cinfo; diff --git a/drivers/clk/ti/clk.c b/drivers/clk/ti/clk.c index 0ebe5c51062b..64bb5e8a3b8c 100644 --- a/drivers/clk/ti/clk.c +++ b/drivers/clk/ti/clk.c @@ -122,14 +122,14 @@ void __iomem *ti_clk_get_reg_addr(struct device_node *node, int index) if (i == CLK_MAX_MEMMAPS) { pr_err("clk-provider not found for %s!\n", node->name); - return ERR_PTR(-ENOENT); + return IOMEM_ERR_PTR(-ENOENT); } reg->index = i; if (of_property_read_u32_index(node, "reg", index, &val)) { pr_err("%s must have reg[%d]!\n", node->name, index); - return ERR_PTR(-EINVAL); + return IOMEM_ERR_PTR(-EINVAL); } reg->offset = val; diff --git a/drivers/clk/ti/clockdomain.c b/drivers/clk/ti/clockdomain.c index 35fe1085480c..b82ef07f3403 100644 --- a/drivers/clk/ti/clockdomain.c +++ b/drivers/clk/ti/clockdomain.c @@ -32,7 +32,7 @@ static void __init of_ti_clockdomain_setup(struct device_node *node) int i; int num_clks; - num_clks = of_count_phandle_with_args(node, "clocks", "#clock-cells"); + num_clks = of_clk_get_parent_count(node); for (i = 0; i < num_clks; i++) { clk = of_clk_get(node, i); diff --git a/drivers/clk/ti/dpll.c b/drivers/clk/ti/dpll.c index 11478a501c30..2aacf7a3bcae 100644 --- a/drivers/clk/ti/dpll.c +++ b/drivers/clk/ti/dpll.c @@ -177,7 +177,7 @@ cleanup: } #if defined(CONFIG_ARCH_OMAP3) && defined(CONFIG_ATAGS) -void __iomem *_get_reg(u8 module, u16 offset) +static void __iomem *_get_reg(u8 module, u16 offset) { u32 reg; struct clk_omap_reg *reg_setup; diff --git a/drivers/clk/ti/fapll.c b/drivers/clk/ti/fapll.c index ffcd8e09e85b..730aa62454a2 100644 --- a/drivers/clk/ti/fapll.c +++ b/drivers/clk/ti/fapll.c @@ -621,13 +621,13 @@ static void __init ti_fapll_setup(struct device_node *node) /* Check for hardwired audio_pll_clk1 */ if (is_audio_pll_clk1(freq)) { - freq = 0; - div = 0; + freq = NULL; + div = NULL; } else { /* Does the synthesizer have a FREQ register? */ v = readl_relaxed(freq); if (!v) - freq = 0; + freq = NULL; } synth_clk = ti_fapll_synth_setup(fd, freq, div, output_instance, output_name, node->name, diff --git a/drivers/clk/ux500/u8500_clk.c b/drivers/clk/ux500/u8500_clk.c index 80069c370a47..4626b97b7d83 100644 --- a/drivers/clk/ux500/u8500_clk.c +++ b/drivers/clk/ux500/u8500_clk.c @@ -116,11 +116,12 @@ void u8500_clk_init(u32 clkrst1_base, u32 clkrst2_base, u32 clkrst3_base, clk_register_clkdev(clk, NULL, "hdmi"); clk_register_clkdev(clk, "hdmi", "mcde"); - clk = clk_reg_prcmu_gate("apeatclk", NULL, PRCMU_APEATCLK, CLK_IS_ROOT); + clk = clk_reg_prcmu_scalable("apeatclk", NULL, PRCMU_APEATCLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); clk_register_clkdev(clk, NULL, "apeat"); - clk = clk_reg_prcmu_gate("apetraceclk", NULL, PRCMU_APETRACECLK, - CLK_IS_ROOT); + clk = clk_reg_prcmu_scalable("apetraceclk", NULL, PRCMU_APETRACECLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); clk_register_clkdev(clk, NULL, "apetrace"); clk = clk_reg_prcmu_gate("mcdeclk", NULL, PRCMU_MCDECLK, CLK_IS_ROOT); diff --git a/drivers/clk/ux500/u8500_of_clk.c b/drivers/clk/ux500/u8500_of_clk.c index 7b55ef89baa5..e319ef912dc6 100644 --- a/drivers/clk/ux500/u8500_of_clk.c +++ b/drivers/clk/ux500/u8500_of_clk.c @@ -166,8 +166,8 @@ void u8500_of_clk_init(u32 clkrst1_base, u32 clkrst2_base, u32 clkrst3_base, clk = clk_reg_prcmu_gate("apeatclk", NULL, PRCMU_APEATCLK, CLK_IS_ROOT); prcmu_clk[PRCMU_APEATCLK] = clk; - clk = clk_reg_prcmu_gate("apetraceclk", NULL, PRCMU_APETRACECLK, - CLK_IS_ROOT); + clk = clk_reg_prcmu_scalable("apetraceclk", NULL, PRCMU_APETRACECLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); prcmu_clk[PRCMU_APETRACECLK] = clk; clk = clk_reg_prcmu_gate("mcdeclk", NULL, PRCMU_MCDECLK, CLK_IS_ROOT); diff --git a/drivers/clk/versatile/clk-sp810.c b/drivers/clk/versatile/clk-sp810.c index c6e86a9a2aa3..a96dd8e53fdb 100644 --- a/drivers/clk/versatile/clk-sp810.c +++ b/drivers/clk/versatile/clk-sp810.c @@ -135,7 +135,7 @@ static struct clk *clk_sp810_timerclken_of_get(struct of_phandle_args *clkspec, return sp810->timerclken[clkspec->args[0]].clk; } -void __init clk_sp810_of_setup(struct device_node *node) +static void __init clk_sp810_of_setup(struct device_node *node) { struct clk_sp810 *sp810 = kzalloc(sizeof(*sp810), GFP_KERNEL); const char *parent_names[2]; @@ -156,7 +156,7 @@ void __init clk_sp810_of_setup(struct device_node *node) "timclk"); parent_names[1] = of_clk_get_parent_name(node, sp810->timclk_index); - if (parent_names[0] <= 0 || parent_names[1] <= 0) { + if (!parent_names[0] || !parent_names[1]) { pr_warn("Failed to obtain parent clocks for SP810!\n"); return; } diff --git a/drivers/clk/zynq/clkc.c b/drivers/clk/zynq/clkc.c index 40cb113be6af..de614384bb44 100644 --- a/drivers/clk/zynq/clkc.c +++ b/drivers/clk/zynq/clkc.c @@ -85,22 +85,29 @@ static DEFINE_SPINLOCK(canmioclk_lock); static DEFINE_SPINLOCK(dbgclk_lock); static DEFINE_SPINLOCK(aperclk_lock); -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 *const armpll_parents[] __initconst = {"armpll_int", + "ps_clk"}; +static const char *const ddrpll_parents[] __initconst = {"ddrpll_int", + "ps_clk"}; +static const char *const iopll_parents[] __initconst = {"iopll_int", + "ps_clk"}; static const char *gem0_mux_parents[] __initdata = {"gem0_div1", "dummy_name"}; static const char *gem1_mux_parents[] __initdata = {"gem1_div1", "dummy_name"}; -static const char *can0_mio_mux2_parents[] __initdata = {"can0_gate", +static const char *const can0_mio_mux2_parents[] __initconst = {"can0_gate", "can0_mio_mux"}; -static const char *can1_mio_mux2_parents[] __initdata = {"can1_gate", +static const char *const can1_mio_mux2_parents[] __initconst = {"can1_gate", "can1_mio_mux"}; static const char *dbg_emio_mux_parents[] __initdata = {"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 *const dbgtrc_emio_input_names[] __initconst = { + "trace_emio_clk"}; +static const char *const gem0_emio_input_names[] __initconst = { + "gem0_emio_clk"}; +static const char *const gem1_emio_input_names[] __initconst = { + "gem1_emio_clk"}; +static const char *const 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, diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 611cb09239eb..cc8a71c267b8 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -36,17 +36,6 @@ config ARM_EXYNOS_CPUFREQ If in doubt, say N. -config ARM_EXYNOS4210_CPUFREQ - bool "SAMSUNG EXYNOS4210" - depends on CPU_EXYNOS4210 - depends on ARM_EXYNOS_CPUFREQ - default y - help - This adds the CPUFreq driver for Samsung EXYNOS4210 - SoC (S5PV310 or S5PC210). - - If in doubt, say N. - config ARM_EXYNOS4X12_CPUFREQ bool "SAMSUNG EXYNOS4x12" depends on SOC_EXYNOS4212 || SOC_EXYNOS4412 diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index cdce92ae2e8b..2169bf792db7 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -54,7 +54,6 @@ obj-$(CONFIG_ARCH_DAVINCI) += davinci-cpufreq.o obj-$(CONFIG_UX500_SOC_DB8500) += dbx500-cpufreq.o obj-$(CONFIG_ARM_EXYNOS_CPUFREQ) += arm-exynos-cpufreq.o arm-exynos-cpufreq-y := exynos-cpufreq.o -arm-exynos-cpufreq-$(CONFIG_ARM_EXYNOS4210_CPUFREQ) += exynos4210-cpufreq.o arm-exynos-cpufreq-$(CONFIG_ARM_EXYNOS4X12_CPUFREQ) += exynos4x12-cpufreq.o arm-exynos-cpufreq-$(CONFIG_ARM_EXYNOS5250_CPUFREQ) += exynos5250-cpufreq.o obj-$(CONFIG_ARM_EXYNOS5440_CPUFREQ) += exynos5440-cpufreq.o diff --git a/drivers/cpufreq/exynos-cpufreq.c b/drivers/cpufreq/exynos-cpufreq.c index 82d2fbb20f7e..fb24aaf4adcf 100644 --- a/drivers/cpufreq/exynos-cpufreq.c +++ b/drivers/cpufreq/exynos-cpufreq.c @@ -168,10 +168,7 @@ static int exynos_cpufreq_probe(struct platform_device *pdev) exynos_info->dev = &pdev->dev; - if (of_machine_is_compatible("samsung,exynos4210")) { - exynos_info->type = EXYNOS_SOC_4210; - ret = exynos4210_cpufreq_init(exynos_info); - } else if (of_machine_is_compatible("samsung,exynos4212")) { + if (of_machine_is_compatible("samsung,exynos4212")) { exynos_info->type = EXYNOS_SOC_4212; ret = exynos4x12_cpufreq_init(exynos_info); } else if (of_machine_is_compatible("samsung,exynos4412")) { diff --git a/drivers/cpufreq/exynos-cpufreq.h b/drivers/cpufreq/exynos-cpufreq.h index 9f2062a7cc02..a3855e4d913d 100644 --- a/drivers/cpufreq/exynos-cpufreq.h +++ b/drivers/cpufreq/exynos-cpufreq.h @@ -18,7 +18,6 @@ enum cpufreq_level_index { }; enum exynos_soc_type { - EXYNOS_SOC_4210, EXYNOS_SOC_4212, EXYNOS_SOC_4412, EXYNOS_SOC_5250, @@ -53,14 +52,6 @@ struct exynos_dvfs_info { void __iomem *cmu_regs; }; -#ifdef CONFIG_ARM_EXYNOS4210_CPUFREQ -extern int exynos4210_cpufreq_init(struct exynos_dvfs_info *); -#else -static inline int exynos4210_cpufreq_init(struct exynos_dvfs_info *info) -{ - return -EOPNOTSUPP; -} -#endif #ifdef CONFIG_ARM_EXYNOS4X12_CPUFREQ extern int exynos4x12_cpufreq_init(struct exynos_dvfs_info *); #else diff --git a/drivers/cpufreq/exynos4210-cpufreq.c b/drivers/cpufreq/exynos4210-cpufreq.c deleted file mode 100644 index 843ec824fd91..000000000000 --- a/drivers/cpufreq/exynos4210-cpufreq.c +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * EXYNOS4210 - CPU frequency scaling support - * - * 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. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "exynos-cpufreq.h" - -static struct clk *cpu_clk; -static struct clk *moutcore; -static struct clk *mout_mpll; -static struct clk *mout_apll; -static struct exynos_dvfs_info *cpufreq; - -static unsigned int exynos4210_volt_table[] = { - 1250000, 1150000, 1050000, 975000, 950000, -}; - -static struct cpufreq_frequency_table exynos4210_freq_table[] = { - {0, L0, 1200 * 1000}, - {0, L1, 1000 * 1000}, - {0, L2, 800 * 1000}, - {0, L3, 500 * 1000}, - {0, L4, 200 * 1000}, - {0, 0, CPUFREQ_TABLE_END}, -}; - -static struct apll_freq apll_freq_4210[] = { - /* - * values: - * freq - * clock divider for CORE, COREM0, COREM1, PERIPH, ATB, PCLK_DBG, APLL, RESERVED - * clock divider for COPY, HPM, RESERVED - * PLL M, P, S - */ - APLL_FREQ(1200, 0, 3, 7, 3, 4, 1, 7, 0, 5, 0, 0, 150, 3, 1), - APLL_FREQ(1000, 0, 3, 7, 3, 4, 1, 7, 0, 4, 0, 0, 250, 6, 1), - APLL_FREQ(800, 0, 3, 7, 3, 3, 1, 7, 0, 3, 0, 0, 200, 6, 1), - APLL_FREQ(500, 0, 3, 7, 3, 3, 1, 7, 0, 3, 0, 0, 250, 6, 2), - APLL_FREQ(200, 0, 1, 3, 1, 3, 1, 0, 0, 3, 0, 0, 200, 6, 3), -}; - -static void exynos4210_set_clkdiv(unsigned int div_index) -{ - unsigned int tmp; - - /* Change Divider - CPU0 */ - - tmp = apll_freq_4210[div_index].clk_div_cpu0; - - __raw_writel(tmp, cpufreq->cmu_regs + EXYNOS4_CLKDIV_CPU); - - do { - tmp = __raw_readl(cpufreq->cmu_regs + EXYNOS4_CLKDIV_STATCPU); - } while (tmp & 0x1111111); - - /* Change Divider - CPU1 */ - - tmp = apll_freq_4210[div_index].clk_div_cpu1; - - __raw_writel(tmp, cpufreq->cmu_regs + EXYNOS4_CLKDIV_CPU1); - - do { - tmp = __raw_readl(cpufreq->cmu_regs + EXYNOS4_CLKDIV_STATCPU1); - } while (tmp & 0x11); -} - -static void exynos4210_set_apll(unsigned int index) -{ - unsigned int tmp, freq = apll_freq_4210[index].freq; - - /* MUX_CORE_SEL = MPLL, ARMCLK uses MPLL for lock time */ - clk_set_parent(moutcore, mout_mpll); - - do { - tmp = (__raw_readl(cpufreq->cmu_regs + EXYNOS4_CLKMUX_STATCPU) - >> EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT); - tmp &= 0x7; - } while (tmp != 0x2); - - clk_set_rate(mout_apll, freq * 1000); - - /* MUX_CORE_SEL = APLL */ - clk_set_parent(moutcore, mout_apll); - - do { - tmp = __raw_readl(cpufreq->cmu_regs + EXYNOS4_CLKMUX_STATCPU); - tmp &= EXYNOS4_CLKMUX_STATCPU_MUXCORE_MASK; - } while (tmp != (0x1 << EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT)); -} - -static void exynos4210_set_frequency(unsigned int old_index, - unsigned int new_index) -{ - if (old_index > new_index) { - exynos4210_set_clkdiv(new_index); - exynos4210_set_apll(new_index); - } else if (old_index < new_index) { - exynos4210_set_apll(new_index); - exynos4210_set_clkdiv(new_index); - } -} - -int exynos4210_cpufreq_init(struct exynos_dvfs_info *info) -{ - struct device_node *np; - unsigned long rate; - - /* - * HACK: This is a temporary workaround to get access to clock - * controller registers directly and remove static mappings and - * dependencies on platform headers. It is necessary to enable - * Exynos multi-platform support and will be removed together with - * this whole driver as soon as Exynos gets migrated to use - * cpufreq-dt driver. - */ - np = of_find_compatible_node(NULL, NULL, "samsung,exynos4210-clock"); - if (!np) { - pr_err("%s: failed to find clock controller DT node\n", - __func__); - return -ENODEV; - } - - info->cmu_regs = of_iomap(np, 0); - if (!info->cmu_regs) { - pr_err("%s: failed to map CMU registers\n", __func__); - return -EFAULT; - } - - cpu_clk = clk_get(NULL, "armclk"); - if (IS_ERR(cpu_clk)) - return PTR_ERR(cpu_clk); - - moutcore = clk_get(NULL, "moutcore"); - if (IS_ERR(moutcore)) - goto err_moutcore; - - mout_mpll = clk_get(NULL, "mout_mpll"); - if (IS_ERR(mout_mpll)) - goto err_mout_mpll; - - rate = clk_get_rate(mout_mpll) / 1000; - - mout_apll = clk_get(NULL, "mout_apll"); - if (IS_ERR(mout_apll)) - goto err_mout_apll; - - info->mpll_freq_khz = rate; - /* 800Mhz */ - info->pll_safe_idx = L2; - info->cpu_clk = cpu_clk; - info->volt_table = exynos4210_volt_table; - info->freq_table = exynos4210_freq_table; - info->set_freq = exynos4210_set_frequency; - - cpufreq = info; - - return 0; - -err_mout_apll: - clk_put(mout_mpll); -err_mout_mpll: - clk_put(moutcore); -err_moutcore: - clk_put(cpu_clk); - - pr_debug("%s: failed initialization\n", __func__); - return -EINVAL; -} diff --git a/include/dt-bindings/clock/bcm-cygnus.h b/include/dt-bindings/clock/bcm-cygnus.h new file mode 100644 index 000000000000..32fbc475087a --- /dev/null +++ b/include/dt-bindings/clock/bcm-cygnus.h @@ -0,0 +1,68 @@ +/* + * BSD LICENSE + * + * Copyright(c) 2014 Broadcom Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Broadcom Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CLOCK_BCM_CYGNUS_H +#define _CLOCK_BCM_CYGNUS_H + +/* GENPLL clock ID */ +#define BCM_CYGNUS_GENPLL 0 +#define BCM_CYGNUS_GENPLL_AXI21_CLK 1 +#define BCM_CYGNUS_GENPLL_250MHZ_CLK 2 +#define BCM_CYGNUS_GENPLL_IHOST_SYS_CLK 3 +#define BCM_CYGNUS_GENPLL_ENET_SW_CLK 4 +#define BCM_CYGNUS_GENPLL_AUDIO_125_CLK 5 +#define BCM_CYGNUS_GENPLL_CAN_CLK 6 + +/* LCPLL0 clock ID */ +#define BCM_CYGNUS_LCPLL0 0 +#define BCM_CYGNUS_LCPLL0_PCIE_PHY_REF_CLK 1 +#define BCM_CYGNUS_LCPLL0_DDR_PHY_CLK 2 +#define BCM_CYGNUS_LCPLL0_SDIO_CLK 3 +#define BCM_CYGNUS_LCPLL0_USB_PHY_REF_CLK 4 +#define BCM_CYGNUS_LCPLL0_SMART_CARD_CLK 5 +#define BCM_CYGNUS_LCPLL0_CH5_UNUSED 6 + +/* MIPI PLL clock ID */ +#define BCM_CYGNUS_MIPIPLL 0 +#define BCM_CYGNUS_MIPIPLL_CH0_UNUSED 1 +#define BCM_CYGNUS_MIPIPLL_CH1_LCD 2 +#define BCM_CYGNUS_MIPIPLL_CH2_V3D 3 +#define BCM_CYGNUS_MIPIPLL_CH3_UNUSED 4 +#define BCM_CYGNUS_MIPIPLL_CH4_UNUSED 5 +#define BCM_CYGNUS_MIPIPLL_CH5_UNUSED 6 + +/* ASIU clock ID */ +#define BCM_CYGNUS_ASIU_KEYPAD_CLK 0 +#define BCM_CYGNUS_ASIU_ADC_CLK 1 +#define BCM_CYGNUS_ASIU_PWM_CLK 2 + +#endif /* _CLOCK_BCM_CYGNUS_H */ diff --git a/include/dt-bindings/clock/hi6220-clock.h b/include/dt-bindings/clock/hi6220-clock.h new file mode 100644 index 000000000000..70ee3833a7a0 --- /dev/null +++ b/include/dt-bindings/clock/hi6220-clock.h @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2015 Hisilicon Limited. + * + * Author: Bintian Wang + * + * 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. + */ + +#ifndef __DT_BINDINGS_CLOCK_HI6220_H +#define __DT_BINDINGS_CLOCK_HI6220_H + +/* clk in Hi6220 AO (always on) controller */ +#define HI6220_NONE_CLOCK 0 + +/* fixed rate clocks */ +#define HI6220_REF32K 1 +#define HI6220_CLK_TCXO 2 +#define HI6220_MMC1_PAD 3 +#define HI6220_MMC2_PAD 4 +#define HI6220_MMC0_PAD 5 +#define HI6220_PLL_BBP 6 +#define HI6220_PLL_GPU 7 +#define HI6220_PLL1_DDR 8 +#define HI6220_PLL_SYS 9 +#define HI6220_PLL_SYS_MEDIA 10 +#define HI6220_DDR_SRC 11 +#define HI6220_PLL_MEDIA 12 +#define HI6220_PLL_DDR 13 + +/* fixed factor clocks */ +#define HI6220_300M 14 +#define HI6220_150M 15 +#define HI6220_PICOPHY_SRC 16 +#define HI6220_MMC0_SRC_SEL 17 +#define HI6220_MMC1_SRC_SEL 18 +#define HI6220_MMC2_SRC_SEL 19 +#define HI6220_VPU_CODEC 20 +#define HI6220_MMC0_SMP 21 +#define HI6220_MMC1_SMP 22 +#define HI6220_MMC2_SMP 23 + +/* gate clocks */ +#define HI6220_WDT0_PCLK 24 +#define HI6220_WDT1_PCLK 25 +#define HI6220_WDT2_PCLK 26 +#define HI6220_TIMER0_PCLK 27 +#define HI6220_TIMER1_PCLK 28 +#define HI6220_TIMER2_PCLK 29 +#define HI6220_TIMER3_PCLK 30 +#define HI6220_TIMER4_PCLK 31 +#define HI6220_TIMER5_PCLK 32 +#define HI6220_TIMER6_PCLK 33 +#define HI6220_TIMER7_PCLK 34 +#define HI6220_TIMER8_PCLK 35 +#define HI6220_UART0_PCLK 36 + +#define HI6220_AO_NR_CLKS 37 + +/* clk in Hi6220 systrl */ +/* gate clock */ +#define HI6220_MMC0_CLK 1 +#define HI6220_MMC0_CIUCLK 2 +#define HI6220_MMC1_CLK 3 +#define HI6220_MMC1_CIUCLK 4 +#define HI6220_MMC2_CLK 5 +#define HI6220_MMC2_CIUCLK 6 +#define HI6220_USBOTG_HCLK 7 +#define HI6220_CLK_PICOPHY 8 +#define HI6220_HIFI 9 +#define HI6220_DACODEC_PCLK 10 +#define HI6220_EDMAC_ACLK 11 +#define HI6220_CS_ATB 12 +#define HI6220_I2C0_CLK 13 +#define HI6220_I2C1_CLK 14 +#define HI6220_I2C2_CLK 15 +#define HI6220_I2C3_CLK 16 +#define HI6220_UART1_PCLK 17 +#define HI6220_UART2_PCLK 18 +#define HI6220_UART3_PCLK 19 +#define HI6220_UART4_PCLK 20 +#define HI6220_SPI_CLK 21 +#define HI6220_TSENSOR_CLK 22 +#define HI6220_MMU_CLK 23 +#define HI6220_HIFI_SEL 24 +#define HI6220_MMC0_SYSPLL 25 +#define HI6220_MMC1_SYSPLL 26 +#define HI6220_MMC2_SYSPLL 27 +#define HI6220_MMC0_SEL 28 +#define HI6220_MMC1_SEL 29 +#define HI6220_BBPPLL_SEL 30 +#define HI6220_MEDIA_PLL_SRC 31 +#define HI6220_MMC2_SEL 32 +#define HI6220_CS_ATB_SYSPLL 33 + +/* mux clocks */ +#define HI6220_MMC0_SRC 34 +#define HI6220_MMC0_SMP_IN 35 +#define HI6220_MMC1_SRC 36 +#define HI6220_MMC1_SMP_IN 37 +#define HI6220_MMC2_SRC 38 +#define HI6220_MMC2_SMP_IN 39 +#define HI6220_HIFI_SRC 40 +#define HI6220_UART1_SRC 41 +#define HI6220_UART2_SRC 42 +#define HI6220_UART3_SRC 43 +#define HI6220_UART4_SRC 44 +#define HI6220_MMC0_MUX0 45 +#define HI6220_MMC1_MUX0 46 +#define HI6220_MMC2_MUX0 47 +#define HI6220_MMC0_MUX1 48 +#define HI6220_MMC1_MUX1 49 +#define HI6220_MMC2_MUX1 50 + +/* divider clocks */ +#define HI6220_CLK_BUS 51 +#define HI6220_MMC0_DIV 52 +#define HI6220_MMC1_DIV 53 +#define HI6220_MMC2_DIV 54 +#define HI6220_HIFI_DIV 55 +#define HI6220_BBPPLL0_DIV 56 +#define HI6220_CS_DAPB 57 +#define HI6220_CS_ATB_DIV 58 + +#define HI6220_SYS_NR_CLKS 59 + +/* clk in Hi6220 media controller */ +/* gate clocks */ +#define HI6220_DSI_PCLK 1 +#define HI6220_G3D_PCLK 2 +#define HI6220_ACLK_CODEC_VPU 3 +#define HI6220_ISP_SCLK 4 +#define HI6220_ADE_CORE 5 +#define HI6220_MED_MMU 6 +#define HI6220_CFG_CSI4PHY 7 +#define HI6220_CFG_CSI2PHY 8 +#define HI6220_ISP_SCLK_GATE 9 +#define HI6220_ISP_SCLK_GATE1 10 +#define HI6220_ADE_CORE_GATE 11 +#define HI6220_CODEC_VPU_GATE 12 +#define HI6220_MED_SYSPLL 13 + +/* mux clocks */ +#define HI6220_1440_1200 14 +#define HI6220_1000_1200 15 +#define HI6220_1000_1440 16 + +/* divider clocks */ +#define HI6220_CODEC_JPEG 17 +#define HI6220_ISP_SCLK_SRC 18 +#define HI6220_ISP_SCLK1 19 +#define HI6220_ADE_CORE_SRC 20 +#define HI6220_ADE_PIX_SRC 21 +#define HI6220_G3D_CLK 22 +#define HI6220_CODEC_VPU_SRC 23 + +#define HI6220_MEDIA_NR_CLKS 24 + +/* clk in Hi6220 power controller */ +/* gate clocks */ +#define HI6220_PLL_GPU_GATE 1 +#define HI6220_PLL1_DDR_GATE 2 +#define HI6220_PLL_DDR_GATE 3 +#define HI6220_PLL_MEDIA_GATE 4 +#define HI6220_PLL0_BBP_GATE 5 + +/* divider clocks */ +#define HI6220_DDRC_SRC 6 +#define HI6220_DDRC_AXI1 7 + +#define HI6220_POWER_NR_CLKS 8 +#endif diff --git a/include/dt-bindings/clock/lpc18xx-ccu.h b/include/dt-bindings/clock/lpc18xx-ccu.h new file mode 100644 index 000000000000..bbfe00b6ab7d --- /dev/null +++ b/include/dt-bindings/clock/lpc18xx-ccu.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2015 Joachim Eastwood + * + * This code is released using a dual license strategy: BSD/GPL + * You can choose the licence that better fits your requirements. + * + * Released under the terms of 3-clause BSD License + * Released under the terms of GNU General Public License Version 2.0 + * + */ + +/* Clock Control Unit 1 (CCU1) clock offsets */ +#define CLK_APB3_BUS 0x100 +#define CLK_APB3_I2C1 0x108 +#define CLK_APB3_DAC 0x110 +#define CLK_APB3_ADC0 0x118 +#define CLK_APB3_ADC1 0x120 +#define CLK_APB3_CAN0 0x128 +#define CLK_APB1_BUS 0x200 +#define CLK_APB1_MOTOCON_PWM 0x208 +#define CLK_APB1_I2C0 0x210 +#define CLK_APB1_I2S 0x218 +#define CLK_APB1_CAN1 0x220 +#define CLK_SPIFI 0x300 +#define CLK_CPU_BUS 0x400 +#define CLK_CPU_SPIFI 0x408 +#define CLK_CPU_GPIO 0x410 +#define CLK_CPU_LCD 0x418 +#define CLK_CPU_ETHERNET 0x420 +#define CLK_CPU_USB0 0x428 +#define CLK_CPU_EMC 0x430 +#define CLK_CPU_SDIO 0x438 +#define CLK_CPU_DMA 0x440 +#define CLK_CPU_CORE 0x448 +#define CLK_CPU_SCT 0x468 +#define CLK_CPU_USB1 0x470 +#define CLK_CPU_EMCDIV 0x478 +#define CLK_CPU_FLASHA 0x480 +#define CLK_CPU_FLASHB 0x488 +#define CLK_CPU_M0APP 0x490 +#define CLK_CPU_ADCHS 0x498 +#define CLK_CPU_EEPROM 0x4a0 +#define CLK_CPU_WWDT 0x500 +#define CLK_CPU_UART0 0x508 +#define CLK_CPU_UART1 0x510 +#define CLK_CPU_SSP0 0x518 +#define CLK_CPU_TIMER0 0x520 +#define CLK_CPU_TIMER1 0x528 +#define CLK_CPU_SCU 0x530 +#define CLK_CPU_CREG 0x538 +#define CLK_CPU_RITIMER 0x600 +#define CLK_CPU_UART2 0x608 +#define CLK_CPU_UART3 0x610 +#define CLK_CPU_TIMER2 0x618 +#define CLK_CPU_TIMER3 0x620 +#define CLK_CPU_SSP1 0x628 +#define CLK_CPU_QEI 0x630 +#define CLK_PERIPH_BUS 0x700 +#define CLK_PERIPH_CORE 0x710 +#define CLK_PERIPH_SGPIO 0x718 +#define CLK_USB0 0x800 +#define CLK_USB1 0x900 +#define CLK_SPI 0xA00 +#define CLK_ADCHS 0xB00 + +/* Clock Control Unit 2 (CCU2) clock offsets */ +#define CLK_AUDIO 0x100 +#define CLK_APB2_UART3 0x200 +#define CLK_APB2_UART2 0x300 +#define CLK_APB0_UART1 0x400 +#define CLK_APB0_UART0 0x500 +#define CLK_APB2_SSP1 0x600 +#define CLK_APB0_SSP0 0x700 +#define CLK_SDIO 0x800 diff --git a/include/dt-bindings/clock/lpc18xx-cgu.h b/include/dt-bindings/clock/lpc18xx-cgu.h new file mode 100644 index 000000000000..6e57c6d2ca66 --- /dev/null +++ b/include/dt-bindings/clock/lpc18xx-cgu.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2015 Joachim Eastwood + * + * This code is released using a dual license strategy: BSD/GPL + * You can choose the licence that better fits your requirements. + * + * Released under the terms of 3-clause BSD License + * Released under the terms of GNU General Public License Version 2.0 + * + */ + +/* LPC18xx/43xx base clock ids */ +#define BASE_SAFE_CLK 0 +#define BASE_USB0_CLK 1 +#define BASE_PERIPH_CLK 2 +#define BASE_USB1_CLK 3 +#define BASE_CPU_CLK 4 +#define BASE_SPIFI_CLK 5 +#define BASE_SPI_CLK 6 +#define BASE_PHY_RX_CLK 7 +#define BASE_PHY_TX_CLK 8 +#define BASE_APB1_CLK 9 +#define BASE_APB3_CLK 10 +#define BASE_LCD_CLK 11 +#define BASE_ADCHS_CLK 12 +#define BASE_SDIO_CLK 13 +#define BASE_SSP0_CLK 14 +#define BASE_SSP1_CLK 15 +#define BASE_UART0_CLK 16 +#define BASE_UART1_CLK 17 +#define BASE_UART2_CLK 18 +#define BASE_UART3_CLK 19 +#define BASE_OUT_CLK 20 +#define BASE_RES1_CLK 21 +#define BASE_RES2_CLK 22 +#define BASE_RES3_CLK 23 +#define BASE_RES4_CLK 24 +#define BASE_AUDIO_CLK 25 +#define BASE_CGU_OUT0_CLK 26 +#define BASE_CGU_OUT1_CLK 27 +#define BASE_CLK_MAX (BASE_CGU_OUT1_CLK + 1) diff --git a/include/dt-bindings/clock/marvell,mmp2.h b/include/dt-bindings/clock/marvell,mmp2.h index 591f7fba89e2..7a510384a82a 100644 --- a/include/dt-bindings/clock/marvell,mmp2.h +++ b/include/dt-bindings/clock/marvell,mmp2.h @@ -48,6 +48,7 @@ #define MMP2_CLK_SSP1 78 #define MMP2_CLK_SSP2 79 #define MMP2_CLK_SSP3 80 +#define MMP2_CLK_TIMER 81 /* axi periphrals */ #define MMP2_CLK_SDH0 101 diff --git a/include/dt-bindings/clock/marvell,pxa168.h b/include/dt-bindings/clock/marvell,pxa168.h index 79630b9d74b8..3e45bdfe1aa4 100644 --- a/include/dt-bindings/clock/marvell,pxa168.h +++ b/include/dt-bindings/clock/marvell,pxa168.h @@ -18,7 +18,9 @@ #define PXA168_CLK_PLL1_13_1_5 18 #define PXA168_CLK_PLL1_2_1_5 19 #define PXA168_CLK_PLL1_3_16 20 +#define PXA168_CLK_PLL1_192 21 #define PXA168_CLK_UART_PLL 27 +#define PXA168_CLK_USB_PLL 28 /* apb periphrals */ #define PXA168_CLK_TWSI0 60 @@ -40,6 +42,7 @@ #define PXA168_CLK_SSP2 76 #define PXA168_CLK_SSP3 77 #define PXA168_CLK_SSP4 78 +#define PXA168_CLK_TIMER 79 /* axi periphrals */ #define PXA168_CLK_DFC 100 diff --git a/include/dt-bindings/clock/marvell,pxa1928.h b/include/dt-bindings/clock/marvell,pxa1928.h new file mode 100644 index 000000000000..d4f2e18919ff --- /dev/null +++ b/include/dt-bindings/clock/marvell,pxa1928.h @@ -0,0 +1,57 @@ +#ifndef __DTS_MARVELL_PXA1928_CLOCK_H +#define __DTS_MARVELL_PXA1928_CLOCK_H + +/* + * Clock ID values here correspond to the control register offset/4. + */ + +/* apb peripherals */ +#define PXA1928_CLK_RTC 0x00 +#define PXA1928_CLK_TWSI0 0x01 +#define PXA1928_CLK_TWSI1 0x02 +#define PXA1928_CLK_TWSI2 0x03 +#define PXA1928_CLK_TWSI3 0x04 +#define PXA1928_CLK_OWIRE 0x05 +#define PXA1928_CLK_KPC 0x06 +#define PXA1928_CLK_TB_ROTARY 0x07 +#define PXA1928_CLK_SW_JTAG 0x08 +#define PXA1928_CLK_TIMER1 0x09 +#define PXA1928_CLK_UART0 0x0b +#define PXA1928_CLK_UART1 0x0c +#define PXA1928_CLK_UART2 0x0d +#define PXA1928_CLK_GPIO 0x0e +#define PXA1928_CLK_PWM0 0x0f +#define PXA1928_CLK_PWM1 0x10 +#define PXA1928_CLK_PWM2 0x11 +#define PXA1928_CLK_PWM3 0x12 +#define PXA1928_CLK_SSP0 0x13 +#define PXA1928_CLK_SSP1 0x14 +#define PXA1928_CLK_SSP2 0x15 + +#define PXA1928_CLK_TWSI4 0x1f +#define PXA1928_CLK_TWSI5 0x20 +#define PXA1928_CLK_UART3 0x22 +#define PXA1928_CLK_THSENS_GLOB 0x24 +#define PXA1928_CLK_THSENS_CPU 0x26 +#define PXA1928_CLK_THSENS_VPU 0x27 +#define PXA1928_CLK_THSENS_GC 0x28 +#define PXA1928_APBC_NR_CLKS 0x30 + + +/* axi peripherals */ +#define PXA1928_CLK_SDH0 0x15 +#define PXA1928_CLK_SDH1 0x16 +#define PXA1928_CLK_USB 0x17 +#define PXA1928_CLK_NAND 0x18 +#define PXA1928_CLK_DMA 0x19 + +#define PXA1928_CLK_SDH2 0x3a +#define PXA1928_CLK_SDH3 0x3b +#define PXA1928_CLK_HSIC 0x3e +#define PXA1928_CLK_SDH4 0x57 +#define PXA1928_CLK_GC3D 0x5d +#define PXA1928_CLK_GC2D 0x5f + +#define PXA1928_APMU_NR_CLKS 0x60 + +#endif diff --git a/include/dt-bindings/clock/marvell,pxa910.h b/include/dt-bindings/clock/marvell,pxa910.h index 719cffb2bea2..135082a0b62f 100644 --- a/include/dt-bindings/clock/marvell,pxa910.h +++ b/include/dt-bindings/clock/marvell,pxa910.h @@ -18,7 +18,9 @@ #define PXA910_CLK_PLL1_13_1_5 18 #define PXA910_CLK_PLL1_2_1_5 19 #define PXA910_CLK_PLL1_3_16 20 +#define PXA910_CLK_PLL1_192 21 #define PXA910_CLK_UART_PLL 27 +#define PXA910_CLK_USB_PLL 28 /* apb periphrals */ #define PXA910_CLK_TWSI0 60 @@ -37,6 +39,8 @@ #define PXA910_CLK_UART2 73 #define PXA910_CLK_SSP0 74 #define PXA910_CLK_SSP1 75 +#define PXA910_CLK_TIMER0 76 +#define PXA910_CLK_TIMER1 77 /* axi periphrals */ #define PXA910_CLK_DFC 100 diff --git a/include/dt-bindings/clock/meson8b-clkc.h b/include/dt-bindings/clock/meson8b-clkc.h new file mode 100644 index 000000000000..bd2720d58e0c --- /dev/null +++ b/include/dt-bindings/clock/meson8b-clkc.h @@ -0,0 +1,25 @@ +/* + * Meson8b clock tree IDs + */ + +#ifndef __MESON8B_CLKC_H +#define __MESON8B_CLKC_H + +#define CLKID_UNUSED 0 +#define CLKID_XTAL 1 +#define CLKID_PLL_FIXED 2 +#define CLKID_PLL_VID 3 +#define CLKID_PLL_SYS 4 +#define CLKID_FCLK_DIV2 5 +#define CLKID_FCLK_DIV3 6 +#define CLKID_FCLK_DIV4 7 +#define CLKID_FCLK_DIV5 8 +#define CLKID_FCLK_DIV7 9 +#define CLKID_CLK81 10 +#define CLKID_MALI 11 +#define CLKID_CPUCLK 12 +#define CLKID_ZERO 13 + +#define CLK_NR_CLKS (CLKID_ZERO + 1) + +#endif /* __MESON8B_CLKC_H */ diff --git a/include/dt-bindings/clock/mt8135-clk.h b/include/dt-bindings/clock/mt8135-clk.h new file mode 100644 index 000000000000..6dac6c091dd2 --- /dev/null +++ b/include/dt-bindings/clock/mt8135-clk.h @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao + * + * 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. + * + * 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 _DT_BINDINGS_CLK_MT8135_H +#define _DT_BINDINGS_CLK_MT8135_H + +/* TOPCKGEN */ + +#define CLK_TOP_DSI0_LNTC_DSICLK 1 +#define CLK_TOP_HDMITX_CLKDIG_CTS 2 +#define CLK_TOP_CLKPH_MCK 3 +#define CLK_TOP_CPUM_TCK_IN 4 +#define CLK_TOP_MAINPLL_806M 5 +#define CLK_TOP_MAINPLL_537P3M 6 +#define CLK_TOP_MAINPLL_322P4M 7 +#define CLK_TOP_MAINPLL_230P3M 8 +#define CLK_TOP_UNIVPLL_624M 9 +#define CLK_TOP_UNIVPLL_416M 10 +#define CLK_TOP_UNIVPLL_249P6M 11 +#define CLK_TOP_UNIVPLL_178P3M 12 +#define CLK_TOP_UNIVPLL_48M 13 +#define CLK_TOP_MMPLL_D2 14 +#define CLK_TOP_MMPLL_D3 15 +#define CLK_TOP_MMPLL_D5 16 +#define CLK_TOP_MMPLL_D7 17 +#define CLK_TOP_MMPLL_D4 18 +#define CLK_TOP_MMPLL_D6 19 +#define CLK_TOP_SYSPLL_D2 20 +#define CLK_TOP_SYSPLL_D4 21 +#define CLK_TOP_SYSPLL_D6 22 +#define CLK_TOP_SYSPLL_D8 23 +#define CLK_TOP_SYSPLL_D10 24 +#define CLK_TOP_SYSPLL_D12 25 +#define CLK_TOP_SYSPLL_D16 26 +#define CLK_TOP_SYSPLL_D24 27 +#define CLK_TOP_SYSPLL_D3 28 +#define CLK_TOP_SYSPLL_D2P5 29 +#define CLK_TOP_SYSPLL_D5 30 +#define CLK_TOP_SYSPLL_D3P5 31 +#define CLK_TOP_UNIVPLL1_D2 32 +#define CLK_TOP_UNIVPLL1_D4 33 +#define CLK_TOP_UNIVPLL1_D6 34 +#define CLK_TOP_UNIVPLL1_D8 35 +#define CLK_TOP_UNIVPLL1_D10 36 +#define CLK_TOP_UNIVPLL2_D2 37 +#define CLK_TOP_UNIVPLL2_D4 38 +#define CLK_TOP_UNIVPLL2_D6 39 +#define CLK_TOP_UNIVPLL2_D8 40 +#define CLK_TOP_UNIVPLL_D3 41 +#define CLK_TOP_UNIVPLL_D5 42 +#define CLK_TOP_UNIVPLL_D7 43 +#define CLK_TOP_UNIVPLL_D10 44 +#define CLK_TOP_UNIVPLL_D26 45 +#define CLK_TOP_APLL 46 +#define CLK_TOP_APLL_D4 47 +#define CLK_TOP_APLL_D8 48 +#define CLK_TOP_APLL_D16 49 +#define CLK_TOP_APLL_D24 50 +#define CLK_TOP_LVDSPLL_D2 51 +#define CLK_TOP_LVDSPLL_D4 52 +#define CLK_TOP_LVDSPLL_D8 53 +#define CLK_TOP_LVDSTX_CLKDIG_CT 54 +#define CLK_TOP_VPLL_DPIX 55 +#define CLK_TOP_TVHDMI_H 56 +#define CLK_TOP_HDMITX_CLKDIG_D2 57 +#define CLK_TOP_HDMITX_CLKDIG_D3 58 +#define CLK_TOP_TVHDMI_D2 59 +#define CLK_TOP_TVHDMI_D4 60 +#define CLK_TOP_MEMPLL_MCK_D4 61 +#define CLK_TOP_AXI_SEL 62 +#define CLK_TOP_SMI_SEL 63 +#define CLK_TOP_MFG_SEL 64 +#define CLK_TOP_IRDA_SEL 65 +#define CLK_TOP_CAM_SEL 66 +#define CLK_TOP_AUD_INTBUS_SEL 67 +#define CLK_TOP_JPG_SEL 68 +#define CLK_TOP_DISP_SEL 69 +#define CLK_TOP_MSDC30_1_SEL 70 +#define CLK_TOP_MSDC30_2_SEL 71 +#define CLK_TOP_MSDC30_3_SEL 72 +#define CLK_TOP_MSDC30_4_SEL 73 +#define CLK_TOP_USB20_SEL 74 +#define CLK_TOP_VENC_SEL 75 +#define CLK_TOP_SPI_SEL 76 +#define CLK_TOP_UART_SEL 77 +#define CLK_TOP_MEM_SEL 78 +#define CLK_TOP_CAMTG_SEL 79 +#define CLK_TOP_AUDIO_SEL 80 +#define CLK_TOP_FIX_SEL 81 +#define CLK_TOP_VDEC_SEL 82 +#define CLK_TOP_DDRPHYCFG_SEL 83 +#define CLK_TOP_DPILVDS_SEL 84 +#define CLK_TOP_PMICSPI_SEL 85 +#define CLK_TOP_MSDC30_0_SEL 86 +#define CLK_TOP_SMI_MFG_AS_SEL 87 +#define CLK_TOP_GCPU_SEL 88 +#define CLK_TOP_DPI1_SEL 89 +#define CLK_TOP_CCI_SEL 90 +#define CLK_TOP_APLL_SEL 91 +#define CLK_TOP_HDMIPLL_SEL 92 +#define CLK_TOP_NR_CLK 93 + +/* APMIXED_SYS */ + +#define CLK_APMIXED_ARMPLL1 1 +#define CLK_APMIXED_ARMPLL2 2 +#define CLK_APMIXED_MAINPLL 3 +#define CLK_APMIXED_UNIVPLL 4 +#define CLK_APMIXED_MMPLL 5 +#define CLK_APMIXED_MSDCPLL 6 +#define CLK_APMIXED_TVDPLL 7 +#define CLK_APMIXED_LVDSPLL 8 +#define CLK_APMIXED_AUDPLL 9 +#define CLK_APMIXED_VDECPLL 10 +#define CLK_APMIXED_NR_CLK 11 + +/* INFRA_SYS */ + +#define CLK_INFRA_PMIC_WRAP 1 +#define CLK_INFRA_PMICSPI 2 +#define CLK_INFRA_CCIF1_AP_CTRL 3 +#define CLK_INFRA_CCIF0_AP_CTRL 4 +#define CLK_INFRA_KP 5 +#define CLK_INFRA_CPUM 6 +#define CLK_INFRA_M4U 7 +#define CLK_INFRA_MFGAXI 8 +#define CLK_INFRA_DEVAPC 9 +#define CLK_INFRA_AUDIO 10 +#define CLK_INFRA_MFG_BUS 11 +#define CLK_INFRA_SMI 12 +#define CLK_INFRA_DBGCLK 13 +#define CLK_INFRA_NR_CLK 14 + +/* PERI_SYS */ + +#define CLK_PERI_I2C5 1 +#define CLK_PERI_I2C4 2 +#define CLK_PERI_I2C3 3 +#define CLK_PERI_I2C2 4 +#define CLK_PERI_I2C1 5 +#define CLK_PERI_I2C0 6 +#define CLK_PERI_UART3 7 +#define CLK_PERI_UART2 8 +#define CLK_PERI_UART1 9 +#define CLK_PERI_UART0 10 +#define CLK_PERI_IRDA 11 +#define CLK_PERI_NLI 12 +#define CLK_PERI_MD_HIF 13 +#define CLK_PERI_AP_HIF 14 +#define CLK_PERI_MSDC30_3 15 +#define CLK_PERI_MSDC30_2 16 +#define CLK_PERI_MSDC30_1 17 +#define CLK_PERI_MSDC20_2 18 +#define CLK_PERI_MSDC20_1 19 +#define CLK_PERI_AP_DMA 20 +#define CLK_PERI_USB1 21 +#define CLK_PERI_USB0 22 +#define CLK_PERI_PWM 23 +#define CLK_PERI_PWM7 24 +#define CLK_PERI_PWM6 25 +#define CLK_PERI_PWM5 26 +#define CLK_PERI_PWM4 27 +#define CLK_PERI_PWM3 28 +#define CLK_PERI_PWM2 29 +#define CLK_PERI_PWM1 30 +#define CLK_PERI_THERM 31 +#define CLK_PERI_NFI 32 +#define CLK_PERI_USBSLV 33 +#define CLK_PERI_USB1_MCU 34 +#define CLK_PERI_USB0_MCU 35 +#define CLK_PERI_GCPU 36 +#define CLK_PERI_FHCTL 37 +#define CLK_PERI_SPI1 38 +#define CLK_PERI_AUXADC 39 +#define CLK_PERI_PERI_PWRAP 40 +#define CLK_PERI_I2C6 41 +#define CLK_PERI_UART0_SEL 42 +#define CLK_PERI_UART1_SEL 43 +#define CLK_PERI_UART2_SEL 44 +#define CLK_PERI_UART3_SEL 45 +#define CLK_PERI_NR_CLK 46 + +#endif /* _DT_BINDINGS_CLK_MT8135_H */ diff --git a/include/dt-bindings/clock/mt8173-clk.h b/include/dt-bindings/clock/mt8173-clk.h new file mode 100644 index 000000000000..4ad76ed882ad --- /dev/null +++ b/include/dt-bindings/clock/mt8173-clk.h @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao + * + * 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. + * + * 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 _DT_BINDINGS_CLK_MT8173_H +#define _DT_BINDINGS_CLK_MT8173_H + +/* TOPCKGEN */ + +#define CLK_TOP_CLKPH_MCK_O 1 +#define CLK_TOP_DPI 2 +#define CLK_TOP_USB_SYSPLL_125M 3 +#define CLK_TOP_HDMITX_DIG_CTS 4 +#define CLK_TOP_ARMCA7PLL_754M 5 +#define CLK_TOP_ARMCA7PLL_502M 6 +#define CLK_TOP_MAIN_H546M 7 +#define CLK_TOP_MAIN_H364M 8 +#define CLK_TOP_MAIN_H218P4M 9 +#define CLK_TOP_MAIN_H156M 10 +#define CLK_TOP_TVDPLL_445P5M 11 +#define CLK_TOP_TVDPLL_594M 12 +#define CLK_TOP_UNIV_624M 13 +#define CLK_TOP_UNIV_416M 14 +#define CLK_TOP_UNIV_249P6M 15 +#define CLK_TOP_UNIV_178P3M 16 +#define CLK_TOP_UNIV_48M 17 +#define CLK_TOP_CLKRTC_EXT 18 +#define CLK_TOP_CLKRTC_INT 19 +#define CLK_TOP_FPC 20 +#define CLK_TOP_HDMITXPLL_D2 21 +#define CLK_TOP_HDMITXPLL_D3 22 +#define CLK_TOP_ARMCA7PLL_D2 23 +#define CLK_TOP_ARMCA7PLL_D3 24 +#define CLK_TOP_APLL1 25 +#define CLK_TOP_APLL2 26 +#define CLK_TOP_DMPLL 27 +#define CLK_TOP_DMPLL_D2 28 +#define CLK_TOP_DMPLL_D4 29 +#define CLK_TOP_DMPLL_D8 30 +#define CLK_TOP_DMPLL_D16 31 +#define CLK_TOP_LVDSPLL_D2 32 +#define CLK_TOP_LVDSPLL_D4 33 +#define CLK_TOP_LVDSPLL_D8 34 +#define CLK_TOP_MMPLL 35 +#define CLK_TOP_MMPLL_D2 36 +#define CLK_TOP_MSDCPLL 37 +#define CLK_TOP_MSDCPLL_D2 38 +#define CLK_TOP_MSDCPLL_D4 39 +#define CLK_TOP_MSDCPLL2 40 +#define CLK_TOP_MSDCPLL2_D2 41 +#define CLK_TOP_MSDCPLL2_D4 42 +#define CLK_TOP_SYSPLL_D2 43 +#define CLK_TOP_SYSPLL1_D2 44 +#define CLK_TOP_SYSPLL1_D4 45 +#define CLK_TOP_SYSPLL1_D8 46 +#define CLK_TOP_SYSPLL1_D16 47 +#define CLK_TOP_SYSPLL_D3 48 +#define CLK_TOP_SYSPLL2_D2 49 +#define CLK_TOP_SYSPLL2_D4 50 +#define CLK_TOP_SYSPLL_D5 51 +#define CLK_TOP_SYSPLL3_D2 52 +#define CLK_TOP_SYSPLL3_D4 53 +#define CLK_TOP_SYSPLL_D7 54 +#define CLK_TOP_SYSPLL4_D2 55 +#define CLK_TOP_SYSPLL4_D4 56 +#define CLK_TOP_TVDPLL 57 +#define CLK_TOP_TVDPLL_D2 58 +#define CLK_TOP_TVDPLL_D4 59 +#define CLK_TOP_TVDPLL_D8 60 +#define CLK_TOP_TVDPLL_D16 61 +#define CLK_TOP_UNIVPLL_D2 62 +#define CLK_TOP_UNIVPLL1_D2 63 +#define CLK_TOP_UNIVPLL1_D4 64 +#define CLK_TOP_UNIVPLL1_D8 65 +#define CLK_TOP_UNIVPLL_D3 66 +#define CLK_TOP_UNIVPLL2_D2 67 +#define CLK_TOP_UNIVPLL2_D4 68 +#define CLK_TOP_UNIVPLL2_D8 69 +#define CLK_TOP_UNIVPLL_D5 70 +#define CLK_TOP_UNIVPLL3_D2 71 +#define CLK_TOP_UNIVPLL3_D4 72 +#define CLK_TOP_UNIVPLL3_D8 73 +#define CLK_TOP_UNIVPLL_D7 74 +#define CLK_TOP_UNIVPLL_D26 75 +#define CLK_TOP_UNIVPLL_D52 76 +#define CLK_TOP_VCODECPLL 77 +#define CLK_TOP_VCODECPLL_370P5 78 +#define CLK_TOP_VENCPLL 79 +#define CLK_TOP_VENCPLL_D2 80 +#define CLK_TOP_VENCPLL_D4 81 +#define CLK_TOP_AXI_SEL 82 +#define CLK_TOP_MEM_SEL 83 +#define CLK_TOP_DDRPHYCFG_SEL 84 +#define CLK_TOP_MM_SEL 85 +#define CLK_TOP_PWM_SEL 86 +#define CLK_TOP_VDEC_SEL 87 +#define CLK_TOP_VENC_SEL 88 +#define CLK_TOP_MFG_SEL 89 +#define CLK_TOP_CAMTG_SEL 90 +#define CLK_TOP_UART_SEL 91 +#define CLK_TOP_SPI_SEL 92 +#define CLK_TOP_USB20_SEL 93 +#define CLK_TOP_USB30_SEL 94 +#define CLK_TOP_MSDC50_0_H_SEL 95 +#define CLK_TOP_MSDC50_0_SEL 96 +#define CLK_TOP_MSDC30_1_SEL 97 +#define CLK_TOP_MSDC30_2_SEL 98 +#define CLK_TOP_MSDC30_3_SEL 99 +#define CLK_TOP_AUDIO_SEL 100 +#define CLK_TOP_AUD_INTBUS_SEL 101 +#define CLK_TOP_PMICSPI_SEL 102 +#define CLK_TOP_SCP_SEL 103 +#define CLK_TOP_ATB_SEL 104 +#define CLK_TOP_VENC_LT_SEL 105 +#define CLK_TOP_DPI0_SEL 106 +#define CLK_TOP_IRDA_SEL 107 +#define CLK_TOP_CCI400_SEL 108 +#define CLK_TOP_AUD_1_SEL 109 +#define CLK_TOP_AUD_2_SEL 110 +#define CLK_TOP_MEM_MFG_IN_SEL 111 +#define CLK_TOP_AXI_MFG_IN_SEL 112 +#define CLK_TOP_SCAM_SEL 113 +#define CLK_TOP_SPINFI_IFR_SEL 114 +#define CLK_TOP_HDMI_SEL 115 +#define CLK_TOP_DPILVDS_SEL 116 +#define CLK_TOP_MSDC50_2_H_SEL 117 +#define CLK_TOP_HDCP_SEL 118 +#define CLK_TOP_HDCP_24M_SEL 119 +#define CLK_TOP_RTC_SEL 120 +#define CLK_TOP_APLL1_DIV0 121 +#define CLK_TOP_APLL1_DIV1 122 +#define CLK_TOP_APLL1_DIV2 123 +#define CLK_TOP_APLL1_DIV3 124 +#define CLK_TOP_APLL1_DIV4 125 +#define CLK_TOP_APLL1_DIV5 126 +#define CLK_TOP_APLL2_DIV0 127 +#define CLK_TOP_APLL2_DIV1 128 +#define CLK_TOP_APLL2_DIV2 129 +#define CLK_TOP_APLL2_DIV3 130 +#define CLK_TOP_APLL2_DIV4 131 +#define CLK_TOP_APLL2_DIV5 132 +#define CLK_TOP_I2S0_M_SEL 133 +#define CLK_TOP_I2S1_M_SEL 134 +#define CLK_TOP_I2S2_M_SEL 135 +#define CLK_TOP_I2S3_M_SEL 136 +#define CLK_TOP_I2S3_B_SEL 137 +#define CLK_TOP_NR_CLK 138 + +/* APMIXED_SYS */ + +#define CLK_APMIXED_ARMCA15PLL 1 +#define CLK_APMIXED_ARMCA7PLL 2 +#define CLK_APMIXED_MAINPLL 3 +#define CLK_APMIXED_UNIVPLL 4 +#define CLK_APMIXED_MMPLL 5 +#define CLK_APMIXED_MSDCPLL 6 +#define CLK_APMIXED_VENCPLL 7 +#define CLK_APMIXED_TVDPLL 8 +#define CLK_APMIXED_MPLL 9 +#define CLK_APMIXED_VCODECPLL 10 +#define CLK_APMIXED_APLL1 11 +#define CLK_APMIXED_APLL2 12 +#define CLK_APMIXED_LVDSPLL 13 +#define CLK_APMIXED_MSDCPLL2 14 +#define CLK_APMIXED_NR_CLK 15 + +/* INFRA_SYS */ + +#define CLK_INFRA_DBGCLK 1 +#define CLK_INFRA_SMI 2 +#define CLK_INFRA_AUDIO 3 +#define CLK_INFRA_GCE 4 +#define CLK_INFRA_L2C_SRAM 5 +#define CLK_INFRA_M4U 6 +#define CLK_INFRA_CPUM 7 +#define CLK_INFRA_KP 8 +#define CLK_INFRA_CEC 9 +#define CLK_INFRA_PMICSPI 10 +#define CLK_INFRA_PMICWRAP 11 +#define CLK_INFRA_NR_CLK 12 + +/* PERI_SYS */ + +#define CLK_PERI_NFI 1 +#define CLK_PERI_THERM 2 +#define CLK_PERI_PWM1 3 +#define CLK_PERI_PWM2 4 +#define CLK_PERI_PWM3 5 +#define CLK_PERI_PWM4 6 +#define CLK_PERI_PWM5 7 +#define CLK_PERI_PWM6 8 +#define CLK_PERI_PWM7 9 +#define CLK_PERI_PWM 10 +#define CLK_PERI_USB0 11 +#define CLK_PERI_USB1 12 +#define CLK_PERI_AP_DMA 13 +#define CLK_PERI_MSDC30_0 14 +#define CLK_PERI_MSDC30_1 15 +#define CLK_PERI_MSDC30_2 16 +#define CLK_PERI_MSDC30_3 17 +#define CLK_PERI_NLI_ARB 18 +#define CLK_PERI_IRDA 19 +#define CLK_PERI_UART0 20 +#define CLK_PERI_UART1 21 +#define CLK_PERI_UART2 22 +#define CLK_PERI_UART3 23 +#define CLK_PERI_I2C0 24 +#define CLK_PERI_I2C1 25 +#define CLK_PERI_I2C2 26 +#define CLK_PERI_I2C3 27 +#define CLK_PERI_I2C4 28 +#define CLK_PERI_AUXADC 29 +#define CLK_PERI_SPI0 30 +#define CLK_PERI_I2C5 31 +#define CLK_PERI_NFIECC 32 +#define CLK_PERI_SPI 33 +#define CLK_PERI_IRRX 34 +#define CLK_PERI_I2C6 35 +#define CLK_PERI_UART0_SEL 36 +#define CLK_PERI_UART1_SEL 37 +#define CLK_PERI_UART2_SEL 38 +#define CLK_PERI_UART3_SEL 39 +#define CLK_PERI_NR_CLK 40 + +#endif /* _DT_BINDINGS_CLK_MT8173_H */ diff --git a/include/dt-bindings/reset-controller/mt8135-resets.h b/include/dt-bindings/reset-controller/mt8135-resets.h new file mode 100644 index 000000000000..1fb629508db2 --- /dev/null +++ b/include/dt-bindings/reset-controller/mt8135-resets.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: Flora Fu, MediaTek + * + * 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. + * + * 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 _DT_BINDINGS_RESET_CONTROLLER_MT8135 +#define _DT_BINDINGS_RESET_CONTROLLER_MT8135 + +/* INFRACFG resets */ +#define MT8135_INFRA_EMI_REG_RST 0 +#define MT8135_INFRA_DRAMC0_A0_RST 1 +#define MT8135_INFRA_CCIF0_RST 2 +#define MT8135_INFRA_APCIRQ_EINT_RST 3 +#define MT8135_INFRA_APXGPT_RST 4 +#define MT8135_INFRA_SCPSYS_RST 5 +#define MT8135_INFRA_CCIF1_RST 6 +#define MT8135_INFRA_PMIC_WRAP_RST 7 +#define MT8135_INFRA_KP_RST 8 +#define MT8135_INFRA_EMI_RST 32 +#define MT8135_INFRA_DRAMC0_RST 34 +#define MT8135_INFRA_SMI_RST 35 +#define MT8135_INFRA_M4U_RST 36 + +/* PERICFG resets */ +#define MT8135_PERI_UART0_SW_RST 0 +#define MT8135_PERI_UART1_SW_RST 1 +#define MT8135_PERI_UART2_SW_RST 2 +#define MT8135_PERI_UART3_SW_RST 3 +#define MT8135_PERI_IRDA_SW_RST 4 +#define MT8135_PERI_PTP_SW_RST 5 +#define MT8135_PERI_AP_HIF_SW_RST 6 +#define MT8135_PERI_GPCU_SW_RST 7 +#define MT8135_PERI_MD_HIF_SW_RST 8 +#define MT8135_PERI_NLI_SW_RST 9 +#define MT8135_PERI_AUXADC_SW_RST 10 +#define MT8135_PERI_DMA_SW_RST 11 +#define MT8135_PERI_NFI_SW_RST 14 +#define MT8135_PERI_PWM_SW_RST 15 +#define MT8135_PERI_THERM_SW_RST 16 +#define MT8135_PERI_MSDC0_SW_RST 17 +#define MT8135_PERI_MSDC1_SW_RST 18 +#define MT8135_PERI_MSDC2_SW_RST 19 +#define MT8135_PERI_MSDC3_SW_RST 20 +#define MT8135_PERI_I2C0_SW_RST 22 +#define MT8135_PERI_I2C1_SW_RST 23 +#define MT8135_PERI_I2C2_SW_RST 24 +#define MT8135_PERI_I2C3_SW_RST 25 +#define MT8135_PERI_I2C4_SW_RST 26 +#define MT8135_PERI_I2C5_SW_RST 27 +#define MT8135_PERI_I2C6_SW_RST 28 +#define MT8135_PERI_USB_SW_RST 29 +#define MT8135_PERI_SPI1_SW_RST 33 +#define MT8135_PERI_PWRAP_BRIDGE_SW_RST 34 + +#endif /* _DT_BINDINGS_RESET_CONTROLLER_MT8135 */ diff --git a/include/dt-bindings/reset-controller/mt8173-resets.h b/include/dt-bindings/reset-controller/mt8173-resets.h new file mode 100644 index 000000000000..9464b37cf68c --- /dev/null +++ b/include/dt-bindings/reset-controller/mt8173-resets.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: Flora Fu, MediaTek + * + * 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. + * + * 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 _DT_BINDINGS_RESET_CONTROLLER_MT8173 +#define _DT_BINDINGS_RESET_CONTROLLER_MT8173 + +/* INFRACFG resets */ +#define MT8173_INFRA_EMI_REG_RST 0 +#define MT8173_INFRA_DRAMC0_A0_RST 1 +#define MT8173_INFRA_APCIRQ_EINT_RST 3 +#define MT8173_INFRA_APXGPT_RST 4 +#define MT8173_INFRA_SCPSYS_RST 5 +#define MT8173_INFRA_KP_RST 6 +#define MT8173_INFRA_PMIC_WRAP_RST 7 +#define MT8173_INFRA_MPIP_RST 8 +#define MT8173_INFRA_CEC_RST 9 +#define MT8173_INFRA_EMI_RST 32 +#define MT8173_INFRA_DRAMC0_RST 34 +#define MT8173_INFRA_APMIXEDSYS_RST 35 +#define MT8173_INFRA_MIPI_DSI_RST 36 +#define MT8173_INFRA_TRNG_RST 37 +#define MT8173_INFRA_SYSIRQ_RST 38 +#define MT8173_INFRA_MIPI_CSI_RST 39 +#define MT8173_INFRA_GCE_FAXI_RST 40 +#define MT8173_INFRA_MMIOMMURST 47 + + +/* PERICFG resets */ +#define MT8173_PERI_UART0_SW_RST 0 +#define MT8173_PERI_UART1_SW_RST 1 +#define MT8173_PERI_UART2_SW_RST 2 +#define MT8173_PERI_UART3_SW_RST 3 +#define MT8173_PERI_IRRX_SW_RST 4 +#define MT8173_PERI_PWM_SW_RST 8 +#define MT8173_PERI_AUXADC_SW_RST 10 +#define MT8173_PERI_DMA_SW_RST 11 +#define MT8173_PERI_I2C6_SW_RST 13 +#define MT8173_PERI_NFI_SW_RST 14 +#define MT8173_PERI_THERM_SW_RST 16 +#define MT8173_PERI_MSDC2_SW_RST 17 +#define MT8173_PERI_MSDC3_SW_RST 18 +#define MT8173_PERI_MSDC0_SW_RST 19 +#define MT8173_PERI_MSDC1_SW_RST 20 +#define MT8173_PERI_I2C0_SW_RST 22 +#define MT8173_PERI_I2C1_SW_RST 23 +#define MT8173_PERI_I2C2_SW_RST 24 +#define MT8173_PERI_I2C3_SW_RST 25 +#define MT8173_PERI_I2C4_SW_RST 26 +#define MT8173_PERI_HDMI_SW_RST 29 +#define MT8173_PERI_SPI0_SW_RST 33 + +#endif /* _DT_BINDINGS_RESET_CONTROLLER_MT8173 */ diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index df695313f975..78842f46f152 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -31,6 +31,7 @@ #define CLK_GET_RATE_NOCACHE BIT(6) /* do not use the cached clk rate */ #define CLK_SET_RATE_NO_REPARENT BIT(7) /* don't re-parent on rate change */ #define CLK_GET_ACCURACY_NOCACHE BIT(8) /* do not use the cached clk accuracy */ +#define CLK_RECALC_NEW_RATES BIT(9) /* recalc rates after notifications */ struct clk_hw; struct clk_core; @@ -209,7 +210,7 @@ struct clk_ops { struct clk_init_data { const char *name; const struct clk_ops *ops; - const char **parent_names; + const char * const *parent_names; u8 num_parents; unsigned long flags; }; @@ -426,12 +427,14 @@ extern const struct clk_ops clk_mux_ops; extern const struct clk_ops clk_mux_ro_ops; struct clk *clk_register_mux(struct device *dev, const char *name, - const char **parent_names, u8 num_parents, unsigned long flags, + const char * const *parent_names, u8 num_parents, + unsigned long flags, void __iomem *reg, u8 shift, u8 width, u8 clk_mux_flags, spinlock_t *lock); struct clk *clk_register_mux_table(struct device *dev, const char *name, - const char **parent_names, u8 num_parents, unsigned long flags, + const char * const *parent_names, u8 num_parents, + unsigned long flags, void __iomem *reg, u8 shift, u32 mask, u8 clk_mux_flags, u32 *table, spinlock_t *lock); @@ -457,7 +460,7 @@ struct clk_fixed_factor { unsigned int div; }; -extern struct clk_ops clk_fixed_factor_ops; +extern const struct clk_ops clk_fixed_factor_ops; struct clk *clk_register_fixed_factor(struct device *dev, const char *name, const char *parent_name, unsigned long flags, unsigned int mult, unsigned int div); @@ -518,7 +521,7 @@ struct clk_composite { }; struct clk *clk_register_composite(struct device *dev, const char *name, - const char **parent_names, int num_parents, + const char * const *parent_names, int num_parents, struct clk_hw *mux_hw, const struct clk_ops *mux_ops, struct clk_hw *rate_hw, const struct clk_ops *rate_ops, struct clk_hw *gate_hw, const struct clk_ops *gate_ops, @@ -589,6 +592,7 @@ long __clk_mux_determine_rate_closest(struct clk_hw *hw, unsigned long rate, unsigned long max_rate, unsigned long *best_parent_rate, struct clk_hw **best_parent_p); +void clk_hw_reparent(struct clk_hw *hw, struct clk_hw *new_parent); static inline void __clk_hw_set_clk(struct clk_hw *dst, struct clk_hw *src) { @@ -624,6 +628,8 @@ struct clk *of_clk_src_simple_get(struct of_phandle_args *clkspec, void *data); struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec, void *data); int of_clk_get_parent_count(struct device_node *np); +int of_clk_parent_fill(struct device_node *np, const char **parents, + unsigned int size); const char *of_clk_get_parent_name(struct device_node *np, int index); void of_clk_init(const struct of_device_id *matches);