Merge branch 'clk-next' into v3.19-rc7

This commit is contained in:
Michael Turquette 2015-02-02 14:59:38 -08:00
commit 54eea32f7e
117 changed files with 12333 additions and 5273 deletions

View File

@ -73,6 +73,8 @@ the operations defined in clk.h:
unsigned long *parent_rate);
long (*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_clk);
int (*set_parent)(struct clk_hw *hw, u8 index);

View File

@ -34,6 +34,8 @@ Required Properties for Clock Controller:
- "samsung,exynos7-clock-peris"
- "samsung,exynos7-clock-fsys0"
- "samsung,exynos7-clock-fsys1"
- "samsung,exynos7-clock-mscl"
- "samsung,exynos7-clock-aud"
- reg: physical base address of the controller and the length of
memory mapped region.
@ -53,6 +55,7 @@ Input clocks for top0 clock controller:
- dout_sclk_bus1_pll
- dout_sclk_cc_pll
- dout_sclk_mfc_pll
- dout_sclk_aud_pll
Input clocks for top1 clock controller:
- fin_pll
@ -76,6 +79,14 @@ Input clocks for peric1 clock controller:
- sclk_uart1
- sclk_uart2
- sclk_uart3
- sclk_spi0
- sclk_spi1
- sclk_spi2
- sclk_spi3
- sclk_spi4
- sclk_i2s1
- sclk_pcm1
- sclk_spdif
Input clocks for peris clock controller:
- fin_pll
@ -91,3 +102,7 @@ Input clocks for fsys1 clock controller:
- dout_aclk_fsys1_200
- dout_sclk_mmc0
- dout_sclk_mmc1
Input clocks for aud clock controller:
- fin_pll
- fout_aud_pll

View File

@ -0,0 +1,21 @@
Qualcomm LPASS Clock & Reset Controller Binding
------------------------------------------------
Required properties :
- compatible : shall contain only one of the following:
"qcom,lcc-msm8960"
"qcom,lcc-apq8064"
"qcom,lcc-ipq8064"
- reg : shall contain base register location and length
- #clock-cells : shall contain 1
- #reset-cells : shall contain 1
Example:
clock-controller@28000000 {
compatible = "qcom,lcc-ipq8064";
reg = <0x28000000 0x1000>;
#clock-cells = <1>;
#reset-cells = <1>;
};

View File

@ -1,6 +1,6 @@
* Clock Block on Freescale CoreNet Platforms
* Clock Block on Freescale QorIQ Platforms
Freescale CoreNet chips take primary clocking input from the external
Freescale qoriq chips take primary clocking input from the external
SYSCLK signal. The SYSCLK input (frequency) is multiplied using
multiple phase locked loops (PLL) to create a variety of frequencies
which can then be passed to a variety of internal logic, including
@ -29,6 +29,7 @@ Required properties:
* "fsl,t4240-clockgen"
* "fsl,b4420-clockgen"
* "fsl,b4860-clockgen"
* "fsl,ls1021a-clockgen"
Chassis clock strings include:
* "fsl,qoriq-clockgen-1.0": for chassis 1.0 clocks
* "fsl,qoriq-clockgen-2.0": for chassis 2.0 clocks

View File

@ -11,6 +11,7 @@ Required Properties:
- compatible: Must be one of the following
- "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,r8a7779-mstp-clocks" for R8A7779 (R-Car H1) MSTP gate clocks
- "renesas,r8a7790-mstp-clocks" for R8A7790 (R-Car H2) MSTP gate clocks

View File

@ -0,0 +1,33 @@
* Renesas R8A73A4 Clock Pulse Generator (CPG)
The CPG generates core clocks for the R8A73A4 SoC. It includes five PLLs
and several fixed ratio dividers.
Required Properties:
- compatible: Must be "renesas,r8a73a4-cpg-clocks"
- reg: Base address and length of the memory resource used by the CPG
- clocks: Reference to the parent clocks ("extal1" and "extal2")
- #clock-cells: Must be 1
- clock-output-names: The names of the clocks. Supported clocks are "main",
"pll0", "pll1", "pll2", "pll2s", "pll2h", "z", "z2", "i", "m3", "b",
"m1", "m2", "zx", "zs", and "hp".
Example
-------
cpg_clocks: cpg_clocks@e6150000 {
compatible = "renesas,r8a73a4-cpg-clocks";
reg = <0 0xe6150000 0 0x10000>;
clocks = <&extal1_clk>, <&extal2_clk>;
#clock-cells = <1>;
clock-output-names = "main", "pll0", "pll1", "pll2",
"pll2s", "pll2h", "z", "z2",
"i", "m3", "b", "m1", "m2",
"zx", "zs", "hp";
};

View File

@ -8,15 +8,18 @@ Required Properties:
- compatible: Must be one of
- "renesas,r8a7790-cpg-clocks" for the r8a7790 CPG
- "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
- reg: Base address and length of the memory resource used by the CPG
- clocks: Reference to the parent clock
- clocks: References to the parent clocks: first to the EXTAL clock, second
to the USB_EXTAL clock
- #clock-cells: Must be 1
- clock-output-names: The names of the clocks. Supported clocks are "main",
"pll0", "pll1", "pll3", "lb", "qspi", "sdh", "sd0", "sd1" and "z"
"pll0", "pll1", "pll3", "lb", "qspi", "sdh", "sd0", "sd1", "z", "rcan", and
"adsp"
Example
@ -26,8 +29,9 @@ Example
compatible = "renesas,r8a7790-cpg-clocks",
"renesas,rcar-gen2-cpg-clocks";
reg = <0 0xe6150000 0 0x1000>;
clocks = <&extal_clk>;
clocks = <&extal_clk &usb_extal_clk>;
#clock-cells = <1>;
clock-output-names = "main", "pll0, "pll1", "pll3",
"lb", "qspi", "sdh", "sd0", "sd1", "z";
"lb", "qspi", "sdh", "sd0", "sd1", "z",
"rcan", "adsp";
};

View File

@ -26,7 +26,7 @@ Required properties:
"allwinner,sun5i-a10s-ahb-gates-clk" - for the AHB gates on A10s
"allwinner,sun7i-a20-ahb-gates-clk" - for the AHB gates on A20
"allwinner,sun6i-a31-ar100-clk" - for the AR100 on A31
"allwinner,sun6i-a31-ahb1-mux-clk" - for the AHB1 multiplexer on A31
"allwinner,sun6i-a31-ahb1-clk" - for the AHB1 clock on A31
"allwinner,sun6i-a31-ahb1-gates-clk" - for the AHB1 gates on A31
"allwinner,sun8i-a23-ahb1-gates-clk" - for the AHB1 gates on A23
"allwinner,sun9i-a80-ahb0-gates-clk" - for the AHB0 gates on A80
@ -55,9 +55,11 @@ Required properties:
"allwinner,sun6i-a31-apb2-gates-clk" - for the APB2 gates on A31
"allwinner,sun8i-a23-apb2-gates-clk" - for the APB2 gates on A23
"allwinner,sun5i-a13-mbus-clk" - for the MBUS clock on A13
"allwinner,sun4i-a10-mmc-output-clk" - for the MMC output clock on A10
"allwinner,sun4i-a10-mmc-sample-clk" - for the MMC sample clock on A10
"allwinner,sun4i-a10-mmc-clk" - for the MMC clock
"allwinner,sun9i-a80-mmc-clk" - for mmc module clocks on A80
"allwinner,sun9i-a80-mmc-config-clk" - for mmc gates + resets on A80
"allwinner,sun4i-a10-mod0-clk" - for the module 0 family of clocks
"allwinner,sun9i-a80-mod0-clk" - for module 0 (storage) clocks on A80
"allwinner,sun8i-a23-mbus-clk" - for the MBUS clock on A23
"allwinner,sun7i-a20-out-clk" - for the external output clocks
"allwinner,sun7i-a20-gmac-clk" - for the GMAC clock module on A20/A31
@ -73,7 +75,9 @@ Required properties for all clocks:
- #clock-cells : from common clock binding; shall be set to 0 except for
the following compatibles where it shall be set to 1:
"allwinner,*-gates-clk", "allwinner,sun4i-pll5-clk",
"allwinner,sun4i-pll6-clk", "allwinner,sun6i-a31-pll6-clk"
"allwinner,sun4i-pll6-clk", "allwinner,sun6i-a31-pll6-clk",
"allwinner,*-usb-clk", "allwinner,*-mmc-clk",
"allwinner,*-mmc-config-clk"
- clock-output-names : shall be the corresponding names of the outputs.
If the clock module only has one output, the name shall be the
module name.
@ -81,6 +85,10 @@ Required properties for all clocks:
And "allwinner,*-usb-clk" clocks also require:
- reset-cells : shall be set to 1
The "allwinner,sun9i-a80-mmc-config-clk" clock also requires:
- #reset-cells : shall be set to 1
- resets : shall be the reset control phandle for the mmc block.
For "allwinner,sun7i-a20-gmac-clk", the parent clocks shall be fixed rate
dummy clocks at 25 MHz and 125 MHz, respectively. See example.
@ -95,6 +103,14 @@ For "allwinner,sun6i-a31-pll6-clk", there are 2 outputs. The first output
is the normal PLL6 output, or "pll6". The second output is rate doubled
PLL6, or "pll6x2".
The "allwinner,*-mmc-clk" clocks have three different outputs: the
main clock, with the ID 0, and the output and sample clocks, with the
IDs 1 and 2, respectively.
The "allwinner,sun9i-a80-mmc-config-clk" clock has one clock/reset output
per mmc controller. The number of outputs is determined by the size of
the address block, which is related to the overall mmc block.
For example:
osc24M: clk@01c20050 {
@ -138,11 +154,11 @@ cpu: cpu@01c20054 {
};
mmc0_clk: clk@01c20088 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20088 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc0";
clock-output-names = "mmc0", "mmc0_output", "mmc0_sample";
};
mii_phy_tx_clk: clk@2 {
@ -170,3 +186,16 @@ gmac_clk: clk@01c20164 {
clocks = <&mii_phy_tx_clk>, <&gmac_int_tx_clk>;
clock-output-names = "gmac";
};
mmc_config_clk: clk@01c13000 {
compatible = "allwinner,sun9i-a80-mmc-config-clk";
reg = <0x01c13000 0x10>;
clocks = <&ahb0_gates 8>;
clock-names = "ahb";
resets = <&ahb0_resets 8>;
reset-names = "ahb";
#clock-cells = <1>;
#reset-cells = <1>;
clock-output-names = "mmc0_config", "mmc1_config",
"mmc2_config", "mmc3_config";
};

View File

@ -0,0 +1,42 @@
Bindings for Texas Instruments CDCE706 programmable 3-PLL clock
synthesizer/multiplier/divider.
Reference: http://www.ti.com/lit/ds/symlink/cdce706.pdf
I2C device node required properties:
- compatible: shall be "ti,cdce706".
- reg: i2c device address, shall be in range [0x68...0x6b].
- #clock-cells: from common clock binding; shall be set to 1.
- clocks: from common clock binding; list of parent clock
handles, shall be reference clock(s) connected to CLK_IN0
and CLK_IN1 pins.
- clock-names: shall be clk_in0 and/or clk_in1. Use clk_in0
in case of crystal oscillator or differential signal input
configuration. Use clk_in0 and clk_in1 in case of independent
single-ended LVCMOS inputs configuration.
Example:
clocks {
clk54: clk54 {
#clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <54000000>;
};
};
...
i2c0: i2c-master@0d090000 {
...
cdce706: clock-synth@69 {
compatible = "ti,cdce706";
#clock-cells = <1>;
reg = <0x69>;
clocks = <&clk54>;
clock-names = "clk_in0";
};
};
...
simple-audio-card,codec {
...
clocks = <&cdce706 4>;
};

View File

@ -0,0 +1,33 @@
Binding for Texas Instruments FAPLL clock.
Binding status: Unstable - ABI compatibility may be broken in the future
This binding uses the common clock binding[1]. It assumes a
register-mapped FAPLL with usually two selectable input clocks
(reference clock and bypass clock), and one or more child
syntesizers.
[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
Required properties:
- compatible : shall be "ti,dm816-fapll-clock"
- #clock-cells : from common clock binding; shall be set to 0.
- clocks : link phandles of parent clocks (clk-ref and clk-bypass)
- reg : address and length of the register set for controlling the FAPLL.
Examples:
main_fapll: main_fapll {
#clock-cells = <1>;
compatible = "ti,dm816-fapll-clock";
reg = <0x400 0x40>;
clocks = <&sys_clkin_ck &sys_clkin_ck>;
clock-indices = <1>, <2>, <3>, <4>, <5>,
<6>, <7>;
clock-output-names = "main_pll_clk1",
"main_pll_clk2",
"main_pll_clk3",
"main_pll_clk4",
"main_pll_clk5",
"main_pll_clk6",
"main_pll_clk7";
};

View File

@ -10,8 +10,8 @@ Absolute maximum transfer rate is 200MB/s
Required properties:
- compatible : "allwinner,sun4i-a10-mmc" or "allwinner,sun5i-a13-mmc"
- reg : mmc controller base registers
- clocks : a list with 2 phandle + clock specifier pairs
- clock-names : must contain "ahb" and "mmc"
- clocks : a list with 4 phandle + clock specifier pairs
- clock-names : must contain "ahb", "mmc", "output" and "sample"
- interrupts : mmc controller interrupt
Optional properties:
@ -25,8 +25,8 @@ Examples:
mmc0: mmc@01c0f000 {
compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c0f000 0x1000>;
clocks = <&ahb_gates 8>, <&mmc0_clk>;
clock-names = "ahb", "mod";
clocks = <&ahb_gates 8>, <&mmc0_clk>, <&mmc0_output_clk>, <&mmc0_sample_clk>;
clock-names = "ahb", "mod", "output", "sample";
interrupts = <0 32 4>;
status = "disabled";
};

View File

@ -9566,6 +9566,11 @@ L: linux-omap@vger.kernel.org
S: Maintained
F: drivers/thermal/ti-soc-thermal/
TI CDCE706 CLOCK DRIVER
M: Max Filippov <jcmvbkbc@gmail.com>
S: Maintained
F: drivers/clk/clk-cdce706.c
TI CLOCK DRIVER
M: Tero Kristo <t-kristo@ti.com>
L: linux-omap@vger.kernel.org

View File

@ -226,35 +226,43 @@
};
mmc0_clk: clk@01c20088 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20088 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc0";
clock-output-names = "mmc0",
"mmc0_output",
"mmc0_sample";
};
mmc1_clk: clk@01c2008c {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c2008c 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc1";
clock-output-names = "mmc1",
"mmc1_output",
"mmc1_sample";
};
mmc2_clk: clk@01c20090 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20090 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc2";
clock-output-names = "mmc2",
"mmc2_output",
"mmc2_sample";
};
mmc3_clk: clk@01c20094 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20094 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc3";
clock-output-names = "mmc3",
"mmc3_output",
"mmc3_sample";
};
ts_clk: clk@01c20098 {
@ -398,8 +406,14 @@
mmc0: mmc@01c0f000 {
compatible = "allwinner,sun4i-a10-mmc";
reg = <0x01c0f000 0x1000>;
clocks = <&ahb_gates 8>, <&mmc0_clk>;
clock-names = "ahb", "mmc";
clocks = <&ahb_gates 8>,
<&mmc0_clk 0>,
<&mmc0_clk 1>,
<&mmc0_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
interrupts = <32>;
status = "disabled";
};
@ -407,8 +421,14 @@
mmc1: mmc@01c10000 {
compatible = "allwinner,sun4i-a10-mmc";
reg = <0x01c10000 0x1000>;
clocks = <&ahb_gates 9>, <&mmc1_clk>;
clock-names = "ahb", "mmc";
clocks = <&ahb_gates 9>,
<&mmc1_clk 0>,
<&mmc1_clk 1>,
<&mmc1_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
interrupts = <33>;
status = "disabled";
};
@ -416,8 +436,14 @@
mmc2: mmc@01c11000 {
compatible = "allwinner,sun4i-a10-mmc";
reg = <0x01c11000 0x1000>;
clocks = <&ahb_gates 10>, <&mmc2_clk>;
clock-names = "ahb", "mmc";
clocks = <&ahb_gates 10>,
<&mmc2_clk 0>,
<&mmc2_clk 1>,
<&mmc2_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
interrupts = <34>;
status = "disabled";
};
@ -425,8 +451,14 @@
mmc3: mmc@01c12000 {
compatible = "allwinner,sun4i-a10-mmc";
reg = <0x01c12000 0x1000>;
clocks = <&ahb_gates 11>, <&mmc3_clk>;
clock-names = "ahb", "mmc";
clocks = <&ahb_gates 11>,
<&mmc3_clk 0>,
<&mmc3_clk 1>,
<&mmc3_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
interrupts = <35>;
status = "disabled";
};

View File

@ -207,27 +207,33 @@
};
mmc0_clk: clk@01c20088 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20088 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc0";
clock-output-names = "mmc0",
"mmc0_output",
"mmc0_sample";
};
mmc1_clk: clk@01c2008c {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c2008c 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc1";
clock-output-names = "mmc1",
"mmc1_output",
"mmc1_sample";
};
mmc2_clk: clk@01c20090 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20090 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc2";
clock-output-names = "mmc2",
"mmc2_output",
"mmc2_sample";
};
ts_clk: clk@01c20098 {
@ -355,8 +361,14 @@
mmc0: mmc@01c0f000 {
compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c0f000 0x1000>;
clocks = <&ahb_gates 8>, <&mmc0_clk>;
clock-names = "ahb", "mmc";
clocks = <&ahb_gates 8>,
<&mmc0_clk 0>,
<&mmc0_clk 1>,
<&mmc0_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
interrupts = <32>;
status = "disabled";
};
@ -364,8 +376,14 @@
mmc1: mmc@01c10000 {
compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c10000 0x1000>;
clocks = <&ahb_gates 9>, <&mmc1_clk>;
clock-names = "ahb", "mmc";
clocks = <&ahb_gates 9>,
<&mmc1_clk 0>,
<&mmc1_clk 1>,
<&mmc1_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
interrupts = <33>;
status = "disabled";
};
@ -373,8 +391,14 @@
mmc2: mmc@01c11000 {
compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c11000 0x1000>;
clocks = <&ahb_gates 10>, <&mmc2_clk>;
clock-names = "ahb", "mmc";
clocks = <&ahb_gates 10>,
<&mmc2_clk 0>,
<&mmc2_clk 1>,
<&mmc2_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
interrupts = <34>;
status = "disabled";
};

View File

@ -190,27 +190,33 @@
};
mmc0_clk: clk@01c20088 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20088 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc0";
clock-output-names = "mmc0",
"mmc0_output",
"mmc0_sample";
};
mmc1_clk: clk@01c2008c {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c2008c 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc1";
clock-output-names = "mmc1",
"mmc1_output",
"mmc1_sample";
};
mmc2_clk: clk@01c20090 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20090 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc2";
clock-output-names = "mmc2",
"mmc2_output",
"mmc2_sample";
};
ts_clk: clk@01c20098 {
@ -322,8 +328,14 @@
mmc0: mmc@01c0f000 {
compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c0f000 0x1000>;
clocks = <&ahb_gates 8>, <&mmc0_clk>;
clock-names = "ahb", "mmc";
clocks = <&ahb_gates 8>,
<&mmc0_clk 0>,
<&mmc0_clk 1>,
<&mmc0_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
interrupts = <32>;
status = "disabled";
};
@ -331,8 +343,14 @@
mmc2: mmc@01c11000 {
compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c11000 0x1000>;
clocks = <&ahb_gates 10>, <&mmc2_clk>;
clock-names = "ahb", "mmc";
clocks = <&ahb_gates 10>,
<&mmc2_clk 0>,
<&mmc2_clk 1>,
<&mmc2_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
interrupts = <34>;
status = "disabled";
};

View File

@ -168,19 +168,11 @@
clock-output-names = "axi";
};
ahb1_mux: ahb1_mux@01c20054 {
#clock-cells = <0>;
compatible = "allwinner,sun6i-a31-ahb1-mux-clk";
reg = <0x01c20054 0x4>;
clocks = <&osc32k>, <&osc24M>, <&axi>, <&pll6 0>;
clock-output-names = "ahb1_mux";
};
ahb1: ahb1@01c20054 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-ahb-clk";
compatible = "allwinner,sun6i-a31-ahb1-clk";
reg = <0x01c20054 0x4>;
clocks = <&ahb1_mux>;
clocks = <&osc32k>, <&osc24M>, <&axi>, <&pll6 0>;
clock-output-names = "ahb1";
};
@ -243,35 +235,43 @@
};
mmc0_clk: clk@01c20088 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20088 0x4>;
clocks = <&osc24M>, <&pll6 0>;
clock-output-names = "mmc0";
clock-output-names = "mmc0",
"mmc0_output",
"mmc0_sample";
};
mmc1_clk: clk@01c2008c {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c2008c 0x4>;
clocks = <&osc24M>, <&pll6 0>;
clock-output-names = "mmc1";
clock-output-names = "mmc1",
"mmc1_output",
"mmc1_sample";
};
mmc2_clk: clk@01c20090 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20090 0x4>;
clocks = <&osc24M>, <&pll6 0>;
clock-output-names = "mmc2";
clock-output-names = "mmc2",
"mmc2_output",
"mmc2_sample";
};
mmc3_clk: clk@01c20094 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20094 0x4>;
clocks = <&osc24M>, <&pll6 0>;
clock-output-names = "mmc3";
clock-output-names = "mmc3",
"mmc3_output",
"mmc3_sample";
};
spi0_clk: clk@01c200a0 {
@ -361,15 +361,21 @@
#dma-cells = <1>;
/* DMA controller requires AHB1 clocked from PLL6 */
assigned-clocks = <&ahb1_mux>;
assigned-clocks = <&ahb1>;
assigned-clock-parents = <&pll6 0>;
};
mmc0: mmc@01c0f000 {
compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c0f000 0x1000>;
clocks = <&ahb1_gates 8>, <&mmc0_clk>;
clock-names = "ahb", "mmc";
clocks = <&ahb1_gates 8>,
<&mmc0_clk 0>,
<&mmc0_clk 1>,
<&mmc0_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
resets = <&ahb1_rst 8>;
reset-names = "ahb";
interrupts = <0 60 4>;
@ -379,8 +385,14 @@
mmc1: mmc@01c10000 {
compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c10000 0x1000>;
clocks = <&ahb1_gates 9>, <&mmc1_clk>;
clock-names = "ahb", "mmc";
clocks = <&ahb1_gates 9>,
<&mmc1_clk 0>,
<&mmc1_clk 1>,
<&mmc1_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
resets = <&ahb1_rst 9>;
reset-names = "ahb";
interrupts = <0 61 4>;
@ -390,8 +402,14 @@
mmc2: mmc@01c11000 {
compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c11000 0x1000>;
clocks = <&ahb1_gates 10>, <&mmc2_clk>;
clock-names = "ahb", "mmc";
clocks = <&ahb1_gates 10>,
<&mmc2_clk 0>,
<&mmc2_clk 1>,
<&mmc2_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
resets = <&ahb1_rst 10>;
reset-names = "ahb";
interrupts = <0 62 4>;
@ -401,8 +419,14 @@
mmc3: mmc@01c12000 {
compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c12000 0x1000>;
clocks = <&ahb1_gates 11>, <&mmc3_clk>;
clock-names = "ahb", "mmc";
clocks = <&ahb1_gates 11>,
<&mmc3_clk 0>,
<&mmc3_clk 1>,
<&mmc3_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
resets = <&ahb1_rst 11>;
reset-names = "ahb";
interrupts = <0 63 4>;

View File

@ -266,35 +266,43 @@
};
mmc0_clk: clk@01c20088 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20088 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc0";
clock-output-names = "mmc0",
"mmc0_output",
"mmc0_sample";
};
mmc1_clk: clk@01c2008c {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c2008c 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc1";
clock-output-names = "mmc1",
"mmc1_output",
"mmc1_sample";
};
mmc2_clk: clk@01c20090 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20090 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc2";
clock-output-names = "mmc2",
"mmc2_output",
"mmc2_sample";
};
mmc3_clk: clk@01c20094 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20094 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc3";
clock-output-names = "mmc3",
"mmc3_output",
"mmc3_sample";
};
ts_clk: clk@01c20098 {
@ -510,8 +518,14 @@
mmc0: mmc@01c0f000 {
compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c0f000 0x1000>;
clocks = <&ahb_gates 8>, <&mmc0_clk>;
clock-names = "ahb", "mmc";
clocks = <&ahb_gates 8>,
<&mmc0_clk 0>,
<&mmc0_clk 1>,
<&mmc0_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
interrupts = <0 32 4>;
status = "disabled";
};
@ -519,8 +533,14 @@
mmc1: mmc@01c10000 {
compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c10000 0x1000>;
clocks = <&ahb_gates 9>, <&mmc1_clk>;
clock-names = "ahb", "mmc";
clocks = <&ahb_gates 9>,
<&mmc1_clk 0>,
<&mmc1_clk 1>,
<&mmc1_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
interrupts = <0 33 4>;
status = "disabled";
};
@ -528,8 +548,14 @@
mmc2: mmc@01c11000 {
compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c11000 0x1000>;
clocks = <&ahb_gates 10>, <&mmc2_clk>;
clock-names = "ahb", "mmc";
clocks = <&ahb_gates 10>,
<&mmc2_clk 0>,
<&mmc2_clk 1>,
<&mmc2_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
interrupts = <0 34 4>;
status = "disabled";
};
@ -537,8 +563,14 @@
mmc3: mmc@01c12000 {
compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c12000 0x1000>;
clocks = <&ahb_gates 11>, <&mmc3_clk>;
clock-names = "ahb", "mmc";
clocks = <&ahb_gates 11>,
<&mmc3_clk 0>,
<&mmc3_clk 1>,
<&mmc3_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
interrupts = <0 35 4>;
status = "disabled";
};

View File

@ -101,11 +101,19 @@
};
/* dummy clock until actually implemented */
pll6: pll6_clk {
pll5: pll5_clk {
#clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <600000000>;
clock-output-names = "pll6";
clock-frequency = <0>;
clock-output-names = "pll5";
};
pll6: clk@01c20028 {
#clock-cells = <1>;
compatible = "allwinner,sun6i-a31-pll6-clk";
reg = <0x01c20028 0x4>;
clocks = <&osc24M>;
clock-output-names = "pll6", "pll6x2";
};
cpu: cpu_clk@01c20050 {
@ -131,19 +139,11 @@
clock-output-names = "axi";
};
ahb1_mux: ahb1_mux_clk@01c20054 {
#clock-cells = <0>;
compatible = "allwinner,sun6i-a31-ahb1-mux-clk";
reg = <0x01c20054 0x4>;
clocks = <&osc32k>, <&osc24M>, <&axi>, <&pll6>;
clock-output-names = "ahb1_mux";
};
ahb1: ahb1_clk@01c20054 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-ahb-clk";
compatible = "allwinner,sun6i-a31-ahb1-clk";
reg = <0x01c20054 0x4>;
clocks = <&ahb1_mux>;
clocks = <&osc32k>, <&osc24M>, <&axi>, <&pll6 0>;
clock-output-names = "ahb1";
};
@ -184,7 +184,7 @@
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-apb1-clk";
reg = <0x01c20058 0x4>;
clocks = <&osc32k>, <&osc24M>, <&pll6>, <&pll6>;
clocks = <&osc32k>, <&osc24M>, <&pll6 0>, <&pll6 0>;
clock-output-names = "apb2";
};
@ -200,27 +200,41 @@
};
mmc0_clk: clk@01c20088 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20088 0x4>;
clocks = <&osc24M>, <&pll6>;
clock-output-names = "mmc0";
clocks = <&osc24M>, <&pll6 0>;
clock-output-names = "mmc0",
"mmc0_output",
"mmc0_sample";
};
mmc1_clk: clk@01c2008c {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c2008c 0x4>;
clocks = <&osc24M>, <&pll6>;
clock-output-names = "mmc1";
clocks = <&osc24M>, <&pll6 0>;
clock-output-names = "mmc1",
"mmc1_output",
"mmc1_sample";
};
mmc2_clk: clk@01c20090 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20090 0x4>;
clocks = <&osc24M>, <&pll6>;
clock-output-names = "mmc2";
clocks = <&osc24M>, <&pll6 0>;
clock-output-names = "mmc2",
"mmc2_output",
"mmc2_sample";
};
mbus_clk: clk@01c2015c {
#clock-cells = <0>;
compatible = "allwinner,sun8i-a23-mbus-clk";
reg = <0x01c2015c 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5>;
clock-output-names = "mbus";
};
};
@ -242,8 +256,14 @@
mmc0: mmc@01c0f000 {
compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c0f000 0x1000>;
clocks = <&ahb1_gates 8>, <&mmc0_clk>;
clock-names = "ahb", "mmc";
clocks = <&ahb1_gates 8>,
<&mmc0_clk 0>,
<&mmc0_clk 1>,
<&mmc0_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
resets = <&ahb1_rst 8>;
reset-names = "ahb";
interrupts = <0 60 4>;
@ -253,8 +273,14 @@
mmc1: mmc@01c10000 {
compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c10000 0x1000>;
clocks = <&ahb1_gates 9>, <&mmc1_clk>;
clock-names = "ahb", "mmc";
clocks = <&ahb1_gates 9>,
<&mmc1_clk 0>,
<&mmc1_clk 1>,
<&mmc1_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
resets = <&ahb1_rst 9>;
reset-names = "ahb";
interrupts = <0 61 4>;
@ -264,8 +290,14 @@
mmc2: mmc@01c11000 {
compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c11000 0x1000>;
clocks = <&ahb1_gates 10>, <&mmc2_clk>;
clock-names = "ahb", "mmc";
clocks = <&ahb1_gates 10>,
<&mmc2_clk 0>,
<&mmc2_clk 1>,
<&mmc2_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
resets = <&ahb1_rst 10>;
reset-names = "ahb";
interrupts = <0 62 4>;

View File

@ -187,7 +187,7 @@ obj-$(CONFIG_SOC_OMAP2430) += clock2430.o
obj-$(CONFIG_ARCH_OMAP3) += $(clock-common) clock3xxx.o
obj-$(CONFIG_ARCH_OMAP3) += clock34xx.o clkt34xx_dpll3m2.o
obj-$(CONFIG_ARCH_OMAP3) += clock3517.o clock36xx.o
obj-$(CONFIG_ARCH_OMAP3) += dpll3xxx.o cclock3xxx_data.o
obj-$(CONFIG_ARCH_OMAP3) += dpll3xxx.o
obj-$(CONFIG_ARCH_OMAP3) += clkt_iclk.o
obj-$(CONFIG_ARCH_OMAP4) += $(clock-common)
obj-$(CONFIG_ARCH_OMAP4) += dpll3xxx.o dpll44xx.o

File diff suppressed because it is too large Load Diff

View File

@ -23,7 +23,6 @@
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/bitops.h>
#include <linux/clk-private.h>
#include <asm/cpu.h>
#include <trace/events/power.h>
@ -629,21 +628,6 @@ const struct clk_hw_omap_ops clkhwops_wait = {
.find_companion = omap2_clk_dflt_find_companion,
};
/**
* omap_clocks_register - register an array of omap_clk
* @ocs: pointer to an array of omap_clk to register
*/
void __init omap_clocks_register(struct omap_clk oclks[], int cnt)
{
struct omap_clk *c;
for (c = oclks; c < oclks + cnt; c++) {
clkdev_add(&c->lk);
if (!__clk_init(NULL, c->lk.clk))
omap2_init_clk_hw_omap_clocks(c->lk.clk);
}
}
/**
* omap2_clk_switch_mpurate_at_boot - switch ARM MPU rate by boot-time argument
* @mpurate_ck_name: clk name of the clock to change rate

View File

@ -40,23 +40,29 @@ struct omap_clk {
struct clockdomain;
#define DEFINE_STRUCT_CLK(_name, _parent_array_name, _clkops_name) \
static struct clk _name = { \
static struct clk_core _name##_core = { \
.name = #_name, \
.hw = &_name##_hw.hw, \
.parent_names = _parent_array_name, \
.num_parents = ARRAY_SIZE(_parent_array_name), \
.ops = &_clkops_name, \
}; \
static struct clk _name = { \
.core = &_name##_core, \
};
#define DEFINE_STRUCT_CLK_FLAGS(_name, _parent_array_name, \
_clkops_name, _flags) \
static struct clk _name = { \
static struct clk_core _name##_core = { \
.name = #_name, \
.hw = &_name##_hw.hw, \
.parent_names = _parent_array_name, \
.num_parents = ARRAY_SIZE(_parent_array_name), \
.ops = &_clkops_name, \
.flags = _flags, \
}; \
static struct clk _name = { \
.core = &_name##_core, \
};
#define DEFINE_STRUCT_CLK_HW_OMAP(_name, _clkdm_name) \
@ -239,7 +245,6 @@ struct ti_clk_features {
extern struct ti_clk_features ti_clk_features;
extern const struct clkops clkops_omap2_dflt_wait;
extern const struct clkops clkops_dummy;
extern const struct clkops clkops_omap2_dflt;
extern struct clk_functions omap2_clk_functions;
@ -248,7 +253,6 @@ extern const struct clksel_rate gpt_32k_rates[];
extern const struct clksel_rate gpt_sys_rates[];
extern const struct clksel_rate gfx_l3_rates[];
extern const struct clksel_rate dsp_ick_rates[];
extern struct clk dummy_ck;
extern const struct clk_hw_omap_ops clkhwops_iclk_wait;
extern const struct clk_hw_omap_ops clkhwops_wait;
@ -273,7 +277,5 @@ extern void __iomem *clk_memmaps[];
extern int omap2_clkops_enable_clkdm(struct clk_hw *hw);
extern void omap2_clkops_disable_clkdm(struct clk_hw *hw);
extern void omap_clocks_register(struct omap_clk *oclks, int cnt);
void __init ti_clk_init_features(void);
#endif

View File

@ -16,7 +16,6 @@
* OMAP3xxx clock definition files.
*/
#include <linux/clk-private.h>
#include "clock.h"
/* clksel_rate data common to 24xx/343x */
@ -114,13 +113,3 @@ const struct clksel_rate div31_1to31_rates[] = {
{ .div = 31, .val = 31, .flags = RATE_IN_4430 | RATE_IN_AM33XX },
{ .div = 0 },
};
/* Clocks shared between various OMAP SoCs */
static struct clk_ops dummy_ck_ops = {};
struct clk dummy_ck = {
.name = "dummy_clk",
.ops = &dummy_ck_ops,
.flags = CLK_IS_BASIC,
};

View File

@ -410,7 +410,7 @@ int omap3_noncore_dpll_enable(struct clk_hw *hw)
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
int r;
struct dpll_data *dd;
struct clk *parent;
struct clk_hw *parent;
dd = clk->dpll_data;
if (!dd)
@ -427,13 +427,13 @@ int omap3_noncore_dpll_enable(struct clk_hw *hw)
}
}
parent = __clk_get_parent(hw->clk);
parent = __clk_get_hw(__clk_get_parent(hw->clk));
if (__clk_get_rate(hw->clk) == __clk_get_rate(dd->clk_bypass)) {
WARN_ON(parent != dd->clk_bypass);
WARN_ON(parent != __clk_get_hw(dd->clk_bypass));
r = _omap3_noncore_dpll_bypass(clk);
} else {
WARN_ON(parent != dd->clk_ref);
WARN_ON(parent != __clk_get_hw(dd->clk_ref));
r = _omap3_noncore_dpll_lock(clk);
}
@ -473,6 +473,8 @@ void omap3_noncore_dpll_disable(struct clk_hw *hw)
* in failure.
*/
long omap3_noncore_dpll_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_clk)
{
@ -549,7 +551,8 @@ int omap3_noncore_dpll_set_rate(struct clk_hw *hw, unsigned long rate,
if (!dd)
return -EINVAL;
if (__clk_get_parent(hw->clk) != dd->clk_ref)
if (__clk_get_hw(__clk_get_parent(hw->clk)) !=
__clk_get_hw(dd->clk_ref))
return -EINVAL;
if (dd->last_rounded_rate == 0)

View File

@ -222,6 +222,8 @@ out:
* in failure.
*/
long omap4_dpll_regm4xen_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_clk)
{

View File

@ -461,7 +461,17 @@ void __init omap3_init_early(void)
omap3xxx_clockdomains_init();
omap3xxx_hwmod_init();
omap_hwmod_init_postsetup();
omap_clk_soc_init = omap3xxx_clk_init;
if (!of_have_populated_dt()) {
omap3_prcm_legacy_iomaps_init();
if (soc_is_am35xx())
omap_clk_soc_init = am35xx_clk_legacy_init;
else if (cpu_is_omap3630())
omap_clk_soc_init = omap36xx_clk_legacy_init;
else if (omap_rev() == OMAP3430_REV_ES1_0)
omap_clk_soc_init = omap3430es1_clk_legacy_init;
else
omap_clk_soc_init = omap3430_clk_legacy_init;
}
}
void __init omap3430_init_early(void)
@ -509,8 +519,6 @@ void __init ti81xx_init_early(void)
omap_hwmod_init_postsetup();
if (of_have_populated_dt())
omap_clk_soc_init = ti81xx_dt_clk_init;
else
omap_clk_soc_init = omap3xxx_clk_init;
}
void __init omap3_init_late(void)
@ -731,15 +739,17 @@ int __init omap_clk_init(void)
ti_clk_init_features();
ret = of_prcm_init();
if (ret)
return ret;
if (of_have_populated_dt()) {
ret = of_prcm_init();
if (ret)
return ret;
of_clk_init(NULL);
of_clk_init(NULL);
ti_dt_clk_init_retry_clks();
ti_dt_clk_init_retry_clks();
ti_dt_clockdomains_setup();
ti_dt_clockdomains_setup();
}
ret = omap_clk_soc_init();

View File

@ -20,6 +20,7 @@ extern void __iomem *prm_base;
extern u16 prm_features;
extern void omap2_set_globals_prm(void __iomem *prm);
int of_prcm_init(void);
void omap3_prcm_legacy_iomaps_init(void);
# endif
/*

View File

@ -35,6 +35,8 @@
#include "prm44xx.h"
#include "common.h"
#include "clock.h"
#include "cm.h"
#include "control.h"
/*
* OMAP_PRCM_MAX_NR_PENDING_REG: maximum number of PRM_IRQ*_MPU regs
@ -637,6 +639,15 @@ int __init of_prcm_init(void)
return 0;
}
void __init omap3_prcm_legacy_iomaps_init(void)
{
ti_clk_ll_ops = &omap_clk_ll_ops;
clk_memmaps[TI_CLKM_CM] = cm_base + OMAP3430_IVA2_MOD;
clk_memmaps[TI_CLKM_PRM] = prm_base + OMAP3430_IVA2_MOD;
clk_memmaps[TI_CLKM_SCRM] = omap_ctrl_base_get();
}
static int __init prm_late_init(void)
{
if (prm_ll_data->late_init)

View File

@ -373,6 +373,8 @@ static long alchemy_calc_div(unsigned long rate, unsigned long prate,
}
static long alchemy_clk_fgcs_detr(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_clk,
int scale, int maxdiv)
@ -546,6 +548,8 @@ static unsigned long alchemy_clk_fgv1_recalc(struct clk_hw *hw,
}
static long alchemy_clk_fgv1_detr(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_clk)
{
@ -678,6 +682,8 @@ static unsigned long alchemy_clk_fgv2_recalc(struct clk_hw *hw,
}
static long alchemy_clk_fgv2_detr(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_clk)
{
@ -897,6 +903,8 @@ static int alchemy_clk_csrc_setr(struct clk_hw *hw, unsigned long rate,
}
static long alchemy_clk_csrc_detr(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_clk)
{

View File

@ -54,6 +54,7 @@
#include <linux/irq.h>
#include <linux/delay.h>
#include <linux/irq_work.h>
#include <linux/clk-provider.h>
#include <asm/trace.h>
#include <asm/io.h>
@ -943,6 +944,10 @@ void __init time_init(void)
init_decrementer_clockevent();
tick_setup_hrtimer_broadcast();
#ifdef CONFIG_COMMON_CLK
of_clk_init(NULL);
#endif
}

View File

@ -1168,6 +1168,11 @@ static void mpc5121_clk_provide_backwards_compat(void)
}
}
/*
* The "fixed-clock" nodes (which includes the oscillator node if the board's
* DT provides one) has already been scanned by the of_clk_init() in
* time_init().
*/
int __init mpc5121_clk_init(void)
{
struct device_node *clk_np;
@ -1186,12 +1191,6 @@ int __init mpc5121_clk_init(void)
/* invalidate all not yet registered clock slots */
mpc512x_clk_preset_data();
/*
* have the device tree scanned for "fixed-clock" nodes (which
* includes the oscillator node if the board's DT provides one)
*/
of_clk_init(NULL);
/*
* add a dummy clock for those situations where a clock spec is
* required yet no real clock is involved

View File

@ -101,12 +101,12 @@ config COMMON_CLK_AXI_CLKGEN
Support for the Analog Devices axi-clkgen pcore clock generator for Xilinx
FPGAs. It is commonly used in Analog Devices' reference designs.
config CLK_PPC_CORENET
bool "Clock driver for PowerPC corenet platforms"
depends on PPC_E500MC && OF
config CLK_QORIQ
bool "Clock driver for Freescale QorIQ platforms"
depends on (PPC_E500MC || ARM) && OF
---help---
This adds the clock driver support for Freescale PowerPC corenet
platforms using common clock framework.
This adds the clock driver support for Freescale QorIQ platforms
using common clock framework.
config COMMON_CLK_XGENE
bool "Clock driver for APM XGene SoC"
@ -134,6 +134,14 @@ config COMMON_CLK_PXA
---help---
Sypport for the Marvell PXA SoC.
config COMMON_CLK_CDCE706
tristate "Clock driver for TI CDCE706 clock synthesizer"
depends on I2C
select REGMAP_I2C
select RATIONAL
---help---
This driver supports TI CDCE706 programmable 3-PLL clock synthesizer.
source "drivers/clk/qcom/Kconfig"
endmenu

View File

@ -16,9 +16,11 @@ endif
# hardware specific clock types
# please keep this section sorted lexicographically by file/directory path name
obj-$(CONFIG_MACH_ASM9260) += clk-asm9260.o
obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN) += clk-axi-clkgen.o
obj-$(CONFIG_ARCH_AXXIA) += clk-axm5516.o
obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o
obj-$(CONFIG_COMMON_CLK_CDCE706) += clk-cdce706.o
obj-$(CONFIG_ARCH_CLPS711X) += clk-clps711x.o
obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o
obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o
@ -30,7 +32,7 @@ obj-$(CONFIG_ARCH_MOXART) += clk-moxart.o
obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o
obj-$(CONFIG_ARCH_NSPIRE) += clk-nspire.o
obj-$(CONFIG_COMMON_CLK_PALMAS) += clk-palmas.o
obj-$(CONFIG_CLK_PPC_CORENET) += clk-ppc-corenet.o
obj-$(CONFIG_CLK_QORIQ) += clk-qoriq.o
obj-$(CONFIG_COMMON_CLK_RK808) += clk-rk808.o
obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o
obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o

View File

@ -56,6 +56,8 @@ static unsigned long clk_programmable_recalc_rate(struct clk_hw *hw,
static long clk_programmable_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)
{

View File

@ -1032,6 +1032,8 @@ static long kona_peri_clk_round_rate(struct clk_hw *hw, unsigned long rate,
}
static long kona_peri_clk_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)
{
struct kona_clk *bcm_clk = to_kona_clk(hw);

348
drivers/clk/clk-asm9260.c Normal file
View File

@ -0,0 +1,348 @@
/*
* Copyright (c) 2014 Oleksij Rempel <linux@rempel-privat.de>.
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/clk-provider.h>
#include <linux/spinlock.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <dt-bindings/clock/alphascale,asm9260.h>
#define HW_AHBCLKCTRL0 0x0020
#define HW_AHBCLKCTRL1 0x0030
#define HW_SYSPLLCTRL 0x0100
#define HW_MAINCLKSEL 0x0120
#define HW_MAINCLKUEN 0x0124
#define HW_UARTCLKSEL 0x0128
#define HW_UARTCLKUEN 0x012c
#define HW_I2S0CLKSEL 0x0130
#define HW_I2S0CLKUEN 0x0134
#define HW_I2S1CLKSEL 0x0138
#define HW_I2S1CLKUEN 0x013c
#define HW_WDTCLKSEL 0x0160
#define HW_WDTCLKUEN 0x0164
#define HW_CLKOUTCLKSEL 0x0170
#define HW_CLKOUTCLKUEN 0x0174
#define HW_CPUCLKDIV 0x017c
#define HW_SYSAHBCLKDIV 0x0180
#define HW_I2S0MCLKDIV 0x0190
#define HW_I2S0SCLKDIV 0x0194
#define HW_I2S1MCLKDIV 0x0188
#define HW_I2S1SCLKDIV 0x018c
#define HW_UART0CLKDIV 0x0198
#define HW_UART1CLKDIV 0x019c
#define HW_UART2CLKDIV 0x01a0
#define HW_UART3CLKDIV 0x01a4
#define HW_UART4CLKDIV 0x01a8
#define HW_UART5CLKDIV 0x01ac
#define HW_UART6CLKDIV 0x01b0
#define HW_UART7CLKDIV 0x01b4
#define HW_UART8CLKDIV 0x01b8
#define HW_UART9CLKDIV 0x01bc
#define HW_SPI0CLKDIV 0x01c0
#define HW_SPI1CLKDIV 0x01c4
#define HW_QUADSPICLKDIV 0x01c8
#define HW_SSP0CLKDIV 0x01d0
#define HW_NANDCLKDIV 0x01d4
#define HW_TRACECLKDIV 0x01e0
#define HW_CAMMCLKDIV 0x01e8
#define HW_WDTCLKDIV 0x01ec
#define HW_CLKOUTCLKDIV 0x01f4
#define HW_MACCLKDIV 0x01f8
#define HW_LCDCLKDIV 0x01fc
#define HW_ADCANACLKDIV 0x0200
static struct clk *clks[MAX_CLKS];
static struct clk_onecell_data clk_data;
static DEFINE_SPINLOCK(asm9260_clk_lock);
struct asm9260_div_clk {
unsigned int idx;
const char *name;
const char *parent_name;
u32 reg;
};
struct asm9260_gate_data {
unsigned int idx;
const char *name;
const char *parent_name;
u32 reg;
u8 bit_idx;
unsigned long flags;
};
struct asm9260_mux_clock {
u8 mask;
u32 *table;
const char *name;
const char **parent_names;
u8 num_parents;
unsigned long offset;
unsigned long flags;
};
static void __iomem *base;
static const struct asm9260_div_clk asm9260_div_clks[] __initconst = {
{ CLKID_SYS_CPU, "cpu_div", "main_gate", HW_CPUCLKDIV },
{ CLKID_SYS_AHB, "ahb_div", "cpu_div", HW_SYSAHBCLKDIV },
/* i2s has two deviders: one for only external mclk and internal
* devider for all clks. */
{ CLKID_SYS_I2S0M, "i2s0m_div", "i2s0_mclk", HW_I2S0MCLKDIV },
{ CLKID_SYS_I2S1M, "i2s1m_div", "i2s1_mclk", HW_I2S1MCLKDIV },
{ CLKID_SYS_I2S0S, "i2s0s_div", "i2s0_gate", HW_I2S0SCLKDIV },
{ CLKID_SYS_I2S1S, "i2s1s_div", "i2s0_gate", HW_I2S1SCLKDIV },
{ CLKID_SYS_UART0, "uart0_div", "uart_gate", HW_UART0CLKDIV },
{ CLKID_SYS_UART1, "uart1_div", "uart_gate", HW_UART1CLKDIV },
{ CLKID_SYS_UART2, "uart2_div", "uart_gate", HW_UART2CLKDIV },
{ CLKID_SYS_UART3, "uart3_div", "uart_gate", HW_UART3CLKDIV },
{ CLKID_SYS_UART4, "uart4_div", "uart_gate", HW_UART4CLKDIV },
{ CLKID_SYS_UART5, "uart5_div", "uart_gate", HW_UART5CLKDIV },
{ CLKID_SYS_UART6, "uart6_div", "uart_gate", HW_UART6CLKDIV },
{ CLKID_SYS_UART7, "uart7_div", "uart_gate", HW_UART7CLKDIV },
{ CLKID_SYS_UART8, "uart8_div", "uart_gate", HW_UART8CLKDIV },
{ CLKID_SYS_UART9, "uart9_div", "uart_gate", HW_UART9CLKDIV },
{ CLKID_SYS_SPI0, "spi0_div", "main_gate", HW_SPI0CLKDIV },
{ CLKID_SYS_SPI1, "spi1_div", "main_gate", HW_SPI1CLKDIV },
{ CLKID_SYS_QUADSPI, "quadspi_div", "main_gate", HW_QUADSPICLKDIV },
{ CLKID_SYS_SSP0, "ssp0_div", "main_gate", HW_SSP0CLKDIV },
{ CLKID_SYS_NAND, "nand_div", "main_gate", HW_NANDCLKDIV },
{ CLKID_SYS_TRACE, "trace_div", "main_gate", HW_TRACECLKDIV },
{ CLKID_SYS_CAMM, "camm_div", "main_gate", HW_CAMMCLKDIV },
{ CLKID_SYS_MAC, "mac_div", "main_gate", HW_MACCLKDIV },
{ CLKID_SYS_LCD, "lcd_div", "main_gate", HW_LCDCLKDIV },
{ CLKID_SYS_ADCANA, "adcana_div", "main_gate", HW_ADCANACLKDIV },
{ CLKID_SYS_WDT, "wdt_div", "wdt_gate", HW_WDTCLKDIV },
{ CLKID_SYS_CLKOUT, "clkout_div", "clkout_gate", HW_CLKOUTCLKDIV },
};
static const struct asm9260_gate_data asm9260_mux_gates[] __initconst = {
{ 0, "main_gate", "main_mux", HW_MAINCLKUEN, 0 },
{ 0, "uart_gate", "uart_mux", HW_UARTCLKUEN, 0 },
{ 0, "i2s0_gate", "i2s0_mux", HW_I2S0CLKUEN, 0 },
{ 0, "i2s1_gate", "i2s1_mux", HW_I2S1CLKUEN, 0 },
{ 0, "wdt_gate", "wdt_mux", HW_WDTCLKUEN, 0 },
{ 0, "clkout_gate", "clkout_mux", HW_CLKOUTCLKUEN, 0 },
};
static const struct asm9260_gate_data asm9260_ahb_gates[] __initconst = {
/* ahb gates */
{ CLKID_AHB_ROM, "rom", "ahb_div",
HW_AHBCLKCTRL0, 1, CLK_IGNORE_UNUSED},
{ CLKID_AHB_RAM, "ram", "ahb_div",
HW_AHBCLKCTRL0, 2, CLK_IGNORE_UNUSED},
{ CLKID_AHB_GPIO, "gpio", "ahb_div",
HW_AHBCLKCTRL0, 4 },
{ CLKID_AHB_MAC, "mac", "ahb_div",
HW_AHBCLKCTRL0, 5 },
{ CLKID_AHB_EMI, "emi", "ahb_div",
HW_AHBCLKCTRL0, 6, CLK_IGNORE_UNUSED},
{ CLKID_AHB_USB0, "usb0", "ahb_div",
HW_AHBCLKCTRL0, 7 },
{ CLKID_AHB_USB1, "usb1", "ahb_div",
HW_AHBCLKCTRL0, 8 },
{ CLKID_AHB_DMA0, "dma0", "ahb_div",
HW_AHBCLKCTRL0, 9 },
{ CLKID_AHB_DMA1, "dma1", "ahb_div",
HW_AHBCLKCTRL0, 10 },
{ CLKID_AHB_UART0, "uart0", "ahb_div",
HW_AHBCLKCTRL0, 11 },
{ CLKID_AHB_UART1, "uart1", "ahb_div",
HW_AHBCLKCTRL0, 12 },
{ CLKID_AHB_UART2, "uart2", "ahb_div",
HW_AHBCLKCTRL0, 13 },
{ CLKID_AHB_UART3, "uart3", "ahb_div",
HW_AHBCLKCTRL0, 14 },
{ CLKID_AHB_UART4, "uart4", "ahb_div",
HW_AHBCLKCTRL0, 15 },
{ CLKID_AHB_UART5, "uart5", "ahb_div",
HW_AHBCLKCTRL0, 16 },
{ CLKID_AHB_UART6, "uart6", "ahb_div",
HW_AHBCLKCTRL0, 17 },
{ CLKID_AHB_UART7, "uart7", "ahb_div",
HW_AHBCLKCTRL0, 18 },
{ CLKID_AHB_UART8, "uart8", "ahb_div",
HW_AHBCLKCTRL0, 19 },
{ CLKID_AHB_UART9, "uart9", "ahb_div",
HW_AHBCLKCTRL0, 20 },
{ CLKID_AHB_I2S0, "i2s0", "ahb_div",
HW_AHBCLKCTRL0, 21 },
{ CLKID_AHB_I2C0, "i2c0", "ahb_div",
HW_AHBCLKCTRL0, 22 },
{ CLKID_AHB_I2C1, "i2c1", "ahb_div",
HW_AHBCLKCTRL0, 23 },
{ CLKID_AHB_SSP0, "ssp0", "ahb_div",
HW_AHBCLKCTRL0, 24 },
{ CLKID_AHB_IOCONFIG, "ioconf", "ahb_div",
HW_AHBCLKCTRL0, 25 },
{ CLKID_AHB_WDT, "wdt", "ahb_div",
HW_AHBCLKCTRL0, 26 },
{ CLKID_AHB_CAN0, "can0", "ahb_div",
HW_AHBCLKCTRL0, 27 },
{ CLKID_AHB_CAN1, "can1", "ahb_div",
HW_AHBCLKCTRL0, 28 },
{ CLKID_AHB_MPWM, "mpwm", "ahb_div",
HW_AHBCLKCTRL0, 29 },
{ CLKID_AHB_SPI0, "spi0", "ahb_div",
HW_AHBCLKCTRL0, 30 },
{ CLKID_AHB_SPI1, "spi1", "ahb_div",
HW_AHBCLKCTRL0, 31 },
{ CLKID_AHB_QEI, "qei", "ahb_div",
HW_AHBCLKCTRL1, 0 },
{ CLKID_AHB_QUADSPI0, "quadspi0", "ahb_div",
HW_AHBCLKCTRL1, 1 },
{ CLKID_AHB_CAMIF, "capmif", "ahb_div",
HW_AHBCLKCTRL1, 2 },
{ CLKID_AHB_LCDIF, "lcdif", "ahb_div",
HW_AHBCLKCTRL1, 3 },
{ CLKID_AHB_TIMER0, "timer0", "ahb_div",
HW_AHBCLKCTRL1, 4 },
{ CLKID_AHB_TIMER1, "timer1", "ahb_div",
HW_AHBCLKCTRL1, 5 },
{ CLKID_AHB_TIMER2, "timer2", "ahb_div",
HW_AHBCLKCTRL1, 6 },
{ CLKID_AHB_TIMER3, "timer3", "ahb_div",
HW_AHBCLKCTRL1, 7 },
{ CLKID_AHB_IRQ, "irq", "ahb_div",
HW_AHBCLKCTRL1, 8, CLK_IGNORE_UNUSED},
{ CLKID_AHB_RTC, "rtc", "ahb_div",
HW_AHBCLKCTRL1, 9 },
{ CLKID_AHB_NAND, "nand", "ahb_div",
HW_AHBCLKCTRL1, 10 },
{ CLKID_AHB_ADC0, "adc0", "ahb_div",
HW_AHBCLKCTRL1, 11 },
{ CLKID_AHB_LED, "led", "ahb_div",
HW_AHBCLKCTRL1, 12 },
{ CLKID_AHB_DAC0, "dac0", "ahb_div",
HW_AHBCLKCTRL1, 13 },
{ CLKID_AHB_LCD, "lcd", "ahb_div",
HW_AHBCLKCTRL1, 14 },
{ CLKID_AHB_I2S1, "i2s1", "ahb_div",
HW_AHBCLKCTRL1, 15 },
{ CLKID_AHB_MAC1, "mac1", "ahb_div",
HW_AHBCLKCTRL1, 16 },
};
static const char __initdata *main_mux_p[] = { NULL, NULL };
static const char __initdata *i2s0_mux_p[] = { NULL, NULL, "i2s0m_div"};
static const char __initdata *i2s1_mux_p[] = { NULL, NULL, "i2s1m_div"};
static const char __initdata *clkout_mux_p[] = { NULL, NULL, "rtc"};
static u32 three_mux_table[] = {0, 1, 3};
static struct asm9260_mux_clock asm9260_mux_clks[] __initdata = {
{ 1, three_mux_table, "main_mux", main_mux_p,
ARRAY_SIZE(main_mux_p), HW_MAINCLKSEL, },
{ 1, three_mux_table, "uart_mux", main_mux_p,
ARRAY_SIZE(main_mux_p), HW_UARTCLKSEL, },
{ 1, three_mux_table, "wdt_mux", main_mux_p,
ARRAY_SIZE(main_mux_p), HW_WDTCLKSEL, },
{ 3, three_mux_table, "i2s0_mux", i2s0_mux_p,
ARRAY_SIZE(i2s0_mux_p), HW_I2S0CLKSEL, },
{ 3, three_mux_table, "i2s1_mux", i2s1_mux_p,
ARRAY_SIZE(i2s1_mux_p), HW_I2S1CLKSEL, },
{ 3, three_mux_table, "clkout_mux", clkout_mux_p,
ARRAY_SIZE(clkout_mux_p), HW_CLKOUTCLKSEL, },
};
static void __init asm9260_acc_init(struct device_node *np)
{
struct clk *clk;
const char *ref_clk, *pll_clk = "pll";
u32 rate;
int n;
u32 accuracy = 0;
base = of_io_request_and_map(np, 0, np->name);
if (!base)
panic("%s: unable to map resource", np->name);
/* register pll */
rate = (ioread32(base + HW_SYSPLLCTRL) & 0xffff) * 1000000;
ref_clk = of_clk_get_parent_name(np, 0);
accuracy = clk_get_accuracy(__clk_lookup(ref_clk));
clk = clk_register_fixed_rate_with_accuracy(NULL, pll_clk,
ref_clk, 0, rate, accuracy);
if (IS_ERR(clk))
panic("%s: can't register REFCLK. Check DT!", np->name);
for (n = 0; n < ARRAY_SIZE(asm9260_mux_clks); n++) {
const struct asm9260_mux_clock *mc = &asm9260_mux_clks[n];
mc->parent_names[0] = ref_clk;
mc->parent_names[1] = pll_clk;
clk = clk_register_mux_table(NULL, mc->name, mc->parent_names,
mc->num_parents, mc->flags, base + mc->offset,
0, mc->mask, 0, mc->table, &asm9260_clk_lock);
}
/* clock mux gate cells */
for (n = 0; n < ARRAY_SIZE(asm9260_mux_gates); n++) {
const struct asm9260_gate_data *gd = &asm9260_mux_gates[n];
clk = clk_register_gate(NULL, gd->name,
gd->parent_name, gd->flags | CLK_SET_RATE_PARENT,
base + gd->reg, gd->bit_idx, 0, &asm9260_clk_lock);
}
/* clock div cells */
for (n = 0; n < ARRAY_SIZE(asm9260_div_clks); n++) {
const struct asm9260_div_clk *dc = &asm9260_div_clks[n];
clks[dc->idx] = clk_register_divider(NULL, dc->name,
dc->parent_name, CLK_SET_RATE_PARENT,
base + dc->reg, 0, 8, CLK_DIVIDER_ONE_BASED,
&asm9260_clk_lock);
}
/* clock ahb gate cells */
for (n = 0; n < ARRAY_SIZE(asm9260_ahb_gates); n++) {
const struct asm9260_gate_data *gd = &asm9260_ahb_gates[n];
clks[gd->idx] = clk_register_gate(NULL, gd->name,
gd->parent_name, gd->flags, base + gd->reg,
gd->bit_idx, 0, &asm9260_clk_lock);
}
/* check for errors on leaf clocks */
for (n = 0; n < MAX_CLKS; n++) {
if (!IS_ERR(clks[n]))
continue;
pr_err("%s: Unable to register leaf clock %d\n",
np->full_name, n);
goto fail;
}
/* register clk-provider */
clk_data.clks = clks;
clk_data.clk_num = MAX_CLKS;
of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
return;
fail:
iounmap(base);
}
CLK_OF_DECLARE(asm9260_acc, "alphascale,asm9260-clock-controller",
asm9260_acc_init);

700
drivers/clk/clk-cdce706.c Normal file
View File

@ -0,0 +1,700 @@
/*
* TI CDCE706 programmable 3-PLL clock synthesizer driver
*
* Copyright (c) 2014 Cadence Design Systems Inc.
*
* Reference: http://www.ti.com/lit/ds/symlink/cdce706.pdf
*
* 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 <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/rational.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#define CDCE706_CLKIN_CLOCK 10
#define CDCE706_CLKIN_SOURCE 11
#define CDCE706_PLL_M_LOW(pll) (1 + 3 * (pll))
#define CDCE706_PLL_N_LOW(pll) (2 + 3 * (pll))
#define CDCE706_PLL_HI(pll) (3 + 3 * (pll))
#define CDCE706_PLL_MUX 3
#define CDCE706_PLL_FVCO 6
#define CDCE706_DIVIDER(div) (13 + (div))
#define CDCE706_CLKOUT(out) (19 + (out))
#define CDCE706_CLKIN_CLOCK_MASK 0x10
#define CDCE706_CLKIN_SOURCE_SHIFT 6
#define CDCE706_CLKIN_SOURCE_MASK 0xc0
#define CDCE706_CLKIN_SOURCE_LVCMOS 0x40
#define CDCE706_PLL_MUX_MASK(pll) (0x80 >> (pll))
#define CDCE706_PLL_LOW_M_MASK 0xff
#define CDCE706_PLL_LOW_N_MASK 0xff
#define CDCE706_PLL_HI_M_MASK 0x1
#define CDCE706_PLL_HI_N_MASK 0x1e
#define CDCE706_PLL_HI_N_SHIFT 1
#define CDCE706_PLL_M_MAX 0x1ff
#define CDCE706_PLL_N_MAX 0xfff
#define CDCE706_PLL_FVCO_MASK(pll) (0x80 >> (pll))
#define CDCE706_PLL_FREQ_MIN 80000000
#define CDCE706_PLL_FREQ_MAX 300000000
#define CDCE706_PLL_FREQ_HI 180000000
#define CDCE706_DIVIDER_PLL(div) (9 + (div) - ((div) > 2) - ((div) > 4))
#define CDCE706_DIVIDER_PLL_SHIFT(div) ((div) < 2 ? 5 : 3 * ((div) & 1))
#define CDCE706_DIVIDER_PLL_MASK(div) (0x7 << CDCE706_DIVIDER_PLL_SHIFT(div))
#define CDCE706_DIVIDER_DIVIDER_MASK 0x7f
#define CDCE706_DIVIDER_DIVIDER_MAX 0x7f
#define CDCE706_CLKOUT_DIVIDER_MASK 0x7
#define CDCE706_CLKOUT_ENABLE_MASK 0x8
static struct regmap_config cdce706_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.val_format_endian = REGMAP_ENDIAN_NATIVE,
};
#define to_hw_data(phw) (container_of((phw), struct cdce706_hw_data, hw))
struct cdce706_hw_data {
struct cdce706_dev_data *dev_data;
unsigned idx;
unsigned parent;
struct clk *clk;
struct clk_hw hw;
unsigned div;
unsigned mul;
unsigned mux;
};
struct cdce706_dev_data {
struct i2c_client *client;
struct regmap *regmap;
struct clk_onecell_data onecell;
struct clk *clks[6];
struct clk *clkin_clk[2];
const char *clkin_name[2];
struct cdce706_hw_data clkin[1];
struct cdce706_hw_data pll[3];
struct cdce706_hw_data divider[6];
struct cdce706_hw_data clkout[6];
};
static const char * const cdce706_source_name[] = {
"clk_in0", "clk_in1",
};
static const char *cdce706_clkin_name[] = {
"clk_in",
};
static const char * const cdce706_pll_name[] = {
"pll1", "pll2", "pll3",
};
static const char *cdce706_divider_parent_name[] = {
"clk_in", "pll1", "pll2", "pll2", "pll3",
};
static const char *cdce706_divider_name[] = {
"p0", "p1", "p2", "p3", "p4", "p5",
};
static const char * const cdce706_clkout_name[] = {
"clk_out0", "clk_out1", "clk_out2", "clk_out3", "clk_out4", "clk_out5",
};
static int cdce706_reg_read(struct cdce706_dev_data *dev_data, unsigned reg,
unsigned *val)
{
int rc = regmap_read(dev_data->regmap, reg | 0x80, val);
if (rc < 0)
dev_err(&dev_data->client->dev, "error reading reg %u", reg);
return rc;
}
static int cdce706_reg_write(struct cdce706_dev_data *dev_data, unsigned reg,
unsigned val)
{
int rc = regmap_write(dev_data->regmap, reg | 0x80, val);
if (rc < 0)
dev_err(&dev_data->client->dev, "error writing reg %u", reg);
return rc;
}
static int cdce706_reg_update(struct cdce706_dev_data *dev_data, unsigned reg,
unsigned mask, unsigned val)
{
int rc = regmap_update_bits(dev_data->regmap, reg | 0x80, mask, val);
if (rc < 0)
dev_err(&dev_data->client->dev, "error updating reg %u", reg);
return rc;
}
static int cdce706_clkin_set_parent(struct clk_hw *hw, u8 index)
{
struct cdce706_hw_data *hwd = to_hw_data(hw);
hwd->parent = index;
return 0;
}
static u8 cdce706_clkin_get_parent(struct clk_hw *hw)
{
struct cdce706_hw_data *hwd = to_hw_data(hw);
return hwd->parent;
}
static const struct clk_ops cdce706_clkin_ops = {
.set_parent = cdce706_clkin_set_parent,
.get_parent = cdce706_clkin_get_parent,
};
static unsigned long cdce706_pll_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct cdce706_hw_data *hwd = to_hw_data(hw);
dev_dbg(&hwd->dev_data->client->dev,
"%s, pll: %d, mux: %d, mul: %u, div: %u\n",
__func__, hwd->idx, hwd->mux, hwd->mul, hwd->div);
if (!hwd->mux) {
if (hwd->div && hwd->mul) {
u64 res = (u64)parent_rate * hwd->mul;
do_div(res, hwd->div);
return res;
}
} else {
if (hwd->div)
return parent_rate / hwd->div;
}
return 0;
}
static long cdce706_pll_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
struct cdce706_hw_data *hwd = to_hw_data(hw);
unsigned long mul, div;
u64 res;
dev_dbg(&hwd->dev_data->client->dev,
"%s, rate: %lu, parent_rate: %lu\n",
__func__, rate, *parent_rate);
rational_best_approximation(rate, *parent_rate,
CDCE706_PLL_N_MAX, CDCE706_PLL_M_MAX,
&mul, &div);
hwd->mul = mul;
hwd->div = div;
dev_dbg(&hwd->dev_data->client->dev,
"%s, pll: %d, mul: %lu, div: %lu\n",
__func__, hwd->idx, mul, div);
res = (u64)*parent_rate * hwd->mul;
do_div(res, hwd->div);
return res;
}
static int cdce706_pll_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct cdce706_hw_data *hwd = to_hw_data(hw);
unsigned long mul = hwd->mul, div = hwd->div;
int err;
dev_dbg(&hwd->dev_data->client->dev,
"%s, pll: %d, mul: %lu, div: %lu\n",
__func__, hwd->idx, mul, div);
err = cdce706_reg_update(hwd->dev_data,
CDCE706_PLL_HI(hwd->idx),
CDCE706_PLL_HI_M_MASK | CDCE706_PLL_HI_N_MASK,
((div >> 8) & CDCE706_PLL_HI_M_MASK) |
((mul >> (8 - CDCE706_PLL_HI_N_SHIFT)) &
CDCE706_PLL_HI_N_MASK));
if (err < 0)
return err;
err = cdce706_reg_write(hwd->dev_data,
CDCE706_PLL_M_LOW(hwd->idx),
div & CDCE706_PLL_LOW_M_MASK);
if (err < 0)
return err;
err = cdce706_reg_write(hwd->dev_data,
CDCE706_PLL_N_LOW(hwd->idx),
mul & CDCE706_PLL_LOW_N_MASK);
if (err < 0)
return err;
err = cdce706_reg_update(hwd->dev_data,
CDCE706_PLL_FVCO,
CDCE706_PLL_FVCO_MASK(hwd->idx),
rate > CDCE706_PLL_FREQ_HI ?
CDCE706_PLL_FVCO_MASK(hwd->idx) : 0);
return err;
}
static const struct clk_ops cdce706_pll_ops = {
.recalc_rate = cdce706_pll_recalc_rate,
.round_rate = cdce706_pll_round_rate,
.set_rate = cdce706_pll_set_rate,
};
static int cdce706_divider_set_parent(struct clk_hw *hw, u8 index)
{
struct cdce706_hw_data *hwd = to_hw_data(hw);
if (hwd->parent == index)
return 0;
hwd->parent = index;
return cdce706_reg_update(hwd->dev_data,
CDCE706_DIVIDER_PLL(hwd->idx),
CDCE706_DIVIDER_PLL_MASK(hwd->idx),
index << CDCE706_DIVIDER_PLL_SHIFT(hwd->idx));
}
static u8 cdce706_divider_get_parent(struct clk_hw *hw)
{
struct cdce706_hw_data *hwd = to_hw_data(hw);
return hwd->parent;
}
static unsigned long cdce706_divider_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct cdce706_hw_data *hwd = to_hw_data(hw);
dev_dbg(&hwd->dev_data->client->dev,
"%s, divider: %d, div: %u\n",
__func__, hwd->idx, hwd->div);
if (hwd->div)
return parent_rate / hwd->div;
return 0;
}
static long cdce706_divider_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
struct cdce706_hw_data *hwd = to_hw_data(hw);
struct cdce706_dev_data *cdce = hwd->dev_data;
unsigned long mul, div;
dev_dbg(&hwd->dev_data->client->dev,
"%s, rate: %lu, parent_rate: %lu\n",
__func__, rate, *parent_rate);
rational_best_approximation(rate, *parent_rate,
1, CDCE706_DIVIDER_DIVIDER_MAX,
&mul, &div);
if (!mul)
div = CDCE706_DIVIDER_DIVIDER_MAX;
if (__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT) {
unsigned long best_diff = rate;
unsigned long best_div = 0;
struct clk *gp_clk = cdce->clkin_clk[cdce->clkin[0].parent];
unsigned long gp_rate = gp_clk ? clk_get_rate(gp_clk) : 0;
for (div = CDCE706_PLL_FREQ_MIN / rate; best_diff &&
div <= CDCE706_PLL_FREQ_MAX / rate; ++div) {
unsigned long n, m;
unsigned long diff;
unsigned long div_rate;
u64 div_rate64;
if (rate * div < CDCE706_PLL_FREQ_MIN)
continue;
rational_best_approximation(rate * div, gp_rate,
CDCE706_PLL_N_MAX,
CDCE706_PLL_M_MAX,
&n, &m);
div_rate64 = (u64)gp_rate * n;
do_div(div_rate64, m);
do_div(div_rate64, div);
div_rate = div_rate64;
diff = max(div_rate, rate) - min(div_rate, rate);
if (diff < best_diff) {
best_diff = diff;
best_div = div;
dev_dbg(&hwd->dev_data->client->dev,
"%s, %lu * %lu / %lu / %lu = %lu\n",
__func__, gp_rate, n, m, div, div_rate);
}
}
div = best_div;
dev_dbg(&hwd->dev_data->client->dev,
"%s, altering parent rate: %lu -> %lu\n",
__func__, *parent_rate, rate * div);
*parent_rate = rate * div;
}
hwd->div = div;
dev_dbg(&hwd->dev_data->client->dev,
"%s, divider: %d, div: %lu\n",
__func__, hwd->idx, div);
return *parent_rate / div;
}
static int cdce706_divider_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct cdce706_hw_data *hwd = to_hw_data(hw);
dev_dbg(&hwd->dev_data->client->dev,
"%s, divider: %d, div: %u\n",
__func__, hwd->idx, hwd->div);
return cdce706_reg_update(hwd->dev_data,
CDCE706_DIVIDER(hwd->idx),
CDCE706_DIVIDER_DIVIDER_MASK,
hwd->div);
}
static const struct clk_ops cdce706_divider_ops = {
.set_parent = cdce706_divider_set_parent,
.get_parent = cdce706_divider_get_parent,
.recalc_rate = cdce706_divider_recalc_rate,
.round_rate = cdce706_divider_round_rate,
.set_rate = cdce706_divider_set_rate,
};
static int cdce706_clkout_prepare(struct clk_hw *hw)
{
struct cdce706_hw_data *hwd = to_hw_data(hw);
return cdce706_reg_update(hwd->dev_data, CDCE706_CLKOUT(hwd->idx),
CDCE706_CLKOUT_ENABLE_MASK,
CDCE706_CLKOUT_ENABLE_MASK);
}
static void cdce706_clkout_unprepare(struct clk_hw *hw)
{
struct cdce706_hw_data *hwd = to_hw_data(hw);
cdce706_reg_update(hwd->dev_data, CDCE706_CLKOUT(hwd->idx),
CDCE706_CLKOUT_ENABLE_MASK, 0);
}
static int cdce706_clkout_set_parent(struct clk_hw *hw, u8 index)
{
struct cdce706_hw_data *hwd = to_hw_data(hw);
if (hwd->parent == index)
return 0;
hwd->parent = index;
return cdce706_reg_update(hwd->dev_data,
CDCE706_CLKOUT(hwd->idx),
CDCE706_CLKOUT_ENABLE_MASK, index);
}
static u8 cdce706_clkout_get_parent(struct clk_hw *hw)
{
struct cdce706_hw_data *hwd = to_hw_data(hw);
return hwd->parent;
}
static unsigned long cdce706_clkout_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
return parent_rate;
}
static long cdce706_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
*parent_rate = rate;
return rate;
}
static int cdce706_clkout_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
return 0;
}
static const struct clk_ops cdce706_clkout_ops = {
.prepare = cdce706_clkout_prepare,
.unprepare = cdce706_clkout_unprepare,
.set_parent = cdce706_clkout_set_parent,
.get_parent = cdce706_clkout_get_parent,
.recalc_rate = cdce706_clkout_recalc_rate,
.round_rate = cdce706_clkout_round_rate,
.set_rate = cdce706_clkout_set_rate,
};
static int cdce706_register_hw(struct cdce706_dev_data *cdce,
struct cdce706_hw_data *hw, unsigned num_hw,
const char * const *clk_names,
struct clk_init_data *init)
{
unsigned i;
for (i = 0; i < num_hw; ++i, ++hw) {
init->name = clk_names[i];
hw->dev_data = cdce;
hw->idx = i;
hw->hw.init = init;
hw->clk = devm_clk_register(&cdce->client->dev,
&hw->hw);
if (IS_ERR(hw->clk)) {
dev_err(&cdce->client->dev, "Failed to register %s\n",
clk_names[i]);
return PTR_ERR(hw->clk);
}
}
return 0;
}
static int cdce706_register_clkin(struct cdce706_dev_data *cdce)
{
struct clk_init_data init = {
.ops = &cdce706_clkin_ops,
.parent_names = cdce->clkin_name,
.num_parents = ARRAY_SIZE(cdce->clkin_name),
};
unsigned i;
int ret;
unsigned clock, source;
for (i = 0; i < ARRAY_SIZE(cdce->clkin_name); ++i) {
struct clk *parent = devm_clk_get(&cdce->client->dev,
cdce706_source_name[i]);
if (IS_ERR(parent)) {
cdce->clkin_name[i] = cdce706_source_name[i];
} else {
cdce->clkin_name[i] = __clk_get_name(parent);
cdce->clkin_clk[i] = parent;
}
}
ret = cdce706_reg_read(cdce, CDCE706_CLKIN_SOURCE, &source);
if (ret < 0)
return ret;
if ((source & CDCE706_CLKIN_SOURCE_MASK) ==
CDCE706_CLKIN_SOURCE_LVCMOS) {
ret = cdce706_reg_read(cdce, CDCE706_CLKIN_CLOCK, &clock);
if (ret < 0)
return ret;
cdce->clkin[0].parent = !!(clock & CDCE706_CLKIN_CLOCK_MASK);
}
ret = cdce706_register_hw(cdce, cdce->clkin,
ARRAY_SIZE(cdce->clkin),
cdce706_clkin_name, &init);
return ret;
}
static int cdce706_register_plls(struct cdce706_dev_data *cdce)
{
struct clk_init_data init = {
.ops = &cdce706_pll_ops,
.parent_names = cdce706_clkin_name,
.num_parents = ARRAY_SIZE(cdce706_clkin_name),
};
unsigned i;
int ret;
unsigned mux;
ret = cdce706_reg_read(cdce, CDCE706_PLL_MUX, &mux);
if (ret < 0)
return ret;
for (i = 0; i < ARRAY_SIZE(cdce->pll); ++i) {
unsigned m, n, v;
ret = cdce706_reg_read(cdce, CDCE706_PLL_M_LOW(i), &m);
if (ret < 0)
return ret;
ret = cdce706_reg_read(cdce, CDCE706_PLL_N_LOW(i), &n);
if (ret < 0)
return ret;
ret = cdce706_reg_read(cdce, CDCE706_PLL_HI(i), &v);
if (ret < 0)
return ret;
cdce->pll[i].div = m | ((v & CDCE706_PLL_HI_M_MASK) << 8);
cdce->pll[i].mul = n | ((v & CDCE706_PLL_HI_N_MASK) <<
(8 - CDCE706_PLL_HI_N_SHIFT));
cdce->pll[i].mux = mux & CDCE706_PLL_MUX_MASK(i);
dev_dbg(&cdce->client->dev,
"%s: i: %u, div: %u, mul: %u, mux: %d\n", __func__, i,
cdce->pll[i].div, cdce->pll[i].mul, cdce->pll[i].mux);
}
ret = cdce706_register_hw(cdce, cdce->pll,
ARRAY_SIZE(cdce->pll),
cdce706_pll_name, &init);
return ret;
}
static int cdce706_register_dividers(struct cdce706_dev_data *cdce)
{
struct clk_init_data init = {
.ops = &cdce706_divider_ops,
.parent_names = cdce706_divider_parent_name,
.num_parents = ARRAY_SIZE(cdce706_divider_parent_name),
.flags = CLK_SET_RATE_PARENT,
};
unsigned i;
int ret;
for (i = 0; i < ARRAY_SIZE(cdce->divider); ++i) {
unsigned val;
ret = cdce706_reg_read(cdce, CDCE706_DIVIDER_PLL(i), &val);
if (ret < 0)
return ret;
cdce->divider[i].parent =
(val & CDCE706_DIVIDER_PLL_MASK(i)) >>
CDCE706_DIVIDER_PLL_SHIFT(i);
ret = cdce706_reg_read(cdce, CDCE706_DIVIDER(i), &val);
if (ret < 0)
return ret;
cdce->divider[i].div = val & CDCE706_DIVIDER_DIVIDER_MASK;
dev_dbg(&cdce->client->dev,
"%s: i: %u, parent: %u, div: %u\n", __func__, i,
cdce->divider[i].parent, cdce->divider[i].div);
}
ret = cdce706_register_hw(cdce, cdce->divider,
ARRAY_SIZE(cdce->divider),
cdce706_divider_name, &init);
return ret;
}
static int cdce706_register_clkouts(struct cdce706_dev_data *cdce)
{
struct clk_init_data init = {
.ops = &cdce706_clkout_ops,
.parent_names = cdce706_divider_name,
.num_parents = ARRAY_SIZE(cdce706_divider_name),
.flags = CLK_SET_RATE_PARENT,
};
unsigned i;
int ret;
for (i = 0; i < ARRAY_SIZE(cdce->clkout); ++i) {
unsigned val;
ret = cdce706_reg_read(cdce, CDCE706_CLKOUT(i), &val);
if (ret < 0)
return ret;
cdce->clkout[i].parent = val & CDCE706_CLKOUT_DIVIDER_MASK;
dev_dbg(&cdce->client->dev,
"%s: i: %u, parent: %u\n", __func__, i,
cdce->clkout[i].parent);
}
ret = cdce706_register_hw(cdce, cdce->clkout,
ARRAY_SIZE(cdce->clkout),
cdce706_clkout_name, &init);
for (i = 0; i < ARRAY_SIZE(cdce->clkout); ++i)
cdce->clks[i] = cdce->clkout[i].clk;
return ret;
}
static int cdce706_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
struct cdce706_dev_data *cdce;
int ret;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -EIO;
cdce = devm_kzalloc(&client->dev, sizeof(*cdce), GFP_KERNEL);
if (!cdce)
return -ENOMEM;
cdce->client = client;
cdce->regmap = devm_regmap_init_i2c(client, &cdce706_regmap_config);
if (IS_ERR(cdce->regmap)) {
dev_err(&client->dev, "Failed to initialize regmap\n");
return -EINVAL;
}
i2c_set_clientdata(client, cdce);
ret = cdce706_register_clkin(cdce);
if (ret < 0)
return ret;
ret = cdce706_register_plls(cdce);
if (ret < 0)
return ret;
ret = cdce706_register_dividers(cdce);
if (ret < 0)
return ret;
ret = cdce706_register_clkouts(cdce);
if (ret < 0)
return ret;
cdce->onecell.clks = cdce->clks;
cdce->onecell.clk_num = ARRAY_SIZE(cdce->clks);
ret = of_clk_add_provider(client->dev.of_node, of_clk_src_onecell_get,
&cdce->onecell);
return ret;
}
static int cdce706_remove(struct i2c_client *client)
{
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id cdce706_dt_match[] = {
{ .compatible = "ti,cdce706" },
{ },
};
MODULE_DEVICE_TABLE(of, cdce706_dt_match);
#endif
static const struct i2c_device_id cdce706_id[] = {
{ "cdce706", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, cdce706_id);
static struct i2c_driver cdce706_i2c_driver = {
.driver = {
.name = "cdce706",
.of_match_table = of_match_ptr(cdce706_dt_match),
},
.probe = cdce706_probe,
.remove = cdce706_remove,
.id_table = cdce706_id,
};
module_i2c_driver(cdce706_i2c_driver);
MODULE_AUTHOR("Max Filippov <jcmvbkbc@gmail.com>");
MODULE_DESCRIPTION("TI CDCE 706 clock synthesizer driver");
MODULE_LICENSE("GPL");

View File

@ -56,6 +56,8 @@ static unsigned long clk_composite_recalc_rate(struct clk_hw *hw,
}
static long clk_composite_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_p)
{
@ -73,7 +75,9 @@ static long clk_composite_determine_rate(struct clk_hw *hw, unsigned long rate,
if (rate_hw && rate_ops && rate_ops->determine_rate) {
rate_hw->clk = hw->clk;
return rate_ops->determine_rate(rate_hw, rate, best_parent_rate,
return rate_ops->determine_rate(rate_hw, rate, min_rate,
max_rate,
best_parent_rate,
best_parent_p);
} else if (rate_hw && rate_ops && rate_ops->round_rate &&
mux_hw && mux_ops && mux_ops->set_parent) {
@ -117,7 +121,8 @@ static long clk_composite_determine_rate(struct clk_hw *hw, unsigned long rate,
return best_rate;
} else if (mux_hw && mux_ops && mux_ops->determine_rate) {
mux_hw->clk = hw->clk;
return mux_ops->determine_rate(mux_hw, rate, best_parent_rate,
return mux_ops->determine_rate(mux_hw, rate, min_rate,
max_rate, best_parent_rate,
best_parent_p);
} else {
pr_err("clk: clk_composite_determine_rate function called, but no mux or rate callback set!\n");

View File

@ -30,7 +30,7 @@
#define to_clk_divider(_hw) container_of(_hw, struct clk_divider, hw)
#define div_mask(d) ((1 << ((d)->width)) - 1)
#define div_mask(width) ((1 << (width)) - 1)
static unsigned int _get_table_maxdiv(const struct clk_div_table *table)
{
@ -54,15 +54,16 @@ static unsigned int _get_table_mindiv(const struct clk_div_table *table)
return mindiv;
}
static unsigned int _get_maxdiv(struct clk_divider *divider)
static unsigned int _get_maxdiv(const struct clk_div_table *table, u8 width,
unsigned long flags)
{
if (divider->flags & CLK_DIVIDER_ONE_BASED)
return div_mask(divider);
if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
return 1 << div_mask(divider);
if (divider->table)
return _get_table_maxdiv(divider->table);
return div_mask(divider) + 1;
if (flags & CLK_DIVIDER_ONE_BASED)
return div_mask(width);
if (flags & CLK_DIVIDER_POWER_OF_TWO)
return 1 << div_mask(width);
if (table)
return _get_table_maxdiv(table);
return div_mask(width) + 1;
}
static unsigned int _get_table_div(const struct clk_div_table *table,
@ -76,14 +77,15 @@ static unsigned int _get_table_div(const struct clk_div_table *table,
return 0;
}
static unsigned int _get_div(struct clk_divider *divider, unsigned int val)
static unsigned int _get_div(const struct clk_div_table *table,
unsigned int val, unsigned long flags)
{
if (divider->flags & CLK_DIVIDER_ONE_BASED)
if (flags & CLK_DIVIDER_ONE_BASED)
return val;
if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
if (flags & CLK_DIVIDER_POWER_OF_TWO)
return 1 << val;
if (divider->table)
return _get_table_div(divider->table, val);
if (table)
return _get_table_div(table, val);
return val + 1;
}
@ -98,29 +100,28 @@ static unsigned int _get_table_val(const struct clk_div_table *table,
return 0;
}
static unsigned int _get_val(struct clk_divider *divider, unsigned int div)
static unsigned int _get_val(const struct clk_div_table *table,
unsigned int div, unsigned long flags)
{
if (divider->flags & CLK_DIVIDER_ONE_BASED)
if (flags & CLK_DIVIDER_ONE_BASED)
return div;
if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
if (flags & CLK_DIVIDER_POWER_OF_TWO)
return __ffs(div);
if (divider->table)
return _get_table_val(divider->table, div);
if (table)
return _get_table_val(table, div);
return div - 1;
}
static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
unsigned long divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate,
unsigned int val,
const struct clk_div_table *table,
unsigned long flags)
{
struct clk_divider *divider = to_clk_divider(hw);
unsigned int div, val;
unsigned int div;
val = clk_readl(divider->reg) >> divider->shift;
val &= div_mask(divider);
div = _get_div(divider, val);
div = _get_div(table, val, flags);
if (!div) {
WARN(!(divider->flags & CLK_DIVIDER_ALLOW_ZERO),
WARN(!(flags & CLK_DIVIDER_ALLOW_ZERO),
"%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n",
__clk_get_name(hw->clk));
return parent_rate;
@ -128,6 +129,20 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
return DIV_ROUND_UP(parent_rate, div);
}
EXPORT_SYMBOL_GPL(divider_recalc_rate);
static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_divider *divider = to_clk_divider(hw);
unsigned int val;
val = clk_readl(divider->reg) >> divider->shift;
val &= div_mask(divider->width);
return divider_recalc_rate(hw, parent_rate, val, divider->table,
divider->flags);
}
/*
* The reverse of DIV_ROUND_UP: The maximum number which
@ -146,12 +161,13 @@ static bool _is_valid_table_div(const struct clk_div_table *table,
return false;
}
static bool _is_valid_div(struct clk_divider *divider, unsigned int div)
static bool _is_valid_div(const struct clk_div_table *table, unsigned int div,
unsigned long flags)
{
if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
if (flags & CLK_DIVIDER_POWER_OF_TWO)
return is_power_of_2(div);
if (divider->table)
return _is_valid_table_div(divider->table, div);
if (table)
return _is_valid_table_div(table, div);
return true;
}
@ -191,71 +207,76 @@ static int _round_down_table(const struct clk_div_table *table, int div)
return down;
}
static int _div_round_up(struct clk_divider *divider,
unsigned long parent_rate, unsigned long rate)
static int _div_round_up(const struct clk_div_table *table,
unsigned long parent_rate, unsigned long rate,
unsigned long flags)
{
int div = DIV_ROUND_UP(parent_rate, rate);
if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
if (flags & CLK_DIVIDER_POWER_OF_TWO)
div = __roundup_pow_of_two(div);
if (divider->table)
div = _round_up_table(divider->table, div);
if (table)
div = _round_up_table(table, div);
return div;
}
static int _div_round_closest(struct clk_divider *divider,
unsigned long parent_rate, unsigned long rate)
static int _div_round_closest(const struct clk_div_table *table,
unsigned long parent_rate, unsigned long rate,
unsigned long flags)
{
int up, down, div;
up = down = div = DIV_ROUND_CLOSEST(parent_rate, rate);
if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) {
if (flags & CLK_DIVIDER_POWER_OF_TWO) {
up = __roundup_pow_of_two(div);
down = __rounddown_pow_of_two(div);
} else if (divider->table) {
up = _round_up_table(divider->table, div);
down = _round_down_table(divider->table, div);
} else if (table) {
up = _round_up_table(table, div);
down = _round_down_table(table, div);
}
return (up - div) <= (div - down) ? up : down;
}
static int _div_round(struct clk_divider *divider, unsigned long parent_rate,
unsigned long rate)
static int _div_round(const struct clk_div_table *table,
unsigned long parent_rate, unsigned long rate,
unsigned long flags)
{
if (divider->flags & CLK_DIVIDER_ROUND_CLOSEST)
return _div_round_closest(divider, parent_rate, rate);
if (flags & CLK_DIVIDER_ROUND_CLOSEST)
return _div_round_closest(table, parent_rate, rate, flags);
return _div_round_up(divider, parent_rate, rate);
return _div_round_up(table, parent_rate, rate, flags);
}
static bool _is_best_div(struct clk_divider *divider,
unsigned long rate, unsigned long now, unsigned long best)
static bool _is_best_div(unsigned long rate, unsigned long now,
unsigned long best, unsigned long flags)
{
if (divider->flags & CLK_DIVIDER_ROUND_CLOSEST)
if (flags & CLK_DIVIDER_ROUND_CLOSEST)
return abs(rate - now) < abs(rate - best);
return now <= rate && now > best;
}
static int _next_div(struct clk_divider *divider, int div)
static int _next_div(const struct clk_div_table *table, int div,
unsigned long flags)
{
div++;
if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
if (flags & CLK_DIVIDER_POWER_OF_TWO)
return __roundup_pow_of_two(div);
if (divider->table)
return _round_up_table(divider->table, div);
if (table)
return _round_up_table(table, div);
return div;
}
static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
unsigned long *best_parent_rate)
unsigned long *best_parent_rate,
const struct clk_div_table *table, u8 width,
unsigned long flags)
{
struct clk_divider *divider = to_clk_divider(hw);
int i, bestdiv = 0;
unsigned long parent_rate, best = 0, now, maxdiv;
unsigned long parent_rate_saved = *best_parent_rate;
@ -263,19 +284,11 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
if (!rate)
rate = 1;
/* if read only, just return current value */
if (divider->flags & CLK_DIVIDER_READ_ONLY) {
bestdiv = readl(divider->reg) >> divider->shift;
bestdiv &= div_mask(divider);
bestdiv = _get_div(divider, bestdiv);
return bestdiv;
}
maxdiv = _get_maxdiv(divider);
maxdiv = _get_maxdiv(table, width, flags);
if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) {
parent_rate = *best_parent_rate;
bestdiv = _div_round(divider, parent_rate, rate);
bestdiv = _div_round(table, parent_rate, rate, flags);
bestdiv = bestdiv == 0 ? 1 : bestdiv;
bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv;
return bestdiv;
@ -287,8 +300,8 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
*/
maxdiv = min(ULONG_MAX / rate, maxdiv);
for (i = 1; i <= maxdiv; i = _next_div(divider, i)) {
if (!_is_valid_div(divider, i))
for (i = 1; i <= maxdiv; i = _next_div(table, i, flags)) {
if (!_is_valid_div(table, i, flags))
continue;
if (rate * i == parent_rate_saved) {
/*
@ -302,7 +315,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
parent_rate = __clk_round_rate(__clk_get_parent(hw->clk),
MULT_ROUND_UP(rate, i));
now = DIV_ROUND_UP(parent_rate, i);
if (_is_best_div(divider, rate, now, best)) {
if (_is_best_div(rate, now, best, flags)) {
bestdiv = i;
best = now;
*best_parent_rate = parent_rate;
@ -310,48 +323,79 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
}
if (!bestdiv) {
bestdiv = _get_maxdiv(divider);
bestdiv = _get_maxdiv(table, width, flags);
*best_parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), 1);
}
return bestdiv;
}
static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
long divider_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate, const struct clk_div_table *table,
u8 width, unsigned long flags)
{
int div;
div = clk_divider_bestdiv(hw, rate, prate);
div = clk_divider_bestdiv(hw, rate, prate, table, width, flags);
return DIV_ROUND_UP(*prate, div);
}
EXPORT_SYMBOL_GPL(divider_round_rate);
static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
{
struct clk_divider *divider = to_clk_divider(hw);
int bestdiv;
/* if read only, just return current value */
if (divider->flags & CLK_DIVIDER_READ_ONLY) {
bestdiv = readl(divider->reg) >> divider->shift;
bestdiv &= div_mask(divider->width);
bestdiv = _get_div(divider->table, bestdiv, divider->flags);
return bestdiv;
}
return divider_round_rate(hw, rate, prate, divider->table,
divider->width, divider->flags);
}
int divider_get_val(unsigned long rate, unsigned long parent_rate,
const struct clk_div_table *table, u8 width,
unsigned long flags)
{
unsigned int div, value;
div = DIV_ROUND_UP(parent_rate, rate);
if (!_is_valid_div(table, div, flags))
return -EINVAL;
value = _get_val(table, div, flags);
return min_t(unsigned int, value, div_mask(width));
}
EXPORT_SYMBOL_GPL(divider_get_val);
static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_divider *divider = to_clk_divider(hw);
unsigned int div, value;
unsigned int value;
unsigned long flags = 0;
u32 val;
div = DIV_ROUND_UP(parent_rate, rate);
if (!_is_valid_div(divider, div))
return -EINVAL;
value = _get_val(divider, div);
if (value > div_mask(divider))
value = div_mask(divider);
value = divider_get_val(rate, parent_rate, divider->table,
divider->width, divider->flags);
if (divider->lock)
spin_lock_irqsave(divider->lock, flags);
if (divider->flags & CLK_DIVIDER_HIWORD_MASK) {
val = div_mask(divider) << (divider->shift + 16);
val = div_mask(divider->width) << (divider->shift + 16);
} else {
val = clk_readl(divider->reg);
val &= ~(div_mask(divider) << divider->shift);
val &= ~(div_mask(divider->width) << divider->shift);
}
val |= value << divider->shift;
clk_writel(val, divider->reg);
@ -463,3 +507,19 @@ struct clk *clk_register_divider_table(struct device *dev, const char *name,
width, clk_divider_flags, table, lock);
}
EXPORT_SYMBOL_GPL(clk_register_divider_table);
void clk_unregister_divider(struct clk *clk)
{
struct clk_divider *div;
struct clk_hw *hw;
hw = __clk_get_hw(clk);
if (!hw)
return;
div = to_clk_divider(hw);
clk_unregister(clk);
kfree(div);
}
EXPORT_SYMBOL_GPL(clk_unregister_divider);

View File

@ -128,7 +128,7 @@ struct clk *clk_register_gate(struct device *dev, const char *name,
struct clk_init_data init;
if (clk_gate_flags & CLK_GATE_HIWORD_MASK) {
if (bit_idx > 16) {
if (bit_idx > 15) {
pr_err("gate bit exceeds LOWORD field\n");
return ERR_PTR(-EINVAL);
}
@ -162,3 +162,19 @@ struct clk *clk_register_gate(struct device *dev, const char *name,
return clk;
}
EXPORT_SYMBOL_GPL(clk_register_gate);
void clk_unregister_gate(struct clk *clk)
{
struct clk_gate *gate;
struct clk_hw *hw;
hw = __clk_get_hw(clk);
if (!hw)
return;
gate = to_clk_gate(hw);
clk_unregister(clk);
kfree(gate);
}
EXPORT_SYMBOL_GPL(clk_unregister_gate);

View File

@ -177,3 +177,19 @@ struct clk *clk_register_mux(struct device *dev, const char *name,
NULL, lock);
}
EXPORT_SYMBOL_GPL(clk_register_mux);
void clk_unregister_mux(struct clk *clk)
{
struct clk_mux *mux;
struct clk_hw *hw;
hw = __clk_get_hw(clk);
if (!hw)
return;
mux = to_clk_mux(hw);
clk_unregister(clk);
kfree(mux);
}
EXPORT_SYMBOL_GPL(clk_unregister_mux);

View File

@ -5,8 +5,11 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* clock driver for Freescale PowerPC corenet SoCs.
* clock driver for Freescale QorIQ SoCs.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/kernel.h>
@ -19,6 +22,7 @@
struct cmux_clk {
struct clk_hw hw;
void __iomem *reg;
unsigned int clk_per_pll;
u32 flags;
};
@ -27,14 +31,12 @@ struct cmux_clk {
#define CLKSEL_ADJUST BIT(0)
#define to_cmux_clk(p) container_of(p, struct cmux_clk, hw)
static unsigned int clocks_per_pll;
static int cmux_set_parent(struct clk_hw *hw, u8 idx)
{
struct cmux_clk *clk = to_cmux_clk(hw);
u32 clksel;
clksel = ((idx / clocks_per_pll) << 2) + idx % clocks_per_pll;
clksel = ((idx / clk->clk_per_pll) << 2) + idx % clk->clk_per_pll;
if (clk->flags & CLKSEL_ADJUST)
clksel += 8;
clksel = (clksel & 0xf) << CLKSEL_SHIFT;
@ -52,12 +54,12 @@ static u8 cmux_get_parent(struct clk_hw *hw)
clksel = (clksel >> CLKSEL_SHIFT) & 0xf;
if (clk->flags & CLKSEL_ADJUST)
clksel -= 8;
clksel = (clksel >> 2) * clocks_per_pll + clksel % 4;
clksel = (clksel >> 2) * clk->clk_per_pll + clksel % 4;
return clksel;
}
const struct clk_ops cmux_ops = {
static const struct clk_ops cmux_ops = {
.get_parent = cmux_get_parent,
.set_parent = cmux_set_parent,
};
@ -72,6 +74,7 @@ static void __init core_mux_init(struct device_node *np)
u32 offset;
const char *clk_name;
const char **parent_names;
struct of_phandle_args clkspec;
rc = of_property_read_u32(np, "reg", &offset);
if (rc) {
@ -85,32 +88,40 @@ static void __init core_mux_init(struct device_node *np)
pr_err("%s: get clock count error\n", np->name);
return;
}
parent_names = kzalloc((sizeof(char *) * count), GFP_KERNEL);
if (!parent_names) {
pr_err("%s: could not allocate parent_names\n", __func__);
parent_names = kcalloc(count, sizeof(char *), GFP_KERNEL);
if (!parent_names)
return;
}
for (i = 0; i < count; i++)
parent_names[i] = of_clk_get_parent_name(np, i);
cmux_clk = kzalloc(sizeof(struct cmux_clk), GFP_KERNEL);
if (!cmux_clk) {
pr_err("%s: could not allocate cmux_clk\n", __func__);
cmux_clk = kzalloc(sizeof(*cmux_clk), GFP_KERNEL);
if (!cmux_clk)
goto err_name;
}
cmux_clk->reg = of_iomap(np, 0);
if (!cmux_clk->reg) {
pr_err("%s: could not map register\n", __func__);
goto err_clk;
}
rc = of_parse_phandle_with_args(np, "clocks", "#clock-cells", 0,
&clkspec);
if (rc) {
pr_err("%s: parse clock node error\n", __func__);
goto err_clk;
}
cmux_clk->clk_per_pll = of_property_count_strings(clkspec.np,
"clock-output-names");
of_node_put(clkspec.np);
node = of_find_compatible_node(NULL, NULL, "fsl,p4080-clockgen");
if (node && (offset >= 0x80))
cmux_clk->flags = CLKSEL_ADJUST;
rc = of_property_read_string_index(np, "clock-output-names",
0, &clk_name);
0, &clk_name);
if (rc) {
pr_err("%s: read clock names error\n", np->name);
goto err_clk;
@ -132,7 +143,7 @@ static void __init core_mux_init(struct device_node *np)
rc = of_clk_add_provider(np, of_clk_src_simple_get, clk);
if (rc) {
pr_err("Could not register clock provider for node:%s\n",
np->name);
np->name);
goto err_clk;
}
goto err_name;
@ -155,7 +166,7 @@ static void __init core_pll_init(struct device_node *np)
base = of_iomap(np, 0);
if (!base) {
pr_err("clk-ppc: iomap error\n");
pr_err("iomap error\n");
return;
}
@ -181,24 +192,17 @@ static void __init core_pll_init(struct device_node *np)
goto err_map;
}
/* output clock number per PLL */
clocks_per_pll = count;
subclks = kzalloc(sizeof(struct clk *) * count, GFP_KERNEL);
if (!subclks) {
pr_err("%s: could not allocate subclks\n", __func__);
subclks = kcalloc(count, sizeof(struct clk *), GFP_KERNEL);
if (!subclks)
goto err_map;
}
onecell_data = kzalloc(sizeof(struct clk_onecell_data), GFP_KERNEL);
if (!onecell_data) {
pr_err("%s: could not allocate onecell_data\n", __func__);
onecell_data = kmalloc(sizeof(*onecell_data), GFP_KERNEL);
if (!onecell_data)
goto err_clks;
}
for (i = 0; i < count; i++) {
rc = of_property_read_string_index(np, "clock-output-names",
i, &clk_name);
i, &clk_name);
if (rc) {
pr_err("%s: could not get clock names\n", np->name);
goto err_cell;
@ -230,7 +234,7 @@ static void __init core_pll_init(struct device_node *np)
rc = of_clk_add_provider(np, of_clk_src_onecell_get, onecell_data);
if (rc) {
pr_err("Could not register clk provider for node:%s\n",
np->name);
np->name);
goto err_cell;
}
@ -252,7 +256,7 @@ static void __init sysclk_init(struct device_node *node)
u32 rate;
if (!np) {
pr_err("ppc-clk: could not get parent node\n");
pr_err("could not get parent node\n");
return;
}
@ -267,40 +271,9 @@ static void __init sysclk_init(struct device_node *node)
if (!IS_ERR(clk))
of_clk_add_provider(np, of_clk_src_simple_get, clk);
}
static const struct of_device_id clk_match[] __initconst = {
{ .compatible = "fsl,qoriq-sysclk-1.0", .data = sysclk_init, },
{ .compatible = "fsl,qoriq-sysclk-2.0", .data = sysclk_init, },
{ .compatible = "fsl,qoriq-core-pll-1.0", .data = core_pll_init, },
{ .compatible = "fsl,qoriq-core-pll-2.0", .data = core_pll_init, },
{ .compatible = "fsl,qoriq-core-mux-1.0", .data = core_mux_init, },
{ .compatible = "fsl,qoriq-core-mux-2.0", .data = core_mux_init, },
{}
};
static int __init ppc_corenet_clk_probe(struct platform_device *pdev)
{
of_clk_init(clk_match);
return 0;
}
static const struct of_device_id ppc_clk_ids[] __initconst = {
{ .compatible = "fsl,qoriq-clockgen-1.0", },
{ .compatible = "fsl,qoriq-clockgen-2.0", },
{}
};
static struct platform_driver ppc_corenet_clk_driver = {
.driver = {
.name = "ppc_corenet_clock",
.of_match_table = ppc_clk_ids,
},
.probe = ppc_corenet_clk_probe,
};
static int __init ppc_corenet_clk_init(void)
{
return platform_driver_register(&ppc_corenet_clk_driver);
}
subsys_initcall(ppc_corenet_clk_init);
CLK_OF_DECLARE(qoriq_sysclk_1, "fsl,qoriq-sysclk-1.0", sysclk_init);
CLK_OF_DECLARE(qoriq_sysclk_2, "fsl,qoriq-sysclk-2.0", sysclk_init);
CLK_OF_DECLARE(qoriq_core_pll_1, "fsl,qoriq-core-pll-1.0", core_pll_init);
CLK_OF_DECLARE(qoriq_core_pll_2, "fsl,qoriq-core-pll-2.0", core_pll_init);
CLK_OF_DECLARE(qoriq_core_mux_1, "fsl,qoriq-core-mux-1.0", core_mux_init);
CLK_OF_DECLARE(qoriq_core_mux_2, "fsl,qoriq-core-mux-2.0", core_mux_init);

File diff suppressed because it is too large Load Diff

View File

@ -9,9 +9,14 @@
* published by the Free Software Foundation.
*/
struct clk_hw;
#if defined(CONFIG_OF) && defined(CONFIG_COMMON_CLK)
struct clk *of_clk_get_by_clkspec(struct of_phandle_args *clkspec);
struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec);
void of_clk_lock(void);
void of_clk_unlock(void);
#endif
struct clk *__clk_create_clk(struct clk_hw *hw, const char *dev_id,
const char *con_id);

View File

@ -19,6 +19,7 @@
#include <linux/mutex.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
#include <linux/of.h>
#include "clk.h"
@ -53,7 +54,7 @@ struct clk *of_clk_get_by_clkspec(struct of_phandle_args *clkspec)
return clk;
}
struct clk *of_clk_get(struct device_node *np, int index)
static struct clk *__of_clk_get(struct device_node *np, int index)
{
struct of_phandle_args clkspec;
struct clk *clk;
@ -69,20 +70,24 @@ struct clk *of_clk_get(struct device_node *np, int index)
clk = of_clk_get_by_clkspec(&clkspec);
of_node_put(clkspec.np);
return clk;
}
struct clk *of_clk_get(struct device_node *np, int index)
{
struct clk *clk = __of_clk_get(np, index);
if (!IS_ERR(clk))
clk = __clk_create_clk(__clk_get_hw(clk), np->full_name, NULL);
return clk;
}
EXPORT_SYMBOL(of_clk_get);
/**
* of_clk_get_by_name() - Parse and lookup a clock referenced by a device node
* @np: pointer to clock consumer node
* @name: name of consumer's clock input, or NULL for the first clock reference
*
* This function parses the clocks and clock-names properties,
* and uses them to look up the struct clk from the registered list of clock
* providers.
*/
struct clk *of_clk_get_by_name(struct device_node *np, const char *name)
static struct clk *__of_clk_get_by_name(struct device_node *np,
const char *dev_id,
const char *name)
{
struct clk *clk = ERR_PTR(-ENOENT);
@ -97,9 +102,11 @@ struct clk *of_clk_get_by_name(struct device_node *np, const char *name)
*/
if (name)
index = of_property_match_string(np, "clock-names", name);
clk = of_clk_get(np, index);
if (!IS_ERR(clk))
clk = __of_clk_get(np, index);
if (!IS_ERR(clk)) {
clk = __clk_create_clk(__clk_get_hw(clk), dev_id, name);
break;
}
else if (name && index >= 0) {
if (PTR_ERR(clk) != -EPROBE_DEFER)
pr_err("ERROR: could not get clock %s:%s(%i)\n",
@ -119,7 +126,33 @@ struct clk *of_clk_get_by_name(struct device_node *np, const char *name)
return clk;
}
/**
* of_clk_get_by_name() - Parse and lookup a clock referenced by a device node
* @np: pointer to clock consumer node
* @name: name of consumer's clock input, or NULL for the first clock reference
*
* This function parses the clocks and clock-names properties,
* and uses them to look up the struct clk from the registered list of clock
* providers.
*/
struct clk *of_clk_get_by_name(struct device_node *np, const char *name)
{
if (!np)
return ERR_PTR(-ENOENT);
return __of_clk_get_by_name(np, np->full_name, name);
}
EXPORT_SYMBOL(of_clk_get_by_name);
#else /* defined(CONFIG_OF) && defined(CONFIG_COMMON_CLK) */
static struct clk *__of_clk_get_by_name(struct device_node *np,
const char *dev_id,
const char *name)
{
return ERR_PTR(-ENOENT);
}
#endif
/*
@ -168,14 +201,29 @@ static struct clk_lookup *clk_find(const char *dev_id, const char *con_id)
struct clk *clk_get_sys(const char *dev_id, const char *con_id)
{
struct clk_lookup *cl;
struct clk *clk = NULL;
mutex_lock(&clocks_mutex);
cl = clk_find(dev_id, con_id);
if (cl && !__clk_get(cl->clk))
if (!cl)
goto out;
if (!__clk_get(cl->clk)) {
cl = NULL;
goto out;
}
#if defined(CONFIG_COMMON_CLK)
clk = __clk_create_clk(__clk_get_hw(cl->clk), dev_id, con_id);
#else
clk = cl->clk;
#endif
out:
mutex_unlock(&clocks_mutex);
return cl ? cl->clk : ERR_PTR(-ENOENT);
return cl ? clk : ERR_PTR(-ENOENT);
}
EXPORT_SYMBOL(clk_get_sys);
@ -185,10 +233,8 @@ struct clk *clk_get(struct device *dev, const char *con_id)
struct clk *clk;
if (dev) {
clk = of_clk_get_by_name(dev->of_node, con_id);
if (!IS_ERR(clk))
return clk;
if (PTR_ERR(clk) == -EPROBE_DEFER)
clk = __of_clk_get_by_name(dev->of_node, dev_id, con_id);
if (!IS_ERR(clk) || PTR_ERR(clk) == -EPROBE_DEFER)
return clk;
}
@ -331,6 +377,7 @@ int clk_register_clkdev(struct clk *clk, const char *con_id,
return 0;
}
EXPORT_SYMBOL(clk_register_clkdev);
/**
* clk_register_clkdevs - register a set of clk_lookup for a struct clk

View File

@ -295,6 +295,8 @@ static unsigned long mmc_clk_recalc_rate(struct clk_hw *hw,
}
static long mmc_clk_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_p)
{

View File

@ -202,6 +202,8 @@ error:
}
static long mmp_clk_mix_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_clk)
{

View File

@ -1,3 +1,4 @@
obj-y += clk-pxa.o
obj-$(CONFIG_PXA25x) += clk-pxa25x.o
obj-$(CONFIG_PXA27x) += clk-pxa27x.o
obj-$(CONFIG_PXA3xx) += clk-pxa3xx.o

View File

@ -0,0 +1,364 @@
/*
* Marvell PXA3xxx family clocks
*
* Copyright (C) 2014 Robert Jarzmik
*
* Heavily inspired from former arch/arm/mach-pxa/pxa3xx.c
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* For non-devicetree platforms. Once pxa is fully converted to devicetree, this
* should go away.
*/
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/of.h>
#include <mach/smemc.h>
#include <mach/pxa3xx-regs.h>
#include <dt-bindings/clock/pxa-clock.h>
#include "clk-pxa.h"
#define KHz 1000
#define MHz (1000 * 1000)
enum {
PXA_CORE_60Mhz = 0,
PXA_CORE_RUN,
PXA_CORE_TURBO,
};
enum {
PXA_BUS_60Mhz = 0,
PXA_BUS_HSS,
};
/* crystal frequency to HSIO bus frequency multiplier (HSS) */
static unsigned char hss_mult[4] = { 8, 12, 16, 24 };
/* crystal frequency to static memory controller multiplier (SMCFS) */
static unsigned int smcfs_mult[8] = { 6, 0, 8, 0, 0, 16, };
static unsigned int df_clkdiv[4] = { 1, 2, 4, 1 };
static const char * const get_freq_khz[] = {
"core", "ring_osc_60mhz", "run", "cpll", "system_bus"
};
/*
* Get the clock frequency as reflected by ACSR and the turbo flag.
* We assume these values have been applied via a fcs.
* If info is not 0 we also display the current settings.
*/
unsigned int pxa3xx_get_clk_frequency_khz(int info)
{
struct clk *clk;
unsigned long clks[5];
int i;
for (i = 0; i < 5; i++) {
clk = clk_get(NULL, get_freq_khz[i]);
if (IS_ERR(clk)) {
clks[i] = 0;
} else {
clks[i] = clk_get_rate(clk);
clk_put(clk);
}
}
if (info) {
pr_info("RO Mode clock: %ld.%02ldMHz\n",
clks[1] / 1000000, (clks[0] % 1000000) / 10000);
pr_info("Run Mode clock: %ld.%02ldMHz\n",
clks[2] / 1000000, (clks[1] % 1000000) / 10000);
pr_info("Turbo Mode clock: %ld.%02ldMHz\n",
clks[3] / 1000000, (clks[2] % 1000000) / 10000);
pr_info("System bus clock: %ld.%02ldMHz\n",
clks[4] / 1000000, (clks[4] % 1000000) / 10000);
}
return (unsigned int)clks[0];
}
static unsigned long clk_pxa3xx_ac97_get_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
unsigned long ac97_div, rate;
ac97_div = AC97_DIV;
/* This may loose precision for some rates but won't for the
* standard 24.576MHz.
*/
rate = parent_rate / 2;
rate /= ((ac97_div >> 12) & 0x7fff);
rate *= (ac97_div & 0xfff);
return rate;
}
PARENTS(clk_pxa3xx_ac97) = { "spll_624mhz" };
RATE_RO_OPS(clk_pxa3xx_ac97, "ac97");
static unsigned long clk_pxa3xx_smemc_get_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
unsigned long acsr = ACSR;
unsigned long memclkcfg = __raw_readl(MEMCLKCFG);
return (parent_rate / 48) * smcfs_mult[(acsr >> 23) & 0x7] /
df_clkdiv[(memclkcfg >> 16) & 0x3];
}
PARENTS(clk_pxa3xx_smemc) = { "spll_624mhz" };
RATE_RO_OPS(clk_pxa3xx_smemc, "smemc");
static bool pxa3xx_is_ring_osc_forced(void)
{
unsigned long acsr = ACSR;
return acsr & ACCR_D0CS;
}
PARENTS(pxa3xx_pbus) = { "ring_osc_60mhz", "spll_624mhz" };
PARENTS(pxa3xx_32Khz_bus) = { "osc_32_768khz", "osc_32_768khz" };
PARENTS(pxa3xx_13MHz_bus) = { "osc_13mhz", "osc_13mhz" };
PARENTS(pxa3xx_ac97_bus) = { "ring_osc_60mhz", "ac97" };
PARENTS(pxa3xx_sbus) = { "ring_osc_60mhz", "system_bus" };
PARENTS(pxa3xx_smemcbus) = { "ring_osc_60mhz", "smemc" };
#define CKEN_AB(bit) ((CKEN_ ## bit > 31) ? &CKENA : &CKENB)
#define PXA3XX_CKEN(dev_id, con_id, parents, mult_lp, div_lp, mult_hp, \
div_hp, bit, is_lp, flags) \
PXA_CKEN(dev_id, con_id, bit, parents, mult_lp, div_lp, \
mult_hp, div_hp, is_lp, CKEN_AB(bit), \
(CKEN_ ## bit % 32), flags)
#define PXA3XX_PBUS_CKEN(dev_id, con_id, bit, mult_lp, div_lp, \
mult_hp, div_hp, delay) \
PXA3XX_CKEN(dev_id, con_id, pxa3xx_pbus_parents, mult_lp, \
div_lp, mult_hp, div_hp, bit, pxa3xx_is_ring_osc_forced, 0)
#define PXA3XX_CKEN_1RATE(dev_id, con_id, bit, parents) \
PXA_CKEN_1RATE(dev_id, con_id, bit, parents, \
CKEN_AB(bit), (CKEN_ ## bit % 32), 0)
static struct desc_clk_cken pxa3xx_clocks[] __initdata = {
PXA3XX_PBUS_CKEN("pxa2xx-uart.0", NULL, FFUART, 1, 4, 1, 42, 1),
PXA3XX_PBUS_CKEN("pxa2xx-uart.1", NULL, BTUART, 1, 4, 1, 42, 1),
PXA3XX_PBUS_CKEN("pxa2xx-uart.2", NULL, STUART, 1, 4, 1, 42, 1),
PXA3XX_PBUS_CKEN("pxa2xx-i2c.0", NULL, I2C, 2, 5, 1, 19, 0),
PXA3XX_PBUS_CKEN("pxa27x-udc", NULL, UDC, 1, 4, 1, 13, 5),
PXA3XX_PBUS_CKEN("pxa27x-ohci", NULL, USBH, 1, 4, 1, 13, 0),
PXA3XX_PBUS_CKEN("pxa3xx-u2d", NULL, USB2, 1, 4, 1, 13, 0),
PXA3XX_PBUS_CKEN("pxa27x-pwm.0", NULL, PWM0, 1, 6, 1, 48, 0),
PXA3XX_PBUS_CKEN("pxa27x-pwm.1", NULL, PWM1, 1, 6, 1, 48, 0),
PXA3XX_PBUS_CKEN("pxa2xx-mci.0", NULL, MMC1, 1, 4, 1, 24, 0),
PXA3XX_PBUS_CKEN("pxa2xx-mci.1", NULL, MMC2, 1, 4, 1, 24, 0),
PXA3XX_PBUS_CKEN("pxa2xx-mci.2", NULL, MMC3, 1, 4, 1, 24, 0),
PXA3XX_CKEN_1RATE("pxa27x-keypad", NULL, KEYPAD,
pxa3xx_32Khz_bus_parents),
PXA3XX_CKEN_1RATE("pxa3xx-ssp.0", NULL, SSP1, pxa3xx_13MHz_bus_parents),
PXA3XX_CKEN_1RATE("pxa3xx-ssp.1", NULL, SSP2, pxa3xx_13MHz_bus_parents),
PXA3XX_CKEN_1RATE("pxa3xx-ssp.2", NULL, SSP3, pxa3xx_13MHz_bus_parents),
PXA3XX_CKEN_1RATE("pxa3xx-ssp.3", NULL, SSP4, pxa3xx_13MHz_bus_parents),
PXA3XX_CKEN(NULL, "AC97CLK", pxa3xx_ac97_bus_parents, 1, 4, 1, 1, AC97,
pxa3xx_is_ring_osc_forced, 0),
PXA3XX_CKEN(NULL, "CAMCLK", pxa3xx_sbus_parents, 1, 2, 1, 1, CAMERA,
pxa3xx_is_ring_osc_forced, 0),
PXA3XX_CKEN("pxa2xx-fb", NULL, pxa3xx_sbus_parents, 1, 1, 1, 1, LCD,
pxa3xx_is_ring_osc_forced, 0),
PXA3XX_CKEN("pxa2xx-pcmcia", NULL, pxa3xx_smemcbus_parents, 1, 4,
1, 1, SMC, pxa3xx_is_ring_osc_forced, CLK_IGNORE_UNUSED),
};
static struct desc_clk_cken pxa300_310_clocks[] __initdata = {
PXA3XX_PBUS_CKEN("pxa3xx-gcu", NULL, PXA300_GCU, 1, 1, 1, 1, 0),
PXA3XX_PBUS_CKEN("pxa3xx-nand", NULL, NAND, 1, 2, 1, 4, 0),
PXA3XX_CKEN_1RATE("pxa3xx-gpio", NULL, GPIO, pxa3xx_13MHz_bus_parents),
};
static struct desc_clk_cken pxa320_clocks[] __initdata = {
PXA3XX_PBUS_CKEN("pxa3xx-nand", NULL, NAND, 1, 2, 1, 6, 0),
PXA3XX_PBUS_CKEN("pxa3xx-gcu", NULL, PXA320_GCU, 1, 1, 1, 1, 0),
PXA3XX_CKEN_1RATE("pxa3xx-gpio", NULL, GPIO, pxa3xx_13MHz_bus_parents),
};
static struct desc_clk_cken pxa93x_clocks[] __initdata = {
PXA3XX_PBUS_CKEN("pxa3xx-gcu", NULL, PXA300_GCU, 1, 1, 1, 1, 0),
PXA3XX_PBUS_CKEN("pxa3xx-nand", NULL, NAND, 1, 2, 1, 4, 0),
PXA3XX_CKEN_1RATE("pxa93x-gpio", NULL, GPIO, pxa3xx_13MHz_bus_parents),
};
static unsigned long clk_pxa3xx_system_bus_get_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
unsigned long acsr = ACSR;
unsigned int hss = (acsr >> 14) & 0x3;
if (pxa3xx_is_ring_osc_forced())
return parent_rate;
return parent_rate / 48 * hss_mult[hss];
}
static u8 clk_pxa3xx_system_bus_get_parent(struct clk_hw *hw)
{
if (pxa3xx_is_ring_osc_forced())
return PXA_BUS_60Mhz;
else
return PXA_BUS_HSS;
}
PARENTS(clk_pxa3xx_system_bus) = { "ring_osc_60mhz", "spll_624mhz" };
MUX_RO_RATE_RO_OPS(clk_pxa3xx_system_bus, "system_bus");
static unsigned long clk_pxa3xx_core_get_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
return parent_rate;
}
static u8 clk_pxa3xx_core_get_parent(struct clk_hw *hw)
{
unsigned long xclkcfg;
unsigned int t;
if (pxa3xx_is_ring_osc_forced())
return PXA_CORE_60Mhz;
/* Read XCLKCFG register turbo bit */
__asm__ __volatile__("mrc\tp14, 0, %0, c6, c0, 0" : "=r"(xclkcfg));
t = xclkcfg & 0x1;
if (t)
return PXA_CORE_TURBO;
return PXA_CORE_RUN;
}
PARENTS(clk_pxa3xx_core) = { "ring_osc_60mhz", "run", "cpll" };
MUX_RO_RATE_RO_OPS(clk_pxa3xx_core, "core");
static unsigned long clk_pxa3xx_run_get_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
unsigned long acsr = ACSR;
unsigned int xn = (acsr & ACCR_XN_MASK) >> 8;
unsigned int t, xclkcfg;
/* Read XCLKCFG register turbo bit */
__asm__ __volatile__("mrc\tp14, 0, %0, c6, c0, 0" : "=r"(xclkcfg));
t = xclkcfg & 0x1;
return t ? (parent_rate / xn) * 2 : parent_rate;
}
PARENTS(clk_pxa3xx_run) = { "cpll" };
RATE_RO_OPS(clk_pxa3xx_run, "run");
static unsigned long clk_pxa3xx_cpll_get_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
unsigned long acsr = ACSR;
unsigned int xn = (acsr & ACCR_XN_MASK) >> 8;
unsigned int xl = acsr & ACCR_XL_MASK;
unsigned int t, xclkcfg;
/* Read XCLKCFG register turbo bit */
__asm__ __volatile__("mrc\tp14, 0, %0, c6, c0, 0" : "=r"(xclkcfg));
t = xclkcfg & 0x1;
pr_info("RJK: parent_rate=%lu, xl=%u, xn=%u\n", parent_rate, xl, xn);
return t ? parent_rate * xl * xn : parent_rate * xl;
}
PARENTS(clk_pxa3xx_cpll) = { "osc_13mhz" };
RATE_RO_OPS(clk_pxa3xx_cpll, "cpll");
static void __init pxa3xx_register_core(void)
{
clk_register_clk_pxa3xx_cpll();
clk_register_clk_pxa3xx_run();
clkdev_pxa_register(CLK_CORE, "core", NULL,
clk_register_clk_pxa3xx_core());
}
static void __init pxa3xx_register_plls(void)
{
clk_register_fixed_rate(NULL, "osc_13mhz", NULL,
CLK_GET_RATE_NOCACHE | CLK_IS_ROOT,
13 * MHz);
clk_register_fixed_rate(NULL, "osc_32_768khz", NULL,
CLK_GET_RATE_NOCACHE | CLK_IS_ROOT,
32768);
clk_register_fixed_rate(NULL, "ring_osc_120mhz", NULL,
CLK_GET_RATE_NOCACHE | CLK_IS_ROOT,
120 * MHz);
clk_register_fixed_rate(NULL, "clk_dummy", NULL, CLK_IS_ROOT, 0);
clk_register_fixed_factor(NULL, "spll_624mhz", "osc_13mhz", 0, 48, 1);
clk_register_fixed_factor(NULL, "ring_osc_60mhz", "ring_osc_120mhz",
0, 1, 2);
}
#define DUMMY_CLK(_con_id, _dev_id, _parent) \
{ .con_id = _con_id, .dev_id = _dev_id, .parent = _parent }
struct dummy_clk {
const char *con_id;
const char *dev_id;
const char *parent;
};
static struct dummy_clk dummy_clks[] __initdata = {
DUMMY_CLK(NULL, "pxa93x-gpio", "osc_13mhz"),
DUMMY_CLK(NULL, "sa1100-rtc", "osc_32_768khz"),
DUMMY_CLK("UARTCLK", "pxa2xx-ir", "STUART"),
DUMMY_CLK(NULL, "pxa3xx-pwri2c.1", "osc_13mhz"),
};
static void __init pxa3xx_dummy_clocks_init(void)
{
struct clk *clk;
struct dummy_clk *d;
const char *name;
int i;
for (i = 0; i < ARRAY_SIZE(dummy_clks); i++) {
d = &dummy_clks[i];
name = d->dev_id ? d->dev_id : d->con_id;
clk = clk_register_fixed_factor(NULL, name, d->parent, 0, 1, 1);
clk_register_clkdev(clk, d->con_id, d->dev_id);
}
}
static void __init pxa3xx_base_clocks_init(void)
{
pxa3xx_register_plls();
pxa3xx_register_core();
clk_register_clk_pxa3xx_system_bus();
clk_register_clk_pxa3xx_ac97();
clk_register_clk_pxa3xx_smemc();
clk_register_gate(NULL, "CLK_POUT", "osc_13mhz", 0,
(void __iomem *)&OSCC, 11, 0, NULL);
}
int __init pxa3xx_clocks_init(void)
{
int ret;
pxa3xx_base_clocks_init();
pxa3xx_dummy_clocks_init();
ret = clk_pxa_cken_init(pxa3xx_clocks, ARRAY_SIZE(pxa3xx_clocks));
if (ret)
return ret;
if (cpu_is_pxa320())
return clk_pxa_cken_init(pxa320_clocks,
ARRAY_SIZE(pxa320_clocks));
if (cpu_is_pxa300() || cpu_is_pxa310())
return clk_pxa_cken_init(pxa300_310_clocks,
ARRAY_SIZE(pxa300_310_clocks));
return clk_pxa_cken_init(pxa93x_clocks, ARRAY_SIZE(pxa93x_clocks));
}
static void __init pxa3xx_dt_clocks_init(struct device_node *np)
{
pxa3xx_clocks_init();
clk_pxa_dt_common_init(np);
}
CLK_OF_DECLARE(pxa_clks, "marvell,pxa300-clocks", pxa3xx_dt_clocks_init);

View File

@ -29,6 +29,15 @@ config IPQ_GCC_806X
Say Y if you want to use peripheral devices such as UART, SPI,
i2c, USB, SD/eMMC, etc.
config IPQ_LCC_806X
tristate "IPQ806x LPASS Clock Controller"
select IPQ_GCC_806X
depends on COMMON_CLK_QCOM
help
Support for the LPASS clock controller on ipq806x devices.
Say Y if you want to use audio devices such as i2s, pcm,
S/PDIF, etc.
config MSM_GCC_8660
tristate "MSM8660 Global Clock Controller"
depends on COMMON_CLK_QCOM
@ -45,6 +54,15 @@ config MSM_GCC_8960
Say Y if you want to use peripheral devices such as UART, SPI,
i2c, USB, SD/eMMC, SATA, PCIe, etc.
config MSM_LCC_8960
tristate "APQ8064/MSM8960 LPASS Clock Controller"
select MSM_GCC_8960
depends on COMMON_CLK_QCOM
help
Support for the LPASS clock controller on apq8064/msm8960 devices.
Say Y if you want to use audio devices such as i2s, pcm,
SLIMBus, etc.
config MSM_MMCC_8960
tristate "MSM8960 Multimedia Clock Controller"
select MSM_GCC_8960

View File

@ -6,13 +6,17 @@ clk-qcom-y += clk-pll.o
clk-qcom-y += clk-rcg.o
clk-qcom-y += clk-rcg2.o
clk-qcom-y += clk-branch.o
clk-qcom-y += clk-regmap-divider.o
clk-qcom-y += clk-regmap-mux.o
clk-qcom-y += reset.o
obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.o
obj-$(CONFIG_APQ_MMCC_8084) += mmcc-apq8084.o
obj-$(CONFIG_IPQ_GCC_806X) += gcc-ipq806x.o
obj-$(CONFIG_IPQ_LCC_806X) += lcc-ipq806x.o
obj-$(CONFIG_MSM_GCC_8660) += gcc-msm8660.o
obj-$(CONFIG_MSM_GCC_8960) += gcc-msm8960.o
obj-$(CONFIG_MSM_LCC_8960) += lcc-msm8960.o
obj-$(CONFIG_MSM_GCC_8974) += gcc-msm8974.o
obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o
obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o

View File

@ -141,6 +141,7 @@ struct pll_freq_tbl *find_freq(const struct pll_freq_tbl *f, unsigned long rate)
static long
clk_pll_determine_rate(struct clk_hw *hw, unsigned long rate,
unsigned long min_rate, unsigned long max_rate,
unsigned long *p_rate, struct clk_hw **p)
{
struct clk_pll *pll = to_clk_pll(hw);

View File

@ -368,6 +368,7 @@ clk_dyn_rcg_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
static long _freq_tbl_determine_rate(struct clk_hw *hw,
const struct freq_tbl *f, unsigned long rate,
unsigned long min_rate, unsigned long max_rate,
unsigned long *p_rate, struct clk_hw **p_hw)
{
unsigned long clk_flags;
@ -397,22 +398,27 @@ static long _freq_tbl_determine_rate(struct clk_hw *hw,
}
static long clk_rcg_determine_rate(struct clk_hw *hw, unsigned long rate,
unsigned long min_rate, unsigned long max_rate,
unsigned long *p_rate, struct clk_hw **p)
{
struct clk_rcg *rcg = to_clk_rcg(hw);
return _freq_tbl_determine_rate(hw, rcg->freq_tbl, rate, p_rate, p);
return _freq_tbl_determine_rate(hw, rcg->freq_tbl, rate, min_rate,
max_rate, p_rate, p);
}
static long clk_dyn_rcg_determine_rate(struct clk_hw *hw, unsigned long rate,
unsigned long min_rate, unsigned long max_rate,
unsigned long *p_rate, struct clk_hw **p)
{
struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw);
return _freq_tbl_determine_rate(hw, rcg->freq_tbl, rate, p_rate, p);
return _freq_tbl_determine_rate(hw, rcg->freq_tbl, rate, min_rate,
max_rate, p_rate, p);
}
static long clk_rcg_bypass_determine_rate(struct clk_hw *hw, unsigned long rate,
unsigned long min_rate, unsigned long max_rate,
unsigned long *p_rate, struct clk_hw **p_hw)
{
struct clk_rcg *rcg = to_clk_rcg(hw);

View File

@ -208,6 +208,7 @@ static long _freq_tbl_determine_rate(struct clk_hw *hw,
}
static long clk_rcg2_determine_rate(struct clk_hw *hw, unsigned long rate,
unsigned long min_rate, unsigned long max_rate,
unsigned long *p_rate, struct clk_hw **p)
{
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
@ -361,6 +362,8 @@ static int clk_edp_pixel_set_rate_and_parent(struct clk_hw *hw,
}
static long clk_edp_pixel_determine_rate(struct clk_hw *hw, unsigned long rate,
unsigned long min_rate,
unsigned long max_rate,
unsigned long *p_rate, struct clk_hw **p)
{
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
@ -412,6 +415,7 @@ const struct clk_ops clk_edp_pixel_ops = {
EXPORT_SYMBOL_GPL(clk_edp_pixel_ops);
static long clk_byte_determine_rate(struct clk_hw *hw, unsigned long rate,
unsigned long min_rate, unsigned long max_rate,
unsigned long *p_rate, struct clk_hw **p_hw)
{
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
@ -476,6 +480,8 @@ static const struct frac_entry frac_table_pixel[] = {
};
static long clk_pixel_determine_rate(struct clk_hw *hw, unsigned long rate,
unsigned long min_rate,
unsigned long max_rate,
unsigned long *p_rate, struct clk_hw **p)
{
struct clk_rcg2 *rcg = to_clk_rcg2(hw);

View File

@ -0,0 +1,70 @@
/*
* Copyright (c) 2014, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/bitops.h>
#include <linux/regmap.h>
#include <linux/export.h>
#include "clk-regmap-divider.h"
static inline struct clk_regmap_div *to_clk_regmap_div(struct clk_hw *hw)
{
return container_of(to_clk_regmap(hw), struct clk_regmap_div, clkr);
}
static long div_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
{
struct clk_regmap_div *divider = to_clk_regmap_div(hw);
return divider_round_rate(hw, rate, prate, NULL, divider->width,
CLK_DIVIDER_ROUND_CLOSEST);
}
static int div_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_regmap_div *divider = to_clk_regmap_div(hw);
struct clk_regmap *clkr = &divider->clkr;
u32 div;
div = divider_get_val(rate, parent_rate, NULL, divider->width,
CLK_DIVIDER_ROUND_CLOSEST);
return regmap_update_bits(clkr->regmap, divider->reg,
(BIT(divider->width) - 1) << divider->shift,
div << divider->shift);
}
static unsigned long div_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_regmap_div *divider = to_clk_regmap_div(hw);
struct clk_regmap *clkr = &divider->clkr;
u32 div;
regmap_read(clkr->regmap, divider->reg, &div);
div >>= divider->shift;
div &= BIT(divider->width) - 1;
return divider_recalc_rate(hw, parent_rate, div, NULL,
CLK_DIVIDER_ROUND_CLOSEST);
}
const struct clk_ops clk_regmap_div_ops = {
.round_rate = div_round_rate,
.set_rate = div_set_rate,
.recalc_rate = div_recalc_rate,
};
EXPORT_SYMBOL_GPL(clk_regmap_div_ops);

View File

@ -0,0 +1,29 @@
/*
* Copyright (c) 2014, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __QCOM_CLK_REGMAP_DIVIDER_H__
#define __QCOM_CLK_REGMAP_DIVIDER_H__
#include <linux/clk-provider.h>
#include "clk-regmap.h"
struct clk_regmap_div {
u32 reg;
u32 shift;
u32 width;
struct clk_regmap clkr;
};
extern const struct clk_ops clk_regmap_div_ops;
#endif

View File

@ -0,0 +1,59 @@
/*
* Copyright (c) 2014, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/bitops.h>
#include <linux/regmap.h>
#include <linux/export.h>
#include "clk-regmap-mux.h"
static inline struct clk_regmap_mux *to_clk_regmap_mux(struct clk_hw *hw)
{
return container_of(to_clk_regmap(hw), struct clk_regmap_mux, clkr);
}
static u8 mux_get_parent(struct clk_hw *hw)
{
struct clk_regmap_mux *mux = to_clk_regmap_mux(hw);
struct clk_regmap *clkr = to_clk_regmap(hw);
unsigned int mask = GENMASK(mux->width - 1, 0);
unsigned int val;
regmap_read(clkr->regmap, mux->reg, &val);
val >>= mux->shift;
val &= mask;
return val;
}
static int mux_set_parent(struct clk_hw *hw, u8 index)
{
struct clk_regmap_mux *mux = to_clk_regmap_mux(hw);
struct clk_regmap *clkr = to_clk_regmap(hw);
unsigned int mask = GENMASK(mux->width + mux->shift - 1, mux->shift);
unsigned int val;
val = index;
val <<= mux->shift;
return regmap_update_bits(clkr->regmap, mux->reg, mask, val);
}
const struct clk_ops clk_regmap_mux_closest_ops = {
.get_parent = mux_get_parent,
.set_parent = mux_set_parent,
.determine_rate = __clk_mux_determine_rate_closest,
};
EXPORT_SYMBOL_GPL(clk_regmap_mux_closest_ops);

View File

@ -0,0 +1,29 @@
/*
* Copyright (c) 2014, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __QCOM_CLK_REGMAP_MUX_H__
#define __QCOM_CLK_REGMAP_MUX_H__
#include <linux/clk-provider.h>
#include "clk-regmap.h"
struct clk_regmap_mux {
u32 reg;
u32 shift;
u32 width;
struct clk_regmap clkr;
};
extern const struct clk_ops clk_regmap_mux_closest_ops;
#endif

View File

@ -75,6 +75,17 @@ static struct clk_pll pll3 = {
},
};
static struct clk_regmap pll4_vote = {
.enable_reg = 0x34c0,
.enable_mask = BIT(4),
.hw.init = &(struct clk_init_data){
.name = "pll4_vote",
.parent_names = (const char *[]){ "pll4" },
.num_parents = 1,
.ops = &clk_pll_vote_ops,
},
};
static struct clk_pll pll8 = {
.l_reg = 0x3144,
.m_reg = 0x3148,
@ -2163,6 +2174,7 @@ static struct clk_regmap *gcc_ipq806x_clks[] = {
[PLL0] = &pll0.clkr,
[PLL0_VOTE] = &pll0_vote,
[PLL3] = &pll3.clkr,
[PLL4_VOTE] = &pll4_vote,
[PLL8] = &pll8.clkr,
[PLL8_VOTE] = &pll8_vote,
[PLL14] = &pll14.clkr,

View File

@ -0,0 +1,473 @@
/*
* Copyright (c) 2014, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/bitops.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/clk-provider.h>
#include <linux/regmap.h>
#include <dt-bindings/clock/qcom,lcc-ipq806x.h>
#include "common.h"
#include "clk-regmap.h"
#include "clk-pll.h"
#include "clk-rcg.h"
#include "clk-branch.h"
#include "clk-regmap-divider.h"
#include "clk-regmap-mux.h"
static struct clk_pll pll4 = {
.l_reg = 0x4,
.m_reg = 0x8,
.n_reg = 0xc,
.config_reg = 0x14,
.mode_reg = 0x0,
.status_reg = 0x18,
.status_bit = 16,
.clkr.hw.init = &(struct clk_init_data){
.name = "pll4",
.parent_names = (const char *[]){ "pxo" },
.num_parents = 1,
.ops = &clk_pll_ops,
},
};
static const struct pll_config pll4_config = {
.l = 0xf,
.m = 0x91,
.n = 0xc7,
.vco_val = 0x0,
.vco_mask = BIT(17) | BIT(16),
.pre_div_val = 0x0,
.pre_div_mask = BIT(19),
.post_div_val = 0x0,
.post_div_mask = BIT(21) | BIT(20),
.mn_ena_mask = BIT(22),
.main_output_mask = BIT(23),
};
#define P_PXO 0
#define P_PLL4 1
static const u8 lcc_pxo_pll4_map[] = {
[P_PXO] = 0,
[P_PLL4] = 2,
};
static const char *lcc_pxo_pll4[] = {
"pxo",
"pll4_vote",
};
static struct freq_tbl clk_tbl_aif_mi2s[] = {
{ 1024000, P_PLL4, 4, 1, 96 },
{ 1411200, P_PLL4, 4, 2, 139 },
{ 1536000, P_PLL4, 4, 1, 64 },
{ 2048000, P_PLL4, 4, 1, 48 },
{ 2116800, P_PLL4, 4, 2, 93 },
{ 2304000, P_PLL4, 4, 2, 85 },
{ 2822400, P_PLL4, 4, 6, 209 },
{ 3072000, P_PLL4, 4, 1, 32 },
{ 3175200, P_PLL4, 4, 1, 31 },
{ 4096000, P_PLL4, 4, 1, 24 },
{ 4233600, P_PLL4, 4, 9, 209 },
{ 4608000, P_PLL4, 4, 3, 64 },
{ 5644800, P_PLL4, 4, 12, 209 },
{ 6144000, P_PLL4, 4, 1, 16 },
{ 6350400, P_PLL4, 4, 2, 31 },
{ 8192000, P_PLL4, 4, 1, 12 },
{ 8467200, P_PLL4, 4, 18, 209 },
{ 9216000, P_PLL4, 4, 3, 32 },
{ 11289600, P_PLL4, 4, 24, 209 },
{ 12288000, P_PLL4, 4, 1, 8 },
{ 12700800, P_PLL4, 4, 27, 209 },
{ 13824000, P_PLL4, 4, 9, 64 },
{ 16384000, P_PLL4, 4, 1, 6 },
{ 16934400, P_PLL4, 4, 41, 238 },
{ 18432000, P_PLL4, 4, 3, 16 },
{ 22579200, P_PLL4, 2, 24, 209 },
{ 24576000, P_PLL4, 4, 1, 4 },
{ 27648000, P_PLL4, 4, 9, 32 },
{ 33868800, P_PLL4, 4, 41, 119 },
{ 36864000, P_PLL4, 4, 3, 8 },
{ 45158400, P_PLL4, 1, 24, 209 },
{ 49152000, P_PLL4, 4, 1, 2 },
{ 50803200, P_PLL4, 1, 27, 209 },
{ }
};
static struct clk_rcg mi2s_osr_src = {
.ns_reg = 0x48,
.md_reg = 0x4c,
.mn = {
.mnctr_en_bit = 8,
.mnctr_reset_bit = 7,
.mnctr_mode_shift = 5,
.n_val_shift = 24,
.m_val_shift = 8,
.width = 8,
},
.p = {
.pre_div_shift = 3,
.pre_div_width = 2,
},
.s = {
.src_sel_shift = 0,
.parent_map = lcc_pxo_pll4_map,
},
.freq_tbl = clk_tbl_aif_mi2s,
.clkr = {
.enable_reg = 0x48,
.enable_mask = BIT(9),
.hw.init = &(struct clk_init_data){
.name = "mi2s_osr_src",
.parent_names = lcc_pxo_pll4,
.num_parents = 2,
.ops = &clk_rcg_ops,
.flags = CLK_SET_RATE_GATE,
},
},
};
static const char *lcc_mi2s_parents[] = {
"mi2s_osr_src",
};
static struct clk_branch mi2s_osr_clk = {
.halt_reg = 0x50,
.halt_bit = 1,
.halt_check = BRANCH_HALT_ENABLE,
.clkr = {
.enable_reg = 0x48,
.enable_mask = BIT(17),
.hw.init = &(struct clk_init_data){
.name = "mi2s_osr_clk",
.parent_names = lcc_mi2s_parents,
.num_parents = 1,
.ops = &clk_branch_ops,
.flags = CLK_SET_RATE_PARENT,
},
},
};
static struct clk_regmap_div mi2s_div_clk = {
.reg = 0x48,
.shift = 10,
.width = 4,
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "mi2s_div_clk",
.parent_names = lcc_mi2s_parents,
.num_parents = 1,
.ops = &clk_regmap_div_ops,
},
},
};
static struct clk_branch mi2s_bit_div_clk = {
.halt_reg = 0x50,
.halt_bit = 0,
.halt_check = BRANCH_HALT_ENABLE,
.clkr = {
.enable_reg = 0x48,
.enable_mask = BIT(15),
.hw.init = &(struct clk_init_data){
.name = "mi2s_bit_div_clk",
.parent_names = (const char *[]){ "mi2s_div_clk" },
.num_parents = 1,
.ops = &clk_branch_ops,
.flags = CLK_SET_RATE_PARENT,
},
},
};
static struct clk_regmap_mux mi2s_bit_clk = {
.reg = 0x48,
.shift = 14,
.width = 1,
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "mi2s_bit_clk",
.parent_names = (const char *[]){
"mi2s_bit_div_clk",
"mi2s_codec_clk",
},
.num_parents = 2,
.ops = &clk_regmap_mux_closest_ops,
.flags = CLK_SET_RATE_PARENT,
},
},
};
static struct freq_tbl clk_tbl_pcm[] = {
{ 64000, P_PLL4, 4, 1, 1536 },
{ 128000, P_PLL4, 4, 1, 768 },
{ 256000, P_PLL4, 4, 1, 384 },
{ 512000, P_PLL4, 4, 1, 192 },
{ 1024000, P_PLL4, 4, 1, 96 },
{ 2048000, P_PLL4, 4, 1, 48 },
{ },
};
static struct clk_rcg pcm_src = {
.ns_reg = 0x54,
.md_reg = 0x58,
.mn = {
.mnctr_en_bit = 8,
.mnctr_reset_bit = 7,
.mnctr_mode_shift = 5,
.n_val_shift = 16,
.m_val_shift = 16,
.width = 16,
},
.p = {
.pre_div_shift = 3,
.pre_div_width = 2,
},
.s = {
.src_sel_shift = 0,
.parent_map = lcc_pxo_pll4_map,
},
.freq_tbl = clk_tbl_pcm,
.clkr = {
.enable_reg = 0x54,
.enable_mask = BIT(9),
.hw.init = &(struct clk_init_data){
.name = "pcm_src",
.parent_names = lcc_pxo_pll4,
.num_parents = 2,
.ops = &clk_rcg_ops,
.flags = CLK_SET_RATE_GATE,
},
},
};
static struct clk_branch pcm_clk_out = {
.halt_reg = 0x5c,
.halt_bit = 0,
.halt_check = BRANCH_HALT_ENABLE,
.clkr = {
.enable_reg = 0x54,
.enable_mask = BIT(11),
.hw.init = &(struct clk_init_data){
.name = "pcm_clk_out",
.parent_names = (const char *[]){ "pcm_src" },
.num_parents = 1,
.ops = &clk_branch_ops,
.flags = CLK_SET_RATE_PARENT,
},
},
};
static struct clk_regmap_mux pcm_clk = {
.reg = 0x54,
.shift = 10,
.width = 1,
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "pcm_clk",
.parent_names = (const char *[]){
"pcm_clk_out",
"pcm_codec_clk",
},
.num_parents = 2,
.ops = &clk_regmap_mux_closest_ops,
.flags = CLK_SET_RATE_PARENT,
},
},
};
static struct freq_tbl clk_tbl_aif_osr[] = {
{ 22050, P_PLL4, 1, 147, 20480 },
{ 32000, P_PLL4, 1, 1, 96 },
{ 44100, P_PLL4, 1, 147, 10240 },
{ 48000, P_PLL4, 1, 1, 64 },
{ 88200, P_PLL4, 1, 147, 5120 },
{ 96000, P_PLL4, 1, 1, 32 },
{ 176400, P_PLL4, 1, 147, 2560 },
{ 192000, P_PLL4, 1, 1, 16 },
{ },
};
static struct clk_rcg spdif_src = {
.ns_reg = 0xcc,
.md_reg = 0xd0,
.mn = {
.mnctr_en_bit = 8,
.mnctr_reset_bit = 7,
.mnctr_mode_shift = 5,
.n_val_shift = 16,
.m_val_shift = 16,
.width = 8,
},
.p = {
.pre_div_shift = 3,
.pre_div_width = 2,
},
.s = {
.src_sel_shift = 0,
.parent_map = lcc_pxo_pll4_map,
},
.freq_tbl = clk_tbl_aif_osr,
.clkr = {
.enable_reg = 0xcc,
.enable_mask = BIT(9),
.hw.init = &(struct clk_init_data){
.name = "spdif_src",
.parent_names = lcc_pxo_pll4,
.num_parents = 2,
.ops = &clk_rcg_ops,
.flags = CLK_SET_RATE_GATE,
},
},
};
static const char *lcc_spdif_parents[] = {
"spdif_src",
};
static struct clk_branch spdif_clk = {
.halt_reg = 0xd4,
.halt_bit = 1,
.halt_check = BRANCH_HALT_ENABLE,
.clkr = {
.enable_reg = 0xcc,
.enable_mask = BIT(12),
.hw.init = &(struct clk_init_data){
.name = "spdif_clk",
.parent_names = lcc_spdif_parents,
.num_parents = 1,
.ops = &clk_branch_ops,
.flags = CLK_SET_RATE_PARENT,
},
},
};
static struct freq_tbl clk_tbl_ahbix[] = {
{ 131072, P_PLL4, 1, 1, 3 },
{ },
};
static struct clk_rcg ahbix_clk = {
.ns_reg = 0x38,
.md_reg = 0x3c,
.mn = {
.mnctr_en_bit = 8,
.mnctr_reset_bit = 7,
.mnctr_mode_shift = 5,
.n_val_shift = 24,
.m_val_shift = 8,
.width = 8,
},
.p = {
.pre_div_shift = 3,
.pre_div_width = 2,
},
.s = {
.src_sel_shift = 0,
.parent_map = lcc_pxo_pll4_map,
},
.freq_tbl = clk_tbl_ahbix,
.clkr = {
.enable_reg = 0x38,
.enable_mask = BIT(10), /* toggle the gfmux to select mn/pxo */
.hw.init = &(struct clk_init_data){
.name = "ahbix",
.parent_names = lcc_pxo_pll4,
.num_parents = 2,
.ops = &clk_rcg_ops,
.flags = CLK_SET_RATE_GATE,
},
},
};
static struct clk_regmap *lcc_ipq806x_clks[] = {
[PLL4] = &pll4.clkr,
[MI2S_OSR_SRC] = &mi2s_osr_src.clkr,
[MI2S_OSR_CLK] = &mi2s_osr_clk.clkr,
[MI2S_DIV_CLK] = &mi2s_div_clk.clkr,
[MI2S_BIT_DIV_CLK] = &mi2s_bit_div_clk.clkr,
[MI2S_BIT_CLK] = &mi2s_bit_clk.clkr,
[PCM_SRC] = &pcm_src.clkr,
[PCM_CLK_OUT] = &pcm_clk_out.clkr,
[PCM_CLK] = &pcm_clk.clkr,
[SPDIF_SRC] = &spdif_src.clkr,
[SPDIF_CLK] = &spdif_clk.clkr,
[AHBIX_CLK] = &ahbix_clk.clkr,
};
static const struct regmap_config lcc_ipq806x_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = 0xfc,
.fast_io = true,
};
static const struct qcom_cc_desc lcc_ipq806x_desc = {
.config = &lcc_ipq806x_regmap_config,
.clks = lcc_ipq806x_clks,
.num_clks = ARRAY_SIZE(lcc_ipq806x_clks),
};
static const struct of_device_id lcc_ipq806x_match_table[] = {
{ .compatible = "qcom,lcc-ipq8064" },
{ }
};
MODULE_DEVICE_TABLE(of, lcc_ipq806x_match_table);
static int lcc_ipq806x_probe(struct platform_device *pdev)
{
u32 val;
struct regmap *regmap;
regmap = qcom_cc_map(pdev, &lcc_ipq806x_desc);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
/* Configure the rate of PLL4 if the bootloader hasn't already */
val = regmap_read(regmap, 0x0, &val);
if (!val)
clk_pll_configure_sr(&pll4, regmap, &pll4_config, true);
/* Enable PLL4 source on the LPASS Primary PLL Mux */
regmap_write(regmap, 0xc4, 0x1);
return qcom_cc_really_probe(pdev, &lcc_ipq806x_desc, regmap);
}
static int lcc_ipq806x_remove(struct platform_device *pdev)
{
qcom_cc_remove(pdev);
return 0;
}
static struct platform_driver lcc_ipq806x_driver = {
.probe = lcc_ipq806x_probe,
.remove = lcc_ipq806x_remove,
.driver = {
.name = "lcc-ipq806x",
.owner = THIS_MODULE,
.of_match_table = lcc_ipq806x_match_table,
},
};
module_platform_driver(lcc_ipq806x_driver);
MODULE_DESCRIPTION("QCOM LCC IPQ806x Driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:lcc-ipq806x");

View File

@ -0,0 +1,585 @@
/*
* Copyright (c) 2014, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/bitops.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/clk-provider.h>
#include <linux/regmap.h>
#include <dt-bindings/clock/qcom,lcc-msm8960.h>
#include "common.h"
#include "clk-regmap.h"
#include "clk-pll.h"
#include "clk-rcg.h"
#include "clk-branch.h"
#include "clk-regmap-divider.h"
#include "clk-regmap-mux.h"
static struct clk_pll pll4 = {
.l_reg = 0x4,
.m_reg = 0x8,
.n_reg = 0xc,
.config_reg = 0x14,
.mode_reg = 0x0,
.status_reg = 0x18,
.status_bit = 16,
.clkr.hw.init = &(struct clk_init_data){
.name = "pll4",
.parent_names = (const char *[]){ "pxo" },
.num_parents = 1,
.ops = &clk_pll_ops,
},
};
#define P_PXO 0
#define P_PLL4 1
static const u8 lcc_pxo_pll4_map[] = {
[P_PXO] = 0,
[P_PLL4] = 2,
};
static const char *lcc_pxo_pll4[] = {
"pxo",
"pll4_vote",
};
static struct freq_tbl clk_tbl_aif_osr_492[] = {
{ 512000, P_PLL4, 4, 1, 240 },
{ 768000, P_PLL4, 4, 1, 160 },
{ 1024000, P_PLL4, 4, 1, 120 },
{ 1536000, P_PLL4, 4, 1, 80 },
{ 2048000, P_PLL4, 4, 1, 60 },
{ 3072000, P_PLL4, 4, 1, 40 },
{ 4096000, P_PLL4, 4, 1, 30 },
{ 6144000, P_PLL4, 4, 1, 20 },
{ 8192000, P_PLL4, 4, 1, 15 },
{ 12288000, P_PLL4, 4, 1, 10 },
{ 24576000, P_PLL4, 4, 1, 5 },
{ 27000000, P_PXO, 1, 0, 0 },
{ }
};
static struct freq_tbl clk_tbl_aif_osr_393[] = {
{ 512000, P_PLL4, 4, 1, 192 },
{ 768000, P_PLL4, 4, 1, 128 },
{ 1024000, P_PLL4, 4, 1, 96 },
{ 1536000, P_PLL4, 4, 1, 64 },
{ 2048000, P_PLL4, 4, 1, 48 },
{ 3072000, P_PLL4, 4, 1, 32 },
{ 4096000, P_PLL4, 4, 1, 24 },
{ 6144000, P_PLL4, 4, 1, 16 },
{ 8192000, P_PLL4, 4, 1, 12 },
{ 12288000, P_PLL4, 4, 1, 8 },
{ 24576000, P_PLL4, 4, 1, 4 },
{ 27000000, P_PXO, 1, 0, 0 },
{ }
};
static struct clk_rcg mi2s_osr_src = {
.ns_reg = 0x48,
.md_reg = 0x4c,
.mn = {
.mnctr_en_bit = 8,
.mnctr_reset_bit = 7,
.mnctr_mode_shift = 5,
.n_val_shift = 24,
.m_val_shift = 8,
.width = 8,
},
.p = {
.pre_div_shift = 3,
.pre_div_width = 2,
},
.s = {
.src_sel_shift = 0,
.parent_map = lcc_pxo_pll4_map,
},
.freq_tbl = clk_tbl_aif_osr_393,
.clkr = {
.enable_reg = 0x48,
.enable_mask = BIT(9),
.hw.init = &(struct clk_init_data){
.name = "mi2s_osr_src",
.parent_names = lcc_pxo_pll4,
.num_parents = 2,
.ops = &clk_rcg_ops,
.flags = CLK_SET_RATE_GATE,
},
},
};
static const char *lcc_mi2s_parents[] = {
"mi2s_osr_src",
};
static struct clk_branch mi2s_osr_clk = {
.halt_reg = 0x50,
.halt_bit = 1,
.halt_check = BRANCH_HALT_ENABLE,
.clkr = {
.enable_reg = 0x48,
.enable_mask = BIT(17),
.hw.init = &(struct clk_init_data){
.name = "mi2s_osr_clk",
.parent_names = lcc_mi2s_parents,
.num_parents = 1,
.ops = &clk_branch_ops,
.flags = CLK_SET_RATE_PARENT,
},
},
};
static struct clk_regmap_div mi2s_div_clk = {
.reg = 0x48,
.shift = 10,
.width = 4,
.clkr = {
.enable_reg = 0x48,
.enable_mask = BIT(15),
.hw.init = &(struct clk_init_data){
.name = "mi2s_div_clk",
.parent_names = lcc_mi2s_parents,
.num_parents = 1,
.ops = &clk_regmap_div_ops,
},
},
};
static struct clk_branch mi2s_bit_div_clk = {
.halt_reg = 0x50,
.halt_bit = 0,
.halt_check = BRANCH_HALT_ENABLE,
.clkr = {
.enable_reg = 0x48,
.enable_mask = BIT(15),
.hw.init = &(struct clk_init_data){
.name = "mi2s_bit_div_clk",
.parent_names = (const char *[]){ "mi2s_div_clk" },
.num_parents = 1,
.ops = &clk_branch_ops,
.flags = CLK_SET_RATE_PARENT,
},
},
};
static struct clk_regmap_mux mi2s_bit_clk = {
.reg = 0x48,
.shift = 14,
.width = 1,
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "mi2s_bit_clk",
.parent_names = (const char *[]){
"mi2s_bit_div_clk",
"mi2s_codec_clk",
},
.num_parents = 2,
.ops = &clk_regmap_mux_closest_ops,
.flags = CLK_SET_RATE_PARENT,
},
},
};
#define CLK_AIF_OSR_DIV(prefix, _ns, _md, hr) \
static struct clk_rcg prefix##_osr_src = { \
.ns_reg = _ns, \
.md_reg = _md, \
.mn = { \
.mnctr_en_bit = 8, \
.mnctr_reset_bit = 7, \
.mnctr_mode_shift = 5, \
.n_val_shift = 24, \
.m_val_shift = 8, \
.width = 8, \
}, \
.p = { \
.pre_div_shift = 3, \
.pre_div_width = 2, \
}, \
.s = { \
.src_sel_shift = 0, \
.parent_map = lcc_pxo_pll4_map, \
}, \
.freq_tbl = clk_tbl_aif_osr_393, \
.clkr = { \
.enable_reg = _ns, \
.enable_mask = BIT(9), \
.hw.init = &(struct clk_init_data){ \
.name = #prefix "_osr_src", \
.parent_names = lcc_pxo_pll4, \
.num_parents = 2, \
.ops = &clk_rcg_ops, \
.flags = CLK_SET_RATE_GATE, \
}, \
}, \
}; \
\
static const char *lcc_##prefix##_parents[] = { \
#prefix "_osr_src", \
}; \
\
static struct clk_branch prefix##_osr_clk = { \
.halt_reg = hr, \
.halt_bit = 1, \
.halt_check = BRANCH_HALT_ENABLE, \
.clkr = { \
.enable_reg = _ns, \
.enable_mask = BIT(21), \
.hw.init = &(struct clk_init_data){ \
.name = #prefix "_osr_clk", \
.parent_names = lcc_##prefix##_parents, \
.num_parents = 1, \
.ops = &clk_branch_ops, \
.flags = CLK_SET_RATE_PARENT, \
}, \
}, \
}; \
\
static struct clk_regmap_div prefix##_div_clk = { \
.reg = _ns, \
.shift = 10, \
.width = 8, \
.clkr = { \
.hw.init = &(struct clk_init_data){ \
.name = #prefix "_div_clk", \
.parent_names = lcc_##prefix##_parents, \
.num_parents = 1, \
.ops = &clk_regmap_div_ops, \
}, \
}, \
}; \
\
static struct clk_branch prefix##_bit_div_clk = { \
.halt_reg = hr, \
.halt_bit = 0, \
.halt_check = BRANCH_HALT_ENABLE, \
.clkr = { \
.enable_reg = _ns, \
.enable_mask = BIT(19), \
.hw.init = &(struct clk_init_data){ \
.name = #prefix "_bit_div_clk", \
.parent_names = (const char *[]){ \
#prefix "_div_clk" \
}, \
.num_parents = 1, \
.ops = &clk_branch_ops, \
.flags = CLK_SET_RATE_PARENT, \
}, \
}, \
}; \
\
static struct clk_regmap_mux prefix##_bit_clk = { \
.reg = _ns, \
.shift = 18, \
.width = 1, \
.clkr = { \
.hw.init = &(struct clk_init_data){ \
.name = #prefix "_bit_clk", \
.parent_names = (const char *[]){ \
#prefix "_bit_div_clk", \
#prefix "_codec_clk", \
}, \
.num_parents = 2, \
.ops = &clk_regmap_mux_closest_ops, \
.flags = CLK_SET_RATE_PARENT, \
}, \
}, \
}
CLK_AIF_OSR_DIV(codec_i2s_mic, 0x60, 0x64, 0x68);
CLK_AIF_OSR_DIV(spare_i2s_mic, 0x78, 0x7c, 0x80);
CLK_AIF_OSR_DIV(codec_i2s_spkr, 0x6c, 0x70, 0x74);
CLK_AIF_OSR_DIV(spare_i2s_spkr, 0x84, 0x88, 0x8c);
static struct freq_tbl clk_tbl_pcm_492[] = {
{ 256000, P_PLL4, 4, 1, 480 },
{ 512000, P_PLL4, 4, 1, 240 },
{ 768000, P_PLL4, 4, 1, 160 },
{ 1024000, P_PLL4, 4, 1, 120 },
{ 1536000, P_PLL4, 4, 1, 80 },
{ 2048000, P_PLL4, 4, 1, 60 },
{ 3072000, P_PLL4, 4, 1, 40 },
{ 4096000, P_PLL4, 4, 1, 30 },
{ 6144000, P_PLL4, 4, 1, 20 },
{ 8192000, P_PLL4, 4, 1, 15 },
{ 12288000, P_PLL4, 4, 1, 10 },
{ 24576000, P_PLL4, 4, 1, 5 },
{ 27000000, P_PXO, 1, 0, 0 },
{ }
};
static struct freq_tbl clk_tbl_pcm_393[] = {
{ 256000, P_PLL4, 4, 1, 384 },
{ 512000, P_PLL4, 4, 1, 192 },
{ 768000, P_PLL4, 4, 1, 128 },
{ 1024000, P_PLL4, 4, 1, 96 },
{ 1536000, P_PLL4, 4, 1, 64 },
{ 2048000, P_PLL4, 4, 1, 48 },
{ 3072000, P_PLL4, 4, 1, 32 },
{ 4096000, P_PLL4, 4, 1, 24 },
{ 6144000, P_PLL4, 4, 1, 16 },
{ 8192000, P_PLL4, 4, 1, 12 },
{ 12288000, P_PLL4, 4, 1, 8 },
{ 24576000, P_PLL4, 4, 1, 4 },
{ 27000000, P_PXO, 1, 0, 0 },
{ }
};
static struct clk_rcg pcm_src = {
.ns_reg = 0x54,
.md_reg = 0x58,
.mn = {
.mnctr_en_bit = 8,
.mnctr_reset_bit = 7,
.mnctr_mode_shift = 5,
.n_val_shift = 16,
.m_val_shift = 16,
.width = 16,
},
.p = {
.pre_div_shift = 3,
.pre_div_width = 2,
},
.s = {
.src_sel_shift = 0,
.parent_map = lcc_pxo_pll4_map,
},
.freq_tbl = clk_tbl_pcm_393,
.clkr = {
.enable_reg = 0x54,
.enable_mask = BIT(9),
.hw.init = &(struct clk_init_data){
.name = "pcm_src",
.parent_names = lcc_pxo_pll4,
.num_parents = 2,
.ops = &clk_rcg_ops,
.flags = CLK_SET_RATE_GATE,
},
},
};
static struct clk_branch pcm_clk_out = {
.halt_reg = 0x5c,
.halt_bit = 0,
.halt_check = BRANCH_HALT_ENABLE,
.clkr = {
.enable_reg = 0x54,
.enable_mask = BIT(11),
.hw.init = &(struct clk_init_data){
.name = "pcm_clk_out",
.parent_names = (const char *[]){ "pcm_src" },
.num_parents = 1,
.ops = &clk_branch_ops,
.flags = CLK_SET_RATE_PARENT,
},
},
};
static struct clk_regmap_mux pcm_clk = {
.reg = 0x54,
.shift = 10,
.width = 1,
.clkr = {
.hw.init = &(struct clk_init_data){
.name = "pcm_clk",
.parent_names = (const char *[]){
"pcm_clk_out",
"pcm_codec_clk",
},
.num_parents = 2,
.ops = &clk_regmap_mux_closest_ops,
.flags = CLK_SET_RATE_PARENT,
},
},
};
static struct clk_rcg slimbus_src = {
.ns_reg = 0xcc,
.md_reg = 0xd0,
.mn = {
.mnctr_en_bit = 8,
.mnctr_reset_bit = 7,
.mnctr_mode_shift = 5,
.n_val_shift = 16,
.m_val_shift = 16,
.width = 8,
},
.p = {
.pre_div_shift = 3,
.pre_div_width = 2,
},
.s = {
.src_sel_shift = 0,
.parent_map = lcc_pxo_pll4_map,
},
.freq_tbl = clk_tbl_aif_osr_393,
.clkr = {
.enable_reg = 0xcc,
.enable_mask = BIT(9),
.hw.init = &(struct clk_init_data){
.name = "slimbus_src",
.parent_names = lcc_pxo_pll4,
.num_parents = 2,
.ops = &clk_rcg_ops,
.flags = CLK_SET_RATE_GATE,
},
},
};
static const char *lcc_slimbus_parents[] = {
"slimbus_src",
};
static struct clk_branch audio_slimbus_clk = {
.halt_reg = 0xd4,
.halt_bit = 0,
.halt_check = BRANCH_HALT_ENABLE,
.clkr = {
.enable_reg = 0xcc,
.enable_mask = BIT(10),
.hw.init = &(struct clk_init_data){
.name = "audio_slimbus_clk",
.parent_names = lcc_slimbus_parents,
.num_parents = 1,
.ops = &clk_branch_ops,
.flags = CLK_SET_RATE_PARENT,
},
},
};
static struct clk_branch sps_slimbus_clk = {
.halt_reg = 0xd4,
.halt_bit = 1,
.halt_check = BRANCH_HALT_ENABLE,
.clkr = {
.enable_reg = 0xcc,
.enable_mask = BIT(12),
.hw.init = &(struct clk_init_data){
.name = "sps_slimbus_clk",
.parent_names = lcc_slimbus_parents,
.num_parents = 1,
.ops = &clk_branch_ops,
.flags = CLK_SET_RATE_PARENT,
},
},
};
static struct clk_regmap *lcc_msm8960_clks[] = {
[PLL4] = &pll4.clkr,
[MI2S_OSR_SRC] = &mi2s_osr_src.clkr,
[MI2S_OSR_CLK] = &mi2s_osr_clk.clkr,
[MI2S_DIV_CLK] = &mi2s_div_clk.clkr,
[MI2S_BIT_DIV_CLK] = &mi2s_bit_div_clk.clkr,
[MI2S_BIT_CLK] = &mi2s_bit_clk.clkr,
[PCM_SRC] = &pcm_src.clkr,
[PCM_CLK_OUT] = &pcm_clk_out.clkr,
[PCM_CLK] = &pcm_clk.clkr,
[SLIMBUS_SRC] = &slimbus_src.clkr,
[AUDIO_SLIMBUS_CLK] = &audio_slimbus_clk.clkr,
[SPS_SLIMBUS_CLK] = &sps_slimbus_clk.clkr,
[CODEC_I2S_MIC_OSR_SRC] = &codec_i2s_mic_osr_src.clkr,
[CODEC_I2S_MIC_OSR_CLK] = &codec_i2s_mic_osr_clk.clkr,
[CODEC_I2S_MIC_DIV_CLK] = &codec_i2s_mic_div_clk.clkr,
[CODEC_I2S_MIC_BIT_DIV_CLK] = &codec_i2s_mic_bit_div_clk.clkr,
[CODEC_I2S_MIC_BIT_CLK] = &codec_i2s_mic_bit_clk.clkr,
[SPARE_I2S_MIC_OSR_SRC] = &spare_i2s_mic_osr_src.clkr,
[SPARE_I2S_MIC_OSR_CLK] = &spare_i2s_mic_osr_clk.clkr,
[SPARE_I2S_MIC_DIV_CLK] = &spare_i2s_mic_div_clk.clkr,
[SPARE_I2S_MIC_BIT_DIV_CLK] = &spare_i2s_mic_bit_div_clk.clkr,
[SPARE_I2S_MIC_BIT_CLK] = &spare_i2s_mic_bit_clk.clkr,
[CODEC_I2S_SPKR_OSR_SRC] = &codec_i2s_spkr_osr_src.clkr,
[CODEC_I2S_SPKR_OSR_CLK] = &codec_i2s_spkr_osr_clk.clkr,
[CODEC_I2S_SPKR_DIV_CLK] = &codec_i2s_spkr_div_clk.clkr,
[CODEC_I2S_SPKR_BIT_DIV_CLK] = &codec_i2s_spkr_bit_div_clk.clkr,
[CODEC_I2S_SPKR_BIT_CLK] = &codec_i2s_spkr_bit_clk.clkr,
[SPARE_I2S_SPKR_OSR_SRC] = &spare_i2s_spkr_osr_src.clkr,
[SPARE_I2S_SPKR_OSR_CLK] = &spare_i2s_spkr_osr_clk.clkr,
[SPARE_I2S_SPKR_DIV_CLK] = &spare_i2s_spkr_div_clk.clkr,
[SPARE_I2S_SPKR_BIT_DIV_CLK] = &spare_i2s_spkr_bit_div_clk.clkr,
[SPARE_I2S_SPKR_BIT_CLK] = &spare_i2s_spkr_bit_clk.clkr,
};
static const struct regmap_config lcc_msm8960_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = 0xfc,
.fast_io = true,
};
static const struct qcom_cc_desc lcc_msm8960_desc = {
.config = &lcc_msm8960_regmap_config,
.clks = lcc_msm8960_clks,
.num_clks = ARRAY_SIZE(lcc_msm8960_clks),
};
static const struct of_device_id lcc_msm8960_match_table[] = {
{ .compatible = "qcom,lcc-msm8960" },
{ .compatible = "qcom,lcc-apq8064" },
{ }
};
MODULE_DEVICE_TABLE(of, lcc_msm8960_match_table);
static int lcc_msm8960_probe(struct platform_device *pdev)
{
u32 val;
struct regmap *regmap;
regmap = qcom_cc_map(pdev, &lcc_msm8960_desc);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
/* Use the correct frequency plan depending on speed of PLL4 */
val = regmap_read(regmap, 0x4, &val);
if (val == 0x12) {
slimbus_src.freq_tbl = clk_tbl_aif_osr_492;
mi2s_osr_src.freq_tbl = clk_tbl_aif_osr_492;
codec_i2s_mic_osr_src.freq_tbl = clk_tbl_aif_osr_492;
spare_i2s_mic_osr_src.freq_tbl = clk_tbl_aif_osr_492;
codec_i2s_spkr_osr_src.freq_tbl = clk_tbl_aif_osr_492;
spare_i2s_spkr_osr_src.freq_tbl = clk_tbl_aif_osr_492;
pcm_src.freq_tbl = clk_tbl_pcm_492;
}
/* Enable PLL4 source on the LPASS Primary PLL Mux */
regmap_write(regmap, 0xc4, 0x1);
return qcom_cc_really_probe(pdev, &lcc_msm8960_desc, regmap);
}
static int lcc_msm8960_remove(struct platform_device *pdev)
{
qcom_cc_remove(pdev);
return 0;
}
static struct platform_driver lcc_msm8960_driver = {
.probe = lcc_msm8960_probe,
.remove = lcc_msm8960_remove,
.driver = {
.name = "lcc-msm8960",
.owner = THIS_MODULE,
.of_match_table = lcc_msm8960_match_table,
},
};
module_platform_driver(lcc_msm8960_driver);
MODULE_DESCRIPTION("QCOM LCC MSM8960 Driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:lcc-msm8960");

View File

@ -535,44 +535,44 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = {
COMPOSITE(0, "uart0_src", mux_pll_src_cpll_gll_usb_npll_p, 0,
RK3288_CLKSEL_CON(13), 13, 2, MFLAGS, 0, 7, DFLAGS,
RK3288_CLKGATE_CON(1), 8, GFLAGS),
COMPOSITE_FRAC(0, "uart0_frac", "uart0_src", 0,
COMPOSITE_FRAC(0, "uart0_frac", "uart0_src", CLK_SET_RATE_PARENT,
RK3288_CLKSEL_CON(17), 0,
RK3288_CLKGATE_CON(1), 9, GFLAGS),
MUX(SCLK_UART0, "sclk_uart0", mux_uart0_p, 0,
MUX(SCLK_UART0, "sclk_uart0", mux_uart0_p, CLK_SET_RATE_PARENT,
RK3288_CLKSEL_CON(13), 8, 2, MFLAGS),
MUX(0, "uart_src", mux_pll_src_cpll_gpll_p, 0,
RK3288_CLKSEL_CON(13), 15, 1, MFLAGS),
COMPOSITE_NOMUX(0, "uart1_src", "uart_src", 0,
RK3288_CLKSEL_CON(14), 0, 7, DFLAGS,
RK3288_CLKGATE_CON(1), 10, GFLAGS),
COMPOSITE_FRAC(0, "uart1_frac", "uart1_src", 0,
COMPOSITE_FRAC(0, "uart1_frac", "uart1_src", CLK_SET_RATE_PARENT,
RK3288_CLKSEL_CON(18), 0,
RK3288_CLKGATE_CON(1), 11, GFLAGS),
MUX(SCLK_UART1, "sclk_uart1", mux_uart1_p, 0,
MUX(SCLK_UART1, "sclk_uart1", mux_uart1_p, CLK_SET_RATE_PARENT,
RK3288_CLKSEL_CON(14), 8, 2, MFLAGS),
COMPOSITE_NOMUX(0, "uart2_src", "uart_src", 0,
RK3288_CLKSEL_CON(15), 0, 7, DFLAGS,
RK3288_CLKGATE_CON(1), 12, GFLAGS),
COMPOSITE_FRAC(0, "uart2_frac", "uart2_src", 0,
COMPOSITE_FRAC(0, "uart2_frac", "uart2_src", CLK_SET_RATE_PARENT,
RK3288_CLKSEL_CON(19), 0,
RK3288_CLKGATE_CON(1), 13, GFLAGS),
MUX(SCLK_UART2, "sclk_uart2", mux_uart2_p, 0,
MUX(SCLK_UART2, "sclk_uart2", mux_uart2_p, CLK_SET_RATE_PARENT,
RK3288_CLKSEL_CON(15), 8, 2, MFLAGS),
COMPOSITE_NOMUX(0, "uart3_src", "uart_src", 0,
RK3288_CLKSEL_CON(16), 0, 7, DFLAGS,
RK3288_CLKGATE_CON(1), 14, GFLAGS),
COMPOSITE_FRAC(0, "uart3_frac", "uart3_src", 0,
COMPOSITE_FRAC(0, "uart3_frac", "uart3_src", CLK_SET_RATE_PARENT,
RK3288_CLKSEL_CON(20), 0,
RK3288_CLKGATE_CON(1), 15, GFLAGS),
MUX(SCLK_UART3, "sclk_uart3", mux_uart3_p, 0,
MUX(SCLK_UART3, "sclk_uart3", mux_uart3_p, CLK_SET_RATE_PARENT,
RK3288_CLKSEL_CON(16), 8, 2, MFLAGS),
COMPOSITE_NOMUX(0, "uart4_src", "uart_src", 0,
RK3288_CLKSEL_CON(3), 0, 7, DFLAGS,
RK3288_CLKGATE_CON(2), 12, GFLAGS),
COMPOSITE_FRAC(0, "uart4_frac", "uart4_src", 0,
COMPOSITE_FRAC(0, "uart4_frac", "uart4_src", CLK_SET_RATE_PARENT,
RK3288_CLKSEL_CON(7), 0,
RK3288_CLKGATE_CON(2), 13, GFLAGS),
MUX(SCLK_UART4, "sclk_uart4", mux_uart4_p, 0,
MUX(SCLK_UART4, "sclk_uart4", mux_uart4_p, CLK_SET_RATE_PARENT,
RK3288_CLKSEL_CON(3), 8, 2, MFLAGS),
COMPOSITE(0, "mac_src", mux_pll_src_npll_cpll_gpll_p, 0,
@ -598,7 +598,7 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = {
GATE(0, "jtag", "ext_jtag", 0,
RK3288_CLKGATE_CON(4), 14, GFLAGS),
COMPOSITE_NODIV(0, "usbphy480m_src", mux_usbphy480m_p, 0,
COMPOSITE_NODIV(SCLK_USBPHY480M_SRC, "usbphy480m_src", mux_usbphy480m_p, 0,
RK3288_CLKSEL_CON(13), 11, 2, MFLAGS,
RK3288_CLKGATE_CON(5), 14, GFLAGS),
COMPOSITE_NODIV(SCLK_HSICPHY480M, "sclk_hsicphy480m", mux_hsicphy480m_p, 0,
@ -704,8 +704,8 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = {
GATE(SCLK_LCDC_PWM0, "sclk_lcdc_pwm0", "xin24m", 0, RK3288_CLKGATE_CON(13), 10, GFLAGS),
GATE(SCLK_LCDC_PWM1, "sclk_lcdc_pwm1", "xin24m", 0, RK3288_CLKGATE_CON(13), 11, GFLAGS),
GATE(0, "sclk_pvtm_core", "xin24m", 0, RK3288_CLKGATE_CON(5), 9, GFLAGS),
GATE(0, "sclk_pvtm_gpu", "xin24m", 0, RK3288_CLKGATE_CON(5), 10, GFLAGS),
GATE(SCLK_PVTM_CORE, "sclk_pvtm_core", "xin24m", 0, RK3288_CLKGATE_CON(5), 9, GFLAGS),
GATE(SCLK_PVTM_GPU, "sclk_pvtm_gpu", "xin24m", 0, RK3288_CLKGATE_CON(5), 10, GFLAGS),
GATE(0, "sclk_mipidsi_24m", "xin24m", 0, RK3288_CLKGATE_CON(5), 15, GFLAGS),
/* sclk_gpu gates */
@ -805,6 +805,20 @@ static int rk3288_clk_suspend(void)
rk3288_saved_cru_regs[i] =
readl_relaxed(rk3288_cru_base + reg_id);
}
/*
* Switch PLLs other than DPLL (for SDRAM) to slow mode to
* avoid crashes on resume. The Mask ROM on the system will
* put APLL, CPLL, and GPLL into slow mode at resume time
* anyway (which is why we restore them), but we might not
* even make it to the Mask ROM if this isn't done at suspend
* time.
*
* NOTE: only APLL truly matters here, but we'll do them all.
*/
writel_relaxed(0xf3030000, rk3288_cru_base + RK3288_MODE_CON);
return 0;
}
@ -866,6 +880,14 @@ static void __init rk3288_clk_init(struct device_node *np)
pr_warn("%s: could not register clock hclk_vcodec_pre: %ld\n",
__func__, PTR_ERR(clk));
/* Watchdog pclk is controlled by RK3288_SGRF_SOC_CON0[1]. */
clk = clk_register_fixed_factor(NULL, "pclk_wdt", "pclk_pd_alive", 0, 1, 1);
if (IS_ERR(clk))
pr_warn("%s: could not register clock pclk_wdt: %ld\n",
__func__, PTR_ERR(clk));
else
rockchip_clk_add_lookup(clk, PCLK_WDT);
rockchip_clk_register_plls(rk3288_pll_clks,
ARRAY_SIZE(rk3288_pll_clks),
RK3288_GRF_SOC_STATUS1);

View File

@ -82,6 +82,26 @@ static const struct of_device_id exynos_audss_clk_of_match[] = {
{},
};
static void exynos_audss_clk_teardown(void)
{
int i;
for (i = EXYNOS_MOUT_AUDSS; i < EXYNOS_DOUT_SRP; i++) {
if (!IS_ERR(clk_table[i]))
clk_unregister_mux(clk_table[i]);
}
for (; i < EXYNOS_SRP_CLK; i++) {
if (!IS_ERR(clk_table[i]))
clk_unregister_divider(clk_table[i]);
}
for (; i < clk_data.clk_num; i++) {
if (!IS_ERR(clk_table[i]))
clk_unregister_gate(clk_table[i]);
}
}
/* register exynos_audss clocks */
static int exynos_audss_clk_probe(struct platform_device *pdev)
{
@ -219,10 +239,7 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
return 0;
unregister:
for (i = 0; i < clk_data.clk_num; i++) {
if (!IS_ERR(clk_table[i]))
clk_unregister(clk_table[i]);
}
exynos_audss_clk_teardown();
if (!IS_ERR(epll))
clk_disable_unprepare(epll);
@ -232,18 +249,13 @@ unregister:
static int exynos_audss_clk_remove(struct platform_device *pdev)
{
int i;
#ifdef CONFIG_PM_SLEEP
unregister_syscore_ops(&exynos_audss_clk_syscore_ops);
#endif
of_clk_del_provider(pdev->dev.of_node);
for (i = 0; i < clk_data.clk_num; i++) {
if (!IS_ERR(clk_table[i]))
clk_unregister(clk_table[i]);
}
exynos_audss_clk_teardown();
if (!IS_ERR(epll))
clk_disable_unprepare(epll);

View File

@ -104,27 +104,6 @@
#define PWR_CTRL1_USE_CORE1_WFI (1 << 1)
#define PWR_CTRL1_USE_CORE0_WFI (1 << 0)
/* list of PLLs to be registered */
enum exynos3250_plls {
apll, mpll, vpll, upll,
nr_plls
};
/* list of PLLs in DMC block to be registered */
enum exynos3250_dmc_plls {
bpll, epll,
nr_dmc_plls
};
static void __iomem *reg_base;
static void __iomem *dmc_reg_base;
/*
* Support for CMU save/restore across system suspends
*/
#ifdef CONFIG_PM_SLEEP
static struct samsung_clk_reg_dump *exynos3250_clk_regs;
static unsigned long exynos3250_cmu_clk_regs[] __initdata = {
SRC_LEFTBUS,
DIV_LEFTBUS,
@ -195,43 +174,6 @@ static unsigned long exynos3250_cmu_clk_regs[] __initdata = {
PWR_CTRL2,
};
static int exynos3250_clk_suspend(void)
{
samsung_clk_save(reg_base, exynos3250_clk_regs,
ARRAY_SIZE(exynos3250_cmu_clk_regs));
return 0;
}
static void exynos3250_clk_resume(void)
{
samsung_clk_restore(reg_base, exynos3250_clk_regs,
ARRAY_SIZE(exynos3250_cmu_clk_regs));
}
static struct syscore_ops exynos3250_clk_syscore_ops = {
.suspend = exynos3250_clk_suspend,
.resume = exynos3250_clk_resume,
};
static void exynos3250_clk_sleep_init(void)
{
exynos3250_clk_regs =
samsung_clk_alloc_reg_dump(exynos3250_cmu_clk_regs,
ARRAY_SIZE(exynos3250_cmu_clk_regs));
if (!exynos3250_clk_regs) {
pr_warn("%s: Failed to allocate sleep save data\n", __func__);
goto err;
}
register_syscore_ops(&exynos3250_clk_syscore_ops);
return;
err:
kfree(exynos3250_clk_regs);
}
#else
static inline void exynos3250_clk_sleep_init(void) { }
#endif
/* list of all parent clock list */
PNAME(mout_vpllsrc_p) = { "fin_pll", };
@ -782,18 +724,18 @@ static struct samsung_pll_rate_table exynos3250_vpll_rates[] = {
{ /* sentinel */ }
};
static struct samsung_pll_clock exynos3250_plls[nr_plls] __initdata = {
[apll] = PLL(pll_35xx, CLK_FOUT_APLL, "fout_apll", "fin_pll",
APLL_LOCK, APLL_CON0, NULL),
[mpll] = PLL(pll_35xx, CLK_FOUT_MPLL, "fout_mpll", "fin_pll",
MPLL_LOCK, MPLL_CON0, NULL),
[vpll] = PLL(pll_36xx, CLK_FOUT_VPLL, "fout_vpll", "fin_pll",
VPLL_LOCK, VPLL_CON0, NULL),
[upll] = PLL(pll_35xx, CLK_FOUT_UPLL, "fout_upll", "fin_pll",
UPLL_LOCK, UPLL_CON0, NULL),
static struct samsung_pll_clock exynos3250_plls[] __initdata = {
PLL(pll_35xx, CLK_FOUT_APLL, "fout_apll", "fin_pll",
APLL_LOCK, APLL_CON0, exynos3250_pll_rates),
PLL(pll_35xx, CLK_FOUT_MPLL, "fout_mpll", "fin_pll",
MPLL_LOCK, MPLL_CON0, exynos3250_pll_rates),
PLL(pll_36xx, CLK_FOUT_VPLL, "fout_vpll", "fin_pll",
VPLL_LOCK, VPLL_CON0, exynos3250_vpll_rates),
PLL(pll_35xx, CLK_FOUT_UPLL, "fout_upll", "fin_pll",
UPLL_LOCK, UPLL_CON0, exynos3250_pll_rates),
};
static void __init exynos3_core_down_clock(void)
static void __init exynos3_core_down_clock(void __iomem *reg_base)
{
unsigned int tmp;
@ -814,38 +756,31 @@ static void __init exynos3_core_down_clock(void)
__raw_writel(0x0, reg_base + PWR_CTRL2);
}
static struct samsung_cmu_info cmu_info __initdata = {
.pll_clks = exynos3250_plls,
.nr_pll_clks = ARRAY_SIZE(exynos3250_plls),
.mux_clks = mux_clks,
.nr_mux_clks = ARRAY_SIZE(mux_clks),
.div_clks = div_clks,
.nr_div_clks = ARRAY_SIZE(div_clks),
.gate_clks = gate_clks,
.nr_gate_clks = ARRAY_SIZE(gate_clks),
.fixed_factor_clks = fixed_factor_clks,
.nr_fixed_factor_clks = ARRAY_SIZE(fixed_factor_clks),
.nr_clk_ids = CLK_NR_CLKS,
.clk_regs = exynos3250_cmu_clk_regs,
.nr_clk_regs = ARRAY_SIZE(exynos3250_cmu_clk_regs),
};
static void __init exynos3250_cmu_init(struct device_node *np)
{
struct samsung_clk_provider *ctx;
reg_base = of_iomap(np, 0);
if (!reg_base)
panic("%s: failed to map registers\n", __func__);
ctx = samsung_clk_init(np, reg_base, CLK_NR_CLKS);
ctx = samsung_cmu_register_one(np, &cmu_info);
if (!ctx)
panic("%s: unable to allocate context.\n", __func__);
return;
samsung_clk_register_fixed_factor(ctx, fixed_factor_clks,
ARRAY_SIZE(fixed_factor_clks));
exynos3250_plls[apll].rate_table = exynos3250_pll_rates;
exynos3250_plls[mpll].rate_table = exynos3250_pll_rates;
exynos3250_plls[vpll].rate_table = exynos3250_vpll_rates;
exynos3250_plls[upll].rate_table = exynos3250_pll_rates;
samsung_clk_register_pll(ctx, exynos3250_plls,
ARRAY_SIZE(exynos3250_plls), reg_base);
samsung_clk_register_mux(ctx, mux_clks, ARRAY_SIZE(mux_clks));
samsung_clk_register_div(ctx, div_clks, ARRAY_SIZE(div_clks));
samsung_clk_register_gate(ctx, gate_clks, ARRAY_SIZE(gate_clks));
exynos3_core_down_clock();
exynos3250_clk_sleep_init();
samsung_clk_of_add_provider(np, ctx);
exynos3_core_down_clock(ctx->reg_base);
}
CLK_OF_DECLARE(exynos3250_cmu, "samsung,exynos3250-cmu", exynos3250_cmu_init);
@ -872,12 +807,6 @@ CLK_OF_DECLARE(exynos3250_cmu, "samsung,exynos3250-cmu", exynos3250_cmu_init);
#define EPLL_CON2 0x111c
#define SRC_EPLL 0x1120
/*
* Support for CMU save/restore across system suspends
*/
#ifdef CONFIG_PM_SLEEP
static struct samsung_clk_reg_dump *exynos3250_dmc_clk_regs;
static unsigned long exynos3250_cmu_dmc_clk_regs[] __initdata = {
BPLL_LOCK,
BPLL_CON0,
@ -899,43 +828,6 @@ static unsigned long exynos3250_cmu_dmc_clk_regs[] __initdata = {
SRC_EPLL,
};
static int exynos3250_dmc_clk_suspend(void)
{
samsung_clk_save(dmc_reg_base, exynos3250_dmc_clk_regs,
ARRAY_SIZE(exynos3250_cmu_dmc_clk_regs));
return 0;
}
static void exynos3250_dmc_clk_resume(void)
{
samsung_clk_restore(dmc_reg_base, exynos3250_dmc_clk_regs,
ARRAY_SIZE(exynos3250_cmu_dmc_clk_regs));
}
static struct syscore_ops exynos3250_dmc_clk_syscore_ops = {
.suspend = exynos3250_dmc_clk_suspend,
.resume = exynos3250_dmc_clk_resume,
};
static void exynos3250_dmc_clk_sleep_init(void)
{
exynos3250_dmc_clk_regs =
samsung_clk_alloc_reg_dump(exynos3250_cmu_dmc_clk_regs,
ARRAY_SIZE(exynos3250_cmu_dmc_clk_regs));
if (!exynos3250_dmc_clk_regs) {
pr_warn("%s: Failed to allocate sleep save data\n", __func__);
goto err;
}
register_syscore_ops(&exynos3250_dmc_clk_syscore_ops);
return;
err:
kfree(exynos3250_dmc_clk_regs);
}
#else
static inline void exynos3250_dmc_clk_sleep_init(void) { }
#endif
PNAME(mout_epll_p) = { "fin_pll", "fout_epll", };
PNAME(mout_bpll_p) = { "fin_pll", "fout_bpll", };
PNAME(mout_mpll_mif_p) = { "fin_pll", "sclk_mpll_mif", };
@ -977,43 +869,28 @@ static struct samsung_div_clock dmc_div_clks[] __initdata = {
DIV(CLK_DIV_DMCD, "div_dmcd", "div_dmc", DIV_DMC1, 11, 3),
};
static struct samsung_pll_clock exynos3250_dmc_plls[nr_dmc_plls] __initdata = {
[bpll] = PLL(pll_35xx, CLK_FOUT_BPLL, "fout_bpll", "fin_pll",
BPLL_LOCK, BPLL_CON0, NULL),
[epll] = PLL(pll_36xx, CLK_FOUT_EPLL, "fout_epll", "fin_pll",
EPLL_LOCK, EPLL_CON0, NULL),
static struct samsung_pll_clock exynos3250_dmc_plls[] __initdata = {
PLL(pll_35xx, CLK_FOUT_BPLL, "fout_bpll", "fin_pll",
BPLL_LOCK, BPLL_CON0, exynos3250_pll_rates),
PLL(pll_36xx, CLK_FOUT_EPLL, "fout_epll", "fin_pll",
EPLL_LOCK, EPLL_CON0, exynos3250_epll_rates),
};
static struct samsung_cmu_info dmc_cmu_info __initdata = {
.pll_clks = exynos3250_dmc_plls,
.nr_pll_clks = ARRAY_SIZE(exynos3250_dmc_plls),
.mux_clks = dmc_mux_clks,
.nr_mux_clks = ARRAY_SIZE(dmc_mux_clks),
.div_clks = dmc_div_clks,
.nr_div_clks = ARRAY_SIZE(dmc_div_clks),
.nr_clk_ids = NR_CLKS_DMC,
.clk_regs = exynos3250_cmu_dmc_clk_regs,
.nr_clk_regs = ARRAY_SIZE(exynos3250_cmu_dmc_clk_regs),
};
static void __init exynos3250_cmu_dmc_init(struct device_node *np)
{
struct samsung_clk_provider *ctx;
dmc_reg_base = of_iomap(np, 0);
if (!dmc_reg_base)
panic("%s: failed to map registers\n", __func__);
ctx = samsung_clk_init(np, dmc_reg_base, NR_CLKS_DMC);
if (!ctx)
panic("%s: unable to allocate context.\n", __func__);
exynos3250_dmc_plls[bpll].rate_table = exynos3250_pll_rates;
exynos3250_dmc_plls[epll].rate_table = exynos3250_epll_rates;
pr_err("CLK registering epll bpll: %d, %d, %d, %d\n",
exynos3250_dmc_plls[bpll].rate_table[0].rate,
exynos3250_dmc_plls[bpll].rate_table[0].mdiv,
exynos3250_dmc_plls[bpll].rate_table[0].pdiv,
exynos3250_dmc_plls[bpll].rate_table[0].sdiv
);
samsung_clk_register_pll(ctx, exynos3250_dmc_plls,
ARRAY_SIZE(exynos3250_dmc_plls), dmc_reg_base);
samsung_clk_register_mux(ctx, dmc_mux_clks, ARRAY_SIZE(dmc_mux_clks));
samsung_clk_register_div(ctx, dmc_div_clks, ARRAY_SIZE(dmc_div_clks));
exynos3250_dmc_clk_sleep_init();
samsung_clk_of_add_provider(np, ctx);
samsung_cmu_register_one(np, &dmc_cmu_info);
}
CLK_OF_DECLARE(exynos3250_cmu_dmc, "samsung,exynos3250-cmu-dmc",
exynos3250_cmu_dmc_init);

View File

@ -703,12 +703,12 @@ static struct samsung_mux_clock exynos4x12_mux_clks[] __initdata = {
/* list of divider clocks supported in all exynos4 soc's */
static struct samsung_div_clock exynos4_div_clks[] __initdata = {
DIV(0, "div_gdl", "mout_gdl", DIV_LEFTBUS, 0, 3),
DIV(CLK_DIV_GDL, "div_gdl", "mout_gdl", DIV_LEFTBUS, 0, 3),
DIV(0, "div_gpl", "div_gdl", DIV_LEFTBUS, 4, 3),
DIV(0, "div_clkout_leftbus", "mout_clkout_leftbus",
CLKOUT_CMU_LEFTBUS, 8, 6),
DIV(0, "div_gdr", "mout_gdr", DIV_RIGHTBUS, 0, 3),
DIV(CLK_DIV_GDR, "div_gdr", "mout_gdr", DIV_RIGHTBUS, 0, 3),
DIV(0, "div_gpr", "div_gdr", DIV_RIGHTBUS, 4, 3),
DIV(0, "div_clkout_rightbus", "mout_clkout_rightbus",
CLKOUT_CMU_RIGHTBUS, 8, 6),
@ -781,10 +781,10 @@ static struct samsung_div_clock exynos4_div_clks[] __initdata = {
CLK_SET_RATE_PARENT, 0),
DIV(0, "div_clkout_top", "mout_clkout_top", CLKOUT_CMU_TOP, 8, 6),
DIV(0, "div_acp", "mout_dmc_bus", DIV_DMC0, 0, 3),
DIV(CLK_DIV_ACP, "div_acp", "mout_dmc_bus", DIV_DMC0, 0, 3),
DIV(0, "div_acp_pclk", "div_acp", DIV_DMC0, 4, 3),
DIV(0, "div_dphy", "mout_dphy", DIV_DMC0, 8, 3),
DIV(0, "div_dmc", "mout_dmc_bus", DIV_DMC0, 12, 3),
DIV(CLK_DIV_DMC, "div_dmc", "mout_dmc_bus", DIV_DMC0, 12, 3),
DIV(0, "div_dmcd", "div_dmc", DIV_DMC0, 16, 3),
DIV(0, "div_dmcp", "div_dmcd", DIV_DMC0, 20, 3),
DIV(0, "div_pwi", "mout_pwi", DIV_DMC1, 8, 4),
@ -829,7 +829,7 @@ static struct samsung_div_clock exynos4x12_div_clks[] __initdata = {
DIV_F(CLK_DIV_MCUISP1, "div_mcuisp1", "div_mcuisp0", E4X12_DIV_ISP1,
8, 3, CLK_GET_RATE_NOCACHE, 0),
DIV(CLK_SCLK_FIMG2D, "sclk_fimg2d", "mout_g2d", DIV_DMC1, 0, 4),
DIV(0, "div_c2c", "mout_c2c", DIV_DMC1, 4, 3),
DIV(CLK_DIV_C2C, "div_c2c", "mout_c2c", DIV_DMC1, 4, 3),
DIV(0, "div_c2c_aclk", "div_c2c", DIV_DMC1, 12, 3),
};

View File

@ -113,19 +113,6 @@
#define DIV_CPU0 0x14500
#define DIV_CPU1 0x14504
enum exynos4415_plls {
apll, epll, g3d_pll, isp_pll, disp_pll,
nr_plls,
};
static struct samsung_clk_provider *exynos4415_ctx;
/*
* Support for CMU save/restore across system suspends
*/
#ifdef CONFIG_PM_SLEEP
static struct samsung_clk_reg_dump *exynos4415_clk_regs;
static unsigned long exynos4415_cmu_clk_regs[] __initdata = {
SRC_LEFTBUS,
DIV_LEFTBUS,
@ -219,41 +206,6 @@ static unsigned long exynos4415_cmu_clk_regs[] __initdata = {
DIV_CPU1,
};
static int exynos4415_clk_suspend(void)
{
samsung_clk_save(exynos4415_ctx->reg_base, exynos4415_clk_regs,
ARRAY_SIZE(exynos4415_cmu_clk_regs));
return 0;
}
static void exynos4415_clk_resume(void)
{
samsung_clk_restore(exynos4415_ctx->reg_base, exynos4415_clk_regs,
ARRAY_SIZE(exynos4415_cmu_clk_regs));
}
static struct syscore_ops exynos4415_clk_syscore_ops = {
.suspend = exynos4415_clk_suspend,
.resume = exynos4415_clk_resume,
};
static void exynos4415_clk_sleep_init(void)
{
exynos4415_clk_regs =
samsung_clk_alloc_reg_dump(exynos4415_cmu_clk_regs,
ARRAY_SIZE(exynos4415_cmu_clk_regs));
if (!exynos4415_clk_regs) {
pr_warn("%s: Failed to allocate sleep save data\n", __func__);
return;
}
register_syscore_ops(&exynos4415_clk_syscore_ops);
}
#else
static inline void exynos4415_clk_sleep_init(void) { }
#endif
/* list of all parent clock list */
PNAME(mout_g3d_pllsrc_p) = { "fin_pll", };
@ -959,56 +911,40 @@ static struct samsung_pll_rate_table exynos4415_epll_rates[] = {
{ /* sentinel */ }
};
static struct samsung_pll_clock exynos4415_plls[nr_plls] __initdata = {
[apll] = PLL(pll_35xx, CLK_FOUT_APLL, "fout_apll", "fin_pll",
APLL_LOCK, APLL_CON0, NULL),
[epll] = PLL(pll_36xx, CLK_FOUT_EPLL, "fout_epll", "fin_pll",
EPLL_LOCK, EPLL_CON0, NULL),
[g3d_pll] = PLL(pll_35xx, CLK_FOUT_G3D_PLL, "fout_g3d_pll",
"mout_g3d_pllsrc", G3D_PLL_LOCK, G3D_PLL_CON0, NULL),
[isp_pll] = PLL(pll_35xx, CLK_FOUT_ISP_PLL, "fout_isp_pll", "fin_pll",
ISP_PLL_LOCK, ISP_PLL_CON0, NULL),
[disp_pll] = PLL(pll_35xx, CLK_FOUT_DISP_PLL, "fout_disp_pll",
"fin_pll", DISP_PLL_LOCK, DISP_PLL_CON0, NULL),
static struct samsung_pll_clock exynos4415_plls[] __initdata = {
PLL(pll_35xx, CLK_FOUT_APLL, "fout_apll", "fin_pll",
APLL_LOCK, APLL_CON0, exynos4415_pll_rates),
PLL(pll_36xx, CLK_FOUT_EPLL, "fout_epll", "fin_pll",
EPLL_LOCK, EPLL_CON0, exynos4415_epll_rates),
PLL(pll_35xx, CLK_FOUT_G3D_PLL, "fout_g3d_pll", "mout_g3d_pllsrc",
G3D_PLL_LOCK, G3D_PLL_CON0, exynos4415_pll_rates),
PLL(pll_35xx, CLK_FOUT_ISP_PLL, "fout_isp_pll", "fin_pll",
ISP_PLL_LOCK, ISP_PLL_CON0, exynos4415_pll_rates),
PLL(pll_35xx, CLK_FOUT_DISP_PLL, "fout_disp_pll",
"fin_pll", DISP_PLL_LOCK, DISP_PLL_CON0, exynos4415_pll_rates),
};
static struct samsung_cmu_info cmu_info __initdata = {
.pll_clks = exynos4415_plls,
.nr_pll_clks = ARRAY_SIZE(exynos4415_plls),
.mux_clks = exynos4415_mux_clks,
.nr_mux_clks = ARRAY_SIZE(exynos4415_mux_clks),
.div_clks = exynos4415_div_clks,
.nr_div_clks = ARRAY_SIZE(exynos4415_div_clks),
.gate_clks = exynos4415_gate_clks,
.nr_gate_clks = ARRAY_SIZE(exynos4415_gate_clks),
.fixed_clks = exynos4415_fixed_rate_clks,
.nr_fixed_clks = ARRAY_SIZE(exynos4415_fixed_rate_clks),
.fixed_factor_clks = exynos4415_fixed_factor_clks,
.nr_fixed_factor_clks = ARRAY_SIZE(exynos4415_fixed_factor_clks),
.nr_clk_ids = CLK_NR_CLKS,
.clk_regs = exynos4415_cmu_clk_regs,
.nr_clk_regs = ARRAY_SIZE(exynos4415_cmu_clk_regs),
};
static void __init exynos4415_cmu_init(struct device_node *np)
{
void __iomem *reg_base;
reg_base = of_iomap(np, 0);
if (!reg_base)
panic("%s: failed to map registers\n", __func__);
exynos4415_ctx = samsung_clk_init(np, reg_base, CLK_NR_CLKS);
if (!exynos4415_ctx)
panic("%s: unable to allocate context.\n", __func__);
exynos4415_plls[apll].rate_table = exynos4415_pll_rates;
exynos4415_plls[epll].rate_table = exynos4415_epll_rates;
exynos4415_plls[g3d_pll].rate_table = exynos4415_pll_rates;
exynos4415_plls[isp_pll].rate_table = exynos4415_pll_rates;
exynos4415_plls[disp_pll].rate_table = exynos4415_pll_rates;
samsung_clk_register_fixed_factor(exynos4415_ctx,
exynos4415_fixed_factor_clks,
ARRAY_SIZE(exynos4415_fixed_factor_clks));
samsung_clk_register_fixed_rate(exynos4415_ctx,
exynos4415_fixed_rate_clks,
ARRAY_SIZE(exynos4415_fixed_rate_clks));
samsung_clk_register_pll(exynos4415_ctx, exynos4415_plls,
ARRAY_SIZE(exynos4415_plls), reg_base);
samsung_clk_register_mux(exynos4415_ctx, exynos4415_mux_clks,
ARRAY_SIZE(exynos4415_mux_clks));
samsung_clk_register_div(exynos4415_ctx, exynos4415_div_clks,
ARRAY_SIZE(exynos4415_div_clks));
samsung_clk_register_gate(exynos4415_ctx, exynos4415_gate_clks,
ARRAY_SIZE(exynos4415_gate_clks));
exynos4415_clk_sleep_init();
samsung_clk_of_add_provider(np, exynos4415_ctx);
samsung_cmu_register_one(np, &cmu_info);
}
CLK_OF_DECLARE(exynos4415_cmu, "samsung,exynos4415-cmu", exynos4415_cmu_init);
@ -1027,16 +963,6 @@ CLK_OF_DECLARE(exynos4415_cmu, "samsung,exynos4415-cmu", exynos4415_cmu_init);
#define SRC_DMC 0x300
#define DIV_DMC1 0x504
enum exynos4415_dmc_plls {
mpll, bpll,
nr_dmc_plls,
};
static struct samsung_clk_provider *exynos4415_dmc_ctx;
#ifdef CONFIG_PM_SLEEP
static struct samsung_clk_reg_dump *exynos4415_dmc_clk_regs;
static unsigned long exynos4415_cmu_dmc_clk_regs[] __initdata = {
MPLL_LOCK,
MPLL_CON0,
@ -1050,42 +976,6 @@ static unsigned long exynos4415_cmu_dmc_clk_regs[] __initdata = {
DIV_DMC1,
};
static int exynos4415_dmc_clk_suspend(void)
{
samsung_clk_save(exynos4415_dmc_ctx->reg_base,
exynos4415_dmc_clk_regs,
ARRAY_SIZE(exynos4415_cmu_dmc_clk_regs));
return 0;
}
static void exynos4415_dmc_clk_resume(void)
{
samsung_clk_restore(exynos4415_dmc_ctx->reg_base,
exynos4415_dmc_clk_regs,
ARRAY_SIZE(exynos4415_cmu_dmc_clk_regs));
}
static struct syscore_ops exynos4415_dmc_clk_syscore_ops = {
.suspend = exynos4415_dmc_clk_suspend,
.resume = exynos4415_dmc_clk_resume,
};
static void exynos4415_dmc_clk_sleep_init(void)
{
exynos4415_dmc_clk_regs =
samsung_clk_alloc_reg_dump(exynos4415_cmu_dmc_clk_regs,
ARRAY_SIZE(exynos4415_cmu_dmc_clk_regs));
if (!exynos4415_dmc_clk_regs) {
pr_warn("%s: Failed to allocate sleep save data\n", __func__);
return;
}
register_syscore_ops(&exynos4415_dmc_clk_syscore_ops);
}
#else
static inline void exynos4415_dmc_clk_sleep_init(void) { }
#endif /* CONFIG_PM_SLEEP */
PNAME(mout_mpll_p) = { "fin_pll", "fout_mpll", };
PNAME(mout_bpll_p) = { "fin_pll", "fout_bpll", };
PNAME(mbpll_p) = { "mout_mpll", "mout_bpll", };
@ -1107,38 +997,28 @@ static struct samsung_div_clock exynos4415_dmc_div_clks[] __initdata = {
DIV(CLK_DMC_DIV_MPLL_PRE, "div_mpll_pre", "mout_mpll", DIV_DMC1, 8, 2),
};
static struct samsung_pll_clock exynos4415_dmc_plls[nr_dmc_plls] __initdata = {
[mpll] = PLL(pll_35xx, CLK_DMC_FOUT_MPLL, "fout_mpll", "fin_pll",
MPLL_LOCK, MPLL_CON0, NULL),
[bpll] = PLL(pll_35xx, CLK_DMC_FOUT_BPLL, "fout_bpll", "fin_pll",
BPLL_LOCK, BPLL_CON0, NULL),
static struct samsung_pll_clock exynos4415_dmc_plls[] __initdata = {
PLL(pll_35xx, CLK_DMC_FOUT_MPLL, "fout_mpll", "fin_pll",
MPLL_LOCK, MPLL_CON0, exynos4415_pll_rates),
PLL(pll_35xx, CLK_DMC_FOUT_BPLL, "fout_bpll", "fin_pll",
BPLL_LOCK, BPLL_CON0, exynos4415_pll_rates),
};
static struct samsung_cmu_info cmu_dmc_info __initdata = {
.pll_clks = exynos4415_dmc_plls,
.nr_pll_clks = ARRAY_SIZE(exynos4415_dmc_plls),
.mux_clks = exynos4415_dmc_mux_clks,
.nr_mux_clks = ARRAY_SIZE(exynos4415_dmc_mux_clks),
.div_clks = exynos4415_dmc_div_clks,
.nr_div_clks = ARRAY_SIZE(exynos4415_dmc_div_clks),
.nr_clk_ids = NR_CLKS_DMC,
.clk_regs = exynos4415_cmu_dmc_clk_regs,
.nr_clk_regs = ARRAY_SIZE(exynos4415_cmu_dmc_clk_regs),
};
static void __init exynos4415_cmu_dmc_init(struct device_node *np)
{
void __iomem *reg_base;
reg_base = of_iomap(np, 0);
if (!reg_base)
panic("%s: failed to map registers\n", __func__);
exynos4415_dmc_ctx = samsung_clk_init(np, reg_base, NR_CLKS_DMC);
if (!exynos4415_dmc_ctx)
panic("%s: unable to allocate context.\n", __func__);
exynos4415_dmc_plls[mpll].rate_table = exynos4415_pll_rates;
exynos4415_dmc_plls[bpll].rate_table = exynos4415_pll_rates;
samsung_clk_register_pll(exynos4415_dmc_ctx, exynos4415_dmc_plls,
ARRAY_SIZE(exynos4415_dmc_plls), reg_base);
samsung_clk_register_mux(exynos4415_dmc_ctx, exynos4415_dmc_mux_clks,
ARRAY_SIZE(exynos4415_dmc_mux_clks));
samsung_clk_register_div(exynos4415_dmc_ctx, exynos4415_dmc_div_clks,
ARRAY_SIZE(exynos4415_dmc_div_clks));
exynos4415_dmc_clk_sleep_init();
samsung_clk_of_add_provider(np, exynos4415_dmc_ctx);
samsung_cmu_register_one(np, &cmu_dmc_info);
}
CLK_OF_DECLARE(exynos4415_cmu_dmc, "samsung,exynos4415-cmu-dmc",
exynos4415_cmu_dmc_init);

View File

@ -34,6 +34,7 @@
#define DIV_TOPC0 0x0600
#define DIV_TOPC1 0x0604
#define DIV_TOPC3 0x060C
#define ENABLE_ACLK_TOPC1 0x0804
static struct samsung_fixed_factor_clock topc_fixed_factor_clks[] __initdata = {
FFACTOR(0, "ffac_topc_bus0_pll_div2", "mout_bus0_pll_ctrl", 1, 2, 0),
@ -45,6 +46,7 @@ static struct samsung_fixed_factor_clock topc_fixed_factor_clks[] __initdata = {
};
/* List of parent clocks for Muxes in CMU_TOPC */
PNAME(mout_aud_pll_ctrl_p) = { "fin_pll", "fout_aud_pll" };
PNAME(mout_bus0_pll_ctrl_p) = { "fin_pll", "fout_bus0_pll" };
PNAME(mout_bus1_pll_ctrl_p) = { "fin_pll", "fout_bus1_pll" };
PNAME(mout_cc_pll_ctrl_p) = { "fin_pll", "fout_cc_pll" };
@ -104,9 +106,11 @@ static struct samsung_mux_clock topc_mux_clks[] __initdata = {
MUX(0, "mout_sclk_bus0_pll_out", mout_sclk_bus0_pll_out_p,
MUX_SEL_TOPC1, 16, 1),
MUX(0, "mout_aud_pll_ctrl", mout_aud_pll_ctrl_p, MUX_SEL_TOPC1, 0, 1),
MUX(0, "mout_aclk_ccore_133", mout_topc_group2, MUX_SEL_TOPC2, 4, 2),
MUX(0, "mout_aclk_mscl_532", mout_topc_group2, MUX_SEL_TOPC3, 20, 2),
MUX(0, "mout_aclk_peris_66", mout_topc_group2, MUX_SEL_TOPC3, 24, 2),
};
@ -114,6 +118,8 @@ static struct samsung_div_clock topc_div_clks[] __initdata = {
DIV(DOUT_ACLK_CCORE_133, "dout_aclk_ccore_133", "mout_aclk_ccore_133",
DIV_TOPC0, 4, 4),
DIV(DOUT_ACLK_MSCL_532, "dout_aclk_mscl_532", "mout_aclk_mscl_532",
DIV_TOPC1, 20, 4),
DIV(DOUT_ACLK_PERIS, "dout_aclk_peris_66", "mout_aclk_peris_66",
DIV_TOPC1, 24, 4),
@ -125,6 +131,18 @@ static struct samsung_div_clock topc_div_clks[] __initdata = {
DIV_TOPC3, 12, 3),
DIV(DOUT_SCLK_MFC_PLL, "dout_sclk_mfc_pll", "mout_mfc_pll_ctrl",
DIV_TOPC3, 16, 3),
DIV(DOUT_SCLK_AUD_PLL, "dout_sclk_aud_pll", "mout_aud_pll_ctrl",
DIV_TOPC3, 28, 3),
};
static struct samsung_pll_rate_table pll1460x_24mhz_tbl[] __initdata = {
PLL_36XX_RATE(491520000, 20, 1, 0, 31457),
{},
};
static struct samsung_gate_clock topc_gate_clks[] __initdata = {
GATE(ACLK_MSCL_532, "aclk_mscl_532", "dout_aclk_mscl_532",
ENABLE_ACLK_TOPC1, 20, 0, 0),
};
static struct samsung_pll_clock topc_pll_clks[] __initdata = {
@ -136,8 +154,8 @@ static struct samsung_pll_clock topc_pll_clks[] __initdata = {
BUS1_DPLL_CON0, NULL),
PLL(pll_1452x, 0, "fout_mfc_pll", "fin_pll", MFC_PLL_LOCK,
MFC_PLL_CON0, NULL),
PLL(pll_1460x, 0, "fout_aud_pll", "fin_pll", AUD_PLL_LOCK,
AUD_PLL_CON0, NULL),
PLL(pll_1460x, FOUT_AUD_PLL, "fout_aud_pll", "fin_pll", AUD_PLL_LOCK,
AUD_PLL_CON0, pll1460x_24mhz_tbl),
};
static struct samsung_cmu_info topc_cmu_info __initdata = {
@ -147,6 +165,8 @@ static struct samsung_cmu_info topc_cmu_info __initdata = {
.nr_mux_clks = ARRAY_SIZE(topc_mux_clks),
.div_clks = topc_div_clks,
.nr_div_clks = ARRAY_SIZE(topc_div_clks),
.gate_clks = topc_gate_clks,
.nr_gate_clks = ARRAY_SIZE(topc_gate_clks),
.fixed_factor_clks = topc_fixed_factor_clks,
.nr_fixed_factor_clks = ARRAY_SIZE(topc_fixed_factor_clks),
.nr_clk_ids = TOPC_NR_CLK,
@ -166,9 +186,18 @@ CLK_OF_DECLARE(exynos7_clk_topc, "samsung,exynos7-clock-topc",
#define MUX_SEL_TOP00 0x0200
#define MUX_SEL_TOP01 0x0204
#define MUX_SEL_TOP03 0x020C
#define MUX_SEL_TOP0_PERIC0 0x0230
#define MUX_SEL_TOP0_PERIC1 0x0234
#define MUX_SEL_TOP0_PERIC2 0x0238
#define MUX_SEL_TOP0_PERIC3 0x023C
#define DIV_TOP03 0x060C
#define DIV_TOP0_PERIC0 0x0630
#define DIV_TOP0_PERIC1 0x0634
#define DIV_TOP0_PERIC2 0x0638
#define DIV_TOP0_PERIC3 0x063C
#define ENABLE_SCLK_TOP0_PERIC0 0x0A30
#define ENABLE_SCLK_TOP0_PERIC1 0x0A34
#define ENABLE_SCLK_TOP0_PERIC2 0x0A38
#define ENABLE_SCLK_TOP0_PERIC3 0x0A3C
/* List of parent clocks for Muxes in CMU_TOP0 */
@ -176,6 +205,7 @@ PNAME(mout_bus0_pll_p) = { "fin_pll", "dout_sclk_bus0_pll" };
PNAME(mout_bus1_pll_p) = { "fin_pll", "dout_sclk_bus1_pll" };
PNAME(mout_cc_pll_p) = { "fin_pll", "dout_sclk_cc_pll" };
PNAME(mout_mfc_pll_p) = { "fin_pll", "dout_sclk_mfc_pll" };
PNAME(mout_aud_pll_p) = { "fin_pll", "dout_sclk_aud_pll" };
PNAME(mout_top0_half_bus0_pll_p) = {"mout_top0_bus0_pll",
"ffac_top0_bus0_pll_div2"};
@ -189,18 +219,34 @@ PNAME(mout_top0_half_mfc_pll_p) = {"mout_top0_mfc_pll",
PNAME(mout_top0_group1) = {"mout_top0_half_bus0_pll",
"mout_top0_half_bus1_pll", "mout_top0_half_cc_pll",
"mout_top0_half_mfc_pll"};
PNAME(mout_top0_group3) = {"ioclk_audiocdclk0",
"ioclk_audiocdclk1", "ioclk_spdif_extclk",
"mout_top0_aud_pll", "mout_top0_half_bus0_pll",
"mout_top0_half_bus1_pll"};
PNAME(mout_top0_group4) = {"ioclk_audiocdclk1", "mout_top0_aud_pll",
"mout_top0_half_bus0_pll", "mout_top0_half_bus1_pll"};
static unsigned long top0_clk_regs[] __initdata = {
MUX_SEL_TOP00,
MUX_SEL_TOP01,
MUX_SEL_TOP03,
MUX_SEL_TOP0_PERIC0,
MUX_SEL_TOP0_PERIC1,
MUX_SEL_TOP0_PERIC2,
MUX_SEL_TOP0_PERIC3,
DIV_TOP03,
DIV_TOP0_PERIC0,
DIV_TOP0_PERIC1,
DIV_TOP0_PERIC2,
DIV_TOP0_PERIC3,
ENABLE_SCLK_TOP0_PERIC0,
ENABLE_SCLK_TOP0_PERIC1,
ENABLE_SCLK_TOP0_PERIC2,
ENABLE_SCLK_TOP0_PERIC3,
};
static struct samsung_mux_clock top0_mux_clks[] __initdata = {
MUX(0, "mout_top0_aud_pll", mout_aud_pll_p, MUX_SEL_TOP00, 0, 1),
MUX(0, "mout_top0_mfc_pll", mout_mfc_pll_p, MUX_SEL_TOP00, 4, 1),
MUX(0, "mout_top0_cc_pll", mout_cc_pll_p, MUX_SEL_TOP00, 8, 1),
MUX(0, "mout_top0_bus1_pll", mout_bus1_pll_p, MUX_SEL_TOP00, 12, 1),
@ -218,10 +264,20 @@ static struct samsung_mux_clock top0_mux_clks[] __initdata = {
MUX(0, "mout_aclk_peric1_66", mout_top0_group1, MUX_SEL_TOP03, 12, 2),
MUX(0, "mout_aclk_peric0_66", mout_top0_group1, MUX_SEL_TOP03, 20, 2),
MUX(0, "mout_sclk_spdif", mout_top0_group3, MUX_SEL_TOP0_PERIC0, 4, 3),
MUX(0, "mout_sclk_pcm1", mout_top0_group4, MUX_SEL_TOP0_PERIC0, 8, 2),
MUX(0, "mout_sclk_i2s1", mout_top0_group4, MUX_SEL_TOP0_PERIC0, 20, 2),
MUX(0, "mout_sclk_spi1", mout_top0_group1, MUX_SEL_TOP0_PERIC1, 8, 2),
MUX(0, "mout_sclk_spi0", mout_top0_group1, MUX_SEL_TOP0_PERIC1, 20, 2),
MUX(0, "mout_sclk_spi3", mout_top0_group1, MUX_SEL_TOP0_PERIC2, 8, 2),
MUX(0, "mout_sclk_spi2", mout_top0_group1, MUX_SEL_TOP0_PERIC2, 20, 2),
MUX(0, "mout_sclk_uart3", mout_top0_group1, MUX_SEL_TOP0_PERIC3, 4, 2),
MUX(0, "mout_sclk_uart2", mout_top0_group1, MUX_SEL_TOP0_PERIC3, 8, 2),
MUX(0, "mout_sclk_uart1", mout_top0_group1, MUX_SEL_TOP0_PERIC3, 12, 2),
MUX(0, "mout_sclk_uart0", mout_top0_group1, MUX_SEL_TOP0_PERIC3, 16, 2),
MUX(0, "mout_sclk_spi4", mout_top0_group1, MUX_SEL_TOP0_PERIC3, 20, 2),
};
static struct samsung_div_clock top0_div_clks[] __initdata = {
@ -230,13 +286,40 @@ static struct samsung_div_clock top0_div_clks[] __initdata = {
DIV(DOUT_ACLK_PERIC0, "dout_aclk_peric0_66", "mout_aclk_peric0_66",
DIV_TOP03, 20, 6),
DIV(0, "dout_sclk_spdif", "mout_sclk_spdif", DIV_TOP0_PERIC0, 4, 4),
DIV(0, "dout_sclk_pcm1", "mout_sclk_pcm1", DIV_TOP0_PERIC0, 8, 12),
DIV(0, "dout_sclk_i2s1", "mout_sclk_i2s1", DIV_TOP0_PERIC0, 20, 10),
DIV(0, "dout_sclk_spi1", "mout_sclk_spi1", DIV_TOP0_PERIC1, 8, 12),
DIV(0, "dout_sclk_spi0", "mout_sclk_spi0", DIV_TOP0_PERIC1, 20, 12),
DIV(0, "dout_sclk_spi3", "mout_sclk_spi3", DIV_TOP0_PERIC2, 8, 12),
DIV(0, "dout_sclk_spi2", "mout_sclk_spi2", DIV_TOP0_PERIC2, 20, 12),
DIV(0, "dout_sclk_uart3", "mout_sclk_uart3", DIV_TOP0_PERIC3, 4, 4),
DIV(0, "dout_sclk_uart2", "mout_sclk_uart2", DIV_TOP0_PERIC3, 8, 4),
DIV(0, "dout_sclk_uart1", "mout_sclk_uart1", DIV_TOP0_PERIC3, 12, 4),
DIV(0, "dout_sclk_uart0", "mout_sclk_uart0", DIV_TOP0_PERIC3, 16, 4),
DIV(0, "dout_sclk_spi4", "mout_sclk_spi4", DIV_TOP0_PERIC3, 20, 12),
};
static struct samsung_gate_clock top0_gate_clks[] __initdata = {
GATE(CLK_SCLK_SPDIF, "sclk_spdif", "dout_sclk_spdif",
ENABLE_SCLK_TOP0_PERIC0, 4, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_PCM1, "sclk_pcm1", "dout_sclk_pcm1",
ENABLE_SCLK_TOP0_PERIC0, 8, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_I2S1, "sclk_i2s1", "dout_sclk_i2s1",
ENABLE_SCLK_TOP0_PERIC0, 20, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_SPI1, "sclk_spi1", "dout_sclk_spi1",
ENABLE_SCLK_TOP0_PERIC1, 8, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_SPI0, "sclk_spi0", "dout_sclk_spi0",
ENABLE_SCLK_TOP0_PERIC1, 20, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_SPI3, "sclk_spi3", "dout_sclk_spi3",
ENABLE_SCLK_TOP0_PERIC2, 8, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_SPI2, "sclk_spi2", "dout_sclk_spi2",
ENABLE_SCLK_TOP0_PERIC2, 20, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_UART3, "sclk_uart3", "dout_sclk_uart3",
ENABLE_SCLK_TOP0_PERIC3, 4, 0, 0),
GATE(CLK_SCLK_UART2, "sclk_uart2", "dout_sclk_uart2",
@ -245,6 +328,8 @@ static struct samsung_gate_clock top0_gate_clks[] __initdata = {
ENABLE_SCLK_TOP0_PERIC3, 12, 0, 0),
GATE(CLK_SCLK_UART0, "sclk_uart0", "dout_sclk_uart0",
ENABLE_SCLK_TOP0_PERIC3, 16, 0, 0),
GATE(CLK_SCLK_SPI4, "sclk_spi4", "dout_sclk_spi4",
ENABLE_SCLK_TOP0_PERIC3, 20, CLK_SET_RATE_PARENT, 0),
};
static struct samsung_fixed_factor_clock top0_fixed_factor_clks[] __initdata = {
@ -343,6 +428,8 @@ static struct samsung_mux_clock top1_mux_clks[] __initdata = {
MUX(0, "mout_aclk_fsys0_200", mout_top1_group1, MUX_SEL_TOP13, 28, 2),
MUX(0, "mout_sclk_mmc2", mout_top1_group1, MUX_SEL_TOP1_FSYS0, 24, 2),
MUX(0, "mout_sclk_usbdrd300", mout_top1_group1,
MUX_SEL_TOP1_FSYS0, 28, 2),
MUX(0, "mout_sclk_mmc1", mout_top1_group1, MUX_SEL_TOP1_FSYS1, 24, 2),
MUX(0, "mout_sclk_mmc0", mout_top1_group1, MUX_SEL_TOP1_FSYS1, 28, 2),
@ -356,6 +443,8 @@ static struct samsung_div_clock top1_div_clks[] __initdata = {
DIV(DOUT_SCLK_MMC2, "dout_sclk_mmc2", "mout_sclk_mmc2",
DIV_TOP1_FSYS0, 24, 4),
DIV(0, "dout_sclk_usbdrd300", "mout_sclk_usbdrd300",
DIV_TOP1_FSYS0, 28, 4),
DIV(DOUT_SCLK_MMC1, "dout_sclk_mmc1", "mout_sclk_mmc1",
DIV_TOP1_FSYS1, 24, 4),
@ -366,6 +455,8 @@ static struct samsung_div_clock top1_div_clks[] __initdata = {
static struct samsung_gate_clock top1_gate_clks[] __initdata = {
GATE(CLK_SCLK_MMC2, "sclk_mmc2", "dout_sclk_mmc2",
ENABLE_SCLK_TOP1_FSYS0, 24, CLK_SET_RATE_PARENT, 0),
GATE(0, "sclk_usbdrd300", "dout_sclk_usbdrd300",
ENABLE_SCLK_TOP1_FSYS0, 28, 0, 0),
GATE(CLK_SCLK_MMC1, "sclk_mmc1", "dout_sclk_mmc1",
ENABLE_SCLK_TOP1_FSYS1, 24, CLK_SET_RATE_PARENT, 0),
@ -514,6 +605,7 @@ static void __init exynos7_clk_peric0_init(struct device_node *np)
/* Register Offset definitions for CMU_PERIC1 (0x14C80000) */
#define MUX_SEL_PERIC10 0x0200
#define MUX_SEL_PERIC11 0x0204
#define MUX_SEL_PERIC12 0x0208
#define ENABLE_PCLK_PERIC1 0x0900
#define ENABLE_SCLK_PERIC10 0x0A00
@ -525,10 +617,16 @@ PNAME(mout_aclk_peric1_66_p) = { "fin_pll", "dout_aclk_peric1_66" };
PNAME(mout_sclk_uart1_p) = { "fin_pll", "sclk_uart1" };
PNAME(mout_sclk_uart2_p) = { "fin_pll", "sclk_uart2" };
PNAME(mout_sclk_uart3_p) = { "fin_pll", "sclk_uart3" };
PNAME(mout_sclk_spi0_p) = { "fin_pll", "sclk_spi0" };
PNAME(mout_sclk_spi1_p) = { "fin_pll", "sclk_spi1" };
PNAME(mout_sclk_spi2_p) = { "fin_pll", "sclk_spi2" };
PNAME(mout_sclk_spi3_p) = { "fin_pll", "sclk_spi3" };
PNAME(mout_sclk_spi4_p) = { "fin_pll", "sclk_spi4" };
static unsigned long peric1_clk_regs[] __initdata = {
MUX_SEL_PERIC10,
MUX_SEL_PERIC11,
MUX_SEL_PERIC12,
ENABLE_PCLK_PERIC1,
ENABLE_SCLK_PERIC10,
};
@ -537,6 +635,16 @@ static struct samsung_mux_clock peric1_mux_clks[] __initdata = {
MUX(0, "mout_aclk_peric1_66_user", mout_aclk_peric1_66_p,
MUX_SEL_PERIC10, 0, 1),
MUX_F(0, "mout_sclk_spi0_user", mout_sclk_spi0_p,
MUX_SEL_PERIC11, 0, 1, CLK_SET_RATE_PARENT, 0),
MUX_F(0, "mout_sclk_spi1_user", mout_sclk_spi1_p,
MUX_SEL_PERIC11, 4, 1, CLK_SET_RATE_PARENT, 0),
MUX_F(0, "mout_sclk_spi2_user", mout_sclk_spi2_p,
MUX_SEL_PERIC11, 8, 1, CLK_SET_RATE_PARENT, 0),
MUX_F(0, "mout_sclk_spi3_user", mout_sclk_spi3_p,
MUX_SEL_PERIC11, 12, 1, CLK_SET_RATE_PARENT, 0),
MUX_F(0, "mout_sclk_spi4_user", mout_sclk_spi4_p,
MUX_SEL_PERIC11, 16, 1, CLK_SET_RATE_PARENT, 0),
MUX(0, "mout_sclk_uart1_user", mout_sclk_uart1_p,
MUX_SEL_PERIC11, 20, 1),
MUX(0, "mout_sclk_uart2_user", mout_sclk_uart2_p,
@ -562,6 +670,22 @@ static struct samsung_gate_clock peric1_gate_clks[] __initdata = {
ENABLE_PCLK_PERIC1, 10, 0, 0),
GATE(PCLK_UART3, "pclk_uart3", "mout_aclk_peric1_66_user",
ENABLE_PCLK_PERIC1, 11, 0, 0),
GATE(PCLK_SPI0, "pclk_spi0", "mout_aclk_peric1_66_user",
ENABLE_PCLK_PERIC1, 12, 0, 0),
GATE(PCLK_SPI1, "pclk_spi1", "mout_aclk_peric1_66_user",
ENABLE_PCLK_PERIC1, 13, 0, 0),
GATE(PCLK_SPI2, "pclk_spi2", "mout_aclk_peric1_66_user",
ENABLE_PCLK_PERIC1, 14, 0, 0),
GATE(PCLK_SPI3, "pclk_spi3", "mout_aclk_peric1_66_user",
ENABLE_PCLK_PERIC1, 15, 0, 0),
GATE(PCLK_SPI4, "pclk_spi4", "mout_aclk_peric1_66_user",
ENABLE_PCLK_PERIC1, 16, 0, 0),
GATE(PCLK_I2S1, "pclk_i2s1", "mout_aclk_peric1_66_user",
ENABLE_PCLK_PERIC1, 17, CLK_SET_RATE_PARENT, 0),
GATE(PCLK_PCM1, "pclk_pcm1", "mout_aclk_peric1_66_user",
ENABLE_PCLK_PERIC1, 18, 0, 0),
GATE(PCLK_SPDIF, "pclk_spdif", "mout_aclk_peric1_66_user",
ENABLE_PCLK_PERIC1, 19, 0, 0),
GATE(SCLK_UART1, "sclk_uart1_user", "mout_sclk_uart1_user",
ENABLE_SCLK_PERIC10, 9, 0, 0),
@ -569,6 +693,22 @@ static struct samsung_gate_clock peric1_gate_clks[] __initdata = {
ENABLE_SCLK_PERIC10, 10, 0, 0),
GATE(SCLK_UART3, "sclk_uart3_user", "mout_sclk_uart3_user",
ENABLE_SCLK_PERIC10, 11, 0, 0),
GATE(SCLK_SPI0, "sclk_spi0_user", "mout_sclk_spi0_user",
ENABLE_SCLK_PERIC10, 12, CLK_SET_RATE_PARENT, 0),
GATE(SCLK_SPI1, "sclk_spi1_user", "mout_sclk_spi1_user",
ENABLE_SCLK_PERIC10, 13, CLK_SET_RATE_PARENT, 0),
GATE(SCLK_SPI2, "sclk_spi2_user", "mout_sclk_spi2_user",
ENABLE_SCLK_PERIC10, 14, CLK_SET_RATE_PARENT, 0),
GATE(SCLK_SPI3, "sclk_spi3_user", "mout_sclk_spi3_user",
ENABLE_SCLK_PERIC10, 15, CLK_SET_RATE_PARENT, 0),
GATE(SCLK_SPI4, "sclk_spi4_user", "mout_sclk_spi4_user",
ENABLE_SCLK_PERIC10, 16, CLK_SET_RATE_PARENT, 0),
GATE(SCLK_I2S1, "sclk_i2s1_user", "sclk_i2s1",
ENABLE_SCLK_PERIC10, 17, CLK_SET_RATE_PARENT, 0),
GATE(SCLK_PCM1, "sclk_pcm1_user", "sclk_pcm1",
ENABLE_SCLK_PERIC10, 18, CLK_SET_RATE_PARENT, 0),
GATE(SCLK_SPDIF, "sclk_spdif_user", "sclk_spdif",
ENABLE_SCLK_PERIC10, 19, CLK_SET_RATE_PARENT, 0),
};
static struct samsung_cmu_info peric1_cmu_info __initdata = {
@ -647,7 +787,12 @@ CLK_OF_DECLARE(exynos7_clk_peris, "samsung,exynos7-clock-peris",
/* Register Offset definitions for CMU_FSYS0 (0x10E90000) */
#define MUX_SEL_FSYS00 0x0200
#define MUX_SEL_FSYS01 0x0204
#define MUX_SEL_FSYS02 0x0208
#define ENABLE_ACLK_FSYS00 0x0800
#define ENABLE_ACLK_FSYS01 0x0804
#define ENABLE_SCLK_FSYS01 0x0A04
#define ENABLE_SCLK_FSYS02 0x0A08
#define ENABLE_SCLK_FSYS04 0x0A10
/*
* List of parent clocks for Muxes in CMU_FSYS0
@ -655,10 +800,29 @@ CLK_OF_DECLARE(exynos7_clk_peris, "samsung,exynos7-clock-peris",
PNAME(mout_aclk_fsys0_200_p) = { "fin_pll", "dout_aclk_fsys0_200" };
PNAME(mout_sclk_mmc2_p) = { "fin_pll", "sclk_mmc2" };
PNAME(mout_sclk_usbdrd300_p) = { "fin_pll", "sclk_usbdrd300" };
PNAME(mout_phyclk_usbdrd300_udrd30_phyclk_p) = { "fin_pll",
"phyclk_usbdrd300_udrd30_phyclock" };
PNAME(mout_phyclk_usbdrd300_udrd30_pipe_pclk_p) = { "fin_pll",
"phyclk_usbdrd300_udrd30_pipe_pclk" };
/* fixed rate clocks used in the FSYS0 block */
struct samsung_fixed_rate_clock fixed_rate_clks_fsys0[] __initdata = {
FRATE(0, "phyclk_usbdrd300_udrd30_phyclock", NULL,
CLK_IS_ROOT, 60000000),
FRATE(0, "phyclk_usbdrd300_udrd30_pipe_pclk", NULL,
CLK_IS_ROOT, 125000000),
};
static unsigned long fsys0_clk_regs[] __initdata = {
MUX_SEL_FSYS00,
MUX_SEL_FSYS01,
MUX_SEL_FSYS02,
ENABLE_ACLK_FSYS00,
ENABLE_ACLK_FSYS01,
ENABLE_SCLK_FSYS01,
ENABLE_SCLK_FSYS02,
ENABLE_SCLK_FSYS04,
};
static struct samsung_mux_clock fsys0_mux_clks[] __initdata = {
@ -666,11 +830,49 @@ static struct samsung_mux_clock fsys0_mux_clks[] __initdata = {
MUX_SEL_FSYS00, 24, 1),
MUX(0, "mout_sclk_mmc2_user", mout_sclk_mmc2_p, MUX_SEL_FSYS01, 24, 1),
MUX(0, "mout_sclk_usbdrd300_user", mout_sclk_usbdrd300_p,
MUX_SEL_FSYS01, 28, 1),
MUX(0, "mout_phyclk_usbdrd300_udrd30_pipe_pclk_user",
mout_phyclk_usbdrd300_udrd30_pipe_pclk_p,
MUX_SEL_FSYS02, 24, 1),
MUX(0, "mout_phyclk_usbdrd300_udrd30_phyclk_user",
mout_phyclk_usbdrd300_udrd30_phyclk_p,
MUX_SEL_FSYS02, 28, 1),
};
static struct samsung_gate_clock fsys0_gate_clks[] __initdata = {
GATE(ACLK_AXIUS_USBDRD30X_FSYS0X, "aclk_axius_usbdrd30x_fsys0x",
"mout_aclk_fsys0_200_user",
ENABLE_ACLK_FSYS00, 19, 0, 0),
GATE(ACLK_PDMA1, "aclk_pdma1", "mout_aclk_fsys0_200_user",
ENABLE_ACLK_FSYS00, 3, 0, 0),
GATE(ACLK_PDMA0, "aclk_pdma0", "mout_aclk_fsys0_200_user",
ENABLE_ACLK_FSYS00, 4, 0, 0),
GATE(ACLK_USBDRD300, "aclk_usbdrd300", "mout_aclk_fsys0_200_user",
ENABLE_ACLK_FSYS01, 29, 0, 0),
GATE(ACLK_MMC2, "aclk_mmc2", "mout_aclk_fsys0_200_user",
ENABLE_ACLK_FSYS01, 31, 0, 0),
GATE(SCLK_USBDRD300_SUSPENDCLK, "sclk_usbdrd300_suspendclk",
"mout_sclk_usbdrd300_user",
ENABLE_SCLK_FSYS01, 4, 0, 0),
GATE(SCLK_USBDRD300_REFCLK, "sclk_usbdrd300_refclk", "fin_pll",
ENABLE_SCLK_FSYS01, 8, 0, 0),
GATE(PHYCLK_USBDRD300_UDRD30_PIPE_PCLK_USER,
"phyclk_usbdrd300_udrd30_pipe_pclk_user",
"mout_phyclk_usbdrd300_udrd30_pipe_pclk_user",
ENABLE_SCLK_FSYS02, 24, 0, 0),
GATE(PHYCLK_USBDRD300_UDRD30_PHYCLK_USER,
"phyclk_usbdrd300_udrd30_phyclk_user",
"mout_phyclk_usbdrd300_udrd30_phyclk_user",
ENABLE_SCLK_FSYS02, 28, 0, 0),
GATE(OSCCLK_PHY_CLKOUT_USB30_PHY, "oscclk_phy_clkout_usb30_phy",
"fin_pll",
ENABLE_SCLK_FSYS04, 28, 0, 0),
};
static struct samsung_cmu_info fsys0_cmu_info __initdata = {
@ -741,3 +943,205 @@ static void __init exynos7_clk_fsys1_init(struct device_node *np)
CLK_OF_DECLARE(exynos7_clk_fsys1, "samsung,exynos7-clock-fsys1",
exynos7_clk_fsys1_init);
#define MUX_SEL_MSCL 0x0200
#define DIV_MSCL 0x0600
#define ENABLE_ACLK_MSCL 0x0800
#define ENABLE_PCLK_MSCL 0x0900
/* List of parent clocks for Muxes in CMU_MSCL */
PNAME(mout_aclk_mscl_532_user_p) = { "fin_pll", "aclk_mscl_532" };
static unsigned long mscl_clk_regs[] __initdata = {
MUX_SEL_MSCL,
DIV_MSCL,
ENABLE_ACLK_MSCL,
ENABLE_PCLK_MSCL,
};
static struct samsung_mux_clock mscl_mux_clks[] __initdata = {
MUX(USERMUX_ACLK_MSCL_532, "usermux_aclk_mscl_532",
mout_aclk_mscl_532_user_p, MUX_SEL_MSCL, 0, 1),
};
static struct samsung_div_clock mscl_div_clks[] __initdata = {
DIV(DOUT_PCLK_MSCL, "dout_pclk_mscl", "usermux_aclk_mscl_532",
DIV_MSCL, 0, 3),
};
static struct samsung_gate_clock mscl_gate_clks[] __initdata = {
GATE(ACLK_MSCL_0, "aclk_mscl_0", "usermux_aclk_mscl_532",
ENABLE_ACLK_MSCL, 31, 0, 0),
GATE(ACLK_MSCL_1, "aclk_mscl_1", "usermux_aclk_mscl_532",
ENABLE_ACLK_MSCL, 30, 0, 0),
GATE(ACLK_JPEG, "aclk_jpeg", "usermux_aclk_mscl_532",
ENABLE_ACLK_MSCL, 29, 0, 0),
GATE(ACLK_G2D, "aclk_g2d", "usermux_aclk_mscl_532",
ENABLE_ACLK_MSCL, 28, 0, 0),
GATE(ACLK_LH_ASYNC_SI_MSCL_0, "aclk_lh_async_si_mscl_0",
"usermux_aclk_mscl_532",
ENABLE_ACLK_MSCL, 27, 0, 0),
GATE(ACLK_LH_ASYNC_SI_MSCL_1, "aclk_lh_async_si_mscl_1",
"usermux_aclk_mscl_532",
ENABLE_ACLK_MSCL, 26, 0, 0),
GATE(ACLK_XIU_MSCLX_0, "aclk_xiu_msclx_0", "usermux_aclk_mscl_532",
ENABLE_ACLK_MSCL, 25, 0, 0),
GATE(ACLK_XIU_MSCLX_1, "aclk_xiu_msclx_1", "usermux_aclk_mscl_532",
ENABLE_ACLK_MSCL, 24, 0, 0),
GATE(ACLK_AXI2ACEL_BRIDGE, "aclk_axi2acel_bridge",
"usermux_aclk_mscl_532",
ENABLE_ACLK_MSCL, 23, 0, 0),
GATE(ACLK_QE_MSCL_0, "aclk_qe_mscl_0", "usermux_aclk_mscl_532",
ENABLE_ACLK_MSCL, 22, 0, 0),
GATE(ACLK_QE_MSCL_1, "aclk_qe_mscl_1", "usermux_aclk_mscl_532",
ENABLE_ACLK_MSCL, 21, 0, 0),
GATE(ACLK_QE_JPEG, "aclk_qe_jpeg", "usermux_aclk_mscl_532",
ENABLE_ACLK_MSCL, 20, 0, 0),
GATE(ACLK_QE_G2D, "aclk_qe_g2d", "usermux_aclk_mscl_532",
ENABLE_ACLK_MSCL, 19, 0, 0),
GATE(ACLK_PPMU_MSCL_0, "aclk_ppmu_mscl_0", "usermux_aclk_mscl_532",
ENABLE_ACLK_MSCL, 18, 0, 0),
GATE(ACLK_PPMU_MSCL_1, "aclk_ppmu_mscl_1", "usermux_aclk_mscl_532",
ENABLE_ACLK_MSCL, 17, 0, 0),
GATE(ACLK_MSCLNP_133, "aclk_msclnp_133", "usermux_aclk_mscl_532",
ENABLE_ACLK_MSCL, 16, 0, 0),
GATE(ACLK_AHB2APB_MSCL0P, "aclk_ahb2apb_mscl0p",
"usermux_aclk_mscl_532",
ENABLE_ACLK_MSCL, 15, 0, 0),
GATE(ACLK_AHB2APB_MSCL1P, "aclk_ahb2apb_mscl1p",
"usermux_aclk_mscl_532",
ENABLE_ACLK_MSCL, 14, 0, 0),
GATE(PCLK_MSCL_0, "pclk_mscl_0", "dout_pclk_mscl",
ENABLE_PCLK_MSCL, 31, 0, 0),
GATE(PCLK_MSCL_1, "pclk_mscl_1", "dout_pclk_mscl",
ENABLE_PCLK_MSCL, 30, 0, 0),
GATE(PCLK_JPEG, "pclk_jpeg", "dout_pclk_mscl",
ENABLE_PCLK_MSCL, 29, 0, 0),
GATE(PCLK_G2D, "pclk_g2d", "dout_pclk_mscl",
ENABLE_PCLK_MSCL, 28, 0, 0),
GATE(PCLK_QE_MSCL_0, "pclk_qe_mscl_0", "dout_pclk_mscl",
ENABLE_PCLK_MSCL, 27, 0, 0),
GATE(PCLK_QE_MSCL_1, "pclk_qe_mscl_1", "dout_pclk_mscl",
ENABLE_PCLK_MSCL, 26, 0, 0),
GATE(PCLK_QE_JPEG, "pclk_qe_jpeg", "dout_pclk_mscl",
ENABLE_PCLK_MSCL, 25, 0, 0),
GATE(PCLK_QE_G2D, "pclk_qe_g2d", "dout_pclk_mscl",
ENABLE_PCLK_MSCL, 24, 0, 0),
GATE(PCLK_PPMU_MSCL_0, "pclk_ppmu_mscl_0", "dout_pclk_mscl",
ENABLE_PCLK_MSCL, 23, 0, 0),
GATE(PCLK_PPMU_MSCL_1, "pclk_ppmu_mscl_1", "dout_pclk_mscl",
ENABLE_PCLK_MSCL, 22, 0, 0),
GATE(PCLK_AXI2ACEL_BRIDGE, "pclk_axi2acel_bridge", "dout_pclk_mscl",
ENABLE_PCLK_MSCL, 21, 0, 0),
GATE(PCLK_PMU_MSCL, "pclk_pmu_mscl", "dout_pclk_mscl",
ENABLE_PCLK_MSCL, 20, 0, 0),
};
static struct samsung_cmu_info mscl_cmu_info __initdata = {
.mux_clks = mscl_mux_clks,
.nr_mux_clks = ARRAY_SIZE(mscl_mux_clks),
.div_clks = mscl_div_clks,
.nr_div_clks = ARRAY_SIZE(mscl_div_clks),
.gate_clks = mscl_gate_clks,
.nr_gate_clks = ARRAY_SIZE(mscl_gate_clks),
.nr_clk_ids = MSCL_NR_CLK,
.clk_regs = mscl_clk_regs,
.nr_clk_regs = ARRAY_SIZE(mscl_clk_regs),
};
static void __init exynos7_clk_mscl_init(struct device_node *np)
{
samsung_cmu_register_one(np, &mscl_cmu_info);
}
CLK_OF_DECLARE(exynos7_clk_mscl, "samsung,exynos7-clock-mscl",
exynos7_clk_mscl_init);
/* Register Offset definitions for CMU_AUD (0x114C0000) */
#define MUX_SEL_AUD 0x0200
#define DIV_AUD0 0x0600
#define DIV_AUD1 0x0604
#define ENABLE_ACLK_AUD 0x0800
#define ENABLE_PCLK_AUD 0x0900
#define ENABLE_SCLK_AUD 0x0A00
/*
* List of parent clocks for Muxes in CMU_AUD
*/
PNAME(mout_aud_pll_user_p) = { "fin_pll", "fout_aud_pll" };
PNAME(mout_aud_group_p) = { "dout_aud_cdclk", "ioclk_audiocdclk0" };
static unsigned long aud_clk_regs[] __initdata = {
MUX_SEL_AUD,
DIV_AUD0,
DIV_AUD1,
ENABLE_ACLK_AUD,
ENABLE_PCLK_AUD,
ENABLE_SCLK_AUD,
};
static struct samsung_mux_clock aud_mux_clks[] __initdata = {
MUX(0, "mout_sclk_i2s", mout_aud_group_p, MUX_SEL_AUD, 12, 1),
MUX(0, "mout_sclk_pcm", mout_aud_group_p, MUX_SEL_AUD, 16, 1),
MUX(0, "mout_aud_pll_user", mout_aud_pll_user_p, MUX_SEL_AUD, 20, 1),
};
static struct samsung_div_clock aud_div_clks[] __initdata = {
DIV(0, "dout_aud_ca5", "mout_aud_pll_user", DIV_AUD0, 0, 4),
DIV(0, "dout_aclk_aud", "dout_aud_ca5", DIV_AUD0, 4, 4),
DIV(0, "dout_aud_pclk_dbg", "dout_aud_ca5", DIV_AUD0, 8, 4),
DIV(0, "dout_sclk_i2s", "mout_sclk_i2s", DIV_AUD1, 0, 4),
DIV(0, "dout_sclk_pcm", "mout_sclk_pcm", DIV_AUD1, 4, 8),
DIV(0, "dout_sclk_uart", "dout_aud_cdclk", DIV_AUD1, 12, 4),
DIV(0, "dout_sclk_slimbus", "dout_aud_cdclk", DIV_AUD1, 16, 5),
DIV(0, "dout_aud_cdclk", "mout_aud_pll_user", DIV_AUD1, 24, 4),
};
static struct samsung_gate_clock aud_gate_clks[] __initdata = {
GATE(SCLK_PCM, "sclk_pcm", "dout_sclk_pcm",
ENABLE_SCLK_AUD, 27, CLK_SET_RATE_PARENT, 0),
GATE(SCLK_I2S, "sclk_i2s", "dout_sclk_i2s",
ENABLE_SCLK_AUD, 28, CLK_SET_RATE_PARENT, 0),
GATE(0, "sclk_uart", "dout_sclk_uart", ENABLE_SCLK_AUD, 29, 0, 0),
GATE(0, "sclk_slimbus", "dout_sclk_slimbus",
ENABLE_SCLK_AUD, 30, 0, 0),
GATE(0, "pclk_dbg_aud", "dout_aud_pclk_dbg", ENABLE_PCLK_AUD, 19, 0, 0),
GATE(0, "pclk_gpio_aud", "dout_aclk_aud", ENABLE_PCLK_AUD, 20, 0, 0),
GATE(0, "pclk_wdt1", "dout_aclk_aud", ENABLE_PCLK_AUD, 22, 0, 0),
GATE(0, "pclk_wdt0", "dout_aclk_aud", ENABLE_PCLK_AUD, 23, 0, 0),
GATE(0, "pclk_slimbus", "dout_aclk_aud", ENABLE_PCLK_AUD, 24, 0, 0),
GATE(0, "pclk_uart", "dout_aclk_aud", ENABLE_PCLK_AUD, 25, 0, 0),
GATE(PCLK_PCM, "pclk_pcm", "dout_aclk_aud",
ENABLE_PCLK_AUD, 26, CLK_SET_RATE_PARENT, 0),
GATE(PCLK_I2S, "pclk_i2s", "dout_aclk_aud",
ENABLE_PCLK_AUD, 27, CLK_SET_RATE_PARENT, 0),
GATE(0, "pclk_timer", "dout_aclk_aud", ENABLE_PCLK_AUD, 28, 0, 0),
GATE(0, "pclk_smmu_aud", "dout_aclk_aud", ENABLE_PCLK_AUD, 31, 0, 0),
GATE(0, "aclk_smmu_aud", "dout_aclk_aud", ENABLE_ACLK_AUD, 27, 0, 0),
GATE(0, "aclk_acel_lh_async_si_top", "dout_aclk_aud",
ENABLE_ACLK_AUD, 28, 0, 0),
GATE(ACLK_ADMA, "aclk_dmac", "dout_aclk_aud", ENABLE_ACLK_AUD, 31, 0, 0),
};
static struct samsung_cmu_info aud_cmu_info __initdata = {
.mux_clks = aud_mux_clks,
.nr_mux_clks = ARRAY_SIZE(aud_mux_clks),
.div_clks = aud_div_clks,
.nr_div_clks = ARRAY_SIZE(aud_div_clks),
.gate_clks = aud_gate_clks,
.nr_gate_clks = ARRAY_SIZE(aud_gate_clks),
.nr_clk_ids = AUD_NR_CLK,
.clk_regs = aud_clk_regs,
.nr_clk_regs = ARRAY_SIZE(aud_clk_regs),
};
static void __init exynos7_clk_aud_init(struct device_node *np)
{
samsung_cmu_register_one(np, &aud_cmu_info);
}
CLK_OF_DECLARE(exynos7_clk_aud, "samsung,exynos7-clock-aud",
exynos7_clk_aud_init);

View File

@ -374,19 +374,24 @@ static void samsung_clk_sleep_init(void __iomem *reg_base,
* Common function which registers plls, muxes, dividers and gates
* for each CMU. It also add CMU register list to register cache.
*/
void __init samsung_cmu_register_one(struct device_node *np,
struct samsung_clk_provider * __init samsung_cmu_register_one(
struct device_node *np,
struct samsung_cmu_info *cmu)
{
void __iomem *reg_base;
struct samsung_clk_provider *ctx;
reg_base = of_iomap(np, 0);
if (!reg_base)
if (!reg_base) {
panic("%s: failed to map registers\n", __func__);
return NULL;
}
ctx = samsung_clk_init(np, reg_base, cmu->nr_clk_ids);
if (!ctx)
if (!ctx) {
panic("%s: unable to alllocate ctx\n", __func__);
return ctx;
}
if (cmu->pll_clks)
samsung_clk_register_pll(ctx, cmu->pll_clks, cmu->nr_pll_clks,
@ -410,4 +415,6 @@ void __init samsung_cmu_register_one(struct device_node *np,
cmu->nr_clk_regs);
samsung_clk_of_add_provider(np, ctx);
return ctx;
}

View File

@ -392,7 +392,8 @@ extern void __init samsung_clk_register_pll(struct samsung_clk_provider *ctx,
struct samsung_pll_clock *pll_list,
unsigned int nr_clk, void __iomem *base);
extern void __init samsung_cmu_register_one(struct device_node *,
extern struct samsung_clk_provider __init *samsung_cmu_register_one(
struct device_node *,
struct samsung_cmu_info *);
extern unsigned long _get_rate(const char *clk_name);

View File

@ -1,9 +1,11 @@
obj-$(CONFIG_ARCH_EMEV2) += clk-emev2.o
obj-$(CONFIG_ARCH_R7S72100) += clk-rz.o
obj-$(CONFIG_ARCH_R8A73A4) += clk-r8a73a4.o
obj-$(CONFIG_ARCH_R8A7740) += clk-r8a7740.o
obj-$(CONFIG_ARCH_R8A7779) += clk-r8a7779.o
obj-$(CONFIG_ARCH_R8A7790) += clk-rcar-gen2.o
obj-$(CONFIG_ARCH_R8A7791) += clk-rcar-gen2.o
obj-$(CONFIG_ARCH_R8A7793) += clk-rcar-gen2.o
obj-$(CONFIG_ARCH_R8A7794) += clk-rcar-gen2.o
obj-$(CONFIG_ARCH_SHMOBILE_MULTI) += clk-div6.o
obj-$(CONFIG_ARCH_SHMOBILE_MULTI) += clk-mstp.o

View File

@ -54,12 +54,19 @@ static int cpg_div6_clock_enable(struct clk_hw *hw)
static void cpg_div6_clock_disable(struct clk_hw *hw)
{
struct div6_clock *clock = to_div6_clock(hw);
u32 val;
/* DIV6 clocks require the divisor field to be non-zero when stopping
* the clock.
val = clk_readl(clock->reg);
val |= CPG_DIV6_CKSTP;
/*
* DIV6 clocks require the divisor field to be non-zero when stopping
* the clock. However, some clocks (e.g. ZB on sh73a0) fail to be
* re-enabled later if the divisor field is changed when stopping the
* clock
*/
clk_writel(clk_readl(clock->reg) | CPG_DIV6_CKSTP | CPG_DIV6_DIV_MASK,
clock->reg);
if (!(val & CPG_DIV6_DIV_MASK))
val |= CPG_DIV6_DIV_MASK;
clk_writel(val, clock->reg);
}
static int cpg_div6_clock_is_enabled(struct clk_hw *hw)

View File

@ -0,0 +1,241 @@
/*
* r8a73a4 Core CPG Clocks
*
* Copyright (C) 2014 Ulrich Hecht
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*/
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/clk/shmobile.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/spinlock.h>
struct r8a73a4_cpg {
struct clk_onecell_data data;
spinlock_t lock;
void __iomem *reg;
};
#define CPG_CKSCR 0xc0
#define CPG_FRQCRA 0x00
#define CPG_FRQCRB 0x04
#define CPG_FRQCRC 0xe0
#define CPG_PLL0CR 0xd8
#define CPG_PLL1CR 0x28
#define CPG_PLL2CR 0x2c
#define CPG_PLL2HCR 0xe4
#define CPG_PLL2SCR 0xf4
#define CLK_ENABLE_ON_INIT BIT(0)
struct div4_clk {
const char *name;
unsigned int reg;
unsigned int shift;
};
static struct div4_clk div4_clks[] = {
{ "i", CPG_FRQCRA, 20 },
{ "m3", CPG_FRQCRA, 12 },
{ "b", CPG_FRQCRA, 8 },
{ "m1", CPG_FRQCRA, 4 },
{ "m2", CPG_FRQCRA, 0 },
{ "zx", CPG_FRQCRB, 12 },
{ "zs", CPG_FRQCRB, 8 },
{ "hp", CPG_FRQCRB, 4 },
{ NULL, 0, 0 },
};
static const struct clk_div_table div4_div_table[] = {
{ 0, 2 }, { 1, 3 }, { 2, 4 }, { 3, 6 }, { 4, 8 }, { 5, 12 },
{ 6, 16 }, { 7, 18 }, { 8, 24 }, { 10, 36 }, { 11, 48 },
{ 12, 10 }, { 0, 0 }
};
static struct clk * __init
r8a73a4_cpg_register_clock(struct device_node *np, struct r8a73a4_cpg *cpg,
const char *name)
{
const struct clk_div_table *table = NULL;
const char *parent_name;
unsigned int shift, reg;
unsigned int mult = 1;
unsigned int div = 1;
if (!strcmp(name, "main")) {
u32 ckscr = clk_readl(cpg->reg + CPG_CKSCR);
switch ((ckscr >> 28) & 3) {
case 0: /* extal1 */
parent_name = of_clk_get_parent_name(np, 0);
break;
case 1: /* extal1 / 2 */
parent_name = of_clk_get_parent_name(np, 0);
div = 2;
break;
case 2: /* extal2 */
parent_name = of_clk_get_parent_name(np, 1);
break;
case 3: /* extal2 / 2 */
parent_name = of_clk_get_parent_name(np, 1);
div = 2;
break;
}
} else if (!strcmp(name, "pll0")) {
/* PLL0/1 are configurable multiplier clocks. Register them as
* fixed factor clocks for now as there's no generic multiplier
* clock implementation and we currently have no need to change
* the multiplier value.
*/
u32 value = clk_readl(cpg->reg + CPG_PLL0CR);
parent_name = "main";
mult = ((value >> 24) & 0x7f) + 1;
if (value & BIT(20))
div = 2;
} else if (!strcmp(name, "pll1")) {
u32 value = clk_readl(cpg->reg + CPG_PLL1CR);
parent_name = "main";
/* XXX: enable bit? */
mult = ((value >> 24) & 0x7f) + 1;
if (value & BIT(7))
div = 2;
} else if (!strncmp(name, "pll2", 4)) {
u32 value, cr;
switch (name[4]) {
case 0:
cr = CPG_PLL2CR;
break;
case 's':
cr = CPG_PLL2SCR;
break;
case 'h':
cr = CPG_PLL2HCR;
break;
default:
return ERR_PTR(-EINVAL);
}
value = clk_readl(cpg->reg + cr);
switch ((value >> 5) & 7) {
case 0:
parent_name = "main";
div = 2;
break;
case 1:
parent_name = "extal2";
div = 2;
break;
case 3:
parent_name = "extal2";
div = 4;
break;
case 4:
parent_name = "main";
break;
case 5:
parent_name = "extal2";
break;
default:
pr_warn("%s: unexpected parent of %s\n", __func__,
name);
return ERR_PTR(-EINVAL);
}
/* XXX: enable bit? */
mult = ((value >> 24) & 0x7f) + 1;
} else if (!strcmp(name, "z") || !strcmp(name, "z2")) {
u32 shift = 8;
parent_name = "pll0";
if (name[1] == '2') {
div = 2;
shift = 0;
}
div *= 32;
mult = 0x20 - ((clk_readl(cpg->reg + CPG_FRQCRC) >> shift)
& 0x1f);
} else {
struct div4_clk *c;
for (c = div4_clks; c->name; c++) {
if (!strcmp(name, c->name))
break;
}
if (!c->name)
return ERR_PTR(-EINVAL);
parent_name = "pll1";
table = div4_div_table;
reg = c->reg;
shift = c->shift;
}
if (!table) {
return clk_register_fixed_factor(NULL, name, parent_name, 0,
mult, div);
} else {
return clk_register_divider_table(NULL, name, parent_name, 0,
cpg->reg + reg, shift, 4, 0,
table, &cpg->lock);
}
}
static void __init r8a73a4_cpg_clocks_init(struct device_node *np)
{
struct r8a73a4_cpg *cpg;
struct clk **clks;
unsigned int i;
int num_clks;
num_clks = of_property_count_strings(np, "clock-output-names");
if (num_clks < 0) {
pr_err("%s: failed to count clocks\n", __func__);
return;
}
cpg = kzalloc(sizeof(*cpg), GFP_KERNEL);
clks = kcalloc(num_clks, sizeof(*clks), GFP_KERNEL);
if (cpg == NULL || clks == NULL) {
/* We're leaking memory on purpose, there's no point in cleaning
* up as the system won't boot anyway.
*/
return;
}
spin_lock_init(&cpg->lock);
cpg->data.clks = clks;
cpg->data.clk_num = num_clks;
cpg->reg = of_iomap(np, 0);
if (WARN_ON(cpg->reg == NULL))
return;
for (i = 0; i < num_clks; ++i) {
const char *name;
struct clk *clk;
of_property_read_string_index(np, "clock-output-names", i,
&name);
clk = r8a73a4_cpg_register_clock(np, cpg, name);
if (IS_ERR(clk))
pr_err("%s: failed to register %s %s clock (%ld)\n",
__func__, np->name, name, PTR_ERR(clk));
else
cpg->data.clks[i] = clk;
}
of_clk_add_provider(np, of_clk_src_onecell_get, &cpg->data);
}
CLK_OF_DECLARE(r8a73a4_cpg_clks, "renesas,r8a73a4-cpg-clocks",
r8a73a4_cpg_clocks_init);

View File

@ -33,6 +33,8 @@ struct rcar_gen2_cpg {
#define CPG_FRQCRC 0x000000e0
#define CPG_FRQCRC_ZFC_MASK (0x1f << 8)
#define CPG_FRQCRC_ZFC_SHIFT 8
#define CPG_ADSPCKCR 0x0000025c
#define CPG_RCANCKCR 0x00000270
/* -----------------------------------------------------------------------------
* Z Clock
@ -161,6 +163,88 @@ static struct clk * __init cpg_z_clk_register(struct rcar_gen2_cpg *cpg)
return clk;
}
static struct clk * __init cpg_rcan_clk_register(struct rcar_gen2_cpg *cpg,
struct device_node *np)
{
const char *parent_name = of_clk_get_parent_name(np, 1);
struct clk_fixed_factor *fixed;
struct clk_gate *gate;
struct clk *clk;
fixed = kzalloc(sizeof(*fixed), GFP_KERNEL);
if (!fixed)
return ERR_PTR(-ENOMEM);
fixed->mult = 1;
fixed->div = 6;
gate = kzalloc(sizeof(*gate), GFP_KERNEL);
if (!gate) {
kfree(fixed);
return ERR_PTR(-ENOMEM);
}
gate->reg = cpg->reg + CPG_RCANCKCR;
gate->bit_idx = 8;
gate->flags = CLK_GATE_SET_TO_DISABLE;
gate->lock = &cpg->lock;
clk = clk_register_composite(NULL, "rcan", &parent_name, 1, NULL, NULL,
&fixed->hw, &clk_fixed_factor_ops,
&gate->hw, &clk_gate_ops, 0);
if (IS_ERR(clk)) {
kfree(gate);
kfree(fixed);
}
return clk;
}
/* ADSP divisors */
static const struct clk_div_table cpg_adsp_div_table[] = {
{ 1, 3 }, { 2, 4 }, { 3, 6 }, { 4, 8 },
{ 5, 12 }, { 6, 16 }, { 7, 18 }, { 8, 24 },
{ 10, 36 }, { 11, 48 }, { 0, 0 },
};
static struct clk * __init cpg_adsp_clk_register(struct rcar_gen2_cpg *cpg)
{
const char *parent_name = "pll1";
struct clk_divider *div;
struct clk_gate *gate;
struct clk *clk;
div = kzalloc(sizeof(*div), GFP_KERNEL);
if (!div)
return ERR_PTR(-ENOMEM);
div->reg = cpg->reg + CPG_ADSPCKCR;
div->width = 4;
div->table = cpg_adsp_div_table;
div->lock = &cpg->lock;
gate = kzalloc(sizeof(*gate), GFP_KERNEL);
if (!gate) {
kfree(div);
return ERR_PTR(-ENOMEM);
}
gate->reg = cpg->reg + CPG_ADSPCKCR;
gate->bit_idx = 8;
gate->flags = CLK_GATE_SET_TO_DISABLE;
gate->lock = &cpg->lock;
clk = clk_register_composite(NULL, "adsp", &parent_name, 1, NULL, NULL,
&div->hw, &clk_divider_ops,
&gate->hw, &clk_gate_ops, 0);
if (IS_ERR(clk)) {
kfree(gate);
kfree(div);
}
return clk;
}
/* -----------------------------------------------------------------------------
* CPG Clock Data
*/
@ -263,6 +347,10 @@ rcar_gen2_cpg_register_clock(struct device_node *np, struct rcar_gen2_cpg *cpg,
shift = 0;
} else if (!strcmp(name, "z")) {
return cpg_z_clk_register(cpg);
} else if (!strcmp(name, "rcan")) {
return cpg_rcan_clk_register(cpg, np);
} else if (!strcmp(name, "adsp")) {
return cpg_adsp_clk_register(cpg);
} else {
return ERR_PTR(-EINVAL);
}

View File

@ -138,16 +138,27 @@ static int flexgen_set_rate(struct clk_hw *hw, unsigned long rate,
struct flexgen *flexgen = to_flexgen(hw);
struct clk_hw *pdiv_hw = &flexgen->pdiv.hw;
struct clk_hw *fdiv_hw = &flexgen->fdiv.hw;
unsigned long primary_div = 0;
unsigned long div = 0;
int ret = 0;
pdiv_hw->clk = hw->clk;
fdiv_hw->clk = hw->clk;
primary_div = clk_best_div(parent_rate, rate);
div = clk_best_div(parent_rate, rate);
clk_divider_ops.set_rate(fdiv_hw, parent_rate, parent_rate);
ret = clk_divider_ops.set_rate(pdiv_hw, rate, rate * primary_div);
/*
* pdiv is mainly targeted for low freq results, while fdiv
* should be used for div <= 64. The other way round can
* lead to 'duty cycle' issues.
*/
if (div <= 64) {
clk_divider_ops.set_rate(pdiv_hw, parent_rate, parent_rate);
ret = clk_divider_ops.set_rate(fdiv_hw, rate, rate * div);
} else {
clk_divider_ops.set_rate(fdiv_hw, parent_rate, parent_rate);
ret = clk_divider_ops.set_rate(pdiv_hw, rate, rate * div);
}
return ret;
}

View File

@ -8,6 +8,7 @@ obj-y += clk-a20-gmac.o
obj-y += clk-mod0.o
obj-y += clk-sun8i-mbus.o
obj-y += clk-sun9i-core.o
obj-y += clk-sun9i-mmc.o
obj-$(CONFIG_MFD_SUN6I_PRCM) += \
clk-sun6i-ar100.o clk-sun6i-apb0.o clk-sun6i-apb0-gates.o \

View File

@ -80,6 +80,8 @@ static long clk_factors_round_rate(struct clk_hw *hw, unsigned long rate,
}
static long clk_factors_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_p)
{
@ -156,9 +158,10 @@ static const struct clk_ops clk_factors_ops = {
.set_rate = clk_factors_set_rate,
};
struct clk * __init sunxi_factors_register(struct device_node *node,
const struct factors_data *data,
spinlock_t *lock)
struct clk *sunxi_factors_register(struct device_node *node,
const struct factors_data *data,
spinlock_t *lock,
void __iomem *reg)
{
struct clk *clk;
struct clk_factors *factors;
@ -168,11 +171,8 @@ struct clk * __init sunxi_factors_register(struct device_node *node,
struct clk_hw *mux_hw = NULL;
const char *clk_name = node->name;
const char *parents[FACTORS_MAX_PARENTS];
void __iomem *reg;
int i = 0;
reg = of_iomap(node, 0);
/* if we have a mux, we will have >1 parents */
while (i < FACTORS_MAX_PARENTS &&
(parents[i] = of_clk_get_parent_name(node, i)) != NULL)

View File

@ -36,8 +36,9 @@ struct clk_factors {
spinlock_t *lock;
};
struct clk * __init sunxi_factors_register(struct device_node *node,
const struct factors_data *data,
spinlock_t *lock);
struct clk *sunxi_factors_register(struct device_node *node,
const struct factors_data *data,
spinlock_t *lock,
void __iomem *reg);
#endif

View File

@ -17,6 +17,7 @@
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include "clk-factors.h"
@ -67,7 +68,7 @@ static struct clk_factors_config sun4i_a10_mod0_config = {
.pwidth = 2,
};
static const struct factors_data sun4i_a10_mod0_data __initconst = {
static const struct factors_data sun4i_a10_mod0_data = {
.enable = 31,
.mux = 24,
.muxmask = BIT(1) | BIT(0),
@ -79,15 +80,95 @@ static DEFINE_SPINLOCK(sun4i_a10_mod0_lock);
static void __init sun4i_a10_mod0_setup(struct device_node *node)
{
sunxi_factors_register(node, &sun4i_a10_mod0_data, &sun4i_a10_mod0_lock);
void __iomem *reg;
reg = of_iomap(node, 0);
if (!reg) {
/*
* This happens with mod0 clk nodes instantiated through
* mfd, as those do not have their resources assigned at
* CLK_OF_DECLARE time yet, so do not print an error.
*/
return;
}
sunxi_factors_register(node, &sun4i_a10_mod0_data,
&sun4i_a10_mod0_lock, reg);
}
CLK_OF_DECLARE(sun4i_a10_mod0, "allwinner,sun4i-a10-mod0-clk", sun4i_a10_mod0_setup);
static int sun4i_a10_mod0_clk_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct resource *r;
void __iomem *reg;
if (!np)
return -ENODEV;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
reg = devm_ioremap_resource(&pdev->dev, r);
if (IS_ERR(reg))
return PTR_ERR(reg);
sunxi_factors_register(np, &sun4i_a10_mod0_data,
&sun4i_a10_mod0_lock, reg);
return 0;
}
static const struct of_device_id sun4i_a10_mod0_clk_dt_ids[] = {
{ .compatible = "allwinner,sun4i-a10-mod0-clk" },
{ /* sentinel */ }
};
static struct platform_driver sun4i_a10_mod0_clk_driver = {
.driver = {
.name = "sun4i-a10-mod0-clk",
.of_match_table = sun4i_a10_mod0_clk_dt_ids,
},
.probe = sun4i_a10_mod0_clk_probe,
};
module_platform_driver(sun4i_a10_mod0_clk_driver);
static const struct factors_data sun9i_a80_mod0_data __initconst = {
.enable = 31,
.mux = 24,
.muxmask = BIT(3) | BIT(2) | BIT(1) | BIT(0),
.table = &sun4i_a10_mod0_config,
.getter = sun4i_a10_get_mod0_factors,
};
static void __init sun9i_a80_mod0_setup(struct device_node *node)
{
void __iomem *reg;
reg = of_io_request_and_map(node, 0, of_node_full_name(node));
if (IS_ERR(reg)) {
pr_err("Could not get registers for mod0-clk: %s\n",
node->name);
return;
}
sunxi_factors_register(node, &sun9i_a80_mod0_data,
&sun4i_a10_mod0_lock, reg);
}
CLK_OF_DECLARE(sun9i_a80_mod0, "allwinner,sun9i-a80-mod0-clk", sun9i_a80_mod0_setup);
static DEFINE_SPINLOCK(sun5i_a13_mbus_lock);
static void __init sun5i_a13_mbus_setup(struct device_node *node)
{
struct clk *mbus = sunxi_factors_register(node, &sun4i_a10_mod0_data, &sun5i_a13_mbus_lock);
struct clk *mbus;
void __iomem *reg;
reg = of_iomap(node, 0);
if (!reg) {
pr_err("Could not get registers for a13-mbus-clk\n");
return;
}
mbus = sunxi_factors_register(node, &sun4i_a10_mod0_data,
&sun5i_a13_mbus_lock, reg);
/* The MBUS clocks needs to be always enabled */
__clk_get(mbus);
@ -95,14 +176,10 @@ static void __init sun5i_a13_mbus_setup(struct device_node *node)
}
CLK_OF_DECLARE(sun5i_a13_mbus, "allwinner,sun5i-a13-mbus-clk", sun5i_a13_mbus_setup);
struct mmc_phase_data {
u8 offset;
};
struct mmc_phase {
struct clk_hw hw;
u8 offset;
void __iomem *reg;
struct mmc_phase_data *data;
spinlock_t *lock;
};
@ -118,7 +195,7 @@ static int mmc_get_phase(struct clk_hw *hw)
u8 delay;
value = readl(phase->reg);
delay = (value >> phase->data->offset) & 0x3;
delay = (value >> phase->offset) & 0x3;
if (!delay)
return 180;
@ -206,8 +283,8 @@ static int mmc_set_phase(struct clk_hw *hw, int degrees)
spin_lock_irqsave(phase->lock, flags);
value = readl(phase->reg);
value &= ~GENMASK(phase->data->offset + 3, phase->data->offset);
value |= delay << phase->data->offset;
value &= ~GENMASK(phase->offset + 3, phase->offset);
value |= delay << phase->offset;
writel(value, phase->reg);
spin_unlock_irqrestore(phase->lock, flags);
@ -219,66 +296,97 @@ static const struct clk_ops mmc_clk_ops = {
.set_phase = mmc_set_phase,
};
static void __init sun4i_a10_mmc_phase_setup(struct device_node *node,
struct mmc_phase_data *data)
/*
* sunxi_mmc_setup - Common setup function for mmc module clocks
*
* The only difference between module clocks on different platforms is the
* width of the mux register bits and the valid values, which are passed in
* through struct factors_data. The phase clocks parts are identical.
*/
static void __init sunxi_mmc_setup(struct device_node *node,
const struct factors_data *data,
spinlock_t *lock)
{
const char *parent_names[1] = { of_clk_get_parent_name(node, 0) };
struct clk_init_data init = {
.num_parents = 1,
.parent_names = parent_names,
.ops = &mmc_clk_ops,
};
struct clk_onecell_data *clk_data;
const char *parent;
void __iomem *reg;
int i;
struct mmc_phase *phase;
struct clk *clk;
reg = of_io_request_and_map(node, 0, of_node_full_name(node));
if (IS_ERR(reg)) {
pr_err("Couldn't map the %s clock registers\n", node->name);
return;
}
phase = kmalloc(sizeof(*phase), GFP_KERNEL);
if (!phase)
clk_data = kmalloc(sizeof(*clk_data), GFP_KERNEL);
if (!clk_data)
return;
phase->hw.init = &init;
clk_data->clks = kcalloc(3, sizeof(*clk_data->clks), GFP_KERNEL);
if (!clk_data->clks)
goto err_free_data;
phase->reg = of_iomap(node, 0);
if (!phase->reg)
goto err_free;
clk_data->clk_num = 3;
clk_data->clks[0] = sunxi_factors_register(node, data, lock, reg);
if (!clk_data->clks[0])
goto err_free_clks;
phase->data = data;
phase->lock = &sun4i_a10_mod0_lock;
parent = __clk_get_name(clk_data->clks[0]);
if (of_property_read_string(node, "clock-output-names", &init.name))
init.name = node->name;
for (i = 1; i < 3; i++) {
struct clk_init_data init = {
.num_parents = 1,
.parent_names = &parent,
.ops = &mmc_clk_ops,
};
struct mmc_phase *phase;
clk = clk_register(NULL, &phase->hw);
if (IS_ERR(clk))
goto err_unmap;
phase = kmalloc(sizeof(*phase), GFP_KERNEL);
if (!phase)
continue;
of_clk_add_provider(node, of_clk_src_simple_get, clk);
phase->hw.init = &init;
phase->reg = reg;
phase->lock = lock;
if (i == 1)
phase->offset = 8;
else
phase->offset = 20;
if (of_property_read_string_index(node, "clock-output-names",
i, &init.name))
init.name = node->name;
clk_data->clks[i] = clk_register(NULL, &phase->hw);
if (IS_ERR(clk_data->clks[i])) {
kfree(phase);
continue;
}
}
of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
return;
err_unmap:
iounmap(phase->reg);
err_free:
kfree(phase);
err_free_clks:
kfree(clk_data->clks);
err_free_data:
kfree(clk_data);
}
static DEFINE_SPINLOCK(sun4i_a10_mmc_lock);
static struct mmc_phase_data mmc_output_clk = {
.offset = 8,
};
static struct mmc_phase_data mmc_sample_clk = {
.offset = 20,
};
static void __init sun4i_a10_mmc_output_setup(struct device_node *node)
static void __init sun4i_a10_mmc_setup(struct device_node *node)
{
sun4i_a10_mmc_phase_setup(node, &mmc_output_clk);
sunxi_mmc_setup(node, &sun4i_a10_mod0_data, &sun4i_a10_mmc_lock);
}
CLK_OF_DECLARE(sun4i_a10_mmc_output, "allwinner,sun4i-a10-mmc-output-clk", sun4i_a10_mmc_output_setup);
CLK_OF_DECLARE(sun4i_a10_mmc, "allwinner,sun4i-a10-mmc-clk", sun4i_a10_mmc_setup);
static void __init sun4i_a10_mmc_sample_setup(struct device_node *node)
static DEFINE_SPINLOCK(sun9i_a80_mmc_lock);
static void __init sun9i_a80_mmc_setup(struct device_node *node)
{
sun4i_a10_mmc_phase_setup(node, &mmc_sample_clk);
sunxi_mmc_setup(node, &sun9i_a80_mod0_data, &sun9i_a80_mmc_lock);
}
CLK_OF_DECLARE(sun4i_a10_mmc_sample, "allwinner,sun4i-a10-mmc-sample-clk", sun4i_a10_mmc_sample_setup);
CLK_OF_DECLARE(sun9i_a80_mmc, "allwinner,sun9i-a80-mmc-clk", sun9i_a80_mmc_setup);

View File

@ -45,6 +45,8 @@ static unsigned long ar100_recalc_rate(struct clk_hw *hw,
}
static long ar100_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_clk)
{

View File

@ -69,8 +69,17 @@ static DEFINE_SPINLOCK(sun8i_a23_mbus_lock);
static void __init sun8i_a23_mbus_setup(struct device_node *node)
{
struct clk *mbus = sunxi_factors_register(node, &sun8i_a23_mbus_data,
&sun8i_a23_mbus_lock);
struct clk *mbus;
void __iomem *reg;
reg = of_iomap(node, 0);
if (!reg) {
pr_err("Could not get registers for a23-mbus-clk\n");
return;
}
mbus = sunxi_factors_register(node, &sun8i_a23_mbus_data,
&sun8i_a23_mbus_lock, reg);
/* The MBUS clocks needs to be always enabled */
__clk_get(mbus);

View File

@ -24,50 +24,51 @@
/**
* sun9i_a80_get_pll4_factors() - calculates n, p, m factors for PLL1
* sun9i_a80_get_pll4_factors() - calculates n, p, m factors for PLL4
* PLL4 rate is calculated as follows
* rate = (parent_rate * n >> p) / (m + 1);
* parent_rate is always 24Mhz
* parent_rate is always 24MHz
*
* p and m are named div1 and div2 in Allwinner's SDK
*/
static void sun9i_a80_get_pll4_factors(u32 *freq, u32 parent_rate,
u8 *n, u8 *k, u8 *m, u8 *p)
u8 *n_ret, u8 *k, u8 *m_ret, u8 *p_ret)
{
int div;
int n;
int m = 1;
int p = 1;
/* Normalize value to a 6M multiple */
div = DIV_ROUND_UP(*freq, 6000000);
/* Normalize value to a 6 MHz multiple (24 MHz / 4) */
n = DIV_ROUND_UP(*freq, 6000000);
/* divs above 256 cannot be odd */
if (div > 256)
div = round_up(div, 2);
/* If n is too large switch to steps of 12 MHz */
if (n > 255) {
m = 0;
n = (n + 1) / 2;
}
/* divs above 512 must be a multiple of 4 */
if (div > 512)
div = round_up(div, 4);
/* If n is still too large switch to steps of 24 MHz */
if (n > 255) {
p = 0;
n = (n + 1) / 2;
}
*freq = 6000000 * div;
/* n must be between 12 and 255 */
if (n > 255)
n = 255;
else if (n < 12)
n = 12;
*freq = ((24000000 * n) >> p) / (m + 1);
/* we were called to round the frequency, we can now return */
if (n == NULL)
if (n_ret == NULL)
return;
/* p will be 1 for divs under 512 */
if (div < 512)
*p = 1;
else
*p = 0;
/* m will be 1 if div is odd */
if (div & 1)
*m = 1;
else
*m = 0;
/* calculate a suitable n based on m and p */
*n = div / (*p + 1) / (*m + 1);
*n_ret = n;
*m_ret = m;
*p_ret = p;
}
static struct clk_factors_config sun9i_a80_pll4_config = {
@ -89,7 +90,17 @@ static DEFINE_SPINLOCK(sun9i_a80_pll4_lock);
static void __init sun9i_a80_pll4_setup(struct device_node *node)
{
sunxi_factors_register(node, &sun9i_a80_pll4_data, &sun9i_a80_pll4_lock);
void __iomem *reg;
reg = of_io_request_and_map(node, 0, of_node_full_name(node));
if (!reg) {
pr_err("Could not get registers for a80-pll4-clk: %s\n",
node->name);
return;
}
sunxi_factors_register(node, &sun9i_a80_pll4_data,
&sun9i_a80_pll4_lock, reg);
}
CLK_OF_DECLARE(sun9i_a80_pll4, "allwinner,sun9i-a80-pll4-clk", sun9i_a80_pll4_setup);
@ -139,8 +150,18 @@ static DEFINE_SPINLOCK(sun9i_a80_gt_lock);
static void __init sun9i_a80_gt_setup(struct device_node *node)
{
struct clk *gt = sunxi_factors_register(node, &sun9i_a80_gt_data,
&sun9i_a80_gt_lock);
void __iomem *reg;
struct clk *gt;
reg = of_io_request_and_map(node, 0, of_node_full_name(node));
if (!reg) {
pr_err("Could not get registers for a80-gt-clk: %s\n",
node->name);
return;
}
gt = sunxi_factors_register(node, &sun9i_a80_gt_data,
&sun9i_a80_gt_lock, reg);
/* The GT bus clock needs to be always enabled */
__clk_get(gt);
@ -194,7 +215,17 @@ static DEFINE_SPINLOCK(sun9i_a80_ahb_lock);
static void __init sun9i_a80_ahb_setup(struct device_node *node)
{
sunxi_factors_register(node, &sun9i_a80_ahb_data, &sun9i_a80_ahb_lock);
void __iomem *reg;
reg = of_io_request_and_map(node, 0, of_node_full_name(node));
if (!reg) {
pr_err("Could not get registers for a80-ahb-clk: %s\n",
node->name);
return;
}
sunxi_factors_register(node, &sun9i_a80_ahb_data,
&sun9i_a80_ahb_lock, reg);
}
CLK_OF_DECLARE(sun9i_a80_ahb, "allwinner,sun9i-a80-ahb-clk", sun9i_a80_ahb_setup);
@ -210,7 +241,17 @@ static DEFINE_SPINLOCK(sun9i_a80_apb0_lock);
static void __init sun9i_a80_apb0_setup(struct device_node *node)
{
sunxi_factors_register(node, &sun9i_a80_apb0_data, &sun9i_a80_apb0_lock);
void __iomem *reg;
reg = of_io_request_and_map(node, 0, of_node_full_name(node));
if (!reg) {
pr_err("Could not get registers for a80-apb0-clk: %s\n",
node->name);
return;
}
sunxi_factors_register(node, &sun9i_a80_apb0_data,
&sun9i_a80_apb0_lock, reg);
}
CLK_OF_DECLARE(sun9i_a80_apb0, "allwinner,sun9i-a80-apb0-clk", sun9i_a80_apb0_setup);
@ -266,6 +307,16 @@ static DEFINE_SPINLOCK(sun9i_a80_apb1_lock);
static void __init sun9i_a80_apb1_setup(struct device_node *node)
{
sunxi_factors_register(node, &sun9i_a80_apb1_data, &sun9i_a80_apb1_lock);
void __iomem *reg;
reg = of_io_request_and_map(node, 0, of_node_full_name(node));
if (!reg) {
pr_err("Could not get registers for a80-apb1-clk: %s\n",
node->name);
return;
}
sunxi_factors_register(node, &sun9i_a80_apb1_data,
&sun9i_a80_apb1_lock, reg);
}
CLK_OF_DECLARE(sun9i_a80_apb1, "allwinner,sun9i-a80-apb1-clk", sun9i_a80_apb1_setup);

View File

@ -0,0 +1,219 @@
/*
* Copyright 2015 Chen-Yu Tsai
*
* Chen-Yu Tsai <wens@csie.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/reset.h>
#include <linux/platform_device.h>
#include <linux/reset-controller.h>
#include <linux/spinlock.h>
#define SUN9I_MMC_WIDTH 4
#define SUN9I_MMC_GATE_BIT 16
#define SUN9I_MMC_RESET_BIT 18
struct sun9i_mmc_clk_data {
spinlock_t lock;
void __iomem *membase;
struct clk *clk;
struct reset_control *reset;
struct clk_onecell_data clk_data;
struct reset_controller_dev rcdev;
};
static int sun9i_mmc_reset_assert(struct reset_controller_dev *rcdev,
unsigned long id)
{
struct sun9i_mmc_clk_data *data = container_of(rcdev,
struct sun9i_mmc_clk_data,
rcdev);
unsigned long flags;
void __iomem *reg = data->membase + SUN9I_MMC_WIDTH * id;
u32 val;
clk_prepare_enable(data->clk);
spin_lock_irqsave(&data->lock, flags);
val = readl(reg);
writel(val & ~BIT(SUN9I_MMC_RESET_BIT), reg);
spin_unlock_irqrestore(&data->lock, flags);
clk_disable_unprepare(data->clk);
return 0;
}
static int sun9i_mmc_reset_deassert(struct reset_controller_dev *rcdev,
unsigned long id)
{
struct sun9i_mmc_clk_data *data = container_of(rcdev,
struct sun9i_mmc_clk_data,
rcdev);
unsigned long flags;
void __iomem *reg = data->membase + SUN9I_MMC_WIDTH * id;
u32 val;
clk_prepare_enable(data->clk);
spin_lock_irqsave(&data->lock, flags);
val = readl(reg);
writel(val | BIT(SUN9I_MMC_RESET_BIT), reg);
spin_unlock_irqrestore(&data->lock, flags);
clk_disable_unprepare(data->clk);
return 0;
}
static struct reset_control_ops sun9i_mmc_reset_ops = {
.assert = sun9i_mmc_reset_assert,
.deassert = sun9i_mmc_reset_deassert,
};
static int sun9i_a80_mmc_config_clk_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct sun9i_mmc_clk_data *data;
struct clk_onecell_data *clk_data;
const char *clk_name = np->name;
const char *clk_parent;
struct resource *r;
int count, i, ret;
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
spin_lock_init(&data->lock);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
/* one clock/reset pair per word */
count = DIV_ROUND_UP((r->end - r->start + 1), SUN9I_MMC_WIDTH);
data->membase = devm_ioremap_resource(&pdev->dev, r);
if (IS_ERR(data->membase))
return PTR_ERR(data->membase);
clk_data = &data->clk_data;
clk_data->clk_num = count;
clk_data->clks = devm_kcalloc(&pdev->dev, count, sizeof(struct clk *),
GFP_KERNEL);
if (!clk_data->clks)
return -ENOMEM;
data->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(data->clk)) {
dev_err(&pdev->dev, "Could not get clock\n");
return PTR_ERR(data->clk);
}
data->reset = devm_reset_control_get(&pdev->dev, NULL);
if (IS_ERR(data->reset)) {
dev_err(&pdev->dev, "Could not get reset control\n");
return PTR_ERR(data->reset);
}
ret = reset_control_deassert(data->reset);
if (ret) {
dev_err(&pdev->dev, "Reset deassert err %d\n", ret);
return ret;
}
clk_parent = __clk_get_name(data->clk);
for (i = 0; i < count; i++) {
of_property_read_string_index(np, "clock-output-names",
i, &clk_name);
clk_data->clks[i] = clk_register_gate(&pdev->dev, clk_name,
clk_parent, 0,
data->membase + SUN9I_MMC_WIDTH * i,
SUN9I_MMC_GATE_BIT, 0,
&data->lock);
if (IS_ERR(clk_data->clks[i])) {
ret = PTR_ERR(clk_data->clks[i]);
goto err_clk_register;
}
}
ret = of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
if (ret)
goto err_clk_provider;
data->rcdev.owner = THIS_MODULE;
data->rcdev.nr_resets = count;
data->rcdev.ops = &sun9i_mmc_reset_ops;
data->rcdev.of_node = pdev->dev.of_node;
ret = reset_controller_register(&data->rcdev);
if (ret)
goto err_rc_reg;
platform_set_drvdata(pdev, data);
return 0;
err_rc_reg:
of_clk_del_provider(np);
err_clk_provider:
for (i = 0; i < count; i++)
clk_unregister(clk_data->clks[i]);
err_clk_register:
reset_control_assert(data->reset);
return ret;
}
static int sun9i_a80_mmc_config_clk_remove(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct sun9i_mmc_clk_data *data = platform_get_drvdata(pdev);
struct clk_onecell_data *clk_data = &data->clk_data;
int i;
reset_controller_unregister(&data->rcdev);
of_clk_del_provider(np);
for (i = 0; i < clk_data->clk_num; i++)
clk_unregister(clk_data->clks[i]);
reset_control_assert(data->reset);
return 0;
}
static const struct of_device_id sun9i_a80_mmc_config_clk_dt_ids[] = {
{ .compatible = "allwinner,sun9i-a80-mmc-config-clk" },
{ /* sentinel */ }
};
static struct platform_driver sun9i_a80_mmc_config_clk_driver = {
.driver = {
.name = "sun9i-a80-mmc-config-clk",
.of_match_table = sun9i_a80_mmc_config_clk_dt_ids,
},
.probe = sun9i_a80_mmc_config_clk_probe,
.remove = sun9i_a80_mmc_config_clk_remove,
};
module_platform_driver(sun9i_a80_mmc_config_clk_driver);
MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
MODULE_DESCRIPTION("Allwinner A80 MMC clock/reset Driver");
MODULE_LICENSE("GPL v2");

View File

@ -20,11 +20,221 @@
#include <linux/of_address.h>
#include <linux/reset-controller.h>
#include <linux/spinlock.h>
#include <linux/log2.h>
#include "clk-factors.h"
static DEFINE_SPINLOCK(clk_lock);
/**
* sun6i_a31_ahb1_clk_setup() - Setup function for a31 ahb1 composite clk
*/
#define SUN6I_AHB1_MAX_PARENTS 4
#define SUN6I_AHB1_MUX_PARENT_PLL6 3
#define SUN6I_AHB1_MUX_SHIFT 12
/* un-shifted mask is what mux_clk expects */
#define SUN6I_AHB1_MUX_MASK 0x3
#define SUN6I_AHB1_MUX_GET_PARENT(reg) ((reg >> SUN6I_AHB1_MUX_SHIFT) & \
SUN6I_AHB1_MUX_MASK)
#define SUN6I_AHB1_DIV_SHIFT 4
#define SUN6I_AHB1_DIV_MASK (0x3 << SUN6I_AHB1_DIV_SHIFT)
#define SUN6I_AHB1_DIV_GET(reg) ((reg & SUN6I_AHB1_DIV_MASK) >> \
SUN6I_AHB1_DIV_SHIFT)
#define SUN6I_AHB1_DIV_SET(reg, div) ((reg & ~SUN6I_AHB1_DIV_MASK) | \
(div << SUN6I_AHB1_DIV_SHIFT))
#define SUN6I_AHB1_PLL6_DIV_SHIFT 6
#define SUN6I_AHB1_PLL6_DIV_MASK (0x3 << SUN6I_AHB1_PLL6_DIV_SHIFT)
#define SUN6I_AHB1_PLL6_DIV_GET(reg) ((reg & SUN6I_AHB1_PLL6_DIV_MASK) >> \
SUN6I_AHB1_PLL6_DIV_SHIFT)
#define SUN6I_AHB1_PLL6_DIV_SET(reg, div) ((reg & ~SUN6I_AHB1_PLL6_DIV_MASK) | \
(div << SUN6I_AHB1_PLL6_DIV_SHIFT))
struct sun6i_ahb1_clk {
struct clk_hw hw;
void __iomem *reg;
};
#define to_sun6i_ahb1_clk(_hw) container_of(_hw, struct sun6i_ahb1_clk, hw)
static unsigned long sun6i_ahb1_clk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct sun6i_ahb1_clk *ahb1 = to_sun6i_ahb1_clk(hw);
unsigned long rate;
u32 reg;
/* Fetch the register value */
reg = readl(ahb1->reg);
/* apply pre-divider first if parent is pll6 */
if (SUN6I_AHB1_MUX_GET_PARENT(reg) == SUN6I_AHB1_MUX_PARENT_PLL6)
parent_rate /= SUN6I_AHB1_PLL6_DIV_GET(reg) + 1;
/* clk divider */
rate = parent_rate >> SUN6I_AHB1_DIV_GET(reg);
return rate;
}
static long sun6i_ahb1_clk_round(unsigned long rate, u8 *divp, u8 *pre_divp,
u8 parent, unsigned long parent_rate)
{
u8 div, calcp, calcm = 1;
/*
* clock can only divide, so we will never be able to achieve
* frequencies higher than the parent frequency
*/
if (parent_rate && rate > parent_rate)
rate = parent_rate;
div = DIV_ROUND_UP(parent_rate, rate);
/* calculate pre-divider if parent is pll6 */
if (parent == SUN6I_AHB1_MUX_PARENT_PLL6) {
if (div < 4)
calcp = 0;
else if (div / 2 < 4)
calcp = 1;
else if (div / 4 < 4)
calcp = 2;
else
calcp = 3;
calcm = DIV_ROUND_UP(div, 1 << calcp);
} else {
calcp = __roundup_pow_of_two(div);
calcp = calcp > 3 ? 3 : calcp;
}
/* we were asked to pass back divider values */
if (divp) {
*divp = calcp;
*pre_divp = calcm - 1;
}
return (parent_rate / calcm) >> calcp;
}
static long sun6i_ahb1_clk_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_clk)
{
struct clk *clk = hw->clk, *parent, *best_parent = NULL;
int i, num_parents;
unsigned long parent_rate, best = 0, child_rate, best_child_rate = 0;
/* find the parent that can help provide the fastest rate <= rate */
num_parents = __clk_get_num_parents(clk);
for (i = 0; i < num_parents; i++) {
parent = clk_get_parent_by_index(clk, i);
if (!parent)
continue;
if (__clk_get_flags(clk) & CLK_SET_RATE_PARENT)
parent_rate = __clk_round_rate(parent, rate);
else
parent_rate = __clk_get_rate(parent);
child_rate = sun6i_ahb1_clk_round(rate, NULL, NULL, i,
parent_rate);
if (child_rate <= rate && child_rate > best_child_rate) {
best_parent = parent;
best = parent_rate;
best_child_rate = child_rate;
}
}
if (best_parent)
*best_parent_clk = __clk_get_hw(best_parent);
*best_parent_rate = best;
return best_child_rate;
}
static int sun6i_ahb1_clk_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct sun6i_ahb1_clk *ahb1 = to_sun6i_ahb1_clk(hw);
unsigned long flags;
u8 div, pre_div, parent;
u32 reg;
spin_lock_irqsave(&clk_lock, flags);
reg = readl(ahb1->reg);
/* need to know which parent is used to apply pre-divider */
parent = SUN6I_AHB1_MUX_GET_PARENT(reg);
sun6i_ahb1_clk_round(rate, &div, &pre_div, parent, parent_rate);
reg = SUN6I_AHB1_DIV_SET(reg, div);
reg = SUN6I_AHB1_PLL6_DIV_SET(reg, pre_div);
writel(reg, ahb1->reg);
spin_unlock_irqrestore(&clk_lock, flags);
return 0;
}
static const struct clk_ops sun6i_ahb1_clk_ops = {
.determine_rate = sun6i_ahb1_clk_determine_rate,
.recalc_rate = sun6i_ahb1_clk_recalc_rate,
.set_rate = sun6i_ahb1_clk_set_rate,
};
static void __init sun6i_ahb1_clk_setup(struct device_node *node)
{
struct clk *clk;
struct sun6i_ahb1_clk *ahb1;
struct clk_mux *mux;
const char *clk_name = node->name;
const char *parents[SUN6I_AHB1_MAX_PARENTS];
void __iomem *reg;
int i = 0;
reg = of_io_request_and_map(node, 0, of_node_full_name(node));
/* we have a mux, we will have >1 parents */
while (i < SUN6I_AHB1_MAX_PARENTS &&
(parents[i] = of_clk_get_parent_name(node, i)) != NULL)
i++;
of_property_read_string(node, "clock-output-names", &clk_name);
ahb1 = kzalloc(sizeof(struct sun6i_ahb1_clk), GFP_KERNEL);
if (!ahb1)
return;
mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL);
if (!mux) {
kfree(ahb1);
return;
}
/* set up clock properties */
mux->reg = reg;
mux->shift = SUN6I_AHB1_MUX_SHIFT;
mux->mask = SUN6I_AHB1_MUX_MASK;
mux->lock = &clk_lock;
ahb1->reg = reg;
clk = clk_register_composite(NULL, clk_name, parents, i,
&mux->hw, &clk_mux_ops,
&ahb1->hw, &sun6i_ahb1_clk_ops,
NULL, NULL, 0);
if (!IS_ERR(clk)) {
of_clk_add_provider(node, of_clk_src_simple_get, clk);
clk_register_clkdev(clk, clk_name, NULL);
}
}
CLK_OF_DECLARE(sun6i_a31_ahb1, "allwinner,sun6i-a31-ahb1-clk", sun6i_ahb1_clk_setup);
/* Maximum number of parents our clocks have */
#define SUNXI_MAX_PARENTS 5
@ -354,43 +564,6 @@ static void sun7i_a20_get_out_factors(u32 *freq, u32 parent_rate,
*p = calcp;
}
/**
* clk_sunxi_mmc_phase_control() - configures MMC clock phase control
*/
void clk_sunxi_mmc_phase_control(struct clk *clk, u8 sample, u8 output)
{
#define to_clk_composite(_hw) container_of(_hw, struct clk_composite, hw)
#define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw)
struct clk_hw *hw = __clk_get_hw(clk);
struct clk_composite *composite = to_clk_composite(hw);
struct clk_hw *rate_hw = composite->rate_hw;
struct clk_factors *factors = to_clk_factors(rate_hw);
unsigned long flags = 0;
u32 reg;
if (factors->lock)
spin_lock_irqsave(factors->lock, flags);
reg = readl(factors->reg);
/* set sample clock phase control */
reg &= ~(0x7 << 20);
reg |= ((sample & 0x7) << 20);
/* set output clock phase control */
reg &= ~(0x7 << 8);
reg |= ((output & 0x7) << 8);
writel(reg, factors->reg);
if (factors->lock)
spin_unlock_irqrestore(factors->lock, flags);
}
EXPORT_SYMBOL(clk_sunxi_mmc_phase_control);
/**
* sunxi_factors_clk_setup() - Setup function for factor clocks
*/
@ -413,6 +586,7 @@ static struct clk_factors_config sun6i_a31_pll1_config = {
.kwidth = 2,
.mshift = 0,
.mwidth = 2,
.n_start = 1,
};
static struct clk_factors_config sun8i_a23_pll1_config = {
@ -520,7 +694,16 @@ static const struct factors_data sun7i_a20_out_data __initconst = {
static struct clk * __init sunxi_factors_clk_setup(struct device_node *node,
const struct factors_data *data)
{
return sunxi_factors_register(node, data, &clk_lock);
void __iomem *reg;
reg = of_iomap(node, 0);
if (!reg) {
pr_err("Could not get registers for factors-clk: %s\n",
node->name);
return NULL;
}
return sunxi_factors_register(node, data, &clk_lock, reg);
}
@ -561,7 +744,7 @@ static void __init sunxi_mux_clk_setup(struct device_node *node,
of_property_read_string(node, "clock-output-names", &clk_name);
clk = clk_register_mux(NULL, clk_name, parents, i,
CLK_SET_RATE_NO_REPARENT, reg,
CLK_SET_RATE_PARENT, reg,
data->shift, SUNXI_MUX_GATE_WIDTH,
0, &clk_lock);
@ -1217,7 +1400,6 @@ CLK_OF_DECLARE(sun7i_a20_clk_init, "allwinner,sun7i-a20", sun5i_init_clocks);
static const char *sun6i_critical_clocks[] __initdata = {
"cpu",
"ahb1_sdram",
};
static void __init sun6i_init_clocks(struct device_node *node)

View File

@ -3,8 +3,10 @@ obj-y += clk.o autoidle.o clockdomain.o
clk-common = dpll.o composite.o divider.o gate.o \
fixed-factor.o mux.o apll.o
obj-$(CONFIG_SOC_AM33XX) += $(clk-common) clk-33xx.o
obj-$(CONFIG_SOC_TI81XX) += $(clk-common) fapll.o clk-816x.o
obj-$(CONFIG_ARCH_OMAP2) += $(clk-common) interface.o clk-2xxx.o
obj-$(CONFIG_ARCH_OMAP3) += $(clk-common) interface.o clk-3xxx.o
obj-$(CONFIG_ARCH_OMAP3) += $(clk-common) interface.o \
clk-3xxx.o clk-3xxx-legacy.o
obj-$(CONFIG_ARCH_OMAP4) += $(clk-common) clk-44xx.o
obj-$(CONFIG_SOC_OMAP5) += $(clk-common) clk-54xx.o
obj-$(CONFIG_SOC_DRA7XX) += $(clk-common) clk-7xx.o \

File diff suppressed because it is too large Load Diff

View File

@ -327,7 +327,6 @@ enum {
OMAP3_SOC_OMAP3430_ES1,
OMAP3_SOC_OMAP3430_ES2_PLUS,
OMAP3_SOC_OMAP3630,
OMAP3_SOC_TI81XX,
};
static int __init omap3xxx_dt_clk_init(int soc_type)
@ -370,7 +369,7 @@ static int __init omap3xxx_dt_clk_init(int soc_type)
(clk_get_rate(clk_get_sys(NULL, "core_ck")) / 1000000),
(clk_get_rate(clk_get_sys(NULL, "arm_fck")) / 1000000));
if (soc_type != OMAP3_SOC_TI81XX && soc_type != OMAP3_SOC_OMAP3430_ES1)
if (soc_type != OMAP3_SOC_OMAP3430_ES1)
omap3_clk_lock_dpll5();
return 0;
@ -390,8 +389,3 @@ int __init am35xx_dt_clk_init(void)
{
return omap3xxx_dt_clk_init(OMAP3_SOC_AM35XX);
}
int __init ti81xx_dt_clk_init(void)
{
return omap3xxx_dt_clk_init(OMAP3_SOC_TI81XX);
}

View File

@ -12,7 +12,7 @@
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/clk-private.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/clk/ti.h>

View File

@ -12,7 +12,7 @@
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/clk-private.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/io.h>
#include <linux/clk/ti.h>

View File

@ -12,7 +12,7 @@
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/clk-private.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/clk/ti.h>

53
drivers/clk/ti/clk-816x.c Normal file
View File

@ -0,0 +1,53 @@
/*
* 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 <linux/kernel.h>
#include <linux/list.h>
#include <linux/clk-provider.h>
#include <linux/clk/ti.h>
static struct ti_dt_clk dm816x_clks[] = {
DT_CLK(NULL, "sys_clkin", "sys_clkin_ck"),
DT_CLK(NULL, "timer_sys_ck", "sys_clkin_ck"),
DT_CLK(NULL, "sys_32k_ck", "sys_32k_ck"),
DT_CLK(NULL, "mpu_ck", "mpu_ck"),
DT_CLK(NULL, "timer1_fck", "timer1_fck"),
DT_CLK(NULL, "timer2_fck", "timer2_fck"),
DT_CLK(NULL, "timer3_fck", "timer3_fck"),
DT_CLK(NULL, "timer4_fck", "timer4_fck"),
DT_CLK(NULL, "timer5_fck", "timer5_fck"),
DT_CLK(NULL, "timer6_fck", "timer6_fck"),
DT_CLK(NULL, "timer7_fck", "timer7_fck"),
DT_CLK(NULL, "sysclk4_ck", "sysclk4_ck"),
DT_CLK(NULL, "sysclk5_ck", "sysclk5_ck"),
DT_CLK(NULL, "sysclk6_ck", "sysclk6_ck"),
DT_CLK(NULL, "sysclk10_ck", "sysclk10_ck"),
DT_CLK(NULL, "sysclk18_ck", "sysclk18_ck"),
DT_CLK(NULL, "sysclk24_ck", "sysclk24_ck"),
DT_CLK("4a100000.ethernet", "sysclk24_ck", "sysclk24_ck"),
{ .node_name = NULL },
};
static const char *enable_init_clks[] = {
"ddr_pll_clk1",
"ddr_pll_clk2",
"ddr_pll_clk3",
};
int __init ti81xx_dt_clk_init(void)
{
ti_dt_clocks_register(dm816x_clks);
omap2_clk_disable_autoidle_all();
omap2_clk_enable_init_clocks(enable_init_clks,
ARRAY_SIZE(enable_init_clks));
return 0;
}

View File

@ -22,6 +22,8 @@
#include <linux/of_address.h>
#include <linux/list.h>
#include "clock.h"
#undef pr_fmt
#define pr_fmt(fmt) "%s: " fmt, __func__
@ -183,3 +185,126 @@ void ti_dt_clk_init_retry_clks(void)
retries--;
}
}
void __init ti_clk_patch_legacy_clks(struct ti_clk **patch)
{
while (*patch) {
memcpy((*patch)->patch, *patch, sizeof(**patch));
patch++;
}
}
struct clk __init *ti_clk_register_clk(struct ti_clk *setup)
{
struct clk *clk;
struct ti_clk_fixed *fixed;
struct ti_clk_fixed_factor *fixed_factor;
struct clk_hw *clk_hw;
if (setup->clk)
return setup->clk;
switch (setup->type) {
case TI_CLK_FIXED:
fixed = setup->data;
clk = clk_register_fixed_rate(NULL, setup->name, NULL,
CLK_IS_ROOT, fixed->frequency);
break;
case TI_CLK_MUX:
clk = ti_clk_register_mux(setup);
break;
case TI_CLK_DIVIDER:
clk = ti_clk_register_divider(setup);
break;
case TI_CLK_COMPOSITE:
clk = ti_clk_register_composite(setup);
break;
case TI_CLK_FIXED_FACTOR:
fixed_factor = setup->data;
clk = clk_register_fixed_factor(NULL, setup->name,
fixed_factor->parent,
0, fixed_factor->mult,
fixed_factor->div);
break;
case TI_CLK_GATE:
clk = ti_clk_register_gate(setup);
break;
case TI_CLK_DPLL:
clk = ti_clk_register_dpll(setup);
break;
default:
pr_err("bad type for %s!\n", setup->name);
clk = ERR_PTR(-EINVAL);
}
if (!IS_ERR(clk)) {
setup->clk = clk;
if (setup->clkdm_name) {
if (__clk_get_flags(clk) & CLK_IS_BASIC) {
pr_warn("can't setup clkdm for basic clk %s\n",
setup->name);
} else {
clk_hw = __clk_get_hw(clk);
to_clk_hw_omap(clk_hw)->clkdm_name =
setup->clkdm_name;
omap2_init_clk_clkdm(clk_hw);
}
}
}
return clk;
}
int __init ti_clk_register_legacy_clks(struct ti_clk_alias *clks)
{
struct clk *clk;
bool retry;
struct ti_clk_alias *retry_clk;
struct ti_clk_alias *tmp;
while (clks->clk) {
clk = ti_clk_register_clk(clks->clk);
if (IS_ERR(clk)) {
if (PTR_ERR(clk) == -EAGAIN) {
list_add(&clks->link, &retry_list);
} else {
pr_err("register for %s failed: %ld\n",
clks->clk->name, PTR_ERR(clk));
return PTR_ERR(clk);
}
} else {
clks->lk.clk = clk;
clkdev_add(&clks->lk);
}
clks++;
}
retry = true;
while (!list_empty(&retry_list) && retry) {
retry = false;
list_for_each_entry_safe(retry_clk, tmp, &retry_list, link) {
pr_debug("retry-init: %s\n", retry_clk->clk->name);
clk = ti_clk_register_clk(retry_clk->clk);
if (IS_ERR(clk)) {
if (PTR_ERR(clk) == -EAGAIN) {
continue;
} else {
pr_err("register for %s failed: %ld\n",
retry_clk->clk->name,
PTR_ERR(clk));
return PTR_ERR(clk);
}
} else {
retry = true;
retry_clk->lk.clk = clk;
clkdev_add(&retry_clk->lk);
list_del(&retry_clk->link);
}
}
}
return 0;
}

172
drivers/clk/ti/clock.h Normal file
View File

@ -0,0 +1,172 @@
/*
* TI Clock driver internal definitions
*
* Copyright (C) 2014 Texas Instruments, Inc
* Tero Kristo (t-kristo@ti.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation 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 __DRIVERS_CLK_TI_CLOCK__
#define __DRIVERS_CLK_TI_CLOCK__
enum {
TI_CLK_FIXED,
TI_CLK_MUX,
TI_CLK_DIVIDER,
TI_CLK_COMPOSITE,
TI_CLK_FIXED_FACTOR,
TI_CLK_GATE,
TI_CLK_DPLL,
};
/* Global flags */
#define CLKF_INDEX_POWER_OF_TWO (1 << 0)
#define CLKF_INDEX_STARTS_AT_ONE (1 << 1)
#define CLKF_SET_RATE_PARENT (1 << 2)
#define CLKF_OMAP3 (1 << 3)
#define CLKF_AM35XX (1 << 4)
/* Gate flags */
#define CLKF_SET_BIT_TO_DISABLE (1 << 5)
#define CLKF_INTERFACE (1 << 6)
#define CLKF_SSI (1 << 7)
#define CLKF_DSS (1 << 8)
#define CLKF_HSOTGUSB (1 << 9)
#define CLKF_WAIT (1 << 10)
#define CLKF_NO_WAIT (1 << 11)
#define CLKF_HSDIV (1 << 12)
#define CLKF_CLKDM (1 << 13)
/* DPLL flags */
#define CLKF_LOW_POWER_STOP (1 << 5)
#define CLKF_LOCK (1 << 6)
#define CLKF_LOW_POWER_BYPASS (1 << 7)
#define CLKF_PER (1 << 8)
#define CLKF_CORE (1 << 9)
#define CLKF_J_TYPE (1 << 10)
#define CLK(dev, con, ck) \
{ \
.lk = { \
.dev_id = dev, \
.con_id = con, \
}, \
.clk = ck, \
}
struct ti_clk {
const char *name;
const char *clkdm_name;
int type;
void *data;
struct ti_clk *patch;
struct clk *clk;
};
struct ti_clk_alias {
struct ti_clk *clk;
struct clk_lookup lk;
struct list_head link;
};
struct ti_clk_fixed {
u32 frequency;
u16 flags;
};
struct ti_clk_mux {
u8 bit_shift;
int num_parents;
u16 reg;
u8 module;
const char **parents;
u16 flags;
};
struct ti_clk_divider {
const char *parent;
u8 bit_shift;
u16 max_div;
u16 reg;
u8 module;
int *dividers;
int num_dividers;
u16 flags;
};
struct ti_clk_fixed_factor {
const char *parent;
u16 div;
u16 mult;
u16 flags;
};
struct ti_clk_gate {
const char *parent;
u8 bit_shift;
u16 reg;
u8 module;
u16 flags;
};
struct ti_clk_composite {
struct ti_clk_divider *divider;
struct ti_clk_mux *mux;
struct ti_clk_gate *gate;
u16 flags;
};
struct ti_clk_clkdm_gate {
const char *parent;
u16 flags;
};
struct ti_clk_dpll {
int num_parents;
u16 control_reg;
u16 idlest_reg;
u16 autoidle_reg;
u16 mult_div1_reg;
u8 module;
const char **parents;
u16 flags;
u8 modes;
u32 mult_mask;
u32 div1_mask;
u32 enable_mask;
u32 autoidle_mask;
u32 freqsel_mask;
u32 idlest_mask;
u32 dco_mask;
u32 sddiv_mask;
u16 max_multiplier;
u16 max_divider;
u8 min_divider;
u8 auto_recal_bit;
u8 recal_en_bit;
u8 recal_st_bit;
};
struct clk *ti_clk_register_gate(struct ti_clk *setup);
struct clk *ti_clk_register_interface(struct ti_clk *setup);
struct clk *ti_clk_register_mux(struct ti_clk *setup);
struct clk *ti_clk_register_divider(struct ti_clk *setup);
struct clk *ti_clk_register_composite(struct ti_clk *setup);
struct clk *ti_clk_register_dpll(struct ti_clk *setup);
struct clk_hw *ti_clk_build_component_div(struct ti_clk_divider *setup);
struct clk_hw *ti_clk_build_component_gate(struct ti_clk_gate *setup);
struct clk_hw *ti_clk_build_component_mux(struct ti_clk_mux *setup);
void ti_clk_patch_legacy_clks(struct ti_clk **patch);
struct clk *ti_clk_register_clk(struct ti_clk *setup);
int ti_clk_register_legacy_clks(struct ti_clk_alias *clks);
#endif

View File

@ -23,6 +23,8 @@
#include <linux/clk/ti.h>
#include <linux/list.h>
#include "clock.h"
#undef pr_fmt
#define pr_fmt(fmt) "%s: " fmt, __func__
@ -116,8 +118,44 @@ static inline struct clk_hw *_get_hw(struct clk_hw_omap_comp *clk, int idx)
#define to_clk_hw_comp(_hw) container_of(_hw, struct clk_hw_omap_comp, hw)
static void __init ti_clk_register_composite(struct clk_hw *hw,
struct device_node *node)
struct clk *ti_clk_register_composite(struct ti_clk *setup)
{
struct ti_clk_composite *comp;
struct clk_hw *gate;
struct clk_hw *mux;
struct clk_hw *div;
int num_parents = 1;
const char **parent_names = NULL;
struct clk *clk;
comp = setup->data;
div = ti_clk_build_component_div(comp->divider);
gate = ti_clk_build_component_gate(comp->gate);
mux = ti_clk_build_component_mux(comp->mux);
if (div)
parent_names = &comp->divider->parent;
if (gate)
parent_names = &comp->gate->parent;
if (mux) {
num_parents = comp->mux->num_parents;
parent_names = comp->mux->parents;
}
clk = clk_register_composite(NULL, setup->name,
parent_names, num_parents, mux,
&ti_clk_mux_ops, div,
&ti_composite_divider_ops, gate,
&ti_composite_gate_ops, 0);
return clk;
}
static void __init _register_composite(struct clk_hw *hw,
struct device_node *node)
{
struct clk *clk;
struct clk_hw_omap_comp *cclk = to_clk_hw_comp(hw);
@ -136,7 +174,7 @@ static void __init ti_clk_register_composite(struct clk_hw *hw,
pr_debug("component %s not ready for %s, retry\n",
cclk->comp_nodes[i]->name, node->name);
if (!ti_clk_retry_init(node, hw,
ti_clk_register_composite))
_register_composite))
return;
goto cleanup;
@ -216,7 +254,7 @@ static void __init of_ti_composite_clk_setup(struct device_node *node)
for (i = 0; i < num_clks; i++)
cclk->comp_nodes[i] = _get_component_node(node, i);
ti_clk_register_composite(&cclk->hw, node);
_register_composite(&cclk->hw, node);
}
CLK_OF_DECLARE(ti_composite_clock, "ti,composite-clock",
of_ti_composite_clk_setup);

View File

@ -21,6 +21,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/clk/ti.h>
#include "clock.h"
#undef pr_fmt
#define pr_fmt(fmt) "%s: " fmt, __func__
@ -300,6 +301,134 @@ static struct clk *_register_divider(struct device *dev, const char *name,
return clk;
}
static struct clk_div_table *
_get_div_table_from_setup(struct ti_clk_divider *setup, u8 *width)
{
int valid_div = 0;
struct clk_div_table *table;
int i;
int div;
u32 val;
u8 flags;
if (!setup->num_dividers) {
/* Clk divider table not provided, determine min/max divs */
flags = setup->flags;
if (flags & CLKF_INDEX_STARTS_AT_ONE)
val = 1;
else
val = 0;
div = 1;
while (div < setup->max_div) {
if (flags & CLKF_INDEX_POWER_OF_TWO)
div <<= 1;
else
div++;
val++;
}
*width = fls(val);
return NULL;
}
for (i = 0; i < setup->num_dividers; i++)
if (setup->dividers[i])
valid_div++;
table = kzalloc(sizeof(*table) * (valid_div + 1), GFP_KERNEL);
if (!table)
return ERR_PTR(-ENOMEM);
valid_div = 0;
*width = 0;
for (i = 0; i < setup->num_dividers; i++)
if (setup->dividers[i]) {
table[valid_div].div = setup->dividers[i];
table[valid_div].val = i;
valid_div++;
*width = i;
}
*width = fls(*width);
return table;
}
struct clk_hw *ti_clk_build_component_div(struct ti_clk_divider *setup)
{
struct clk_divider *div;
struct clk_omap_reg *reg;
if (!setup)
return NULL;
div = kzalloc(sizeof(*div), GFP_KERNEL);
if (!div)
return ERR_PTR(-ENOMEM);
reg = (struct clk_omap_reg *)&div->reg;
reg->index = setup->module;
reg->offset = setup->reg;
if (setup->flags & CLKF_INDEX_STARTS_AT_ONE)
div->flags |= CLK_DIVIDER_ONE_BASED;
if (setup->flags & CLKF_INDEX_POWER_OF_TWO)
div->flags |= CLK_DIVIDER_POWER_OF_TWO;
div->table = _get_div_table_from_setup(setup, &div->width);
div->shift = setup->bit_shift;
return &div->hw;
}
struct clk *ti_clk_register_divider(struct ti_clk *setup)
{
struct ti_clk_divider *div;
struct clk_omap_reg *reg_setup;
u32 reg;
u8 width;
u32 flags = 0;
u8 div_flags = 0;
struct clk_div_table *table;
struct clk *clk;
div = setup->data;
reg_setup = (struct clk_omap_reg *)&reg;
reg_setup->index = div->module;
reg_setup->offset = div->reg;
if (div->flags & CLKF_INDEX_STARTS_AT_ONE)
div_flags |= CLK_DIVIDER_ONE_BASED;
if (div->flags & CLKF_INDEX_POWER_OF_TWO)
div_flags |= CLK_DIVIDER_POWER_OF_TWO;
if (div->flags & CLKF_SET_RATE_PARENT)
flags |= CLK_SET_RATE_PARENT;
table = _get_div_table_from_setup(div, &width);
if (IS_ERR(table))
return (struct clk *)table;
clk = _register_divider(NULL, setup->name, div->parent,
flags, (void __iomem *)reg, div->bit_shift,
width, div_flags, table, NULL);
if (IS_ERR(clk))
kfree(table);
return clk;
}
static struct clk_div_table *
__init ti_clk_get_div_table(struct device_node *node)
{
@ -455,7 +584,8 @@ static void __init of_ti_divider_clk_setup(struct device_node *node)
goto cleanup;
clk = _register_divider(NULL, node->name, parent_name, flags, reg,
shift, width, clk_divider_flags, table, NULL);
shift, width, clk_divider_flags, table,
NULL);
if (!IS_ERR(clk)) {
of_clk_add_provider(node, of_clk_src_simple_get, clk);

View File

@ -21,6 +21,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/clk/ti.h>
#include "clock.h"
#undef pr_fmt
#define pr_fmt(fmt) "%s: " fmt, __func__
@ -130,7 +131,7 @@ static const struct clk_ops dpll_x2_ck_ops = {
};
/**
* ti_clk_register_dpll - low level registration of a DPLL clock
* _register_dpll - low level registration of a DPLL clock
* @hw: hardware clock definition for the clock
* @node: device node for the clock
*
@ -138,8 +139,8 @@ static const struct clk_ops dpll_x2_ck_ops = {
* clk-bypass is missing), the clock is added to retry list and
* the initialization is retried on later stage.
*/
static void __init ti_clk_register_dpll(struct clk_hw *hw,
struct device_node *node)
static void __init _register_dpll(struct clk_hw *hw,
struct device_node *node)
{
struct clk_hw_omap *clk_hw = to_clk_hw_omap(hw);
struct dpll_data *dd = clk_hw->dpll_data;
@ -151,7 +152,7 @@ static void __init ti_clk_register_dpll(struct clk_hw *hw,
if (IS_ERR(dd->clk_ref) || IS_ERR(dd->clk_bypass)) {
pr_debug("clk-ref or clk-bypass missing for %s, retry later\n",
node->name);
if (!ti_clk_retry_init(node, hw, ti_clk_register_dpll))
if (!ti_clk_retry_init(node, hw, _register_dpll))
return;
goto cleanup;
@ -175,20 +176,116 @@ cleanup:
kfree(clk_hw);
}
void __iomem *_get_reg(u8 module, u16 offset)
{
u32 reg;
struct clk_omap_reg *reg_setup;
reg_setup = (struct clk_omap_reg *)&reg;
reg_setup->index = module;
reg_setup->offset = offset;
return (void __iomem *)reg;
}
struct clk *ti_clk_register_dpll(struct ti_clk *setup)
{
struct clk_hw_omap *clk_hw;
struct clk_init_data init = { NULL };
struct dpll_data *dd;
struct clk *clk;
struct ti_clk_dpll *dpll;
const struct clk_ops *ops = &omap3_dpll_ck_ops;
struct clk *clk_ref;
struct clk *clk_bypass;
dpll = setup->data;
if (dpll->num_parents < 2)
return ERR_PTR(-EINVAL);
clk_ref = clk_get_sys(NULL, dpll->parents[0]);
clk_bypass = clk_get_sys(NULL, dpll->parents[1]);
if (IS_ERR_OR_NULL(clk_ref) || IS_ERR_OR_NULL(clk_bypass))
return ERR_PTR(-EAGAIN);
dd = kzalloc(sizeof(*dd), GFP_KERNEL);
clk_hw = kzalloc(sizeof(*clk_hw), GFP_KERNEL);
if (!dd || !clk_hw) {
clk = ERR_PTR(-ENOMEM);
goto cleanup;
}
clk_hw->dpll_data = dd;
clk_hw->ops = &clkhwops_omap3_dpll;
clk_hw->hw.init = &init;
clk_hw->flags = MEMMAP_ADDRESSING;
init.name = setup->name;
init.ops = ops;
init.num_parents = dpll->num_parents;
init.parent_names = dpll->parents;
dd->control_reg = _get_reg(dpll->module, dpll->control_reg);
dd->idlest_reg = _get_reg(dpll->module, dpll->idlest_reg);
dd->mult_div1_reg = _get_reg(dpll->module, dpll->mult_div1_reg);
dd->autoidle_reg = _get_reg(dpll->module, dpll->autoidle_reg);
dd->modes = dpll->modes;
dd->div1_mask = dpll->div1_mask;
dd->idlest_mask = dpll->idlest_mask;
dd->mult_mask = dpll->mult_mask;
dd->autoidle_mask = dpll->autoidle_mask;
dd->enable_mask = dpll->enable_mask;
dd->sddiv_mask = dpll->sddiv_mask;
dd->dco_mask = dpll->dco_mask;
dd->max_divider = dpll->max_divider;
dd->min_divider = dpll->min_divider;
dd->max_multiplier = dpll->max_multiplier;
dd->auto_recal_bit = dpll->auto_recal_bit;
dd->recal_en_bit = dpll->recal_en_bit;
dd->recal_st_bit = dpll->recal_st_bit;
dd->clk_ref = clk_ref;
dd->clk_bypass = clk_bypass;
if (dpll->flags & CLKF_CORE)
ops = &omap3_dpll_core_ck_ops;
if (dpll->flags & CLKF_PER)
ops = &omap3_dpll_per_ck_ops;
if (dpll->flags & CLKF_J_TYPE)
dd->flags |= DPLL_J_TYPE;
clk = clk_register(NULL, &clk_hw->hw);
if (!IS_ERR(clk))
return clk;
cleanup:
kfree(dd);
kfree(clk_hw);
return clk;
}
#if defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_SOC_OMAP5) || \
defined(CONFIG_SOC_DRA7XX) || defined(CONFIG_SOC_AM33XX) || \
defined(CONFIG_SOC_AM43XX)
/**
* ti_clk_register_dpll_x2 - Registers a DPLLx2 clock
* _register_dpll_x2 - Registers a DPLLx2 clock
* @node: device node for this clock
* @ops: clk_ops for this clock
* @hw_ops: clk_hw_ops for this clock
*
* Initializes a DPLL x 2 clock from device tree data.
*/
static void ti_clk_register_dpll_x2(struct device_node *node,
const struct clk_ops *ops,
const struct clk_hw_omap_ops *hw_ops)
static void _register_dpll_x2(struct device_node *node,
const struct clk_ops *ops,
const struct clk_hw_omap_ops *hw_ops)
{
struct clk *clk;
struct clk_init_data init = { NULL };
@ -318,7 +415,7 @@ static void __init of_ti_dpll_setup(struct device_node *node,
if (dpll_mode)
dd->modes = dpll_mode;
ti_clk_register_dpll(&clk_hw->hw, node);
_register_dpll(&clk_hw->hw, node);
return;
cleanup:
@ -332,7 +429,7 @@ cleanup:
defined(CONFIG_SOC_DRA7XX)
static void __init of_ti_omap4_dpll_x2_setup(struct device_node *node)
{
ti_clk_register_dpll_x2(node, &dpll_x2_ck_ops, &clkhwops_omap4_dpllmx);
_register_dpll_x2(node, &dpll_x2_ck_ops, &clkhwops_omap4_dpllmx);
}
CLK_OF_DECLARE(ti_omap4_dpll_x2_clock, "ti,omap4-dpll-x2-clock",
of_ti_omap4_dpll_x2_setup);
@ -341,7 +438,7 @@ CLK_OF_DECLARE(ti_omap4_dpll_x2_clock, "ti,omap4-dpll-x2-clock",
#if defined(CONFIG_SOC_AM33XX) || defined(CONFIG_SOC_AM43XX)
static void __init of_ti_am3_dpll_x2_setup(struct device_node *node)
{
ti_clk_register_dpll_x2(node, &dpll_x2_ck_ops, NULL);
_register_dpll_x2(node, &dpll_x2_ck_ops, NULL);
}
CLK_OF_DECLARE(ti_am3_dpll_x2_clock, "ti,am3-dpll-x2-clock",
of_ti_am3_dpll_x2_setup);

410
drivers/clk/ti/fapll.c Normal file
View File

@ -0,0 +1,410 @@
/*
* 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 <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/clk/ti.h>
#include <asm/div64.h>
/* FAPLL Control Register PLL_CTRL */
#define FAPLL_MAIN_LOCK BIT(7)
#define FAPLL_MAIN_PLLEN BIT(3)
#define FAPLL_MAIN_BP BIT(2)
#define FAPLL_MAIN_LOC_CTL BIT(0)
/* FAPLL powerdown register PWD */
#define FAPLL_PWD_OFFSET 4
#define MAX_FAPLL_OUTPUTS 7
#define FAPLL_MAX_RETRIES 1000
#define to_fapll(_hw) container_of(_hw, struct fapll_data, hw)
#define to_synth(_hw) container_of(_hw, struct fapll_synth, hw)
/* The bypass bit is inverted on the ddr_pll.. */
#define fapll_is_ddr_pll(va) (((u32)(va) & 0xffff) == 0x0440)
/*
* The audio_pll_clk1 input is hard wired to the 27MHz bypass clock,
* and the audio_pll_clk1 synthesizer is hardwared to 32KiHz output.
*/
#define is_ddr_pll_clk1(va) (((u32)(va) & 0xffff) == 0x044c)
#define is_audio_pll_clk1(va) (((u32)(va) & 0xffff) == 0x04a8)
/* Synthesizer divider register */
#define SYNTH_LDMDIV1 BIT(8)
/* Synthesizer frequency register */
#define SYNTH_LDFREQ BIT(31)
struct fapll_data {
struct clk_hw hw;
void __iomem *base;
const char *name;
struct clk *clk_ref;
struct clk *clk_bypass;
struct clk_onecell_data outputs;
bool bypass_bit_inverted;
};
struct fapll_synth {
struct clk_hw hw;
struct fapll_data *fd;
int index;
void __iomem *freq;
void __iomem *div;
const char *name;
struct clk *clk_pll;
};
static bool ti_fapll_clock_is_bypass(struct fapll_data *fd)
{
u32 v = readl_relaxed(fd->base);
if (fd->bypass_bit_inverted)
return !(v & FAPLL_MAIN_BP);
else
return !!(v & FAPLL_MAIN_BP);
}
static int ti_fapll_enable(struct clk_hw *hw)
{
struct fapll_data *fd = to_fapll(hw);
u32 v = readl_relaxed(fd->base);
v |= (1 << FAPLL_MAIN_PLLEN);
writel_relaxed(v, fd->base);
return 0;
}
static void ti_fapll_disable(struct clk_hw *hw)
{
struct fapll_data *fd = to_fapll(hw);
u32 v = readl_relaxed(fd->base);
v &= ~(1 << FAPLL_MAIN_PLLEN);
writel_relaxed(v, fd->base);
}
static int ti_fapll_is_enabled(struct clk_hw *hw)
{
struct fapll_data *fd = to_fapll(hw);
u32 v = readl_relaxed(fd->base);
return v & (1 << FAPLL_MAIN_PLLEN);
}
static unsigned long ti_fapll_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct fapll_data *fd = to_fapll(hw);
u32 fapll_n, fapll_p, v;
long long rate;
if (ti_fapll_clock_is_bypass(fd))
return parent_rate;
rate = parent_rate;
/* PLL pre-divider is P and multiplier is N */
v = readl_relaxed(fd->base);
fapll_p = (v >> 8) & 0xff;
if (fapll_p)
do_div(rate, fapll_p);
fapll_n = v >> 16;
if (fapll_n)
rate *= fapll_n;
return rate;
}
static u8 ti_fapll_get_parent(struct clk_hw *hw)
{
struct fapll_data *fd = to_fapll(hw);
if (ti_fapll_clock_is_bypass(fd))
return 1;
return 0;
}
static struct clk_ops ti_fapll_ops = {
.enable = ti_fapll_enable,
.disable = ti_fapll_disable,
.is_enabled = ti_fapll_is_enabled,
.recalc_rate = ti_fapll_recalc_rate,
.get_parent = ti_fapll_get_parent,
};
static int ti_fapll_synth_enable(struct clk_hw *hw)
{
struct fapll_synth *synth = to_synth(hw);
u32 v = readl_relaxed(synth->fd->base + FAPLL_PWD_OFFSET);
v &= ~(1 << synth->index);
writel_relaxed(v, synth->fd->base + FAPLL_PWD_OFFSET);
return 0;
}
static void ti_fapll_synth_disable(struct clk_hw *hw)
{
struct fapll_synth *synth = to_synth(hw);
u32 v = readl_relaxed(synth->fd->base + FAPLL_PWD_OFFSET);
v |= 1 << synth->index;
writel_relaxed(v, synth->fd->base + FAPLL_PWD_OFFSET);
}
static int ti_fapll_synth_is_enabled(struct clk_hw *hw)
{
struct fapll_synth *synth = to_synth(hw);
u32 v = readl_relaxed(synth->fd->base + FAPLL_PWD_OFFSET);
return !(v & (1 << synth->index));
}
/*
* See dm816x TRM chapter 1.10.3 Flying Adder PLL fore more info
*/
static unsigned long ti_fapll_synth_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct fapll_synth *synth = to_synth(hw);
u32 synth_div_m;
long long rate;
/* The audio_pll_clk1 is hardwired to produce 32.768KiHz clock */
if (!synth->div)
return 32768;
/*
* PLL in bypass sets the synths in bypass mode too. The PLL rate
* can be also be set to 27MHz, so we can't use parent_rate to
* check for bypass mode.
*/
if (ti_fapll_clock_is_bypass(synth->fd))
return parent_rate;
rate = parent_rate;
/*
* Synth frequency integer and fractional divider.
* Note that the phase output K is 8, so the result needs
* to be multiplied by 8.
*/
if (synth->freq) {
u32 v, synth_int_div, synth_frac_div, synth_div_freq;
v = readl_relaxed(synth->freq);
synth_int_div = (v >> 24) & 0xf;
synth_frac_div = v & 0xffffff;
synth_div_freq = (synth_int_div * 10000000) + synth_frac_div;
rate *= 10000000;
do_div(rate, synth_div_freq);
rate *= 8;
}
/* Synth ost-divider M */
synth_div_m = readl_relaxed(synth->div) & 0xff;
do_div(rate, synth_div_m);
return rate;
}
static struct clk_ops ti_fapll_synt_ops = {
.enable = ti_fapll_synth_enable,
.disable = ti_fapll_synth_disable,
.is_enabled = ti_fapll_synth_is_enabled,
.recalc_rate = ti_fapll_synth_recalc_rate,
};
static struct clk * __init ti_fapll_synth_setup(struct fapll_data *fd,
void __iomem *freq,
void __iomem *div,
int index,
const char *name,
const char *parent,
struct clk *pll_clk)
{
struct clk_init_data *init;
struct fapll_synth *synth;
init = kzalloc(sizeof(*init), GFP_KERNEL);
if (!init)
return ERR_PTR(-ENOMEM);
init->ops = &ti_fapll_synt_ops;
init->name = name;
init->parent_names = &parent;
init->num_parents = 1;
synth = kzalloc(sizeof(*synth), GFP_KERNEL);
if (!synth)
goto free;
synth->fd = fd;
synth->index = index;
synth->freq = freq;
synth->div = div;
synth->name = name;
synth->hw.init = init;
synth->clk_pll = pll_clk;
return clk_register(NULL, &synth->hw);
free:
kfree(synth);
kfree(init);
return ERR_PTR(-ENOMEM);
}
static void __init ti_fapll_setup(struct device_node *node)
{
struct fapll_data *fd;
struct clk_init_data *init = NULL;
const char *parent_name[2];
struct clk *pll_clk;
int i;
fd = kzalloc(sizeof(*fd), GFP_KERNEL);
if (!fd)
return;
fd->outputs.clks = kzalloc(sizeof(struct clk *) *
MAX_FAPLL_OUTPUTS + 1,
GFP_KERNEL);
if (!fd->outputs.clks)
goto free;
init = kzalloc(sizeof(*init), GFP_KERNEL);
if (!init)
goto free;
init->ops = &ti_fapll_ops;
init->name = node->name;
init->num_parents = of_clk_get_parent_count(node);
if (init->num_parents != 2) {
pr_err("%s must have two parents\n", node->name);
goto free;
}
parent_name[0] = of_clk_get_parent_name(node, 0);
parent_name[1] = of_clk_get_parent_name(node, 1);
init->parent_names = parent_name;
fd->clk_ref = of_clk_get(node, 0);
if (IS_ERR(fd->clk_ref)) {
pr_err("%s could not get clk_ref\n", node->name);
goto free;
}
fd->clk_bypass = of_clk_get(node, 1);
if (IS_ERR(fd->clk_bypass)) {
pr_err("%s could not get clk_bypass\n", node->name);
goto free;
}
fd->base = of_iomap(node, 0);
if (!fd->base) {
pr_err("%s could not get IO base\n", node->name);
goto free;
}
if (fapll_is_ddr_pll(fd->base))
fd->bypass_bit_inverted = true;
fd->name = node->name;
fd->hw.init = init;
/* Register the parent PLL */
pll_clk = clk_register(NULL, &fd->hw);
if (IS_ERR(pll_clk))
goto unmap;
fd->outputs.clks[0] = pll_clk;
fd->outputs.clk_num++;
/*
* Set up the child synthesizers starting at index 1 as the
* PLL output is at index 0. We need to check the clock-indices
* for numbering in case there are holes in the synth mapping,
* and then probe the synth register to see if it has a FREQ
* register available.
*/
for (i = 0; i < MAX_FAPLL_OUTPUTS; i++) {
const char *output_name;
void __iomem *freq, *div;
struct clk *synth_clk;
int output_instance;
u32 v;
if (of_property_read_string_index(node, "clock-output-names",
i, &output_name))
continue;
if (of_property_read_u32_index(node, "clock-indices", i,
&output_instance))
output_instance = i;
freq = fd->base + (output_instance * 8);
div = freq + 4;
/* Check for hardwired audio_pll_clk1 */
if (is_audio_pll_clk1(freq)) {
freq = 0;
div = 0;
} else {
/* Does the synthesizer have a FREQ register? */
v = readl_relaxed(freq);
if (!v)
freq = 0;
}
synth_clk = ti_fapll_synth_setup(fd, freq, div, output_instance,
output_name, node->name,
pll_clk);
if (IS_ERR(synth_clk))
continue;
fd->outputs.clks[output_instance] = synth_clk;
fd->outputs.clk_num++;
clk_register_clkdev(synth_clk, output_name, NULL);
}
/* Register the child synthesizers as the FAPLL outputs */
of_clk_add_provider(node, of_clk_src_onecell_get, &fd->outputs);
/* Add clock alias for the outputs */
kfree(init);
return;
unmap:
iounmap(fd->base);
free:
if (fd->clk_bypass)
clk_put(fd->clk_bypass);
if (fd->clk_ref)
clk_put(fd->clk_ref);
kfree(fd->outputs.clks);
kfree(fd);
kfree(init);
}
CLK_OF_DECLARE(ti_fapll_clock, "ti,dm816-fapll-clock", ti_fapll_setup);

View File

@ -22,6 +22,8 @@
#include <linux/of_address.h>
#include <linux/clk/ti.h>
#include "clock.h"
#define to_clk_divider(_hw) container_of(_hw, struct clk_divider, hw)
#undef pr_fmt
@ -90,63 +92,162 @@ static int omap36xx_gate_clk_enable_with_hsdiv_restore(struct clk_hw *clk)
return ret;
}
static struct clk *_register_gate(struct device *dev, const char *name,
const char *parent_name, unsigned long flags,
void __iomem *reg, u8 bit_idx,
u8 clk_gate_flags, const struct clk_ops *ops,
const struct clk_hw_omap_ops *hw_ops)
{
struct clk_init_data init = { NULL };
struct clk_hw_omap *clk_hw;
struct clk *clk;
clk_hw = kzalloc(sizeof(*clk_hw), GFP_KERNEL);
if (!clk_hw)
return ERR_PTR(-ENOMEM);
clk_hw->hw.init = &init;
init.name = name;
init.ops = ops;
clk_hw->enable_reg = reg;
clk_hw->enable_bit = bit_idx;
clk_hw->ops = hw_ops;
clk_hw->flags = MEMMAP_ADDRESSING | clk_gate_flags;
init.parent_names = &parent_name;
init.num_parents = 1;
init.flags = flags;
clk = clk_register(NULL, &clk_hw->hw);
if (IS_ERR(clk))
kfree(clk_hw);
return clk;
}
struct clk *ti_clk_register_gate(struct ti_clk *setup)
{
const struct clk_ops *ops = &omap_gate_clk_ops;
const struct clk_hw_omap_ops *hw_ops = NULL;
u32 reg;
struct clk_omap_reg *reg_setup;
u32 flags = 0;
u8 clk_gate_flags = 0;
struct ti_clk_gate *gate;
gate = setup->data;
if (gate->flags & CLKF_INTERFACE)
return ti_clk_register_interface(setup);
reg_setup = (struct clk_omap_reg *)&reg;
if (gate->flags & CLKF_SET_RATE_PARENT)
flags |= CLK_SET_RATE_PARENT;
if (gate->flags & CLKF_SET_BIT_TO_DISABLE)
clk_gate_flags |= INVERT_ENABLE;
if (gate->flags & CLKF_HSDIV) {
ops = &omap_gate_clk_hsdiv_restore_ops;
hw_ops = &clkhwops_wait;
}
if (gate->flags & CLKF_DSS)
hw_ops = &clkhwops_omap3430es2_dss_usbhost_wait;
if (gate->flags & CLKF_WAIT)
hw_ops = &clkhwops_wait;
if (gate->flags & CLKF_CLKDM)
ops = &omap_gate_clkdm_clk_ops;
if (gate->flags & CLKF_AM35XX)
hw_ops = &clkhwops_am35xx_ipss_module_wait;
reg_setup->index = gate->module;
reg_setup->offset = gate->reg;
return _register_gate(NULL, setup->name, gate->parent, flags,
(void __iomem *)reg, gate->bit_shift,
clk_gate_flags, ops, hw_ops);
}
struct clk_hw *ti_clk_build_component_gate(struct ti_clk_gate *setup)
{
struct clk_hw_omap *gate;
struct clk_omap_reg *reg;
const struct clk_hw_omap_ops *ops = &clkhwops_wait;
if (!setup)
return NULL;
gate = kzalloc(sizeof(*gate), GFP_KERNEL);
if (!gate)
return ERR_PTR(-ENOMEM);
reg = (struct clk_omap_reg *)&gate->enable_reg;
reg->index = setup->module;
reg->offset = setup->reg;
gate->enable_bit = setup->bit_shift;
if (setup->flags & CLKF_NO_WAIT)
ops = NULL;
if (setup->flags & CLKF_INTERFACE)
ops = &clkhwops_iclk_wait;
gate->ops = ops;
gate->flags = MEMMAP_ADDRESSING;
return &gate->hw;
}
static void __init _of_ti_gate_clk_setup(struct device_node *node,
const struct clk_ops *ops,
const struct clk_hw_omap_ops *hw_ops)
{
struct clk *clk;
struct clk_init_data init = { NULL };
struct clk_hw_omap *clk_hw;
const char *clk_name = node->name;
const char *parent_name;
void __iomem *reg = NULL;
u8 enable_bit = 0;
u32 val;
clk_hw = kzalloc(sizeof(*clk_hw), GFP_KERNEL);
if (!clk_hw)
return;
clk_hw->hw.init = &init;
init.name = clk_name;
init.ops = ops;
u32 flags = 0;
u8 clk_gate_flags = 0;
if (ops != &omap_gate_clkdm_clk_ops) {
clk_hw->enable_reg = ti_clk_get_reg_addr(node, 0);
if (!clk_hw->enable_reg)
goto cleanup;
reg = ti_clk_get_reg_addr(node, 0);
if (!reg)
return;
if (!of_property_read_u32(node, "ti,bit-shift", &val))
clk_hw->enable_bit = val;
enable_bit = val;
}
clk_hw->ops = hw_ops;
clk_hw->flags = MEMMAP_ADDRESSING;
if (of_clk_get_parent_count(node) != 1) {
pr_err("%s must have 1 parent\n", clk_name);
goto cleanup;
pr_err("%s must have 1 parent\n", node->name);
return;
}
parent_name = of_clk_get_parent_name(node, 0);
init.parent_names = &parent_name;
init.num_parents = 1;
if (of_property_read_bool(node, "ti,set-rate-parent"))
init.flags |= CLK_SET_RATE_PARENT;
flags |= CLK_SET_RATE_PARENT;
if (of_property_read_bool(node, "ti,set-bit-to-disable"))
clk_hw->flags |= INVERT_ENABLE;
clk_gate_flags |= INVERT_ENABLE;
clk = clk_register(NULL, &clk_hw->hw);
clk = _register_gate(NULL, node->name, parent_name, flags, reg,
enable_bit, clk_gate_flags, ops, hw_ops);
if (!IS_ERR(clk)) {
if (!IS_ERR(clk))
of_clk_add_provider(node, of_clk_src_simple_get, clk);
return;
}
cleanup:
kfree(clk_hw);
}
static void __init

View File

@ -20,6 +20,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/clk/ti.h>
#include "clock.h"
#undef pr_fmt
#define pr_fmt(fmt) "%s: " fmt, __func__
@ -31,53 +32,100 @@ static const struct clk_ops ti_interface_clk_ops = {
.is_enabled = &omap2_dflt_clk_is_enabled,
};
static void __init _of_ti_interface_clk_setup(struct device_node *node,
const struct clk_hw_omap_ops *ops)
static struct clk *_register_interface(struct device *dev, const char *name,
const char *parent_name,
void __iomem *reg, u8 bit_idx,
const struct clk_hw_omap_ops *ops)
{
struct clk *clk;
struct clk_init_data init = { NULL };
struct clk_hw_omap *clk_hw;
const char *parent_name;
u32 val;
struct clk *clk;
clk_hw = kzalloc(sizeof(*clk_hw), GFP_KERNEL);
if (!clk_hw)
return;
return ERR_PTR(-ENOMEM);
clk_hw->hw.init = &init;
clk_hw->ops = ops;
clk_hw->flags = MEMMAP_ADDRESSING;
clk_hw->enable_reg = reg;
clk_hw->enable_bit = bit_idx;
clk_hw->enable_reg = ti_clk_get_reg_addr(node, 0);
if (!clk_hw->enable_reg)
goto cleanup;
if (!of_property_read_u32(node, "ti,bit-shift", &val))
clk_hw->enable_bit = val;
init.name = node->name;
init.name = name;
init.ops = &ti_interface_clk_ops;
init.flags = 0;
parent_name = of_clk_get_parent_name(node, 0);
if (!parent_name) {
pr_err("%s must have a parent\n", node->name);
goto cleanup;
}
init.num_parents = 1;
init.parent_names = &parent_name;
clk = clk_register(NULL, &clk_hw->hw);
if (!IS_ERR(clk)) {
of_clk_add_provider(node, of_clk_src_simple_get, clk);
if (IS_ERR(clk))
kfree(clk_hw);
else
omap2_init_clk_hw_omap_clocks(clk);
return clk;
}
struct clk *ti_clk_register_interface(struct ti_clk *setup)
{
const struct clk_hw_omap_ops *ops = &clkhwops_iclk_wait;
u32 reg;
struct clk_omap_reg *reg_setup;
struct ti_clk_gate *gate;
gate = setup->data;
reg_setup = (struct clk_omap_reg *)&reg;
reg_setup->index = gate->module;
reg_setup->offset = gate->reg;
if (gate->flags & CLKF_NO_WAIT)
ops = &clkhwops_iclk;
if (gate->flags & CLKF_HSOTGUSB)
ops = &clkhwops_omap3430es2_iclk_hsotgusb_wait;
if (gate->flags & CLKF_DSS)
ops = &clkhwops_omap3430es2_iclk_dss_usbhost_wait;
if (gate->flags & CLKF_SSI)
ops = &clkhwops_omap3430es2_iclk_ssi_wait;
if (gate->flags & CLKF_AM35XX)
ops = &clkhwops_am35xx_ipss_wait;
return _register_interface(NULL, setup->name, gate->parent,
(void __iomem *)reg, gate->bit_shift, ops);
}
static void __init _of_ti_interface_clk_setup(struct device_node *node,
const struct clk_hw_omap_ops *ops)
{
struct clk *clk;
const char *parent_name;
void __iomem *reg;
u8 enable_bit = 0;
u32 val;
reg = ti_clk_get_reg_addr(node, 0);
if (!reg)
return;
if (!of_property_read_u32(node, "ti,bit-shift", &val))
enable_bit = val;
parent_name = of_clk_get_parent_name(node, 0);
if (!parent_name) {
pr_err("%s must have a parent\n", node->name);
return;
}
cleanup:
kfree(clk_hw);
clk = _register_interface(NULL, node->name, parent_name, reg,
enable_bit, ops);
if (!IS_ERR(clk))
of_clk_add_provider(node, of_clk_src_simple_get, clk);
}
static void __init of_ti_interface_clk_setup(struct device_node *node)

View File

@ -21,6 +21,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/clk/ti.h>
#include "clock.h"
#undef pr_fmt
#define pr_fmt(fmt) "%s: " fmt, __func__
@ -144,6 +145,39 @@ static struct clk *_register_mux(struct device *dev, const char *name,
return clk;
}
struct clk *ti_clk_register_mux(struct ti_clk *setup)
{
struct ti_clk_mux *mux;
u32 flags;
u8 mux_flags = 0;
struct clk_omap_reg *reg_setup;
u32 reg;
u32 mask;
reg_setup = (struct clk_omap_reg *)&reg;
mux = setup->data;
flags = CLK_SET_RATE_NO_REPARENT;
mask = mux->num_parents;
if (!(mux->flags & CLKF_INDEX_STARTS_AT_ONE))
mask--;
mask = (1 << fls(mask)) - 1;
reg_setup->index = mux->module;
reg_setup->offset = mux->reg;
if (mux->flags & CLKF_INDEX_STARTS_AT_ONE)
mux_flags |= CLK_MUX_INDEX_ONE;
if (mux->flags & CLKF_SET_RATE_PARENT)
flags |= CLK_SET_RATE_PARENT;
return _register_mux(NULL, setup->name, mux->parents, mux->num_parents,
flags, (void __iomem *)reg, mux->bit_shift, mask,
mux_flags, NULL, NULL);
}
/**
* of_mux_clk_setup - Setup function for simple mux rate clock
* @node: DT node for the clock
@ -194,8 +228,9 @@ static void of_mux_clk_setup(struct device_node *node)
mask = (1 << fls(mask)) - 1;
clk = _register_mux(NULL, node->name, parent_names, num_parents, flags,
reg, shift, mask, clk_mux_flags, NULL, NULL);
clk = _register_mux(NULL, node->name, parent_names, num_parents,
flags, reg, shift, mask, clk_mux_flags, NULL,
NULL);
if (!IS_ERR(clk))
of_clk_add_provider(node, of_clk_src_simple_get, clk);
@ -205,6 +240,37 @@ cleanup:
}
CLK_OF_DECLARE(mux_clk, "ti,mux-clock", of_mux_clk_setup);
struct clk_hw *ti_clk_build_component_mux(struct ti_clk_mux *setup)
{
struct clk_mux *mux;
struct clk_omap_reg *reg;
int num_parents;
if (!setup)
return NULL;
mux = kzalloc(sizeof(*mux), GFP_KERNEL);
if (!mux)
return ERR_PTR(-ENOMEM);
reg = (struct clk_omap_reg *)&mux->reg;
mux->shift = setup->bit_shift;
reg->index = setup->module;
reg->offset = setup->reg;
if (setup->flags & CLKF_INDEX_STARTS_AT_ONE)
mux->flags |= CLK_MUX_INDEX_ONE;
num_parents = setup->num_parents;
mux->mask = num_parents - 1;
mux->mask = (1 << fls(mux->mask)) - 1;
return &mux->hw;
}
static void __init of_ti_composite_mux_clk_setup(struct device_node *node)
{
struct clk_mux *mux;

View File

@ -8,7 +8,6 @@
*/
#include <linux/clk-provider.h>
#include <linux/clk-private.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/err.h>

Some files were not shown because too many files have changed in this diff Show More