mirror of
https://github.com/torvalds/linux
synced 2024-09-22 20:37:18 +00:00
Merge remote-tracking branch 'linaro/clk-next' into clk-next
This commit is contained in:
commit
0099d88516
|
@ -77,6 +77,11 @@ the operations defined in clk.h:
|
|||
int (*set_parent)(struct clk_hw *hw, u8 index);
|
||||
u8 (*get_parent)(struct clk_hw *hw);
|
||||
int (*set_rate)(struct clk_hw *hw, unsigned long);
|
||||
int (*set_rate_and_parent)(struct clk_hw *hw,
|
||||
unsigned long rate,
|
||||
unsigned long parent_rate, u8 index);
|
||||
unsigned long (*recalc_accuracy)(struct clk_hw *hw,
|
||||
unsigned long parent_accuracy);
|
||||
void (*init)(struct clk_hw *hw);
|
||||
};
|
||||
|
||||
|
@ -202,6 +207,8 @@ optional or must be evaluated on a case-by-case basis.
|
|||
.set_parent | | | n | y | n |
|
||||
.get_parent | | | n | y | n |
|
||||
| | | | | |
|
||||
.recalc_accuracy| | | | | |
|
||||
| | | | | |
|
||||
.init | | | | | |
|
||||
-----------------------------------------------------------
|
||||
[1] either one of round_rate or determine_rate is required.
|
||||
|
|
|
@ -8,12 +8,29 @@ Required Properties:
|
|||
|
||||
- compatible: should be one of the following:
|
||||
- "samsung,exynos4210-audss-clock" - controller compatible with all Exynos4 SoCs.
|
||||
- "samsung,exynos5250-audss-clock" - controller compatible with all Exynos5 SoCs.
|
||||
|
||||
- "samsung,exynos5250-audss-clock" - controller compatible with Exynos5250
|
||||
SoCs.
|
||||
- "samsung,exynos5420-audss-clock" - controller compatible with Exynos5420
|
||||
SoCs.
|
||||
- reg: physical base address and length of the controller's register set.
|
||||
|
||||
- #clock-cells: should be 1.
|
||||
|
||||
- clocks:
|
||||
- pll_ref: Fixed rate PLL reference clock, parent of mout_audss. "fin_pll"
|
||||
is used if not specified.
|
||||
- pll_in: Input PLL to the AudioSS block, parent of mout_audss. "fout_epll"
|
||||
is used if not specified.
|
||||
- cdclk: External i2s clock, parent of mout_i2s. "cdclk0" is used if not
|
||||
specified.
|
||||
- sclk_audio: Audio bus clock, parent of mout_i2s. "sclk_audio0" is used if
|
||||
not specified.
|
||||
- sclk_pcm_in: PCM clock, parent of sclk_pcm. "sclk_pcm0" is used if not
|
||||
specified.
|
||||
|
||||
- clock-names: Aliases for the above clocks. They should be "pll_ref",
|
||||
"pll_in", "cdclk", "sclk_audio", and "sclk_pcm_in" respectively.
|
||||
|
||||
The following is the list of clocks generated by the controller. Each clock is
|
||||
assigned an identifier and client nodes use this identifier to specify the
|
||||
clock which they consume. Some of the clocks are available only on a particular
|
||||
|
@ -34,8 +51,10 @@ i2s_bus 6
|
|||
sclk_i2s 7
|
||||
pcm_bus 8
|
||||
sclk_pcm 9
|
||||
adma 10 Exynos5420
|
||||
|
||||
Example 1: An example of a clock controller node is listed below.
|
||||
Example 1: An example of a clock controller node using the default input
|
||||
clock names is listed below.
|
||||
|
||||
clock_audss: audss-clock-controller@3810000 {
|
||||
compatible = "samsung,exynos5250-audss-clock";
|
||||
|
@ -43,7 +62,19 @@ clock_audss: audss-clock-controller@3810000 {
|
|||
#clock-cells = <1>;
|
||||
};
|
||||
|
||||
Example 2: I2S controller node that consumes the clock generated by the clock
|
||||
Example 2: An example of a clock controller node with the input clocks
|
||||
specified.
|
||||
|
||||
clock_audss: audss-clock-controller@3810000 {
|
||||
compatible = "samsung,exynos5250-audss-clock";
|
||||
reg = <0x03810000 0x0C>;
|
||||
#clock-cells = <1>;
|
||||
clocks = <&clock 1>, <&clock 7>, <&clock 138>, <&clock 160>,
|
||||
<&ext_i2s_clk>;
|
||||
clock-names = "pll_ref", "pll_in", "sclk_audio", "sclk_pcm_in", "cdclk";
|
||||
};
|
||||
|
||||
Example 3: I2S controller node that consumes the clock generated by the clock
|
||||
controller. Refer to the standard clock bindings for information
|
||||
about 'clocks' and 'clock-names' property.
|
||||
|
||||
|
|
98
Documentation/devicetree/bindings/clock/emev2-clock.txt
Normal file
98
Documentation/devicetree/bindings/clock/emev2-clock.txt
Normal file
|
@ -0,0 +1,98 @@
|
|||
Device tree Clock bindings for Renesas EMMA Mobile EV2
|
||||
|
||||
This binding uses the common clock binding.
|
||||
|
||||
* SMU
|
||||
System Management Unit described in user's manual R19UH0037EJ1000_SMU.
|
||||
This is not a clock provider, but clocks under SMU depend on it.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "renesas,emev2-smu"
|
||||
- reg: Address and Size of SMU registers
|
||||
|
||||
* SMU_CLKDIV
|
||||
Function block with an input mux and a divider, which corresponds to
|
||||
"Serial clock generator" in fig."Clock System Overview" of the manual,
|
||||
and "xxx frequency division setting register" (XXXCLKDIV) registers.
|
||||
This makes internal (neither input nor output) clock that is provided
|
||||
to input of xxxGCLK block.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "renesas,emev2-smu-clkdiv"
|
||||
- reg: Byte offset from SMU base and Bit position in the register
|
||||
- clocks: Parent clocks. Input clocks as described in clock-bindings.txt
|
||||
- #clock-cells: Should be <0>
|
||||
|
||||
* SMU_GCLK
|
||||
Clock gating node shown as "Clock stop processing block" in the
|
||||
fig."Clock System Overview" of the manual.
|
||||
Registers are "xxx clock gate control register" (XXXGCLKCTRL).
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "renesas,emev2-smu-gclk"
|
||||
- reg: Byte offset from SMU base and Bit position in the register
|
||||
- clocks: Input clock as described in clock-bindings.txt
|
||||
- #clock-cells: Should be <0>
|
||||
|
||||
Example of provider:
|
||||
|
||||
usia_u0_sclkdiv: usia_u0_sclkdiv {
|
||||
compatible = "renesas,emev2-smu-clkdiv";
|
||||
reg = <0x610 0>;
|
||||
clocks = <&pll3_fo>, <&pll4_fo>, <&pll1_fo>, <&osc1_fo>;
|
||||
#clock-cells = <0>;
|
||||
};
|
||||
|
||||
usia_u0_sclk: usia_u0_sclk {
|
||||
compatible = "renesas,emev2-smu-gclk";
|
||||
reg = <0x4a0 1>;
|
||||
clocks = <&usia_u0_sclkdiv>;
|
||||
#clock-cells = <0>;
|
||||
};
|
||||
|
||||
Example of consumer:
|
||||
|
||||
uart@e1020000 {
|
||||
compatible = "renesas,em-uart";
|
||||
reg = <0xe1020000 0x38>;
|
||||
interrupts = <0 8 0>;
|
||||
clocks = <&usia_u0_sclk>;
|
||||
clock-names = "sclk";
|
||||
};
|
||||
|
||||
Example of clock-tree description:
|
||||
|
||||
This describes a clock path in the clock tree
|
||||
c32ki -> pll3_fo -> usia_u0_sclkdiv -> usia_u0_sclk
|
||||
|
||||
smu@e0110000 {
|
||||
compatible = "renesas,emev2-smu";
|
||||
reg = <0xe0110000 0x10000>;
|
||||
#address-cells = <2>;
|
||||
#size-cells = <0>;
|
||||
|
||||
c32ki: c32ki {
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <32768>;
|
||||
#clock-cells = <0>;
|
||||
};
|
||||
pll3_fo: pll3_fo {
|
||||
compatible = "fixed-factor-clock";
|
||||
clocks = <&c32ki>;
|
||||
clock-div = <1>;
|
||||
clock-mult = <7000>;
|
||||
#clock-cells = <0>;
|
||||
};
|
||||
usia_u0_sclkdiv: usia_u0_sclkdiv {
|
||||
compatible = "renesas,emev2-smu-clkdiv";
|
||||
reg = <0x610 0>;
|
||||
clocks = <&pll3_fo>;
|
||||
#clock-cells = <0>;
|
||||
};
|
||||
usia_u0_sclk: usia_u0_sclk {
|
||||
compatible = "renesas,emev2-smu-gclk";
|
||||
reg = <0x4a0 1>;
|
||||
clocks = <&usia_u0_sclkdiv>;
|
||||
#clock-cells = <0>;
|
||||
};
|
||||
};
|
|
@ -62,6 +62,7 @@ clock which they consume.
|
|||
div_i2s1 157
|
||||
div_i2s2 158
|
||||
sclk_hdmiphy 159
|
||||
div_pcm0 160
|
||||
|
||||
|
||||
[Peripheral Clock Gates]
|
||||
|
@ -159,6 +160,8 @@ clock which they consume.
|
|||
mixer 343
|
||||
hdmi 344
|
||||
g2d 345
|
||||
mdma0 346
|
||||
smmu_mdma0 347
|
||||
|
||||
|
||||
[Clock Muxes]
|
||||
|
|
|
@ -10,6 +10,8 @@ Required properties:
|
|||
- clock-frequency : frequency of clock in Hz. Should be a single cell.
|
||||
|
||||
Optional properties:
|
||||
- clock-accuracy : accuracy of clock in ppb (parts per billion).
|
||||
Should be a single cell.
|
||||
- gpios : From common gpio binding; gpio connection to clock enable pin.
|
||||
- clock-output-names : From common clock binding.
|
||||
|
||||
|
@ -18,4 +20,5 @@ Example:
|
|||
compatible = "fixed-clock";
|
||||
#clock-cells = <0>;
|
||||
clock-frequency = <1000000000>;
|
||||
clock-accuracy = <100>;
|
||||
};
|
||||
|
|
|
@ -19,6 +19,6 @@ Example:
|
|||
compatible = "fixed-factor-clock";
|
||||
clocks = <&parentclk>;
|
||||
#clock-cells = <0>;
|
||||
div = <2>;
|
||||
mult = <1>;
|
||||
clock-div = <2>;
|
||||
clock-mult = <1>;
|
||||
};
|
||||
|
|
19
Documentation/devicetree/bindings/clock/hi3620-clock.txt
Normal file
19
Documentation/devicetree/bindings/clock/hi3620-clock.txt
Normal file
|
@ -0,0 +1,19 @@
|
|||
* Hisilicon Hi3620 Clock Controller
|
||||
|
||||
The Hi3620 clock controller generates and supplies clock to various
|
||||
controllers within the Hi3620 SoC.
|
||||
|
||||
Required Properties:
|
||||
|
||||
- compatible: should be one of the following.
|
||||
- "hisilicon,hi3620-clock" - controller compatible with Hi3620 SoC.
|
||||
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
|
||||
- #clock-cells: should be 1.
|
||||
|
||||
Each clock is assigned an identifier and client nodes use this identifier
|
||||
to specify the clock which they consume.
|
||||
|
||||
All these identifier could be found in <dt-bindings/clock/hi3620-clock.h>.
|
|
@ -17,13 +17,14 @@ Required properties:
|
|||
- reg - pll control0 and pll multipler registers
|
||||
- reg-names : control and multiplier. The multiplier is applicable only for
|
||||
main pll clock
|
||||
- fixed-postdiv : fixed post divider value
|
||||
- fixed-postdiv : fixed post divider value. If absent, use clkod register bits
|
||||
for postdiv
|
||||
|
||||
Example:
|
||||
mainpllclk: mainpllclk@2310110 {
|
||||
#clock-cells = <0>;
|
||||
compatible = "ti,keystone,main-pll-clock";
|
||||
clocks = <&refclkmain>;
|
||||
clocks = <&refclksys>;
|
||||
reg = <0x02620350 4>, <0x02310110 4>;
|
||||
reg-names = "control", "multiplier";
|
||||
fixed-postdiv = <2>;
|
||||
|
@ -32,11 +33,10 @@ Example:
|
|||
papllclk: papllclk@2620358 {
|
||||
#clock-cells = <0>;
|
||||
compatible = "ti,keystone,pll-clock";
|
||||
clocks = <&refclkmain>;
|
||||
clocks = <&refclkpass>;
|
||||
clock-output-names = "pa-pll-clk";
|
||||
reg = <0x02620358 4>;
|
||||
reg-names = "control";
|
||||
fixed-postdiv = <6>;
|
||||
};
|
||||
|
||||
Required properties:
|
||||
|
|
38
Documentation/devicetree/bindings/clock/maxim,max77686.txt
Normal file
38
Documentation/devicetree/bindings/clock/maxim,max77686.txt
Normal file
|
@ -0,0 +1,38 @@
|
|||
Binding for Maxim MAX77686 32k clock generator block
|
||||
|
||||
This is a part of device tree bindings of MAX77686 multi-function device.
|
||||
More information can be found in bindings/mfd/max77686.txt file.
|
||||
|
||||
The MAX77686 contains three 32.768khz clock outputs that can be controlled
|
||||
(gated/ungated) over I2C.
|
||||
|
||||
Following properties should be presend in main device node of the MFD chip.
|
||||
|
||||
Required properties:
|
||||
- #clock-cells: simple one-cell clock specifier format is used, where the
|
||||
only cell is used as an index of the clock inside the provider. Following
|
||||
indices are allowed:
|
||||
- 0: 32khz_ap clock,
|
||||
- 1: 32khz_cp clock,
|
||||
- 2: 32khz_pmic clock.
|
||||
|
||||
Example: Node of the MFD chip
|
||||
|
||||
max77686: max77686@09 {
|
||||
compatible = "maxim,max77686";
|
||||
interrupt-parent = <&wakeup_eint>;
|
||||
interrupts = <26 0>;
|
||||
reg = <0x09>;
|
||||
#clock-cells = <1>;
|
||||
|
||||
/* ... */
|
||||
};
|
||||
|
||||
Example: Clock consumer node
|
||||
|
||||
foo@0 {
|
||||
compatible = "bar,foo";
|
||||
/* ... */
|
||||
clock-names = "my-clock";
|
||||
clocks = <&max77686 2>;
|
||||
};
|
|
@ -0,0 +1,59 @@
|
|||
NVIDIA Tegra124 Clock And Reset Controller
|
||||
|
||||
This binding uses the common clock binding:
|
||||
Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
|
||||
The CAR (Clock And Reset) Controller on Tegra is the HW module responsible
|
||||
for muxing and gating Tegra's clocks, and setting their rates.
|
||||
|
||||
Required properties :
|
||||
- compatible : Should be "nvidia,tegra124-car"
|
||||
- reg : Should contain CAR registers location and length
|
||||
- clocks : Should contain phandle and clock specifiers for two clocks:
|
||||
the 32 KHz "32k_in", and the board-specific oscillator "osc".
|
||||
- #clock-cells : Should be 1.
|
||||
In clock consumers, this cell represents the clock ID exposed by the
|
||||
CAR. The assignments may be found in header file
|
||||
<dt-bindings/clock/tegra124-car.h>.
|
||||
|
||||
Example SoC include file:
|
||||
|
||||
/ {
|
||||
tegra_car: clock {
|
||||
compatible = "nvidia,tegra124-car";
|
||||
reg = <0x60006000 0x1000>;
|
||||
#clock-cells = <1>;
|
||||
};
|
||||
|
||||
usb@c5004000 {
|
||||
clocks = <&tegra_car TEGRA124_CLK_USB2>;
|
||||
};
|
||||
};
|
||||
|
||||
Example board file:
|
||||
|
||||
/ {
|
||||
clocks {
|
||||
compatible = "simple-bus";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
osc: clock@0 {
|
||||
compatible = "fixed-clock";
|
||||
reg = <0>;
|
||||
#clock-cells = <0>;
|
||||
clock-frequency = <112400000>;
|
||||
};
|
||||
|
||||
clk_32k: clock@1 {
|
||||
compatible = "fixed-clock";
|
||||
reg = <1>;
|
||||
#clock-cells = <0>;
|
||||
clock-frequency = <32768>;
|
||||
};
|
||||
};
|
||||
|
||||
&tegra_car {
|
||||
clocks = <&clk_32k> <&osc>;
|
||||
};
|
||||
};
|
21
Documentation/devicetree/bindings/clock/qcom,gcc.txt
Normal file
21
Documentation/devicetree/bindings/clock/qcom,gcc.txt
Normal file
|
@ -0,0 +1,21 @@
|
|||
Qualcomm Global Clock & Reset Controller Binding
|
||||
------------------------------------------------
|
||||
|
||||
Required properties :
|
||||
- compatible : shall contain only one of the following:
|
||||
|
||||
"qcom,gcc-msm8660"
|
||||
"qcom,gcc-msm8960"
|
||||
"qcom,gcc-msm8974"
|
||||
|
||||
- reg : shall contain base register location and length
|
||||
- #clock-cells : shall contain 1
|
||||
- #reset-cells : shall contain 1
|
||||
|
||||
Example:
|
||||
clock-controller@900000 {
|
||||
compatible = "qcom,gcc-msm8960";
|
||||
reg = <0x900000 0x4000>;
|
||||
#clock-cells = <1>;
|
||||
#reset-cells = <1>;
|
||||
};
|
21
Documentation/devicetree/bindings/clock/qcom,mmcc.txt
Normal file
21
Documentation/devicetree/bindings/clock/qcom,mmcc.txt
Normal file
|
@ -0,0 +1,21 @@
|
|||
Qualcomm Multimedia Clock & Reset Controller Binding
|
||||
----------------------------------------------------
|
||||
|
||||
Required properties :
|
||||
- compatible : shall contain only one of the following:
|
||||
|
||||
"qcom,mmcc-msm8660"
|
||||
"qcom,mmcc-msm8960"
|
||||
"qcom,mmcc-msm8974"
|
||||
|
||||
- reg : shall contain base register location and length
|
||||
- #clock-cells : shall contain 1
|
||||
- #reset-cells : shall contain 1
|
||||
|
||||
Example:
|
||||
clock-controller@4000000 {
|
||||
compatible = "qcom,mmcc-msm8960";
|
||||
reg = <0x4000000 0x1000>;
|
||||
#clock-cells = <1>;
|
||||
#reset-cells = <1>;
|
||||
};
|
|
@ -0,0 +1,28 @@
|
|||
* Renesas CPG DIV6 Clock
|
||||
|
||||
The CPG DIV6 clocks are variable factor clocks provided by the Clock Pulse
|
||||
Generator (CPG). They clock input is divided by a configurable factor from 1
|
||||
to 64.
|
||||
|
||||
Required Properties:
|
||||
|
||||
- compatible: Must be one of the following
|
||||
- "renesas,r8a7790-div6-clock" for R8A7790 (R-Car H2) DIV6 clocks
|
||||
- "renesas,r8a7791-div6-clock" for R8A7791 (R-Car M2) DIV6 clocks
|
||||
- "renesas,cpg-div6-clock" for generic DIV6 clocks
|
||||
- reg: Base address and length of the memory resource used by the DIV6 clock
|
||||
- clocks: Reference to the parent clock
|
||||
- #clock-cells: Must be 0
|
||||
- clock-output-names: The name of the clock as a free-form string
|
||||
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
sd2_clk: sd2_clk@e6150078 {
|
||||
compatible = "renesas,r8a7790-div6-clock", "renesas,cpg-div6-clock";
|
||||
reg = <0 0xe6150078 0 4>;
|
||||
clocks = <&pll1_div2_clk>;
|
||||
#clock-cells = <0>;
|
||||
clock-output-names = "sd2";
|
||||
};
|
|
@ -0,0 +1,51 @@
|
|||
* Renesas CPG Module Stop (MSTP) Clocks
|
||||
|
||||
The CPG can gate SoC device clocks. The gates are organized in groups of up to
|
||||
32 gates.
|
||||
|
||||
This device tree binding describes a single 32 gate clocks group per node.
|
||||
Clocks are referenced by user nodes by the MSTP node phandle and the clock
|
||||
index in the group, from 0 to 31.
|
||||
|
||||
Required Properties:
|
||||
|
||||
- compatible: Must be one of the following
|
||||
- "renesas,r8a7790-mstp-clocks" for R8A7790 (R-Car H2) MSTP gate clocks
|
||||
- "renesas,r8a7791-mstp-clocks" for R8A7791 (R-Car M2) MSTP gate clocks
|
||||
- "renesas,cpg-mstp-clock" for generic MSTP gate clocks
|
||||
- reg: Base address and length of the I/O mapped registers used by the MSTP
|
||||
clocks. The first register is the clock control register and is mandatory.
|
||||
The second register is the clock status register and is optional when not
|
||||
implemented in hardware.
|
||||
- clocks: Reference to the parent clocks, one per output clock. The parents
|
||||
must appear in the same order as the output clocks.
|
||||
- #clock-cells: Must be 1
|
||||
- clock-output-names: The name of the clocks as free-form strings
|
||||
- renesas,indices: Indices of the gate clocks into the group (0 to 31)
|
||||
|
||||
The clocks, clock-output-names and renesas,indices properties contain one
|
||||
entry per gate clock. The MSTP groups are sparsely populated. Unimplemented
|
||||
gate clocks must not be declared.
|
||||
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
#include <dt-bindings/clock/r8a7790-clock.h>
|
||||
|
||||
mstp3_clks: mstp3_clks@e615013c {
|
||||
compatible = "renesas,r8a7790-mstp-clocks", "renesas,cpg-mstp-clocks";
|
||||
reg = <0 0xe615013c 0 4>, <0 0xe6150048 0 4>;
|
||||
clocks = <&cp_clk>, <&mmc1_clk>, <&sd3_clk>, <&sd2_clk>,
|
||||
<&cpg_clocks R8A7790_CLK_SD1>, <&cpg_clocks R8A7790_CLK_SD0>,
|
||||
<&mmc0_clk>;
|
||||
#clock-cells = <1>;
|
||||
clock-output-names =
|
||||
"tpu0", "mmcif1", "sdhi3", "sdhi2",
|
||||
"sdhi1", "sdhi0", "mmcif0";
|
||||
renesas,clock-indices = <
|
||||
R8A7790_CLK_TPU0 R8A7790_CLK_MMCIF1 R8A7790_CLK_SDHI3
|
||||
R8A7790_CLK_SDHI2 R8A7790_CLK_SDHI1 R8A7790_CLK_SDHI0
|
||||
R8A7790_CLK_MMCIF0
|
||||
>;
|
||||
};
|
|
@ -0,0 +1,32 @@
|
|||
* Renesas R-Car Gen2 Clock Pulse Generator (CPG)
|
||||
|
||||
The CPG generates core clocks for the R-Car Gen2 SoCs. It includes three PLLs
|
||||
and several fixed ratio dividers.
|
||||
|
||||
Required Properties:
|
||||
|
||||
- compatible: Must be one of
|
||||
- "renesas,r8a7790-cpg-clocks" for the r8a7790 CPG
|
||||
- "renesas,r8a7791-cpg-clocks" for the r8a7791 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
|
||||
- #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"
|
||||
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
cpg_clocks: cpg_clocks@e6150000 {
|
||||
compatible = "renesas,r8a7790-cpg-clocks",
|
||||
"renesas,rcar-gen2-cpg-clocks";
|
||||
reg = <0 0xe6150000 0 0x1000>;
|
||||
clocks = <&extal_clk>;
|
||||
#clock-cells = <1>;
|
||||
clock-output-names = "main", "pll0, "pll1", "pll3",
|
||||
"lb", "qspi", "sdh", "sd0", "sd1", "z";
|
||||
};
|
39
Documentation/devicetree/bindings/clock/silabs,si570.txt
Normal file
39
Documentation/devicetree/bindings/clock/silabs,si570.txt
Normal file
|
@ -0,0 +1,39 @@
|
|||
Binding for Silicon Labs 570, 571, 598 and 599 programmable
|
||||
I2C clock generators.
|
||||
|
||||
Reference
|
||||
This binding uses the common clock binding[1]. Details about the devices can be
|
||||
found in the data sheets[2][3].
|
||||
|
||||
[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
[2] Si570/571 Data Sheet
|
||||
http://www.silabs.com/Support%20Documents/TechnicalDocs/si570.pdf
|
||||
[3] Si598/599 Data Sheet
|
||||
http://www.silabs.com/Support%20Documents/TechnicalDocs/si598-99.pdf
|
||||
|
||||
Required properties:
|
||||
- compatible: Shall be one of "silabs,si570", "silabs,si571",
|
||||
"silabs,si598", "silabs,si599"
|
||||
- reg: I2C device address.
|
||||
- #clock-cells: From common clock bindings: Shall be 0.
|
||||
- factory-fout: Factory set default frequency. This frequency is part specific.
|
||||
The correct frequency for the part used has to be provided in
|
||||
order to generate the correct output frequencies. For more
|
||||
details, please refer to the data sheet.
|
||||
- temperature-stability: Temperature stability of the device in PPM. Should be
|
||||
one of: 7, 20, 50 or 100.
|
||||
|
||||
Optional properties:
|
||||
- clock-output-names: From common clock bindings. Recommended to be "si570".
|
||||
- clock-frequency: Output frequency to generate. This defines the output
|
||||
frequency set during boot. It can be reprogrammed during
|
||||
runtime through the common clock framework.
|
||||
|
||||
Example:
|
||||
si570: clock-generator@5d {
|
||||
#clock-cells = <0>;
|
||||
compatible = "silabs,si570";
|
||||
temperature-stability = <50>;
|
||||
reg = <0x5d>;
|
||||
factory-fout = <156250000>;
|
||||
};
|
|
@ -7,8 +7,10 @@ This binding uses the common clock binding[1].
|
|||
Required properties:
|
||||
- compatible : shall be one of the following:
|
||||
"allwinner,sun4i-osc-clk" - for a gatable oscillator
|
||||
"allwinner,sun4i-pll1-clk" - for the main PLL clock
|
||||
"allwinner,sun4i-pll1-clk" - for the main PLL clock and PLL4
|
||||
"allwinner,sun6i-a31-pll1-clk" - for the main PLL clock on A31
|
||||
"allwinner,sun4i-pll5-clk" - for the PLL5 clock
|
||||
"allwinner,sun4i-pll6-clk" - for the PLL6 clock
|
||||
"allwinner,sun4i-cpu-clk" - for the CPU multiplexer clock
|
||||
"allwinner,sun4i-axi-clk" - for the AXI clock
|
||||
"allwinner,sun4i-axi-gates-clk" - for the AXI gates
|
||||
|
@ -33,10 +35,14 @@ Required properties:
|
|||
"allwinner,sun7i-a20-apb1-gates-clk" - for the APB1 gates on A20
|
||||
"allwinner,sun6i-a31-apb2-div-clk" - for the APB2 gates on A31
|
||||
"allwinner,sun6i-a31-apb2-gates-clk" - for the APB2 gates on A31
|
||||
"allwinner,sun4i-mod0-clk" - for the module 0 family of clocks
|
||||
"allwinner,sun7i-a20-out-clk" - for the external output clocks
|
||||
|
||||
Required properties for all clocks:
|
||||
- reg : shall be the control register address for the clock.
|
||||
- clocks : shall be the input parent clock(s) phandle for the clock
|
||||
- clocks : shall be the input parent clock(s) phandle for the clock. For
|
||||
multiplexed clocks, the list order must match the hardware
|
||||
programming order.
|
||||
- #clock-cells : from common clock binding; shall be set to 0 except for
|
||||
"allwinner,*-gates-clk" where it shall be set to 1
|
||||
|
||||
|
|
|
@ -22,6 +22,10 @@ Required properties:
|
|||
Optional properties:
|
||||
- clocks : as described in the clock bindings
|
||||
- clock-names : as described in the clock bindings
|
||||
- fclk-enable : Bit mask to enable FCLKs statically at boot time.
|
||||
Bit [0..3] correspond to FCLK0..FCLK3. The corresponding
|
||||
FCLK will only be enabled if it is actually running at
|
||||
boot time.
|
||||
|
||||
Clock inputs:
|
||||
The following strings are optional parameters to the 'clock-names' property in
|
||||
|
|
|
@ -7,6 +7,9 @@ different i2c slave address,presently for which we are statically creating i2c
|
|||
client while probing.This document describes the binding for mfd device and
|
||||
PMIC submodule.
|
||||
|
||||
Binding for the built-in 32k clock generator block is defined separately
|
||||
in bindings/clk/maxim,max77686.txt file.
|
||||
|
||||
Required properties:
|
||||
- compatible : Must be "maxim,max77686";
|
||||
- reg : Specifies the i2c slave address of PMIC block.
|
||||
|
|
14
MAINTAINERS
14
MAINTAINERS
|
@ -1322,6 +1322,14 @@ F: drivers/rtc/rtc-ab8500.c
|
|||
F: drivers/rtc/rtc-pl031.c
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-stericsson.git
|
||||
|
||||
ARM/Ux500 CLOCK FRAMEWORK SUPPORT
|
||||
M: Ulf Hansson <ulf.hansson@linaro.org>
|
||||
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||
T: git git://git.linaro.org/people/ulfh/clk.git
|
||||
S: Maintained
|
||||
F: drivers/clk/ux500/
|
||||
F: include/linux/platform_data/clk-ux500.h
|
||||
|
||||
ARM/VFP SUPPORT
|
||||
M: Russell King <linux@arm.linux.org.uk>
|
||||
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||
|
@ -7395,6 +7403,12 @@ L: linux-media@vger.kernel.org
|
|||
S: Supported
|
||||
F: drivers/media/i2c/s5c73m3/*
|
||||
|
||||
SAMSUNG SOC CLOCK DRIVERS
|
||||
M: Tomasz Figa <t.figa@samsung.com>
|
||||
S: Supported
|
||||
L: linux-samsung-soc@vger.kernel.org (moderated for non-subscribers)
|
||||
F: drivers/clk/samsung/
|
||||
|
||||
SERIAL DRIVERS
|
||||
M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
L: linux-serial@vger.kernel.org
|
||||
|
|
|
@ -48,6 +48,7 @@ max77686@09 {
|
|||
pinctrl-0 = <&max77686_irq>;
|
||||
wakeup-source;
|
||||
reg = <0x09>;
|
||||
#clock-cells = <1>;
|
||||
|
||||
voltage-regulators {
|
||||
ldo1_reg: LDO1 {
|
||||
|
|
|
@ -119,6 +119,7 @@ i2c@13860000 {
|
|||
max77686: pmic@09 {
|
||||
compatible = "maxim,max77686";
|
||||
reg = <0x09>;
|
||||
#clock-cells = <1>;
|
||||
|
||||
voltage-regulators {
|
||||
ldo1_reg: LDO1 {
|
||||
|
|
|
@ -139,6 +139,7 @@ max77686_pmic@09 {
|
|||
interrupt-parent = <&gpx0>;
|
||||
interrupts = <7 0>;
|
||||
reg = <0x09>;
|
||||
#clock-cells = <1>;
|
||||
|
||||
voltage-regulators {
|
||||
ldo1_reg: ldo1 {
|
||||
|
|
|
@ -88,6 +88,8 @@ clock_audss: audss-clock-controller@3810000 {
|
|||
compatible = "samsung,exynos5250-audss-clock";
|
||||
reg = <0x03810000 0x0C>;
|
||||
#clock-cells = <1>;
|
||||
clocks = <&clock 1>, <&clock 7>, <&clock 138>, <&clock 160>;
|
||||
clock-names = "pll_ref", "pll_in", "sclk_audio", "sclk_pcm_in";
|
||||
};
|
||||
|
||||
timer {
|
||||
|
@ -559,7 +561,7 @@ mdma0: mdma@10800000 {
|
|||
compatible = "arm,pl330", "arm,primecell";
|
||||
reg = <0x10800000 0x1000>;
|
||||
interrupts = <0 33 0>;
|
||||
clocks = <&clock 271>;
|
||||
clocks = <&clock 346>;
|
||||
clock-names = "apb_pclk";
|
||||
#dma-cells = <1>;
|
||||
#dma-channels = <8>;
|
||||
|
|
|
@ -76,8 +76,8 @@ clock_audss: audss-clock-controller@3810000 {
|
|||
compatible = "samsung,exynos5420-audss-clock";
|
||||
reg = <0x03810000 0x0C>;
|
||||
#clock-cells = <1>;
|
||||
clocks = <&clock 148>;
|
||||
clock-names = "sclk_audio";
|
||||
clocks = <&clock 1>, <&clock 5>, <&clock 148>, <&clock 149>;
|
||||
clock-names = "pll_ref", "pll_in", "sclk_audio", "sclk_pcm_in";
|
||||
};
|
||||
|
||||
codec@11000000 {
|
||||
|
|
|
@ -14,12 +14,14 @@
|
|||
|
||||
#include <linux/slab.h>
|
||||
|
||||
#ifndef CONFIG_COMMON_CLK
|
||||
#ifdef CONFIG_HAVE_MACH_CLKDEV
|
||||
#include <mach/clkdev.h>
|
||||
#else
|
||||
#define __clk_get(clk) ({ 1; })
|
||||
#define __clk_put(clk) do { } while (0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static inline struct clk_lookup_alloc *__clkdev_alloc(size_t size)
|
||||
{
|
||||
|
|
|
@ -8,7 +8,9 @@ static inline struct clk_lookup_alloc *__clkdev_alloc(size_t size)
|
|||
return kzalloc(size, GFP_KERNEL);
|
||||
}
|
||||
|
||||
#ifndef CONFIG_COMMON_CLK
|
||||
#define __clk_put(clk)
|
||||
#define __clk_get(clk) ({ 1; })
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -14,8 +14,10 @@
|
|||
|
||||
#include <linux/slab.h>
|
||||
|
||||
#ifndef CONFIG_COMMON_CLK
|
||||
#define __clk_get(clk) ({ 1; })
|
||||
#define __clk_put(clk) do { } while (0)
|
||||
#endif
|
||||
|
||||
static inline struct clk_lookup_alloc *__clkdev_alloc(size_t size)
|
||||
{
|
||||
|
|
|
@ -25,7 +25,9 @@ static inline struct clk_lookup_alloc *__clkdev_alloc(size_t size)
|
|||
return kzalloc(size, GFP_KERNEL);
|
||||
}
|
||||
|
||||
#ifndef CONFIG_COMMON_CLK
|
||||
#define __clk_put(clk)
|
||||
#define __clk_get(clk) ({ 1; })
|
||||
#endif
|
||||
|
||||
#endif /* __CLKDEV_H__ */
|
||||
|
|
|
@ -23,16 +23,6 @@ config COMMON_CLK
|
|||
menu "Common Clock Framework"
|
||||
depends on COMMON_CLK
|
||||
|
||||
config COMMON_CLK_DEBUG
|
||||
bool "DebugFS representation of clock tree"
|
||||
select DEBUG_FS
|
||||
---help---
|
||||
Creates a directory hierarchy in debugfs for visualizing the clk
|
||||
tree structure. Each directory contains read-only members
|
||||
that export information specific to that clk node: clk_rate,
|
||||
clk_flags, clk_prepare_count, clk_enable_count &
|
||||
clk_notifier_count.
|
||||
|
||||
config COMMON_CLK_WM831X
|
||||
tristate "Clock driver for WM831x/2x PMICs"
|
||||
depends on MFD_WM831X
|
||||
|
@ -64,6 +54,16 @@ config COMMON_CLK_SI5351
|
|||
This driver supports Silicon Labs 5351A/B/C programmable clock
|
||||
generators.
|
||||
|
||||
config COMMON_CLK_SI570
|
||||
tristate "Clock driver for SiLabs 570 and compatible devices"
|
||||
depends on I2C
|
||||
depends on OF
|
||||
select REGMAP_I2C
|
||||
help
|
||||
---help---
|
||||
This driver supports Silicon Labs 570/571/598/599 programmable
|
||||
clock generators.
|
||||
|
||||
config COMMON_CLK_S2MPS11
|
||||
tristate "Clock driver for S2MPS11 MFD"
|
||||
depends on MFD_SEC_CORE
|
||||
|
@ -107,6 +107,8 @@ config COMMON_CLK_KEYSTONE
|
|||
Supports clock drivers for Keystone based SOCs. These SOCs have local
|
||||
a power sleep control module that gate the clock to the IPs and PLLs.
|
||||
|
||||
source "drivers/clk/qcom/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
||||
source "drivers/clk/mvebu/Kconfig"
|
||||
|
|
|
@ -14,13 +14,14 @@ obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o
|
|||
obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o
|
||||
obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o
|
||||
obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o
|
||||
obj-$(CONFIG_ARCH_HI3xxx) += hisilicon/
|
||||
obj-$(CONFIG_ARCH_NSPIRE) += clk-nspire.o
|
||||
obj-$(CONFIG_ARCH_MXS) += mxs/
|
||||
obj-$(CONFIG_ARCH_SOCFPGA) += socfpga/
|
||||
obj-$(CONFIG_PLAT_SPEAR) += spear/
|
||||
obj-$(CONFIG_ARCH_U300) += clk-u300.o
|
||||
obj-$(CONFIG_COMMON_CLK_VERSATILE) += versatile/
|
||||
obj-$(CONFIG_ARCH_SIRF) += clk-prima2.o
|
||||
obj-$(CONFIG_COMMON_CLK_QCOM) += qcom/
|
||||
obj-$(CONFIG_PLAT_ORION) += mvebu/
|
||||
ifeq ($(CONFIG_COMMON_CLK), y)
|
||||
obj-$(CONFIG_ARCH_MMP) += mmp/
|
||||
|
@ -30,11 +31,13 @@ obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
|
|||
obj-$(CONFIG_ARCH_SUNXI) += sunxi/
|
||||
obj-$(CONFIG_ARCH_U8500) += ux500/
|
||||
obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o
|
||||
obj-$(CONFIG_ARCH_SIRF) += sirf/
|
||||
obj-$(CONFIG_ARCH_ZYNQ) += zynq/
|
||||
obj-$(CONFIG_ARCH_TEGRA) += tegra/
|
||||
obj-$(CONFIG_PLAT_SAMSUNG) += samsung/
|
||||
obj-$(CONFIG_COMMON_CLK_XGENE) += clk-xgene.o
|
||||
obj-$(CONFIG_COMMON_CLK_KEYSTONE) += keystone/
|
||||
obj-$(CONFIG_ARCH_SHMOBILE_MULTI) += shmobile/
|
||||
|
||||
obj-$(CONFIG_X86) += x86/
|
||||
|
||||
|
@ -43,6 +46,7 @@ obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN) += clk-axi-clkgen.o
|
|||
obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
|
||||
obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o
|
||||
obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o
|
||||
obj-$(CONFIG_COMMON_CLK_SI570) += clk-si570.o
|
||||
obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o
|
||||
obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o
|
||||
obj-$(CONFIG_CLK_PPC_CORENET) += clk-ppc-corenet.o
|
||||
|
|
|
@ -55,6 +55,30 @@ static unsigned long clk_composite_recalc_rate(struct clk_hw *hw,
|
|||
return rate_ops->recalc_rate(rate_hw, parent_rate);
|
||||
}
|
||||
|
||||
static long clk_composite_determine_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *best_parent_rate,
|
||||
struct clk **best_parent_p)
|
||||
{
|
||||
struct clk_composite *composite = to_clk_composite(hw);
|
||||
const struct clk_ops *rate_ops = composite->rate_ops;
|
||||
const struct clk_ops *mux_ops = composite->mux_ops;
|
||||
struct clk_hw *rate_hw = composite->rate_hw;
|
||||
struct clk_hw *mux_hw = composite->mux_hw;
|
||||
|
||||
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,
|
||||
best_parent_p);
|
||||
} 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,
|
||||
best_parent_p);
|
||||
} else {
|
||||
pr_err("clk: clk_composite_determine_rate function called, but no mux or rate callback set!\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static long clk_composite_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
|
@ -147,6 +171,8 @@ struct clk *clk_register_composite(struct device *dev, const char *name,
|
|||
composite->mux_ops = mux_ops;
|
||||
clk_composite_ops->get_parent = clk_composite_get_parent;
|
||||
clk_composite_ops->set_parent = clk_composite_set_parent;
|
||||
if (mux_ops->determine_rate)
|
||||
clk_composite_ops->determine_rate = clk_composite_determine_rate;
|
||||
}
|
||||
|
||||
if (rate_hw && rate_ops) {
|
||||
|
@ -170,6 +196,8 @@ struct clk *clk_register_composite(struct device *dev, const char *name,
|
|||
composite->rate_hw = rate_hw;
|
||||
composite->rate_ops = rate_ops;
|
||||
clk_composite_ops->recalc_rate = clk_composite_recalc_rate;
|
||||
if (rate_ops->determine_rate)
|
||||
clk_composite_ops->determine_rate = clk_composite_determine_rate;
|
||||
}
|
||||
|
||||
if (gate_hw && gate_ops) {
|
||||
|
|
|
@ -34,22 +34,31 @@ static unsigned long clk_fixed_rate_recalc_rate(struct clk_hw *hw,
|
|||
return to_clk_fixed_rate(hw)->fixed_rate;
|
||||
}
|
||||
|
||||
static unsigned long clk_fixed_rate_recalc_accuracy(struct clk_hw *hw,
|
||||
unsigned long parent_accuracy)
|
||||
{
|
||||
return to_clk_fixed_rate(hw)->fixed_accuracy;
|
||||
}
|
||||
|
||||
const struct clk_ops clk_fixed_rate_ops = {
|
||||
.recalc_rate = clk_fixed_rate_recalc_rate,
|
||||
.recalc_accuracy = clk_fixed_rate_recalc_accuracy,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(clk_fixed_rate_ops);
|
||||
|
||||
/**
|
||||
* clk_register_fixed_rate - register fixed-rate clock with the clock framework
|
||||
* clk_register_fixed_rate_with_accuracy - register fixed-rate clock with the
|
||||
* clock framework
|
||||
* @dev: device that is registering this clock
|
||||
* @name: name of this clock
|
||||
* @parent_name: name of clock's parent
|
||||
* @flags: framework-specific flags
|
||||
* @fixed_rate: non-adjustable clock rate
|
||||
* @fixed_accuracy: non-adjustable clock rate
|
||||
*/
|
||||
struct clk *clk_register_fixed_rate(struct device *dev, const char *name,
|
||||
const char *parent_name, unsigned long flags,
|
||||
unsigned long fixed_rate)
|
||||
struct clk *clk_register_fixed_rate_with_accuracy(struct device *dev,
|
||||
const char *name, const char *parent_name, unsigned long flags,
|
||||
unsigned long fixed_rate, unsigned long fixed_accuracy)
|
||||
{
|
||||
struct clk_fixed_rate *fixed;
|
||||
struct clk *clk;
|
||||
|
@ -70,16 +79,33 @@ struct clk *clk_register_fixed_rate(struct device *dev, const char *name,
|
|||
|
||||
/* struct clk_fixed_rate assignments */
|
||||
fixed->fixed_rate = fixed_rate;
|
||||
fixed->fixed_accuracy = fixed_accuracy;
|
||||
fixed->hw.init = &init;
|
||||
|
||||
/* register the clock */
|
||||
clk = clk_register(dev, &fixed->hw);
|
||||
|
||||
if (IS_ERR(clk))
|
||||
kfree(fixed);
|
||||
|
||||
return clk;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(clk_register_fixed_rate_with_accuracy);
|
||||
|
||||
/**
|
||||
* clk_register_fixed_rate - register fixed-rate clock with the clock framework
|
||||
* @dev: device that is registering this clock
|
||||
* @name: name of this clock
|
||||
* @parent_name: name of clock's parent
|
||||
* @flags: framework-specific flags
|
||||
* @fixed_rate: non-adjustable clock rate
|
||||
*/
|
||||
struct clk *clk_register_fixed_rate(struct device *dev, const char *name,
|
||||
const char *parent_name, unsigned long flags,
|
||||
unsigned long fixed_rate)
|
||||
{
|
||||
return clk_register_fixed_rate_with_accuracy(dev, name, parent_name,
|
||||
flags, fixed_rate, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(clk_register_fixed_rate);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
|
@ -91,13 +117,18 @@ void of_fixed_clk_setup(struct device_node *node)
|
|||
struct clk *clk;
|
||||
const char *clk_name = node->name;
|
||||
u32 rate;
|
||||
u32 accuracy = 0;
|
||||
|
||||
if (of_property_read_u32(node, "clock-frequency", &rate))
|
||||
return;
|
||||
|
||||
of_property_read_u32(node, "clock-accuracy", &accuracy);
|
||||
|
||||
of_property_read_string(node, "clock-output-names", &clk_name);
|
||||
|
||||
clk = clk_register_fixed_rate(NULL, clk_name, NULL, CLK_IS_ROOT, rate);
|
||||
clk = clk_register_fixed_rate_with_accuracy(NULL, clk_name, NULL,
|
||||
CLK_IS_ROOT, rate,
|
||||
accuracy);
|
||||
if (!IS_ERR(clk))
|
||||
of_clk_add_provider(node, of_clk_src_simple_get, clk);
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ static void max77686_clk_unprepare(struct clk_hw *hw)
|
|||
MAX77686_REG_32KHZ, max77686->mask, ~max77686->mask);
|
||||
}
|
||||
|
||||
static int max77686_clk_is_enabled(struct clk_hw *hw)
|
||||
static int max77686_clk_is_prepared(struct clk_hw *hw)
|
||||
{
|
||||
struct max77686_clk *max77686 = to_max77686_clk(hw);
|
||||
int ret;
|
||||
|
@ -81,10 +81,17 @@ static int max77686_clk_is_enabled(struct clk_hw *hw)
|
|||
return val & max77686->mask;
|
||||
}
|
||||
|
||||
static unsigned long max77686_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return 32768;
|
||||
}
|
||||
|
||||
static struct clk_ops max77686_clk_ops = {
|
||||
.prepare = max77686_clk_prepare,
|
||||
.unprepare = max77686_clk_unprepare,
|
||||
.is_enabled = max77686_clk_is_enabled,
|
||||
.is_prepared = max77686_clk_is_prepared,
|
||||
.recalc_rate = max77686_recalc_rate,
|
||||
};
|
||||
|
||||
static struct clk_init_data max77686_clks_init[MAX77686_CLKS_NUM] = {
|
||||
|
@ -105,38 +112,38 @@ static struct clk_init_data max77686_clks_init[MAX77686_CLKS_NUM] = {
|
|||
},
|
||||
};
|
||||
|
||||
static int max77686_clk_register(struct device *dev,
|
||||
static struct clk *max77686_clk_register(struct device *dev,
|
||||
struct max77686_clk *max77686)
|
||||
{
|
||||
struct clk *clk;
|
||||
struct clk_hw *hw = &max77686->hw;
|
||||
|
||||
clk = clk_register(dev, hw);
|
||||
|
||||
if (IS_ERR(clk))
|
||||
return -ENOMEM;
|
||||
return clk;
|
||||
|
||||
max77686->lookup = kzalloc(sizeof(struct clk_lookup), GFP_KERNEL);
|
||||
if (!max77686->lookup)
|
||||
return -ENOMEM;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
max77686->lookup->con_id = hw->init->name;
|
||||
max77686->lookup->clk = clk;
|
||||
|
||||
clkdev_add(max77686->lookup);
|
||||
|
||||
return 0;
|
||||
return clk;
|
||||
}
|
||||
|
||||
static int max77686_clk_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent);
|
||||
struct max77686_clk **max77686_clks;
|
||||
struct max77686_clk *max77686_clks[MAX77686_CLKS_NUM];
|
||||
struct clk **clocks;
|
||||
int i, ret;
|
||||
|
||||
max77686_clks = devm_kzalloc(&pdev->dev, sizeof(struct max77686_clk *)
|
||||
clocks = devm_kzalloc(&pdev->dev, sizeof(struct clk *)
|
||||
* MAX77686_CLKS_NUM, GFP_KERNEL);
|
||||
if (!max77686_clks)
|
||||
if (!clocks)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < MAX77686_CLKS_NUM; i++) {
|
||||
|
@ -151,47 +158,63 @@ static int max77686_clk_probe(struct platform_device *pdev)
|
|||
max77686_clks[i]->mask = 1 << i;
|
||||
max77686_clks[i]->hw.init = &max77686_clks_init[i];
|
||||
|
||||
ret = max77686_clk_register(&pdev->dev, max77686_clks[i]);
|
||||
if (ret) {
|
||||
switch (i) {
|
||||
case MAX77686_CLK_AP:
|
||||
dev_err(&pdev->dev, "Fail to register CLK_AP\n");
|
||||
goto err_clk_ap;
|
||||
break;
|
||||
case MAX77686_CLK_CP:
|
||||
dev_err(&pdev->dev, "Fail to register CLK_CP\n");
|
||||
goto err_clk_cp;
|
||||
break;
|
||||
case MAX77686_CLK_PMIC:
|
||||
dev_err(&pdev->dev, "Fail to register CLK_PMIC\n");
|
||||
goto err_clk_pmic;
|
||||
}
|
||||
clocks[i] = max77686_clk_register(&pdev->dev, max77686_clks[i]);
|
||||
if (IS_ERR(clocks[i])) {
|
||||
ret = PTR_ERR(clocks[i]);
|
||||
dev_err(&pdev->dev, "failed to register %s\n",
|
||||
max77686_clks[i]->hw.init->name);
|
||||
goto err_clocks;
|
||||
}
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, max77686_clks);
|
||||
platform_set_drvdata(pdev, clocks);
|
||||
|
||||
goto out;
|
||||
if (iodev->dev->of_node) {
|
||||
struct clk_onecell_data *of_data;
|
||||
|
||||
of_data = devm_kzalloc(&pdev->dev,
|
||||
sizeof(*of_data), GFP_KERNEL);
|
||||
if (!of_data) {
|
||||
ret = -ENOMEM;
|
||||
goto err_clocks;
|
||||
}
|
||||
|
||||
of_data->clks = clocks;
|
||||
of_data->clk_num = MAX77686_CLKS_NUM;
|
||||
ret = of_clk_add_provider(iodev->dev->of_node,
|
||||
of_clk_src_onecell_get, of_data);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to register OF clock provider\n");
|
||||
goto err_clocks;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_clocks:
|
||||
for (--i; i >= 0; --i) {
|
||||
clkdev_drop(max77686_clks[i]->lookup);
|
||||
clk_unregister(max77686_clks[i]->hw.clk);
|
||||
}
|
||||
|
||||
err_clk_pmic:
|
||||
clkdev_drop(max77686_clks[MAX77686_CLK_CP]->lookup);
|
||||
kfree(max77686_clks[MAX77686_CLK_CP]->hw.clk);
|
||||
err_clk_cp:
|
||||
clkdev_drop(max77686_clks[MAX77686_CLK_AP]->lookup);
|
||||
kfree(max77686_clks[MAX77686_CLK_AP]->hw.clk);
|
||||
err_clk_ap:
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max77686_clk_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct max77686_clk **max77686_clks = platform_get_drvdata(pdev);
|
||||
struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent);
|
||||
struct clk **clocks = platform_get_drvdata(pdev);
|
||||
int i;
|
||||
|
||||
if (iodev->dev->of_node)
|
||||
of_clk_del_provider(iodev->dev->of_node);
|
||||
|
||||
for (i = 0; i < MAX77686_CLKS_NUM; i++) {
|
||||
clkdev_drop(max77686_clks[i]->lookup);
|
||||
kfree(max77686_clks[i]->hw.clk);
|
||||
struct clk_hw *hw = __clk_get_hw(clocks[i]);
|
||||
struct max77686_clk *max77686 = to_max77686_clk(hw);
|
||||
|
||||
clkdev_drop(max77686->lookup);
|
||||
clk_unregister(clocks[i]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
531
drivers/clk/clk-si570.c
Normal file
531
drivers/clk/clk-si570.c
Normal file
|
@ -0,0 +1,531 @@
|
|||
/*
|
||||
* Driver for Silicon Labs Si570/Si571 Programmable XO/VCXO
|
||||
*
|
||||
* Copyright (C) 2010, 2011 Ericsson AB.
|
||||
* Copyright (C) 2011 Guenter Roeck.
|
||||
* Copyright (C) 2011 - 2013 Xilinx Inc.
|
||||
*
|
||||
* Author: Guenter Roeck <guenter.roeck@ericsson.com>
|
||||
* Sören Brinkmann <soren.brinkmann@xilinx.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/* Si570 registers */
|
||||
#define SI570_REG_HS_N1 7
|
||||
#define SI570_REG_N1_RFREQ0 8
|
||||
#define SI570_REG_RFREQ1 9
|
||||
#define SI570_REG_RFREQ2 10
|
||||
#define SI570_REG_RFREQ3 11
|
||||
#define SI570_REG_RFREQ4 12
|
||||
#define SI570_REG_CONTROL 135
|
||||
#define SI570_REG_FREEZE_DCO 137
|
||||
#define SI570_DIV_OFFSET_7PPM 6
|
||||
|
||||
#define HS_DIV_SHIFT 5
|
||||
#define HS_DIV_MASK 0xe0
|
||||
#define HS_DIV_OFFSET 4
|
||||
#define N1_6_2_MASK 0x1f
|
||||
#define N1_1_0_MASK 0xc0
|
||||
#define RFREQ_37_32_MASK 0x3f
|
||||
|
||||
#define SI570_MIN_FREQ 10000000L
|
||||
#define SI570_MAX_FREQ 1417500000L
|
||||
#define SI598_MAX_FREQ 525000000L
|
||||
|
||||
#define FDCO_MIN 4850000000LL
|
||||
#define FDCO_MAX 5670000000LL
|
||||
|
||||
#define SI570_CNTRL_RECALL (1 << 0)
|
||||
#define SI570_CNTRL_FREEZE_M (1 << 5)
|
||||
#define SI570_CNTRL_NEWFREQ (1 << 6)
|
||||
|
||||
#define SI570_FREEZE_DCO (1 << 4)
|
||||
|
||||
/**
|
||||
* struct clk_si570:
|
||||
* @hw: Clock hw struct
|
||||
* @regmap: Device's regmap
|
||||
* @div_offset: Rgister offset for dividers
|
||||
* @max_freq: Maximum frequency for this device
|
||||
* @fxtal: Factory xtal frequency
|
||||
* @n1: Clock divider N1
|
||||
* @hs_div: Clock divider HSDIV
|
||||
* @rfreq: Clock multiplier RFREQ
|
||||
* @frequency: Current output frequency
|
||||
* @i2c_client: I2C client pointer
|
||||
*/
|
||||
struct clk_si570 {
|
||||
struct clk_hw hw;
|
||||
struct regmap *regmap;
|
||||
unsigned int div_offset;
|
||||
u64 max_freq;
|
||||
u64 fxtal;
|
||||
unsigned int n1;
|
||||
unsigned int hs_div;
|
||||
u64 rfreq;
|
||||
u64 frequency;
|
||||
struct i2c_client *i2c_client;
|
||||
};
|
||||
#define to_clk_si570(_hw) container_of(_hw, struct clk_si570, hw)
|
||||
|
||||
enum clk_si570_variant {
|
||||
si57x,
|
||||
si59x
|
||||
};
|
||||
|
||||
/**
|
||||
* si570_get_divs() - Read clock dividers from HW
|
||||
* @data: Pointer to struct clk_si570
|
||||
* @rfreq: Fractional multiplier (output)
|
||||
* @n1: Divider N1 (output)
|
||||
* @hs_div: Divider HSDIV (output)
|
||||
* Returns 0 on success, negative errno otherwise.
|
||||
*
|
||||
* Retrieve clock dividers and multipliers from the HW.
|
||||
*/
|
||||
static int si570_get_divs(struct clk_si570 *data, u64 *rfreq,
|
||||
unsigned int *n1, unsigned int *hs_div)
|
||||
{
|
||||
int err;
|
||||
u8 reg[6];
|
||||
u64 tmp;
|
||||
|
||||
err = regmap_bulk_read(data->regmap, SI570_REG_HS_N1 + data->div_offset,
|
||||
reg, ARRAY_SIZE(reg));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
*hs_div = ((reg[0] & HS_DIV_MASK) >> HS_DIV_SHIFT) + HS_DIV_OFFSET;
|
||||
*n1 = ((reg[0] & N1_6_2_MASK) << 2) + ((reg[1] & N1_1_0_MASK) >> 6) + 1;
|
||||
/* Handle invalid cases */
|
||||
if (*n1 > 1)
|
||||
*n1 &= ~1;
|
||||
|
||||
tmp = reg[1] & RFREQ_37_32_MASK;
|
||||
tmp = (tmp << 8) + reg[2];
|
||||
tmp = (tmp << 8) + reg[3];
|
||||
tmp = (tmp << 8) + reg[4];
|
||||
tmp = (tmp << 8) + reg[5];
|
||||
*rfreq = tmp;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* si570_get_defaults() - Get default values
|
||||
* @data: Driver data structure
|
||||
* @fout: Factory frequency output
|
||||
* Returns 0 on success, negative errno otherwise.
|
||||
*/
|
||||
static int si570_get_defaults(struct clk_si570 *data, u64 fout)
|
||||
{
|
||||
int err;
|
||||
u64 fdco;
|
||||
|
||||
regmap_write(data->regmap, SI570_REG_CONTROL, SI570_CNTRL_RECALL);
|
||||
|
||||
err = si570_get_divs(data, &data->rfreq, &data->n1, &data->hs_div);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* Accept optional precision loss to avoid arithmetic overflows.
|
||||
* Acceptable per Silicon Labs Application Note AN334.
|
||||
*/
|
||||
fdco = fout * data->n1 * data->hs_div;
|
||||
if (fdco >= (1LL << 36))
|
||||
data->fxtal = div64_u64(fdco << 24, data->rfreq >> 4);
|
||||
else
|
||||
data->fxtal = div64_u64(fdco << 28, data->rfreq);
|
||||
|
||||
data->frequency = fout;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* si570_update_rfreq() - Update clock multiplier
|
||||
* @data: Driver data structure
|
||||
* Passes on regmap_bulk_write() return value.
|
||||
*/
|
||||
static int si570_update_rfreq(struct clk_si570 *data)
|
||||
{
|
||||
u8 reg[5];
|
||||
|
||||
reg[0] = ((data->n1 - 1) << 6) |
|
||||
((data->rfreq >> 32) & RFREQ_37_32_MASK);
|
||||
reg[1] = (data->rfreq >> 24) & 0xff;
|
||||
reg[2] = (data->rfreq >> 16) & 0xff;
|
||||
reg[3] = (data->rfreq >> 8) & 0xff;
|
||||
reg[4] = data->rfreq & 0xff;
|
||||
|
||||
return regmap_bulk_write(data->regmap, SI570_REG_N1_RFREQ0 +
|
||||
data->div_offset, reg, ARRAY_SIZE(reg));
|
||||
}
|
||||
|
||||
/**
|
||||
* si570_calc_divs() - Caluclate clock dividers
|
||||
* @frequency: Target frequency
|
||||
* @data: Driver data structure
|
||||
* @out_rfreq: RFREG fractional multiplier (output)
|
||||
* @out_n1: Clock divider N1 (output)
|
||||
* @out_hs_div: Clock divider HSDIV (output)
|
||||
* Returns 0 on success, negative errno otherwise.
|
||||
*
|
||||
* Calculate the clock dividers (@out_hs_div, @out_n1) and clock multiplier
|
||||
* (@out_rfreq) for a given target @frequency.
|
||||
*/
|
||||
static int si570_calc_divs(unsigned long frequency, struct clk_si570 *data,
|
||||
u64 *out_rfreq, unsigned int *out_n1, unsigned int *out_hs_div)
|
||||
{
|
||||
int i;
|
||||
unsigned int n1, hs_div;
|
||||
u64 fdco, best_fdco = ULLONG_MAX;
|
||||
static const uint8_t si570_hs_div_values[] = { 11, 9, 7, 6, 5, 4 };
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(si570_hs_div_values); i++) {
|
||||
hs_div = si570_hs_div_values[i];
|
||||
/* Calculate lowest possible value for n1 */
|
||||
n1 = div_u64(div_u64(FDCO_MIN, hs_div), frequency);
|
||||
if (!n1 || (n1 & 1))
|
||||
n1++;
|
||||
while (n1 <= 128) {
|
||||
fdco = (u64)frequency * (u64)hs_div * (u64)n1;
|
||||
if (fdco > FDCO_MAX)
|
||||
break;
|
||||
if (fdco >= FDCO_MIN && fdco < best_fdco) {
|
||||
*out_n1 = n1;
|
||||
*out_hs_div = hs_div;
|
||||
*out_rfreq = div64_u64(fdco << 28, data->fxtal);
|
||||
best_fdco = fdco;
|
||||
}
|
||||
n1 += (n1 == 1 ? 1 : 2);
|
||||
}
|
||||
}
|
||||
|
||||
if (best_fdco == ULLONG_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long si570_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
int err;
|
||||
u64 rfreq, rate;
|
||||
unsigned int n1, hs_div;
|
||||
struct clk_si570 *data = to_clk_si570(hw);
|
||||
|
||||
err = si570_get_divs(data, &rfreq, &n1, &hs_div);
|
||||
if (err) {
|
||||
dev_err(&data->i2c_client->dev, "unable to recalc rate\n");
|
||||
return data->frequency;
|
||||
}
|
||||
|
||||
rfreq = div_u64(rfreq, hs_div * n1);
|
||||
rate = (data->fxtal * rfreq) >> 28;
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
static long si570_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
int err;
|
||||
u64 rfreq;
|
||||
unsigned int n1, hs_div;
|
||||
struct clk_si570 *data = to_clk_si570(hw);
|
||||
|
||||
if (!rate)
|
||||
return 0;
|
||||
|
||||
if (div64_u64(abs(rate - data->frequency) * 10000LL,
|
||||
data->frequency) < 35) {
|
||||
rfreq = div64_u64((data->rfreq * rate) +
|
||||
div64_u64(data->frequency, 2), data->frequency);
|
||||
n1 = data->n1;
|
||||
hs_div = data->hs_div;
|
||||
|
||||
} else {
|
||||
err = si570_calc_divs(rate, data, &rfreq, &n1, &hs_div);
|
||||
if (err) {
|
||||
dev_err(&data->i2c_client->dev,
|
||||
"unable to round rate\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
/**
|
||||
* si570_set_frequency() - Adjust output frequency
|
||||
* @data: Driver data structure
|
||||
* @frequency: Target frequency
|
||||
* Returns 0 on success.
|
||||
*
|
||||
* Update output frequency for big frequency changes (> 3,500 ppm).
|
||||
*/
|
||||
static int si570_set_frequency(struct clk_si570 *data, unsigned long frequency)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = si570_calc_divs(frequency, data, &data->rfreq, &data->n1,
|
||||
&data->hs_div);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* The DCO reg should be accessed with a read-modify-write operation
|
||||
* per AN334
|
||||
*/
|
||||
regmap_write(data->regmap, SI570_REG_FREEZE_DCO, SI570_FREEZE_DCO);
|
||||
regmap_write(data->regmap, SI570_REG_HS_N1 + data->div_offset,
|
||||
((data->hs_div - HS_DIV_OFFSET) << HS_DIV_SHIFT) |
|
||||
(((data->n1 - 1) >> 2) & N1_6_2_MASK));
|
||||
si570_update_rfreq(data);
|
||||
regmap_write(data->regmap, SI570_REG_FREEZE_DCO, 0);
|
||||
regmap_write(data->regmap, SI570_REG_CONTROL, SI570_CNTRL_NEWFREQ);
|
||||
|
||||
/* Applying a new frequency can take up to 10ms */
|
||||
usleep_range(10000, 12000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* si570_set_frequency_small() - Adjust output frequency
|
||||
* @data: Driver data structure
|
||||
* @frequency: Target frequency
|
||||
* Returns 0 on success.
|
||||
*
|
||||
* Update output frequency for small frequency changes (< 3,500 ppm).
|
||||
*/
|
||||
static int si570_set_frequency_small(struct clk_si570 *data,
|
||||
unsigned long frequency)
|
||||
{
|
||||
/*
|
||||
* This is a re-implementation of DIV_ROUND_CLOSEST
|
||||
* using the div64_u64 function lieu of letting the compiler
|
||||
* insert EABI calls
|
||||
*/
|
||||
data->rfreq = div64_u64((data->rfreq * frequency) +
|
||||
div_u64(data->frequency, 2), data->frequency);
|
||||
regmap_write(data->regmap, SI570_REG_CONTROL, SI570_CNTRL_FREEZE_M);
|
||||
si570_update_rfreq(data);
|
||||
regmap_write(data->regmap, SI570_REG_CONTROL, 0);
|
||||
|
||||
/* Applying a new frequency (small change) can take up to 100us */
|
||||
usleep_range(100, 200);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int si570_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_si570 *data = to_clk_si570(hw);
|
||||
struct i2c_client *client = data->i2c_client;
|
||||
int err;
|
||||
|
||||
if (rate < SI570_MIN_FREQ || rate > data->max_freq) {
|
||||
dev_err(&client->dev,
|
||||
"requested frequency %lu Hz is out of range\n", rate);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (div64_u64(abs(rate - data->frequency) * 10000LL,
|
||||
data->frequency) < 35)
|
||||
err = si570_set_frequency_small(data, rate);
|
||||
else
|
||||
err = si570_set_frequency(data, rate);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
data->frequency = rate;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops si570_clk_ops = {
|
||||
.recalc_rate = si570_recalc_rate,
|
||||
.round_rate = si570_round_rate,
|
||||
.set_rate = si570_set_rate,
|
||||
};
|
||||
|
||||
static bool si570_regmap_is_volatile(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case SI570_REG_CONTROL:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool si570_regmap_is_writeable(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case SI570_REG_HS_N1 ... (SI570_REG_RFREQ4 + SI570_DIV_OFFSET_7PPM):
|
||||
case SI570_REG_CONTROL:
|
||||
case SI570_REG_FREEZE_DCO:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static struct regmap_config si570_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.max_register = 137,
|
||||
.writeable_reg = si570_regmap_is_writeable,
|
||||
.volatile_reg = si570_regmap_is_volatile,
|
||||
};
|
||||
|
||||
static int si570_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct clk_si570 *data;
|
||||
struct clk_init_data init;
|
||||
struct clk *clk;
|
||||
u32 initial_fout, factory_fout, stability;
|
||||
int err;
|
||||
enum clk_si570_variant variant = id->driver_data;
|
||||
|
||||
data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
init.ops = &si570_clk_ops;
|
||||
init.flags = CLK_IS_ROOT;
|
||||
init.num_parents = 0;
|
||||
data->hw.init = &init;
|
||||
data->i2c_client = client;
|
||||
|
||||
if (variant == si57x) {
|
||||
err = of_property_read_u32(client->dev.of_node,
|
||||
"temperature-stability", &stability);
|
||||
if (err) {
|
||||
dev_err(&client->dev,
|
||||
"'temperature-stability' property missing\n");
|
||||
return err;
|
||||
}
|
||||
/* adjust register offsets for 7ppm devices */
|
||||
if (stability == 7)
|
||||
data->div_offset = SI570_DIV_OFFSET_7PPM;
|
||||
|
||||
data->max_freq = SI570_MAX_FREQ;
|
||||
} else {
|
||||
data->max_freq = SI598_MAX_FREQ;
|
||||
}
|
||||
|
||||
if (of_property_read_string(client->dev.of_node, "clock-output-names",
|
||||
&init.name))
|
||||
init.name = client->dev.of_node->name;
|
||||
|
||||
err = of_property_read_u32(client->dev.of_node, "factory-fout",
|
||||
&factory_fout);
|
||||
if (err) {
|
||||
dev_err(&client->dev, "'factory-fout' property missing\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
data->regmap = devm_regmap_init_i2c(client, &si570_regmap_config);
|
||||
if (IS_ERR(data->regmap)) {
|
||||
dev_err(&client->dev, "failed to allocate register map\n");
|
||||
return PTR_ERR(data->regmap);
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, data);
|
||||
err = si570_get_defaults(data, factory_fout);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
clk = devm_clk_register(&client->dev, &data->hw);
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&client->dev, "clock registration failed\n");
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
err = of_clk_add_provider(client->dev.of_node, of_clk_src_simple_get,
|
||||
clk);
|
||||
if (err) {
|
||||
dev_err(&client->dev, "unable to add clk provider\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Read the requested initial output frequency from device tree */
|
||||
if (!of_property_read_u32(client->dev.of_node, "clock-frequency",
|
||||
&initial_fout)) {
|
||||
err = clk_set_rate(clk, initial_fout);
|
||||
if (err) {
|
||||
of_clk_del_provider(client->dev.of_node);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
/* Display a message indicating that we've successfully registered */
|
||||
dev_info(&client->dev, "registered, current frequency %llu Hz\n",
|
||||
data->frequency);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int si570_remove(struct i2c_client *client)
|
||||
{
|
||||
of_clk_del_provider(client->dev.of_node);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id si570_id[] = {
|
||||
{ "si570", si57x },
|
||||
{ "si571", si57x },
|
||||
{ "si598", si59x },
|
||||
{ "si599", si59x },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, si570_id);
|
||||
|
||||
static const struct of_device_id clk_si570_of_match[] = {
|
||||
{ .compatible = "silabs,si570" },
|
||||
{ .compatible = "silabs,si571" },
|
||||
{ .compatible = "silabs,si598" },
|
||||
{ .compatible = "silabs,si599" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, clk_si570_of_match);
|
||||
|
||||
static struct i2c_driver si570_driver = {
|
||||
.driver = {
|
||||
.name = "si570",
|
||||
.of_match_table = clk_si570_of_match,
|
||||
},
|
||||
.probe = si570_probe,
|
||||
.remove = si570_remove,
|
||||
.id_table = si570_id,
|
||||
};
|
||||
module_i2c_driver(si570_driver);
|
||||
|
||||
MODULE_AUTHOR("Guenter Roeck <guenter.roeck@ericsson.com>");
|
||||
MODULE_AUTHOR("Soeren Brinkmann <soren.brinkmann@xilinx.com");
|
||||
MODULE_DESCRIPTION("Si570 driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -641,7 +641,7 @@ static unsigned long vtwm_pll_recalc_rate(struct clk_hw *hw,
|
|||
return pll_freq;
|
||||
}
|
||||
|
||||
const struct clk_ops vtwm_pll_ops = {
|
||||
static const struct clk_ops vtwm_pll_ops = {
|
||||
.round_rate = vtwm_pll_round_rate,
|
||||
.set_rate = vtwm_pll_set_rate,
|
||||
.recalc_rate = vtwm_pll_recalc_rate,
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
#include <linux/init.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#include "clk.h"
|
||||
|
||||
static DEFINE_SPINLOCK(enable_lock);
|
||||
static DEFINE_MUTEX(prepare_lock);
|
||||
|
||||
|
@ -92,7 +94,7 @@ static void clk_enable_unlock(unsigned long flags)
|
|||
|
||||
/*** debugfs support ***/
|
||||
|
||||
#ifdef CONFIG_COMMON_CLK_DEBUG
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
static struct dentry *rootdir;
|
||||
|
@ -104,10 +106,11 @@ static void clk_summary_show_one(struct seq_file *s, struct clk *c, int level)
|
|||
if (!c)
|
||||
return;
|
||||
|
||||
seq_printf(s, "%*s%-*s %-11d %-12d %-10lu",
|
||||
seq_printf(s, "%*s%-*s %-11d %-12d %-10lu %-11lu",
|
||||
level * 3 + 1, "",
|
||||
30 - level * 3, c->name,
|
||||
c->enable_count, c->prepare_count, clk_get_rate(c));
|
||||
c->enable_count, c->prepare_count, clk_get_rate(c),
|
||||
clk_get_accuracy(c));
|
||||
seq_printf(s, "\n");
|
||||
}
|
||||
|
||||
|
@ -129,8 +132,8 @@ static int clk_summary_show(struct seq_file *s, void *data)
|
|||
{
|
||||
struct clk *c;
|
||||
|
||||
seq_printf(s, " clock enable_cnt prepare_cnt rate\n");
|
||||
seq_printf(s, "---------------------------------------------------------------------\n");
|
||||
seq_printf(s, " clock enable_cnt prepare_cnt rate accuracy\n");
|
||||
seq_printf(s, "---------------------------------------------------------------------------------\n");
|
||||
|
||||
clk_prepare_lock();
|
||||
|
||||
|
@ -167,6 +170,7 @@ static void clk_dump_one(struct seq_file *s, struct clk *c, int level)
|
|||
seq_printf(s, "\"enable_count\": %d,", c->enable_count);
|
||||
seq_printf(s, "\"prepare_count\": %d,", c->prepare_count);
|
||||
seq_printf(s, "\"rate\": %lu", clk_get_rate(c));
|
||||
seq_printf(s, "\"accuracy\": %lu", clk_get_accuracy(c));
|
||||
}
|
||||
|
||||
static void clk_dump_subtree(struct seq_file *s, struct clk *c, int level)
|
||||
|
@ -248,6 +252,11 @@ static int clk_debug_create_one(struct clk *clk, struct dentry *pdentry)
|
|||
if (!d)
|
||||
goto err_out;
|
||||
|
||||
d = debugfs_create_u32("clk_accuracy", S_IRUGO, clk->dentry,
|
||||
(u32 *)&clk->accuracy);
|
||||
if (!d)
|
||||
goto err_out;
|
||||
|
||||
d = debugfs_create_x32("clk_flags", S_IRUGO, clk->dentry,
|
||||
(u32 *)&clk->flags);
|
||||
if (!d)
|
||||
|
@ -272,7 +281,8 @@ static int clk_debug_create_one(struct clk *clk, struct dentry *pdentry)
|
|||
goto out;
|
||||
|
||||
err_out:
|
||||
debugfs_remove(clk->dentry);
|
||||
debugfs_remove_recursive(clk->dentry);
|
||||
clk->dentry = NULL;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
@ -342,6 +352,21 @@ static int clk_debug_register(struct clk *clk)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* clk_debug_unregister - remove a clk node from the debugfs clk tree
|
||||
* @clk: the clk being removed from the debugfs clk tree
|
||||
*
|
||||
* Dynamically removes a clk and all it's children clk nodes from the
|
||||
* debugfs clk tree if clk->dentry points to debugfs created by
|
||||
* clk_debug_register in __clk_init.
|
||||
*
|
||||
* Caller must hold prepare_lock.
|
||||
*/
|
||||
static void clk_debug_unregister(struct clk *clk)
|
||||
{
|
||||
debugfs_remove_recursive(clk->dentry);
|
||||
}
|
||||
|
||||
/**
|
||||
* clk_debug_reparent - reparent clk node in the debugfs clk tree
|
||||
* @clk: the clk being reparented
|
||||
|
@ -432,6 +457,9 @@ static inline int clk_debug_register(struct clk *clk) { return 0; }
|
|||
static inline void clk_debug_reparent(struct clk *clk, struct clk *new_parent)
|
||||
{
|
||||
}
|
||||
static inline void clk_debug_unregister(struct clk *clk)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/* caller must hold prepare_lock */
|
||||
|
@ -602,6 +630,14 @@ unsigned long __clk_get_rate(struct clk *clk)
|
|||
return ret;
|
||||
}
|
||||
|
||||
unsigned long __clk_get_accuracy(struct clk *clk)
|
||||
{
|
||||
if (!clk)
|
||||
return 0;
|
||||
|
||||
return clk->accuracy;
|
||||
}
|
||||
|
||||
unsigned long __clk_get_flags(struct clk *clk)
|
||||
{
|
||||
return !clk ? 0 : clk->flags;
|
||||
|
@ -1015,6 +1051,59 @@ static int __clk_notify(struct clk *clk, unsigned long msg,
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* __clk_recalc_accuracies
|
||||
* @clk: first clk in the subtree
|
||||
*
|
||||
* Walks the subtree of clks starting with clk and recalculates accuracies as
|
||||
* it goes. Note that if a clk does not implement the .recalc_accuracy
|
||||
* callback then it is assumed that the clock will take on the accuracy of it's
|
||||
* parent.
|
||||
*
|
||||
* Caller must hold prepare_lock.
|
||||
*/
|
||||
static void __clk_recalc_accuracies(struct clk *clk)
|
||||
{
|
||||
unsigned long parent_accuracy = 0;
|
||||
struct clk *child;
|
||||
|
||||
if (clk->parent)
|
||||
parent_accuracy = clk->parent->accuracy;
|
||||
|
||||
if (clk->ops->recalc_accuracy)
|
||||
clk->accuracy = clk->ops->recalc_accuracy(clk->hw,
|
||||
parent_accuracy);
|
||||
else
|
||||
clk->accuracy = parent_accuracy;
|
||||
|
||||
hlist_for_each_entry(child, &clk->children, child_node)
|
||||
__clk_recalc_accuracies(child);
|
||||
}
|
||||
|
||||
/**
|
||||
* clk_get_accuracy - return the accuracy of clk
|
||||
* @clk: the clk whose accuracy is being returned
|
||||
*
|
||||
* Simply returns the cached accuracy of the clk, unless
|
||||
* CLK_GET_ACCURACY_NOCACHE flag is set, which means a recalc_rate will be
|
||||
* issued.
|
||||
* If clk is NULL then returns 0.
|
||||
*/
|
||||
long clk_get_accuracy(struct clk *clk)
|
||||
{
|
||||
unsigned long accuracy;
|
||||
|
||||
clk_prepare_lock();
|
||||
if (clk && (clk->flags & CLK_GET_ACCURACY_NOCACHE))
|
||||
__clk_recalc_accuracies(clk);
|
||||
|
||||
accuracy = __clk_get_accuracy(clk);
|
||||
clk_prepare_unlock();
|
||||
|
||||
return accuracy;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(clk_get_accuracy);
|
||||
|
||||
/**
|
||||
* __clk_recalc_rates
|
||||
* @clk: first clk in the subtree
|
||||
|
@ -1129,10 +1218,9 @@ static void clk_reparent(struct clk *clk, struct clk *new_parent)
|
|||
clk->parent = new_parent;
|
||||
}
|
||||
|
||||
static int __clk_set_parent(struct clk *clk, struct clk *parent, u8 p_index)
|
||||
static struct clk *__clk_set_parent_before(struct clk *clk, struct clk *parent)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
struct clk *old_parent = clk->parent;
|
||||
|
||||
/*
|
||||
|
@ -1163,6 +1251,34 @@ static int __clk_set_parent(struct clk *clk, struct clk *parent, u8 p_index)
|
|||
clk_reparent(clk, parent);
|
||||
clk_enable_unlock(flags);
|
||||
|
||||
return old_parent;
|
||||
}
|
||||
|
||||
static void __clk_set_parent_after(struct clk *clk, struct clk *parent,
|
||||
struct clk *old_parent)
|
||||
{
|
||||
/*
|
||||
* Finish the migration of prepare state and undo the changes done
|
||||
* for preventing a race with clk_enable().
|
||||
*/
|
||||
if (clk->prepare_count) {
|
||||
clk_disable(clk);
|
||||
clk_disable(old_parent);
|
||||
__clk_unprepare(old_parent);
|
||||
}
|
||||
|
||||
/* update debugfs with new clk tree topology */
|
||||
clk_debug_reparent(clk, parent);
|
||||
}
|
||||
|
||||
static int __clk_set_parent(struct clk *clk, struct clk *parent, u8 p_index)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
struct clk *old_parent;
|
||||
|
||||
old_parent = __clk_set_parent_before(clk, parent);
|
||||
|
||||
/* change clock input source */
|
||||
if (parent && clk->ops->set_parent)
|
||||
ret = clk->ops->set_parent(clk->hw, p_index);
|
||||
|
@ -1180,18 +1296,8 @@ static int __clk_set_parent(struct clk *clk, struct clk *parent, u8 p_index)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Finish the migration of prepare state and undo the changes done
|
||||
* for preventing a race with clk_enable().
|
||||
*/
|
||||
if (clk->prepare_count) {
|
||||
clk_disable(clk);
|
||||
clk_disable(old_parent);
|
||||
__clk_unprepare(old_parent);
|
||||
}
|
||||
__clk_set_parent_after(clk, parent, old_parent);
|
||||
|
||||
/* update debugfs with new clk tree topology */
|
||||
clk_debug_reparent(clk, parent);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1376,17 +1482,32 @@ static void clk_change_rate(struct clk *clk)
|
|||
struct clk *child;
|
||||
unsigned long old_rate;
|
||||
unsigned long best_parent_rate = 0;
|
||||
bool skip_set_rate = false;
|
||||
struct clk *old_parent;
|
||||
|
||||
old_rate = clk->rate;
|
||||
|
||||
/* set parent */
|
||||
if (clk->new_parent && clk->new_parent != clk->parent)
|
||||
__clk_set_parent(clk, clk->new_parent, clk->new_parent_index);
|
||||
|
||||
if (clk->parent)
|
||||
if (clk->new_parent)
|
||||
best_parent_rate = clk->new_parent->rate;
|
||||
else if (clk->parent)
|
||||
best_parent_rate = clk->parent->rate;
|
||||
|
||||
if (clk->ops->set_rate)
|
||||
if (clk->new_parent && clk->new_parent != clk->parent) {
|
||||
old_parent = __clk_set_parent_before(clk, clk->new_parent);
|
||||
|
||||
if (clk->ops->set_rate_and_parent) {
|
||||
skip_set_rate = true;
|
||||
clk->ops->set_rate_and_parent(clk->hw, clk->new_rate,
|
||||
best_parent_rate,
|
||||
clk->new_parent_index);
|
||||
} else if (clk->ops->set_parent) {
|
||||
clk->ops->set_parent(clk->hw, clk->new_parent_index);
|
||||
}
|
||||
|
||||
__clk_set_parent_after(clk, clk->new_parent, old_parent);
|
||||
}
|
||||
|
||||
if (!skip_set_rate && clk->ops->set_rate)
|
||||
clk->ops->set_rate(clk->hw, clk->new_rate, best_parent_rate);
|
||||
|
||||
if (clk->ops->recalc_rate)
|
||||
|
@ -1551,6 +1672,7 @@ void __clk_reparent(struct clk *clk, struct clk *new_parent)
|
|||
{
|
||||
clk_reparent(clk, new_parent);
|
||||
clk_debug_reparent(clk, new_parent);
|
||||
__clk_recalc_accuracies(clk);
|
||||
__clk_recalc_rates(clk, POST_RATE_CHANGE);
|
||||
}
|
||||
|
||||
|
@ -1621,11 +1743,13 @@ int clk_set_parent(struct clk *clk, struct clk *parent)
|
|||
/* do the re-parent */
|
||||
ret = __clk_set_parent(clk, parent, p_index);
|
||||
|
||||
/* propagate rate recalculation accordingly */
|
||||
if (ret)
|
||||
/* propagate rate an accuracy recalculation accordingly */
|
||||
if (ret) {
|
||||
__clk_recalc_rates(clk, ABORT_RATE_CHANGE);
|
||||
else
|
||||
} else {
|
||||
__clk_recalc_rates(clk, POST_RATE_CHANGE);
|
||||
__clk_recalc_accuracies(clk);
|
||||
}
|
||||
|
||||
out:
|
||||
clk_prepare_unlock();
|
||||
|
@ -1678,6 +1802,14 @@ int __clk_init(struct device *dev, struct clk *clk)
|
|||
goto out;
|
||||
}
|
||||
|
||||
if (clk->ops->set_rate_and_parent &&
|
||||
!(clk->ops->set_parent && clk->ops->set_rate)) {
|
||||
pr_warn("%s: %s must implement .set_parent & .set_rate\n",
|
||||
__func__, clk->name);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* throw a WARN if any entries in parent_names are NULL */
|
||||
for (i = 0; i < clk->num_parents; i++)
|
||||
WARN(!clk->parent_names[i],
|
||||
|
@ -1729,6 +1861,21 @@ int __clk_init(struct device *dev, struct clk *clk)
|
|||
else
|
||||
hlist_add_head(&clk->child_node, &clk_orphan_list);
|
||||
|
||||
/*
|
||||
* Set clk's accuracy. The preferred method is to use
|
||||
* .recalc_accuracy. For simple clocks and lazy developers the default
|
||||
* fallback is to use the parent's accuracy. If a clock doesn't have a
|
||||
* parent (or is orphaned) then accuracy is set to zero (perfect
|
||||
* clock).
|
||||
*/
|
||||
if (clk->ops->recalc_accuracy)
|
||||
clk->accuracy = clk->ops->recalc_accuracy(clk->hw,
|
||||
__clk_get_accuracy(clk->parent));
|
||||
else if (clk->parent)
|
||||
clk->accuracy = clk->parent->accuracy;
|
||||
else
|
||||
clk->accuracy = 0;
|
||||
|
||||
/*
|
||||
* Set clk's rate. The preferred method is to use .recalc_rate. For
|
||||
* simple clocks and lazy developers the default fallback is to use the
|
||||
|
@ -1743,6 +1890,7 @@ int __clk_init(struct device *dev, struct clk *clk)
|
|||
else
|
||||
clk->rate = 0;
|
||||
|
||||
clk_debug_register(clk);
|
||||
/*
|
||||
* walk the list of orphan clocks and reparent any that are children of
|
||||
* this clock
|
||||
|
@ -1773,8 +1921,7 @@ int __clk_init(struct device *dev, struct clk *clk)
|
|||
if (clk->ops->init)
|
||||
clk->ops->init(clk->hw);
|
||||
|
||||
clk_debug_register(clk);
|
||||
|
||||
kref_init(&clk->ref);
|
||||
out:
|
||||
clk_prepare_unlock();
|
||||
|
||||
|
@ -1810,6 +1957,10 @@ struct clk *__clk_register(struct device *dev, struct clk_hw *hw)
|
|||
clk->flags = hw->init->flags;
|
||||
clk->parent_names = hw->init->parent_names;
|
||||
clk->num_parents = hw->init->num_parents;
|
||||
if (dev && dev->driver)
|
||||
clk->owner = dev->driver->owner;
|
||||
else
|
||||
clk->owner = NULL;
|
||||
|
||||
ret = __clk_init(dev, clk);
|
||||
if (ret)
|
||||
|
@ -1830,6 +1981,8 @@ static int _clk_register(struct device *dev, struct clk_hw *hw, struct clk *clk)
|
|||
goto fail_name;
|
||||
}
|
||||
clk->ops = hw->init->ops;
|
||||
if (dev && dev->driver)
|
||||
clk->owner = dev->driver->owner;
|
||||
clk->hw = hw;
|
||||
clk->flags = hw->init->flags;
|
||||
clk->num_parents = hw->init->num_parents;
|
||||
|
@ -1904,13 +2057,104 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(clk_register);
|
||||
|
||||
/*
|
||||
* Free memory allocated for a clock.
|
||||
* Caller must hold prepare_lock.
|
||||
*/
|
||||
static void __clk_release(struct kref *ref)
|
||||
{
|
||||
struct clk *clk = container_of(ref, struct clk, ref);
|
||||
int i = clk->num_parents;
|
||||
|
||||
kfree(clk->parents);
|
||||
while (--i >= 0)
|
||||
kfree(clk->parent_names[i]);
|
||||
|
||||
kfree(clk->parent_names);
|
||||
kfree(clk->name);
|
||||
kfree(clk);
|
||||
}
|
||||
|
||||
/*
|
||||
* Empty clk_ops for unregistered clocks. These are used temporarily
|
||||
* after clk_unregister() was called on a clock and until last clock
|
||||
* consumer calls clk_put() and the struct clk object is freed.
|
||||
*/
|
||||
static int clk_nodrv_prepare_enable(struct clk_hw *hw)
|
||||
{
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static void clk_nodrv_disable_unprepare(struct clk_hw *hw)
|
||||
{
|
||||
WARN_ON_ONCE(1);
|
||||
}
|
||||
|
||||
static int clk_nodrv_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static int clk_nodrv_set_parent(struct clk_hw *hw, u8 index)
|
||||
{
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static const struct clk_ops clk_nodrv_ops = {
|
||||
.enable = clk_nodrv_prepare_enable,
|
||||
.disable = clk_nodrv_disable_unprepare,
|
||||
.prepare = clk_nodrv_prepare_enable,
|
||||
.unprepare = clk_nodrv_disable_unprepare,
|
||||
.set_rate = clk_nodrv_set_rate,
|
||||
.set_parent = clk_nodrv_set_parent,
|
||||
};
|
||||
|
||||
/**
|
||||
* clk_unregister - unregister a currently registered clock
|
||||
* @clk: clock to unregister
|
||||
*
|
||||
* Currently unimplemented.
|
||||
*/
|
||||
void clk_unregister(struct clk *clk) {}
|
||||
void clk_unregister(struct clk *clk)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (!clk || WARN_ON_ONCE(IS_ERR(clk)))
|
||||
return;
|
||||
|
||||
clk_prepare_lock();
|
||||
|
||||
if (clk->ops == &clk_nodrv_ops) {
|
||||
pr_err("%s: unregistered clock: %s\n", __func__, clk->name);
|
||||
goto out;
|
||||
}
|
||||
/*
|
||||
* Assign empty clock ops for consumers that might still hold
|
||||
* a reference to this clock.
|
||||
*/
|
||||
flags = clk_enable_lock();
|
||||
clk->ops = &clk_nodrv_ops;
|
||||
clk_enable_unlock(flags);
|
||||
|
||||
if (!hlist_empty(&clk->children)) {
|
||||
struct clk *child;
|
||||
|
||||
/* Reparent all children to the orphan list. */
|
||||
hlist_for_each_entry(child, &clk->children, child_node)
|
||||
clk_set_parent(child, NULL);
|
||||
}
|
||||
|
||||
clk_debug_unregister(clk);
|
||||
|
||||
hlist_del_init(&clk->child_node);
|
||||
|
||||
if (clk->prepare_count)
|
||||
pr_warn("%s: unregistering prepared clock: %s\n",
|
||||
__func__, clk->name);
|
||||
|
||||
kref_put(&clk->ref, __clk_release);
|
||||
out:
|
||||
clk_prepare_unlock();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(clk_unregister);
|
||||
|
||||
static void devm_clk_release(struct device *dev, void *res)
|
||||
|
@ -1970,6 +2214,31 @@ void devm_clk_unregister(struct device *dev, struct clk *clk)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(devm_clk_unregister);
|
||||
|
||||
/*
|
||||
* clkdev helpers
|
||||
*/
|
||||
int __clk_get(struct clk *clk)
|
||||
{
|
||||
if (clk && !try_module_get(clk->owner))
|
||||
return 0;
|
||||
|
||||
kref_get(&clk->ref);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void __clk_put(struct clk *clk)
|
||||
{
|
||||
if (WARN_ON_ONCE(IS_ERR(clk)))
|
||||
return;
|
||||
|
||||
clk_prepare_lock();
|
||||
kref_put(&clk->ref, __clk_release);
|
||||
clk_prepare_unlock();
|
||||
|
||||
if (clk)
|
||||
module_put(clk->owner);
|
||||
}
|
||||
|
||||
/*** clk rate change notifiers ***/
|
||||
|
||||
/**
|
||||
|
@ -2110,7 +2379,18 @@ static const struct of_device_id __clk_of_table_sentinel
|
|||
__used __section(__clk_of_table_end);
|
||||
|
||||
static LIST_HEAD(of_clk_providers);
|
||||
static DEFINE_MUTEX(of_clk_lock);
|
||||
static DEFINE_MUTEX(of_clk_mutex);
|
||||
|
||||
/* of_clk_provider list locking helpers */
|
||||
void of_clk_lock(void)
|
||||
{
|
||||
mutex_lock(&of_clk_mutex);
|
||||
}
|
||||
|
||||
void of_clk_unlock(void)
|
||||
{
|
||||
mutex_unlock(&of_clk_mutex);
|
||||
}
|
||||
|
||||
struct clk *of_clk_src_simple_get(struct of_phandle_args *clkspec,
|
||||
void *data)
|
||||
|
@ -2154,9 +2434,9 @@ int of_clk_add_provider(struct device_node *np,
|
|||
cp->data = data;
|
||||
cp->get = clk_src_get;
|
||||
|
||||
mutex_lock(&of_clk_lock);
|
||||
mutex_lock(&of_clk_mutex);
|
||||
list_add(&cp->link, &of_clk_providers);
|
||||
mutex_unlock(&of_clk_lock);
|
||||
mutex_unlock(&of_clk_mutex);
|
||||
pr_debug("Added clock from %s\n", np->full_name);
|
||||
|
||||
return 0;
|
||||
|
@ -2171,7 +2451,7 @@ void of_clk_del_provider(struct device_node *np)
|
|||
{
|
||||
struct of_clk_provider *cp;
|
||||
|
||||
mutex_lock(&of_clk_lock);
|
||||
mutex_lock(&of_clk_mutex);
|
||||
list_for_each_entry(cp, &of_clk_providers, link) {
|
||||
if (cp->node == np) {
|
||||
list_del(&cp->link);
|
||||
|
@ -2180,24 +2460,33 @@ void of_clk_del_provider(struct device_node *np)
|
|||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&of_clk_lock);
|
||||
mutex_unlock(&of_clk_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_clk_del_provider);
|
||||
|
||||
struct clk *of_clk_get_from_provider(struct of_phandle_args *clkspec)
|
||||
struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec)
|
||||
{
|
||||
struct of_clk_provider *provider;
|
||||
struct clk *clk = ERR_PTR(-ENOENT);
|
||||
|
||||
/* Check if we have such a provider in our array */
|
||||
mutex_lock(&of_clk_lock);
|
||||
list_for_each_entry(provider, &of_clk_providers, link) {
|
||||
if (provider->node == clkspec->np)
|
||||
clk = provider->get(clkspec, provider->data);
|
||||
if (!IS_ERR(clk))
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&of_clk_lock);
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
struct clk *of_clk_get_from_provider(struct of_phandle_args *clkspec)
|
||||
{
|
||||
struct clk *clk;
|
||||
|
||||
mutex_lock(&of_clk_mutex);
|
||||
clk = __of_clk_get_from_provider(clkspec);
|
||||
mutex_unlock(&of_clk_mutex);
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
|
16
drivers/clk/clk.h
Normal file
16
drivers/clk/clk.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* linux/drivers/clk/clk.h
|
||||
*
|
||||
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
|
||||
* Sylwester Nawrocki <s.nawrocki@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_OF) && defined(CONFIG_COMMON_CLK)
|
||||
struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec);
|
||||
void of_clk_lock(void);
|
||||
void of_clk_unlock(void);
|
||||
#endif
|
|
@ -21,6 +21,8 @@
|
|||
#include <linux/clkdev.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include "clk.h"
|
||||
|
||||
static LIST_HEAD(clocks);
|
||||
static DEFINE_MUTEX(clocks_mutex);
|
||||
|
||||
|
@ -39,7 +41,13 @@ struct clk *of_clk_get(struct device_node *np, int index)
|
|||
if (rc)
|
||||
return ERR_PTR(rc);
|
||||
|
||||
clk = of_clk_get_from_provider(&clkspec);
|
||||
of_clk_lock();
|
||||
clk = __of_clk_get_from_provider(&clkspec);
|
||||
|
||||
if (!IS_ERR(clk) && !__clk_get(clk))
|
||||
clk = ERR_PTR(-ENOENT);
|
||||
|
||||
of_clk_unlock();
|
||||
of_node_put(clkspec.np);
|
||||
return clk;
|
||||
}
|
||||
|
@ -157,7 +165,7 @@ struct clk *clk_get(struct device *dev, const char *con_id)
|
|||
|
||||
if (dev) {
|
||||
clk = of_clk_get_by_name(dev->of_node, con_id);
|
||||
if (!IS_ERR(clk) && __clk_get(clk))
|
||||
if (!IS_ERR(clk))
|
||||
return clk;
|
||||
}
|
||||
|
||||
|
|
5
drivers/clk/hisilicon/Makefile
Normal file
5
drivers/clk/hisilicon/Makefile
Normal file
|
@ -0,0 +1,5 @@
|
|||
#
|
||||
# Hisilicon Clock specific Makefile
|
||||
#
|
||||
|
||||
obj-y += clk.o clkgate-separated.o clk-hi3620.o
|
242
drivers/clk/hisilicon/clk-hi3620.c
Normal file
242
drivers/clk/hisilicon/clk-hi3620.c
Normal file
|
@ -0,0 +1,242 @@
|
|||
/*
|
||||
* Hisilicon Hi3620 clock driver
|
||||
*
|
||||
* Copyright (c) 2012-2013 Hisilicon Limited.
|
||||
* Copyright (c) 2012-2013 Linaro Limited.
|
||||
*
|
||||
* Author: Haojian Zhuang <haojian.zhuang@linaro.org>
|
||||
* Xin Li <li.xin@linaro.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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
#include <dt-bindings/clock/hi3620-clock.h>
|
||||
|
||||
#include "clk.h"
|
||||
|
||||
/* clock parent list */
|
||||
static const char *timer0_mux_p[] __initdata = { "osc32k", "timerclk01", };
|
||||
static const char *timer1_mux_p[] __initdata = { "osc32k", "timerclk01", };
|
||||
static const char *timer2_mux_p[] __initdata = { "osc32k", "timerclk23", };
|
||||
static const char *timer3_mux_p[] __initdata = { "osc32k", "timerclk23", };
|
||||
static const char *timer4_mux_p[] __initdata = { "osc32k", "timerclk45", };
|
||||
static const char *timer5_mux_p[] __initdata = { "osc32k", "timerclk45", };
|
||||
static const char *timer6_mux_p[] __initdata = { "osc32k", "timerclk67", };
|
||||
static const char *timer7_mux_p[] __initdata = { "osc32k", "timerclk67", };
|
||||
static const char *timer8_mux_p[] __initdata = { "osc32k", "timerclk89", };
|
||||
static const char *timer9_mux_p[] __initdata = { "osc32k", "timerclk89", };
|
||||
static const char *uart0_mux_p[] __initdata = { "osc26m", "pclk", };
|
||||
static const char *uart1_mux_p[] __initdata = { "osc26m", "pclk", };
|
||||
static const char *uart2_mux_p[] __initdata = { "osc26m", "pclk", };
|
||||
static const char *uart3_mux_p[] __initdata = { "osc26m", "pclk", };
|
||||
static const char *uart4_mux_p[] __initdata = { "osc26m", "pclk", };
|
||||
static const char *spi0_mux_p[] __initdata = { "osc26m", "rclk_cfgaxi", };
|
||||
static const char *spi1_mux_p[] __initdata = { "osc26m", "rclk_cfgaxi", };
|
||||
static const char *spi2_mux_p[] __initdata = { "osc26m", "rclk_cfgaxi", };
|
||||
/* share axi parent */
|
||||
static const char *saxi_mux_p[] __initdata = { "armpll3", "armpll2", };
|
||||
static const char *pwm0_mux_p[] __initdata = { "osc32k", "osc26m", };
|
||||
static const char *pwm1_mux_p[] __initdata = { "osc32k", "osc26m", };
|
||||
static const char *sd_mux_p[] __initdata = { "armpll2", "armpll3", };
|
||||
static const char *mmc1_mux_p[] __initdata = { "armpll2", "armpll3", };
|
||||
static const char *mmc1_mux2_p[] __initdata = { "osc26m", "mmc1_div", };
|
||||
static const char *g2d_mux_p[] __initdata = { "armpll2", "armpll3", };
|
||||
static const char *venc_mux_p[] __initdata = { "armpll2", "armpll3", };
|
||||
static const char *vdec_mux_p[] __initdata = { "armpll2", "armpll3", };
|
||||
static const char *vpp_mux_p[] __initdata = { "armpll2", "armpll3", };
|
||||
static const char *edc0_mux_p[] __initdata = { "armpll2", "armpll3", };
|
||||
static const char *ldi0_mux_p[] __initdata = { "armpll2", "armpll4",
|
||||
"armpll3", "armpll5", };
|
||||
static const char *edc1_mux_p[] __initdata = { "armpll2", "armpll3", };
|
||||
static const char *ldi1_mux_p[] __initdata = { "armpll2", "armpll4",
|
||||
"armpll3", "armpll5", };
|
||||
static const char *rclk_hsic_p[] __initdata = { "armpll3", "armpll2", };
|
||||
static const char *mmc2_mux_p[] __initdata = { "armpll2", "armpll3", };
|
||||
static const char *mmc3_mux_p[] __initdata = { "armpll2", "armpll3", };
|
||||
|
||||
|
||||
/* fixed rate clocks */
|
||||
static struct hisi_fixed_rate_clock hi3620_fixed_rate_clks[] __initdata = {
|
||||
{ HI3620_OSC32K, "osc32k", NULL, CLK_IS_ROOT, 32768, },
|
||||
{ HI3620_OSC26M, "osc26m", NULL, CLK_IS_ROOT, 26000000, },
|
||||
{ HI3620_PCLK, "pclk", NULL, CLK_IS_ROOT, 26000000, },
|
||||
{ HI3620_PLL_ARM0, "armpll0", NULL, CLK_IS_ROOT, 1600000000, },
|
||||
{ HI3620_PLL_ARM1, "armpll1", NULL, CLK_IS_ROOT, 1600000000, },
|
||||
{ HI3620_PLL_PERI, "armpll2", NULL, CLK_IS_ROOT, 1440000000, },
|
||||
{ HI3620_PLL_USB, "armpll3", NULL, CLK_IS_ROOT, 1440000000, },
|
||||
{ HI3620_PLL_HDMI, "armpll4", NULL, CLK_IS_ROOT, 1188000000, },
|
||||
{ HI3620_PLL_GPU, "armpll5", NULL, CLK_IS_ROOT, 1300000000, },
|
||||
};
|
||||
|
||||
/* fixed factor clocks */
|
||||
static struct hisi_fixed_factor_clock hi3620_fixed_factor_clks[] __initdata = {
|
||||
{ HI3620_RCLK_TCXO, "rclk_tcxo", "osc26m", 1, 4, 0, },
|
||||
{ HI3620_RCLK_CFGAXI, "rclk_cfgaxi", "armpll2", 1, 30, 0, },
|
||||
{ HI3620_RCLK_PICO, "rclk_pico", "hsic_div", 1, 40, 0, },
|
||||
};
|
||||
|
||||
static struct hisi_mux_clock hi3620_mux_clks[] __initdata = {
|
||||
{ HI3620_TIMER0_MUX, "timer0_mux", timer0_mux_p, ARRAY_SIZE(timer0_mux_p), CLK_SET_RATE_PARENT, 0, 15, 2, 0, },
|
||||
{ HI3620_TIMER1_MUX, "timer1_mux", timer1_mux_p, ARRAY_SIZE(timer1_mux_p), CLK_SET_RATE_PARENT, 0, 17, 2, 0, },
|
||||
{ HI3620_TIMER2_MUX, "timer2_mux", timer2_mux_p, ARRAY_SIZE(timer2_mux_p), CLK_SET_RATE_PARENT, 0, 19, 2, 0, },
|
||||
{ HI3620_TIMER3_MUX, "timer3_mux", timer3_mux_p, ARRAY_SIZE(timer3_mux_p), CLK_SET_RATE_PARENT, 0, 21, 2, 0, },
|
||||
{ HI3620_TIMER4_MUX, "timer4_mux", timer4_mux_p, ARRAY_SIZE(timer4_mux_p), CLK_SET_RATE_PARENT, 0x18, 0, 2, 0, },
|
||||
{ HI3620_TIMER5_MUX, "timer5_mux", timer5_mux_p, ARRAY_SIZE(timer5_mux_p), CLK_SET_RATE_PARENT, 0x18, 2, 2, 0, },
|
||||
{ HI3620_TIMER6_MUX, "timer6_mux", timer6_mux_p, ARRAY_SIZE(timer6_mux_p), CLK_SET_RATE_PARENT, 0x18, 4, 2, 0, },
|
||||
{ HI3620_TIMER7_MUX, "timer7_mux", timer7_mux_p, ARRAY_SIZE(timer7_mux_p), CLK_SET_RATE_PARENT, 0x18, 6, 2, 0, },
|
||||
{ HI3620_TIMER8_MUX, "timer8_mux", timer8_mux_p, ARRAY_SIZE(timer8_mux_p), CLK_SET_RATE_PARENT, 0x18, 8, 2, 0, },
|
||||
{ HI3620_TIMER9_MUX, "timer9_mux", timer9_mux_p, ARRAY_SIZE(timer9_mux_p), CLK_SET_RATE_PARENT, 0x18, 10, 2, 0, },
|
||||
{ HI3620_UART0_MUX, "uart0_mux", uart0_mux_p, ARRAY_SIZE(uart0_mux_p), CLK_SET_RATE_PARENT, 0x100, 7, 1, CLK_MUX_HIWORD_MASK, },
|
||||
{ HI3620_UART1_MUX, "uart1_mux", uart1_mux_p, ARRAY_SIZE(uart1_mux_p), CLK_SET_RATE_PARENT, 0x100, 8, 1, CLK_MUX_HIWORD_MASK, },
|
||||
{ HI3620_UART2_MUX, "uart2_mux", uart2_mux_p, ARRAY_SIZE(uart2_mux_p), CLK_SET_RATE_PARENT, 0x100, 9, 1, CLK_MUX_HIWORD_MASK, },
|
||||
{ HI3620_UART3_MUX, "uart3_mux", uart3_mux_p, ARRAY_SIZE(uart3_mux_p), CLK_SET_RATE_PARENT, 0x100, 10, 1, CLK_MUX_HIWORD_MASK, },
|
||||
{ HI3620_UART4_MUX, "uart4_mux", uart4_mux_p, ARRAY_SIZE(uart4_mux_p), CLK_SET_RATE_PARENT, 0x100, 11, 1, CLK_MUX_HIWORD_MASK, },
|
||||
{ HI3620_SPI0_MUX, "spi0_mux", spi0_mux_p, ARRAY_SIZE(spi0_mux_p), CLK_SET_RATE_PARENT, 0x100, 12, 1, CLK_MUX_HIWORD_MASK, },
|
||||
{ HI3620_SPI1_MUX, "spi1_mux", spi1_mux_p, ARRAY_SIZE(spi1_mux_p), CLK_SET_RATE_PARENT, 0x100, 13, 1, CLK_MUX_HIWORD_MASK, },
|
||||
{ HI3620_SPI2_MUX, "spi2_mux", spi2_mux_p, ARRAY_SIZE(spi2_mux_p), CLK_SET_RATE_PARENT, 0x100, 14, 1, CLK_MUX_HIWORD_MASK, },
|
||||
{ HI3620_SAXI_MUX, "saxi_mux", saxi_mux_p, ARRAY_SIZE(saxi_mux_p), CLK_SET_RATE_PARENT, 0x100, 15, 1, CLK_MUX_HIWORD_MASK, },
|
||||
{ HI3620_PWM0_MUX, "pwm0_mux", pwm0_mux_p, ARRAY_SIZE(pwm0_mux_p), CLK_SET_RATE_PARENT, 0x104, 10, 1, CLK_MUX_HIWORD_MASK, },
|
||||
{ HI3620_PWM1_MUX, "pwm1_mux", pwm1_mux_p, ARRAY_SIZE(pwm1_mux_p), CLK_SET_RATE_PARENT, 0x104, 11, 1, CLK_MUX_HIWORD_MASK, },
|
||||
{ HI3620_SD_MUX, "sd_mux", sd_mux_p, ARRAY_SIZE(sd_mux_p), CLK_SET_RATE_PARENT, 0x108, 4, 1, CLK_MUX_HIWORD_MASK, },
|
||||
{ HI3620_MMC1_MUX, "mmc1_mux", mmc1_mux_p, ARRAY_SIZE(mmc1_mux_p), CLK_SET_RATE_PARENT, 0x108, 9, 1, CLK_MUX_HIWORD_MASK, },
|
||||
{ HI3620_MMC1_MUX2, "mmc1_mux2", mmc1_mux2_p, ARRAY_SIZE(mmc1_mux2_p), CLK_SET_RATE_PARENT, 0x108, 10, 1, CLK_MUX_HIWORD_MASK, },
|
||||
{ HI3620_G2D_MUX, "g2d_mux", g2d_mux_p, ARRAY_SIZE(g2d_mux_p), CLK_SET_RATE_PARENT, 0x10c, 5, 1, CLK_MUX_HIWORD_MASK, },
|
||||
{ HI3620_VENC_MUX, "venc_mux", venc_mux_p, ARRAY_SIZE(venc_mux_p), CLK_SET_RATE_PARENT, 0x10c, 11, 1, CLK_MUX_HIWORD_MASK, },
|
||||
{ HI3620_VDEC_MUX, "vdec_mux", vdec_mux_p, ARRAY_SIZE(vdec_mux_p), CLK_SET_RATE_PARENT, 0x110, 5, 1, CLK_MUX_HIWORD_MASK, },
|
||||
{ HI3620_VPP_MUX, "vpp_mux", vpp_mux_p, ARRAY_SIZE(vpp_mux_p), CLK_SET_RATE_PARENT, 0x110, 11, 1, CLK_MUX_HIWORD_MASK, },
|
||||
{ HI3620_EDC0_MUX, "edc0_mux", edc0_mux_p, ARRAY_SIZE(edc0_mux_p), CLK_SET_RATE_PARENT, 0x114, 6, 1, CLK_MUX_HIWORD_MASK, },
|
||||
{ HI3620_LDI0_MUX, "ldi0_mux", ldi0_mux_p, ARRAY_SIZE(ldi0_mux_p), CLK_SET_RATE_PARENT, 0x114, 13, 2, CLK_MUX_HIWORD_MASK, },
|
||||
{ HI3620_EDC1_MUX, "edc1_mux", edc1_mux_p, ARRAY_SIZE(edc1_mux_p), CLK_SET_RATE_PARENT, 0x118, 6, 1, CLK_MUX_HIWORD_MASK, },
|
||||
{ HI3620_LDI1_MUX, "ldi1_mux", ldi1_mux_p, ARRAY_SIZE(ldi1_mux_p), CLK_SET_RATE_PARENT, 0x118, 14, 2, CLK_MUX_HIWORD_MASK, },
|
||||
{ HI3620_RCLK_HSIC, "rclk_hsic", rclk_hsic_p, ARRAY_SIZE(rclk_hsic_p), CLK_SET_RATE_PARENT, 0x130, 2, 1, CLK_MUX_HIWORD_MASK, },
|
||||
{ HI3620_MMC2_MUX, "mmc2_mux", mmc2_mux_p, ARRAY_SIZE(mmc2_mux_p), CLK_SET_RATE_PARENT, 0x140, 4, 1, CLK_MUX_HIWORD_MASK, },
|
||||
{ HI3620_MMC3_MUX, "mmc3_mux", mmc3_mux_p, ARRAY_SIZE(mmc3_mux_p), CLK_SET_RATE_PARENT, 0x140, 9, 1, CLK_MUX_HIWORD_MASK, },
|
||||
};
|
||||
|
||||
static struct hisi_divider_clock hi3620_div_clks[] __initdata = {
|
||||
{ HI3620_SHAREAXI_DIV, "saxi_div", "saxi_mux", 0, 0x100, 0, 5, CLK_DIVIDER_HIWORD_MASK, NULL, },
|
||||
{ HI3620_CFGAXI_DIV, "cfgaxi_div", "saxi_div", 0, 0x100, 5, 2, CLK_DIVIDER_HIWORD_MASK, NULL, },
|
||||
{ HI3620_SD_DIV, "sd_div", "sd_mux", 0, 0x108, 0, 4, CLK_DIVIDER_HIWORD_MASK, NULL, },
|
||||
{ HI3620_MMC1_DIV, "mmc1_div", "mmc1_mux", 0, 0x108, 5, 4, CLK_DIVIDER_HIWORD_MASK, NULL, },
|
||||
{ HI3620_HSIC_DIV, "hsic_div", "rclk_hsic", 0, 0x130, 0, 2, CLK_DIVIDER_HIWORD_MASK, NULL, },
|
||||
{ HI3620_MMC2_DIV, "mmc2_div", "mmc2_mux", 0, 0x140, 0, 4, CLK_DIVIDER_HIWORD_MASK, NULL, },
|
||||
{ HI3620_MMC3_DIV, "mmc3_div", "mmc3_mux", 0, 0x140, 5, 4, CLK_DIVIDER_HIWORD_MASK, NULL, },
|
||||
};
|
||||
|
||||
static struct hisi_gate_clock hi3620_seperated_gate_clks[] __initdata = {
|
||||
{ HI3620_TIMERCLK01, "timerclk01", "timer_rclk01", CLK_SET_RATE_PARENT, 0x20, 0, 0, },
|
||||
{ HI3620_TIMER_RCLK01, "timer_rclk01", "rclk_tcxo", CLK_SET_RATE_PARENT, 0x20, 1, 0, },
|
||||
{ HI3620_TIMERCLK23, "timerclk23", "timer_rclk23", CLK_SET_RATE_PARENT, 0x20, 2, 0, },
|
||||
{ HI3620_TIMER_RCLK23, "timer_rclk23", "rclk_tcxo", CLK_SET_RATE_PARENT, 0x20, 3, 0, },
|
||||
{ HI3620_RTCCLK, "rtcclk", "pclk", CLK_SET_RATE_PARENT, 0x20, 5, 0, },
|
||||
{ HI3620_KPC_CLK, "kpc_clk", "pclk", CLK_SET_RATE_PARENT, 0x20, 6, 0, },
|
||||
{ HI3620_GPIOCLK0, "gpioclk0", "pclk", CLK_SET_RATE_PARENT, 0x20, 8, 0, },
|
||||
{ HI3620_GPIOCLK1, "gpioclk1", "pclk", CLK_SET_RATE_PARENT, 0x20, 9, 0, },
|
||||
{ HI3620_GPIOCLK2, "gpioclk2", "pclk", CLK_SET_RATE_PARENT, 0x20, 10, 0, },
|
||||
{ HI3620_GPIOCLK3, "gpioclk3", "pclk", CLK_SET_RATE_PARENT, 0x20, 11, 0, },
|
||||
{ HI3620_GPIOCLK4, "gpioclk4", "pclk", CLK_SET_RATE_PARENT, 0x20, 12, 0, },
|
||||
{ HI3620_GPIOCLK5, "gpioclk5", "pclk", CLK_SET_RATE_PARENT, 0x20, 13, 0, },
|
||||
{ HI3620_GPIOCLK6, "gpioclk6", "pclk", CLK_SET_RATE_PARENT, 0x20, 14, 0, },
|
||||
{ HI3620_GPIOCLK7, "gpioclk7", "pclk", CLK_SET_RATE_PARENT, 0x20, 15, 0, },
|
||||
{ HI3620_GPIOCLK8, "gpioclk8", "pclk", CLK_SET_RATE_PARENT, 0x20, 16, 0, },
|
||||
{ HI3620_GPIOCLK9, "gpioclk9", "pclk", CLK_SET_RATE_PARENT, 0x20, 17, 0, },
|
||||
{ HI3620_GPIOCLK10, "gpioclk10", "pclk", CLK_SET_RATE_PARENT, 0x20, 18, 0, },
|
||||
{ HI3620_GPIOCLK11, "gpioclk11", "pclk", CLK_SET_RATE_PARENT, 0x20, 19, 0, },
|
||||
{ HI3620_GPIOCLK12, "gpioclk12", "pclk", CLK_SET_RATE_PARENT, 0x20, 20, 0, },
|
||||
{ HI3620_GPIOCLK13, "gpioclk13", "pclk", CLK_SET_RATE_PARENT, 0x20, 21, 0, },
|
||||
{ HI3620_GPIOCLK14, "gpioclk14", "pclk", CLK_SET_RATE_PARENT, 0x20, 22, 0, },
|
||||
{ HI3620_GPIOCLK15, "gpioclk15", "pclk", CLK_SET_RATE_PARENT, 0x20, 23, 0, },
|
||||
{ HI3620_GPIOCLK16, "gpioclk16", "pclk", CLK_SET_RATE_PARENT, 0x20, 24, 0, },
|
||||
{ HI3620_GPIOCLK17, "gpioclk17", "pclk", CLK_SET_RATE_PARENT, 0x20, 25, 0, },
|
||||
{ HI3620_GPIOCLK18, "gpioclk18", "pclk", CLK_SET_RATE_PARENT, 0x20, 26, 0, },
|
||||
{ HI3620_GPIOCLK19, "gpioclk19", "pclk", CLK_SET_RATE_PARENT, 0x20, 27, 0, },
|
||||
{ HI3620_GPIOCLK20, "gpioclk20", "pclk", CLK_SET_RATE_PARENT, 0x20, 28, 0, },
|
||||
{ HI3620_GPIOCLK21, "gpioclk21", "pclk", CLK_SET_RATE_PARENT, 0x20, 29, 0, },
|
||||
{ HI3620_DPHY0_CLK, "dphy0_clk", "osc26m", CLK_SET_RATE_PARENT, 0x30, 15, 0, },
|
||||
{ HI3620_DPHY1_CLK, "dphy1_clk", "osc26m", CLK_SET_RATE_PARENT, 0x30, 16, 0, },
|
||||
{ HI3620_DPHY2_CLK, "dphy2_clk", "osc26m", CLK_SET_RATE_PARENT, 0x30, 17, 0, },
|
||||
{ HI3620_USBPHY_CLK, "usbphy_clk", "rclk_pico", CLK_SET_RATE_PARENT, 0x30, 24, 0, },
|
||||
{ HI3620_ACP_CLK, "acp_clk", "rclk_cfgaxi", CLK_SET_RATE_PARENT, 0x30, 28, 0, },
|
||||
{ HI3620_TIMERCLK45, "timerclk45", "rclk_tcxo", CLK_SET_RATE_PARENT, 0x40, 3, 0, },
|
||||
{ HI3620_TIMERCLK67, "timerclk67", "rclk_tcxo", CLK_SET_RATE_PARENT, 0x40, 4, 0, },
|
||||
{ HI3620_TIMERCLK89, "timerclk89", "rclk_tcxo", CLK_SET_RATE_PARENT, 0x40, 5, 0, },
|
||||
{ HI3620_PWMCLK0, "pwmclk0", "pwm0_mux", CLK_SET_RATE_PARENT, 0x40, 7, 0, },
|
||||
{ HI3620_PWMCLK1, "pwmclk1", "pwm1_mux", CLK_SET_RATE_PARENT, 0x40, 8, 0, },
|
||||
{ HI3620_UARTCLK0, "uartclk0", "uart0_mux", CLK_SET_RATE_PARENT, 0x40, 16, 0, },
|
||||
{ HI3620_UARTCLK1, "uartclk1", "uart1_mux", CLK_SET_RATE_PARENT, 0x40, 17, 0, },
|
||||
{ HI3620_UARTCLK2, "uartclk2", "uart2_mux", CLK_SET_RATE_PARENT, 0x40, 18, 0, },
|
||||
{ HI3620_UARTCLK3, "uartclk3", "uart3_mux", CLK_SET_RATE_PARENT, 0x40, 19, 0, },
|
||||
{ HI3620_UARTCLK4, "uartclk4", "uart4_mux", CLK_SET_RATE_PARENT, 0x40, 20, 0, },
|
||||
{ HI3620_SPICLK0, "spiclk0", "spi0_mux", CLK_SET_RATE_PARENT, 0x40, 21, 0, },
|
||||
{ HI3620_SPICLK1, "spiclk1", "spi1_mux", CLK_SET_RATE_PARENT, 0x40, 22, 0, },
|
||||
{ HI3620_SPICLK2, "spiclk2", "spi2_mux", CLK_SET_RATE_PARENT, 0x40, 23, 0, },
|
||||
{ HI3620_I2CCLK0, "i2cclk0", "pclk", CLK_SET_RATE_PARENT, 0x40, 24, 0, },
|
||||
{ HI3620_I2CCLK1, "i2cclk1", "pclk", CLK_SET_RATE_PARENT, 0x40, 25, 0, },
|
||||
{ HI3620_SCI_CLK, "sci_clk", "osc26m", CLK_SET_RATE_PARENT, 0x40, 26, 0, },
|
||||
{ HI3620_I2CCLK2, "i2cclk2", "pclk", CLK_SET_RATE_PARENT, 0x40, 28, 0, },
|
||||
{ HI3620_I2CCLK3, "i2cclk3", "pclk", CLK_SET_RATE_PARENT, 0x40, 29, 0, },
|
||||
{ HI3620_DDRC_PER_CLK, "ddrc_per_clk", "rclk_cfgaxi", CLK_SET_RATE_PARENT, 0x50, 9, 0, },
|
||||
{ HI3620_DMAC_CLK, "dmac_clk", "rclk_cfgaxi", CLK_SET_RATE_PARENT, 0x50, 10, 0, },
|
||||
{ HI3620_USB2DVC_CLK, "usb2dvc_clk", "rclk_cfgaxi", CLK_SET_RATE_PARENT, 0x50, 17, 0, },
|
||||
{ HI3620_SD_CLK, "sd_clk", "sd_div", CLK_SET_RATE_PARENT, 0x50, 20, 0, },
|
||||
{ HI3620_MMC_CLK1, "mmc_clk1", "mmc1_mux2", CLK_SET_RATE_PARENT, 0x50, 21, 0, },
|
||||
{ HI3620_MMC_CLK2, "mmc_clk2", "mmc2_div", CLK_SET_RATE_PARENT, 0x50, 22, 0, },
|
||||
{ HI3620_MMC_CLK3, "mmc_clk3", "mmc3_div", CLK_SET_RATE_PARENT, 0x50, 23, 0, },
|
||||
{ HI3620_MCU_CLK, "mcu_clk", "acp_clk", CLK_SET_RATE_PARENT, 0x50, 24, 0, },
|
||||
};
|
||||
|
||||
static void __init hi3620_clk_init(struct device_node *np)
|
||||
{
|
||||
void __iomem *base;
|
||||
|
||||
if (np) {
|
||||
base = of_iomap(np, 0);
|
||||
if (!base) {
|
||||
pr_err("failed to map Hi3620 clock registers\n");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
pr_err("failed to find Hi3620 clock node in DTS\n");
|
||||
return;
|
||||
}
|
||||
|
||||
hisi_clk_init(np, HI3620_NR_CLKS);
|
||||
|
||||
hisi_clk_register_fixed_rate(hi3620_fixed_rate_clks,
|
||||
ARRAY_SIZE(hi3620_fixed_rate_clks),
|
||||
base);
|
||||
hisi_clk_register_fixed_factor(hi3620_fixed_factor_clks,
|
||||
ARRAY_SIZE(hi3620_fixed_factor_clks),
|
||||
base);
|
||||
hisi_clk_register_mux(hi3620_mux_clks, ARRAY_SIZE(hi3620_mux_clks),
|
||||
base);
|
||||
hisi_clk_register_divider(hi3620_div_clks, ARRAY_SIZE(hi3620_div_clks),
|
||||
base);
|
||||
hisi_clk_register_gate_sep(hi3620_seperated_gate_clks,
|
||||
ARRAY_SIZE(hi3620_seperated_gate_clks),
|
||||
base);
|
||||
}
|
||||
CLK_OF_DECLARE(hi3620_clk, "hisilicon,hi3620-clock", hi3620_clk_init);
|
171
drivers/clk/hisilicon/clk.c
Normal file
171
drivers/clk/hisilicon/clk.c
Normal file
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* Hisilicon clock driver
|
||||
*
|
||||
* Copyright (c) 2012-2013 Hisilicon Limited.
|
||||
* Copyright (c) 2012-2013 Linaro Limited.
|
||||
*
|
||||
* Author: Haojian Zhuang <haojian.zhuang@linaro.org>
|
||||
* Xin Li <li.xin@linaro.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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
#include "clk.h"
|
||||
|
||||
static DEFINE_SPINLOCK(hisi_clk_lock);
|
||||
static struct clk **clk_table;
|
||||
static struct clk_onecell_data clk_data;
|
||||
|
||||
void __init hisi_clk_init(struct device_node *np, int nr_clks)
|
||||
{
|
||||
clk_table = kzalloc(sizeof(struct clk *) * nr_clks, GFP_KERNEL);
|
||||
if (!clk_table) {
|
||||
pr_err("%s: could not allocate clock lookup table\n", __func__);
|
||||
return;
|
||||
}
|
||||
clk_data.clks = clk_table;
|
||||
clk_data.clk_num = nr_clks;
|
||||
of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
|
||||
}
|
||||
|
||||
void __init hisi_clk_register_fixed_rate(struct hisi_fixed_rate_clock *clks,
|
||||
int nums, void __iomem *base)
|
||||
{
|
||||
struct clk *clk;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nums; i++) {
|
||||
clk = clk_register_fixed_rate(NULL, clks[i].name,
|
||||
clks[i].parent_name,
|
||||
clks[i].flags,
|
||||
clks[i].fixed_rate);
|
||||
if (IS_ERR(clk)) {
|
||||
pr_err("%s: failed to register clock %s\n",
|
||||
__func__, clks[i].name);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void __init hisi_clk_register_fixed_factor(struct hisi_fixed_factor_clock *clks,
|
||||
int nums, void __iomem *base)
|
||||
{
|
||||
struct clk *clk;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nums; i++) {
|
||||
clk = clk_register_fixed_factor(NULL, clks[i].name,
|
||||
clks[i].parent_name,
|
||||
clks[i].flags, clks[i].mult,
|
||||
clks[i].div);
|
||||
if (IS_ERR(clk)) {
|
||||
pr_err("%s: failed to register clock %s\n",
|
||||
__func__, clks[i].name);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void __init hisi_clk_register_mux(struct hisi_mux_clock *clks,
|
||||
int nums, void __iomem *base)
|
||||
{
|
||||
struct clk *clk;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nums; i++) {
|
||||
clk = clk_register_mux(NULL, clks[i].name, clks[i].parent_names,
|
||||
clks[i].num_parents, clks[i].flags,
|
||||
base + clks[i].offset, clks[i].shift,
|
||||
clks[i].width, clks[i].mux_flags,
|
||||
&hisi_clk_lock);
|
||||
if (IS_ERR(clk)) {
|
||||
pr_err("%s: failed to register clock %s\n",
|
||||
__func__, clks[i].name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (clks[i].alias)
|
||||
clk_register_clkdev(clk, clks[i].alias, NULL);
|
||||
|
||||
clk_table[clks[i].id] = clk;
|
||||
}
|
||||
}
|
||||
|
||||
void __init hisi_clk_register_divider(struct hisi_divider_clock *clks,
|
||||
int nums, void __iomem *base)
|
||||
{
|
||||
struct clk *clk;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nums; i++) {
|
||||
clk = clk_register_divider_table(NULL, clks[i].name,
|
||||
clks[i].parent_name,
|
||||
clks[i].flags,
|
||||
base + clks[i].offset,
|
||||
clks[i].shift, clks[i].width,
|
||||
clks[i].div_flags,
|
||||
clks[i].table,
|
||||
&hisi_clk_lock);
|
||||
if (IS_ERR(clk)) {
|
||||
pr_err("%s: failed to register clock %s\n",
|
||||
__func__, clks[i].name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (clks[i].alias)
|
||||
clk_register_clkdev(clk, clks[i].alias, NULL);
|
||||
|
||||
clk_table[clks[i].id] = clk;
|
||||
}
|
||||
}
|
||||
|
||||
void __init hisi_clk_register_gate_sep(struct hisi_gate_clock *clks,
|
||||
int nums, void __iomem *base)
|
||||
{
|
||||
struct clk *clk;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nums; i++) {
|
||||
clk = hisi_register_clkgate_sep(NULL, clks[i].name,
|
||||
clks[i].parent_name,
|
||||
clks[i].flags,
|
||||
base + clks[i].offset,
|
||||
clks[i].bit_idx,
|
||||
clks[i].gate_flags,
|
||||
&hisi_clk_lock);
|
||||
if (IS_ERR(clk)) {
|
||||
pr_err("%s: failed to register clock %s\n",
|
||||
__func__, clks[i].name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (clks[i].alias)
|
||||
clk_register_clkdev(clk, clks[i].alias, NULL);
|
||||
|
||||
clk_table[clks[i].id] = clk;
|
||||
}
|
||||
}
|
103
drivers/clk/hisilicon/clk.h
Normal file
103
drivers/clk/hisilicon/clk.h
Normal file
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Hisilicon Hi3620 clock gate driver
|
||||
*
|
||||
* Copyright (c) 2012-2013 Hisilicon Limited.
|
||||
* Copyright (c) 2012-2013 Linaro Limited.
|
||||
*
|
||||
* Author: Haojian Zhuang <haojian.zhuang@linaro.org>
|
||||
* Xin Li <li.xin@linaro.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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __HISI_CLK_H
|
||||
#define __HISI_CLK_H
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
struct hisi_fixed_rate_clock {
|
||||
unsigned int id;
|
||||
char *name;
|
||||
const char *parent_name;
|
||||
unsigned long flags;
|
||||
unsigned long fixed_rate;
|
||||
};
|
||||
|
||||
struct hisi_fixed_factor_clock {
|
||||
unsigned int id;
|
||||
char *name;
|
||||
const char *parent_name;
|
||||
unsigned long mult;
|
||||
unsigned long div;
|
||||
unsigned long flags;
|
||||
};
|
||||
|
||||
struct hisi_mux_clock {
|
||||
unsigned int id;
|
||||
const char *name;
|
||||
const char **parent_names;
|
||||
u8 num_parents;
|
||||
unsigned long flags;
|
||||
unsigned long offset;
|
||||
u8 shift;
|
||||
u8 width;
|
||||
u8 mux_flags;
|
||||
const char *alias;
|
||||
};
|
||||
|
||||
struct hisi_divider_clock {
|
||||
unsigned int id;
|
||||
const char *name;
|
||||
const char *parent_name;
|
||||
unsigned long flags;
|
||||
unsigned long offset;
|
||||
u8 shift;
|
||||
u8 width;
|
||||
u8 div_flags;
|
||||
struct clk_div_table *table;
|
||||
const char *alias;
|
||||
};
|
||||
|
||||
struct hisi_gate_clock {
|
||||
unsigned int id;
|
||||
const char *name;
|
||||
const char *parent_name;
|
||||
unsigned long flags;
|
||||
unsigned long offset;
|
||||
u8 bit_idx;
|
||||
u8 gate_flags;
|
||||
const char *alias;
|
||||
};
|
||||
|
||||
struct clk *hisi_register_clkgate_sep(struct device *, const char *,
|
||||
const char *, unsigned long,
|
||||
void __iomem *, u8,
|
||||
u8, spinlock_t *);
|
||||
|
||||
void __init hisi_clk_init(struct device_node *, int);
|
||||
void __init hisi_clk_register_fixed_rate(struct hisi_fixed_rate_clock *,
|
||||
int, void __iomem *);
|
||||
void __init hisi_clk_register_fixed_factor(struct hisi_fixed_factor_clock *,
|
||||
int, void __iomem *);
|
||||
void __init hisi_clk_register_mux(struct hisi_mux_clock *, int,
|
||||
void __iomem *);
|
||||
void __init hisi_clk_register_divider(struct hisi_divider_clock *,
|
||||
int, void __iomem *);
|
||||
void __init hisi_clk_register_gate_sep(struct hisi_gate_clock *,
|
||||
int, void __iomem *);
|
||||
#endif /* __HISI_CLK_H */
|
130
drivers/clk/hisilicon/clkgate-separated.c
Normal file
130
drivers/clk/hisilicon/clkgate-separated.c
Normal file
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* Hisilicon clock separated gate driver
|
||||
*
|
||||
* Copyright (c) 2012-2013 Hisilicon Limited.
|
||||
* Copyright (c) 2012-2013 Linaro Limited.
|
||||
*
|
||||
* Author: Haojian Zhuang <haojian.zhuang@linaro.org>
|
||||
* Xin Li <li.xin@linaro.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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
#include "clk.h"
|
||||
|
||||
/* clock separated gate register offset */
|
||||
#define CLKGATE_SEPERATED_ENABLE 0x0
|
||||
#define CLKGATE_SEPERATED_DISABLE 0x4
|
||||
#define CLKGATE_SEPERATED_STATUS 0x8
|
||||
|
||||
struct clkgate_separated {
|
||||
struct clk_hw hw;
|
||||
void __iomem *enable; /* enable register */
|
||||
u8 bit_idx; /* bits in enable/disable register */
|
||||
u8 flags;
|
||||
spinlock_t *lock;
|
||||
};
|
||||
|
||||
static int clkgate_separated_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct clkgate_separated *sclk;
|
||||
unsigned long flags = 0;
|
||||
u32 reg;
|
||||
|
||||
sclk = container_of(hw, struct clkgate_separated, hw);
|
||||
if (sclk->lock)
|
||||
spin_lock_irqsave(sclk->lock, flags);
|
||||
reg = BIT(sclk->bit_idx);
|
||||
writel_relaxed(reg, sclk->enable);
|
||||
readl_relaxed(sclk->enable + CLKGATE_SEPERATED_STATUS);
|
||||
if (sclk->lock)
|
||||
spin_unlock_irqrestore(sclk->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void clkgate_separated_disable(struct clk_hw *hw)
|
||||
{
|
||||
struct clkgate_separated *sclk;
|
||||
unsigned long flags = 0;
|
||||
u32 reg;
|
||||
|
||||
sclk = container_of(hw, struct clkgate_separated, hw);
|
||||
if (sclk->lock)
|
||||
spin_lock_irqsave(sclk->lock, flags);
|
||||
reg = BIT(sclk->bit_idx);
|
||||
writel_relaxed(reg, sclk->enable + CLKGATE_SEPERATED_DISABLE);
|
||||
readl_relaxed(sclk->enable + CLKGATE_SEPERATED_STATUS);
|
||||
if (sclk->lock)
|
||||
spin_unlock_irqrestore(sclk->lock, flags);
|
||||
}
|
||||
|
||||
static int clkgate_separated_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct clkgate_separated *sclk;
|
||||
u32 reg;
|
||||
|
||||
sclk = container_of(hw, struct clkgate_separated, hw);
|
||||
reg = readl_relaxed(sclk->enable + CLKGATE_SEPERATED_STATUS);
|
||||
reg &= BIT(sclk->bit_idx);
|
||||
|
||||
return reg ? 1 : 0;
|
||||
}
|
||||
|
||||
static struct clk_ops clkgate_separated_ops = {
|
||||
.enable = clkgate_separated_enable,
|
||||
.disable = clkgate_separated_disable,
|
||||
.is_enabled = clkgate_separated_is_enabled,
|
||||
};
|
||||
|
||||
struct clk *hisi_register_clkgate_sep(struct device *dev, const char *name,
|
||||
const char *parent_name,
|
||||
unsigned long flags,
|
||||
void __iomem *reg, u8 bit_idx,
|
||||
u8 clk_gate_flags, spinlock_t *lock)
|
||||
{
|
||||
struct clkgate_separated *sclk;
|
||||
struct clk *clk;
|
||||
struct clk_init_data init;
|
||||
|
||||
sclk = kzalloc(sizeof(*sclk), GFP_KERNEL);
|
||||
if (!sclk) {
|
||||
pr_err("%s: fail to allocate separated gated clk\n", __func__);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
init.name = name;
|
||||
init.ops = &clkgate_separated_ops;
|
||||
init.flags = flags | CLK_IS_BASIC;
|
||||
init.parent_names = (parent_name ? &parent_name : NULL);
|
||||
init.num_parents = (parent_name ? 1 : 0);
|
||||
|
||||
sclk->enable = reg + CLKGATE_SEPERATED_ENABLE;
|
||||
sclk->bit_idx = bit_idx;
|
||||
sclk->flags = clk_gate_flags;
|
||||
sclk->hw.init = &init;
|
||||
|
||||
clk = clk_register(dev, &sclk->hw);
|
||||
if (IS_ERR(clk))
|
||||
kfree(sclk);
|
||||
return clk;
|
||||
}
|
|
@ -223,8 +223,7 @@ static void __init of_psc_clk_init(struct device_node *node, spinlock_t *lock)
|
|||
data->domain_base = of_iomap(node, i);
|
||||
if (!data->domain_base) {
|
||||
pr_err("%s: domain ioremap failed\n", __func__);
|
||||
iounmap(data->control_base);
|
||||
goto out;
|
||||
goto unmap_ctrl;
|
||||
}
|
||||
|
||||
of_property_read_u32(node, "domain-id", &data->domain_id);
|
||||
|
@ -237,16 +236,21 @@ static void __init of_psc_clk_init(struct device_node *node, spinlock_t *lock)
|
|||
parent_name = of_clk_get_parent_name(node, 0);
|
||||
if (!parent_name) {
|
||||
pr_err("%s: Parent clock not found\n", __func__);
|
||||
goto out;
|
||||
goto unmap_domain;
|
||||
}
|
||||
|
||||
clk = clk_register_psc(NULL, clk_name, parent_name, data, lock);
|
||||
if (clk) {
|
||||
if (!IS_ERR(clk)) {
|
||||
of_clk_add_provider(node, of_clk_src_simple_get, clk);
|
||||
return;
|
||||
}
|
||||
|
||||
pr_err("%s: error registering clk %s\n", __func__, node->name);
|
||||
|
||||
unmap_domain:
|
||||
iounmap(data->domain_base);
|
||||
unmap_ctrl:
|
||||
iounmap(data->control_base);
|
||||
out:
|
||||
kfree(data);
|
||||
return;
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
#define MAIN_PLLM_HIGH_MASK 0x7f000
|
||||
#define PLLM_HIGH_SHIFT 6
|
||||
#define PLLD_MASK 0x3f
|
||||
#define CLKOD_MASK 0x780000
|
||||
#define CLKOD_SHIFT 19
|
||||
|
||||
/**
|
||||
* struct clk_pll_data - pll data structure
|
||||
|
@ -41,7 +43,10 @@
|
|||
* @pllm_upper_mask: multiplier upper mask
|
||||
* @pllm_upper_shift: multiplier upper shift
|
||||
* @plld_mask: divider mask
|
||||
* @postdiv: Post divider
|
||||
* @clkod_mask: output divider mask
|
||||
* @clkod_shift: output divider shift
|
||||
* @plld_mask: divider mask
|
||||
* @postdiv: Fixed post divider
|
||||
*/
|
||||
struct clk_pll_data {
|
||||
bool has_pllctrl;
|
||||
|
@ -53,6 +58,8 @@ struct clk_pll_data {
|
|||
u32 pllm_upper_mask;
|
||||
u32 pllm_upper_shift;
|
||||
u32 plld_mask;
|
||||
u32 clkod_mask;
|
||||
u32 clkod_shift;
|
||||
u32 postdiv;
|
||||
};
|
||||
|
||||
|
@ -90,7 +97,13 @@ static unsigned long clk_pllclk_recalc(struct clk_hw *hw,
|
|||
mult |= ((val & pll_data->pllm_upper_mask)
|
||||
>> pll_data->pllm_upper_shift);
|
||||
prediv = (val & pll_data->plld_mask);
|
||||
postdiv = pll_data->postdiv;
|
||||
|
||||
if (!pll_data->has_pllctrl)
|
||||
/* read post divider from od bits*/
|
||||
postdiv = ((val & pll_data->clkod_mask) >>
|
||||
pll_data->clkod_shift) + 1;
|
||||
else
|
||||
postdiv = pll_data->postdiv;
|
||||
|
||||
rate /= (prediv + 1);
|
||||
rate = (rate * (mult + 1));
|
||||
|
@ -155,8 +168,11 @@ static void __init _of_pll_clk_init(struct device_node *node, bool pllctrl)
|
|||
}
|
||||
|
||||
parent_name = of_clk_get_parent_name(node, 0);
|
||||
if (of_property_read_u32(node, "fixed-postdiv", &pll_data->postdiv))
|
||||
goto out;
|
||||
if (of_property_read_u32(node, "fixed-postdiv", &pll_data->postdiv)) {
|
||||
/* assume the PLL has output divider register bits */
|
||||
pll_data->clkod_mask = CLKOD_MASK;
|
||||
pll_data->clkod_shift = CLKOD_SHIFT;
|
||||
}
|
||||
|
||||
i = of_property_match_string(node, "reg-names", "control");
|
||||
pll_data->pll_ctl0 = of_iomap(node, i);
|
||||
|
|
|
@ -4,15 +4,20 @@ config MVEBU_CLK_COMMON
|
|||
config MVEBU_CLK_CPU
|
||||
bool
|
||||
|
||||
config MVEBU_CLK_COREDIV
|
||||
bool
|
||||
|
||||
config ARMADA_370_CLK
|
||||
bool
|
||||
select MVEBU_CLK_COMMON
|
||||
select MVEBU_CLK_CPU
|
||||
select MVEBU_CLK_COREDIV
|
||||
|
||||
config ARMADA_XP_CLK
|
||||
bool
|
||||
select MVEBU_CLK_COMMON
|
||||
select MVEBU_CLK_CPU
|
||||
select MVEBU_CLK_COREDIV
|
||||
|
||||
config DOVE_CLK
|
||||
bool
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
obj-$(CONFIG_MVEBU_CLK_COMMON) += common.o
|
||||
obj-$(CONFIG_MVEBU_CLK_CPU) += clk-cpu.o
|
||||
obj-$(CONFIG_MVEBU_CLK_COREDIV) += clk-corediv.o
|
||||
|
||||
obj-$(CONFIG_ARMADA_370_CLK) += armada-370.o
|
||||
obj-$(CONFIG_ARMADA_XP_CLK) += armada-xp.o
|
||||
|
|
223
drivers/clk/mvebu/clk-corediv.c
Normal file
223
drivers/clk/mvebu/clk-corediv.c
Normal file
|
@ -0,0 +1,223 @@
|
|||
/*
|
||||
* MVEBU Core divider clock
|
||||
*
|
||||
* Copyright (C) 2013 Marvell
|
||||
*
|
||||
* Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include "common.h"
|
||||
|
||||
#define CORE_CLK_DIV_RATIO_MASK 0xff
|
||||
#define CORE_CLK_DIV_RATIO_RELOAD BIT(8)
|
||||
#define CORE_CLK_DIV_ENABLE_OFFSET 24
|
||||
#define CORE_CLK_DIV_RATIO_OFFSET 0x8
|
||||
|
||||
struct clk_corediv_desc {
|
||||
unsigned int mask;
|
||||
unsigned int offset;
|
||||
unsigned int fieldbit;
|
||||
};
|
||||
|
||||
struct clk_corediv {
|
||||
struct clk_hw hw;
|
||||
void __iomem *reg;
|
||||
struct clk_corediv_desc desc;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
static struct clk_onecell_data clk_data;
|
||||
|
||||
static const struct clk_corediv_desc mvebu_corediv_desc[] __initconst = {
|
||||
{ .mask = 0x3f, .offset = 8, .fieldbit = 1 }, /* NAND clock */
|
||||
};
|
||||
|
||||
#define to_corediv_clk(p) container_of(p, struct clk_corediv, hw)
|
||||
|
||||
static int clk_corediv_is_enabled(struct clk_hw *hwclk)
|
||||
{
|
||||
struct clk_corediv *corediv = to_corediv_clk(hwclk);
|
||||
struct clk_corediv_desc *desc = &corediv->desc;
|
||||
u32 enable_mask = BIT(desc->fieldbit) << CORE_CLK_DIV_ENABLE_OFFSET;
|
||||
|
||||
return !!(readl(corediv->reg) & enable_mask);
|
||||
}
|
||||
|
||||
static int clk_corediv_enable(struct clk_hw *hwclk)
|
||||
{
|
||||
struct clk_corediv *corediv = to_corediv_clk(hwclk);
|
||||
struct clk_corediv_desc *desc = &corediv->desc;
|
||||
unsigned long flags = 0;
|
||||
u32 reg;
|
||||
|
||||
spin_lock_irqsave(&corediv->lock, flags);
|
||||
|
||||
reg = readl(corediv->reg);
|
||||
reg |= (BIT(desc->fieldbit) << CORE_CLK_DIV_ENABLE_OFFSET);
|
||||
writel(reg, corediv->reg);
|
||||
|
||||
spin_unlock_irqrestore(&corediv->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void clk_corediv_disable(struct clk_hw *hwclk)
|
||||
{
|
||||
struct clk_corediv *corediv = to_corediv_clk(hwclk);
|
||||
struct clk_corediv_desc *desc = &corediv->desc;
|
||||
unsigned long flags = 0;
|
||||
u32 reg;
|
||||
|
||||
spin_lock_irqsave(&corediv->lock, flags);
|
||||
|
||||
reg = readl(corediv->reg);
|
||||
reg &= ~(BIT(desc->fieldbit) << CORE_CLK_DIV_ENABLE_OFFSET);
|
||||
writel(reg, corediv->reg);
|
||||
|
||||
spin_unlock_irqrestore(&corediv->lock, flags);
|
||||
}
|
||||
|
||||
static unsigned long clk_corediv_recalc_rate(struct clk_hw *hwclk,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_corediv *corediv = to_corediv_clk(hwclk);
|
||||
struct clk_corediv_desc *desc = &corediv->desc;
|
||||
u32 reg, div;
|
||||
|
||||
reg = readl(corediv->reg + CORE_CLK_DIV_RATIO_OFFSET);
|
||||
div = (reg >> desc->offset) & desc->mask;
|
||||
return parent_rate / div;
|
||||
}
|
||||
|
||||
static long clk_corediv_round_rate(struct clk_hw *hwclk, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
/* Valid ratio are 1:4, 1:5, 1:6 and 1:8 */
|
||||
u32 div;
|
||||
|
||||
div = *parent_rate / rate;
|
||||
if (div < 4)
|
||||
div = 4;
|
||||
else if (div > 6)
|
||||
div = 8;
|
||||
|
||||
return *parent_rate / div;
|
||||
}
|
||||
|
||||
static int clk_corediv_set_rate(struct clk_hw *hwclk, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_corediv *corediv = to_corediv_clk(hwclk);
|
||||
struct clk_corediv_desc *desc = &corediv->desc;
|
||||
unsigned long flags = 0;
|
||||
u32 reg, div;
|
||||
|
||||
div = parent_rate / rate;
|
||||
|
||||
spin_lock_irqsave(&corediv->lock, flags);
|
||||
|
||||
/* Write new divider to the divider ratio register */
|
||||
reg = readl(corediv->reg + CORE_CLK_DIV_RATIO_OFFSET);
|
||||
reg &= ~(desc->mask << desc->offset);
|
||||
reg |= (div & desc->mask) << desc->offset;
|
||||
writel(reg, corediv->reg + CORE_CLK_DIV_RATIO_OFFSET);
|
||||
|
||||
/* Set reload-force for this clock */
|
||||
reg = readl(corediv->reg) | BIT(desc->fieldbit);
|
||||
writel(reg, corediv->reg);
|
||||
|
||||
/* Now trigger the clock update */
|
||||
reg = readl(corediv->reg) | CORE_CLK_DIV_RATIO_RELOAD;
|
||||
writel(reg, corediv->reg);
|
||||
|
||||
/*
|
||||
* Wait for clocks to settle down, and then clear all the
|
||||
* ratios request and the reload request.
|
||||
*/
|
||||
udelay(1000);
|
||||
reg &= ~(CORE_CLK_DIV_RATIO_MASK | CORE_CLK_DIV_RATIO_RELOAD);
|
||||
writel(reg, corediv->reg);
|
||||
udelay(1000);
|
||||
|
||||
spin_unlock_irqrestore(&corediv->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops corediv_ops = {
|
||||
.enable = clk_corediv_enable,
|
||||
.disable = clk_corediv_disable,
|
||||
.is_enabled = clk_corediv_is_enabled,
|
||||
.recalc_rate = clk_corediv_recalc_rate,
|
||||
.round_rate = clk_corediv_round_rate,
|
||||
.set_rate = clk_corediv_set_rate,
|
||||
};
|
||||
|
||||
static void __init mvebu_corediv_clk_init(struct device_node *node)
|
||||
{
|
||||
struct clk_init_data init;
|
||||
struct clk_corediv *corediv;
|
||||
struct clk **clks;
|
||||
void __iomem *base;
|
||||
const char *parent_name;
|
||||
const char *clk_name;
|
||||
int i;
|
||||
|
||||
base = of_iomap(node, 0);
|
||||
if (WARN_ON(!base))
|
||||
return;
|
||||
|
||||
parent_name = of_clk_get_parent_name(node, 0);
|
||||
|
||||
clk_data.clk_num = ARRAY_SIZE(mvebu_corediv_desc);
|
||||
|
||||
/* clks holds the clock array */
|
||||
clks = kcalloc(clk_data.clk_num, sizeof(struct clk *),
|
||||
GFP_KERNEL);
|
||||
if (WARN_ON(!clks))
|
||||
goto err_unmap;
|
||||
/* corediv holds the clock specific array */
|
||||
corediv = kcalloc(clk_data.clk_num, sizeof(struct clk_corediv),
|
||||
GFP_KERNEL);
|
||||
if (WARN_ON(!corediv))
|
||||
goto err_free_clks;
|
||||
|
||||
spin_lock_init(&corediv->lock);
|
||||
|
||||
for (i = 0; i < clk_data.clk_num; i++) {
|
||||
of_property_read_string_index(node, "clock-output-names",
|
||||
i, &clk_name);
|
||||
init.num_parents = 1;
|
||||
init.parent_names = &parent_name;
|
||||
init.name = clk_name;
|
||||
init.ops = &corediv_ops;
|
||||
init.flags = 0;
|
||||
|
||||
corediv[i].desc = mvebu_corediv_desc[i];
|
||||
corediv[i].reg = base;
|
||||
corediv[i].hw.init = &init;
|
||||
|
||||
clks[i] = clk_register(NULL, &corediv[i].hw);
|
||||
WARN_ON(IS_ERR(clks[i]));
|
||||
}
|
||||
|
||||
clk_data.clks = clks;
|
||||
of_clk_add_provider(node, of_clk_src_onecell_get, &clk_data);
|
||||
return;
|
||||
|
||||
err_free_clks:
|
||||
kfree(clks);
|
||||
err_unmap:
|
||||
iounmap(base);
|
||||
}
|
||||
CLK_OF_DECLARE(mvebu_corediv_clk, "marvell,armada-370-corediv-clock",
|
||||
mvebu_corediv_clk_init);
|
|
@ -101,7 +101,7 @@ static const struct clk_ops cpu_ops = {
|
|||
.set_rate = clk_cpu_set_rate,
|
||||
};
|
||||
|
||||
void __init of_cpu_clk_setup(struct device_node *node)
|
||||
static void __init of_cpu_clk_setup(struct device_node *node)
|
||||
{
|
||||
struct cpu_clk *cpuclk;
|
||||
void __iomem *clock_complex_base = of_iomap(node, 0);
|
||||
|
|
47
drivers/clk/qcom/Kconfig
Normal file
47
drivers/clk/qcom/Kconfig
Normal file
|
@ -0,0 +1,47 @@
|
|||
config COMMON_CLK_QCOM
|
||||
tristate "Support for Qualcomm's clock controllers"
|
||||
depends on OF
|
||||
select REGMAP_MMIO
|
||||
select RESET_CONTROLLER
|
||||
|
||||
config MSM_GCC_8660
|
||||
tristate "MSM8660 Global Clock Controller"
|
||||
depends on COMMON_CLK_QCOM
|
||||
help
|
||||
Support for the global clock controller on msm8660 devices.
|
||||
Say Y if you want to use peripheral devices such as UART, SPI,
|
||||
i2c, USB, SD/eMMC, etc.
|
||||
|
||||
config MSM_GCC_8960
|
||||
tristate "MSM8960 Global Clock Controller"
|
||||
depends on COMMON_CLK_QCOM
|
||||
help
|
||||
Support for the global clock controller on msm8960 devices.
|
||||
Say Y if you want to use peripheral devices such as UART, SPI,
|
||||
i2c, USB, SD/eMMC, SATA, PCIe, etc.
|
||||
|
||||
config MSM_MMCC_8960
|
||||
tristate "MSM8960 Multimedia Clock Controller"
|
||||
select MSM_GCC_8960
|
||||
depends on COMMON_CLK_QCOM
|
||||
help
|
||||
Support for the multimedia clock controller on msm8960 devices.
|
||||
Say Y if you want to support multimedia devices such as display,
|
||||
graphics, video encode/decode, camera, etc.
|
||||
|
||||
config MSM_GCC_8974
|
||||
tristate "MSM8974 Global Clock Controller"
|
||||
depends on COMMON_CLK_QCOM
|
||||
help
|
||||
Support for the global clock controller on msm8974 devices.
|
||||
Say Y if you want to use peripheral devices such as UART, SPI,
|
||||
i2c, USB, SD/eMMC, SATA, PCIe, etc.
|
||||
|
||||
config MSM_MMCC_8974
|
||||
tristate "MSM8974 Multimedia Clock Controller"
|
||||
select MSM_GCC_8974
|
||||
depends on COMMON_CLK_QCOM
|
||||
help
|
||||
Support for the multimedia clock controller on msm8974 devices.
|
||||
Say Y if you want to support multimedia devices such as display,
|
||||
graphics, video encode/decode, camera, etc.
|
14
drivers/clk/qcom/Makefile
Normal file
14
drivers/clk/qcom/Makefile
Normal file
|
@ -0,0 +1,14 @@
|
|||
obj-$(CONFIG_COMMON_CLK_QCOM) += clk-qcom.o
|
||||
|
||||
clk-qcom-$(CONFIG_COMMON_CLK_QCOM) += clk-regmap.o
|
||||
clk-qcom-$(CONFIG_COMMON_CLK_QCOM) += clk-pll.o
|
||||
clk-qcom-$(CONFIG_COMMON_CLK_QCOM) += clk-rcg.o
|
||||
clk-qcom-$(CONFIG_COMMON_CLK_QCOM) += clk-rcg2.o
|
||||
clk-qcom-$(CONFIG_COMMON_CLK_QCOM) += clk-branch.o
|
||||
clk-qcom-$(CONFIG_COMMON_CLK_QCOM) += reset.o
|
||||
|
||||
obj-$(CONFIG_MSM_GCC_8660) += gcc-msm8660.o
|
||||
obj-$(CONFIG_MSM_GCC_8960) += gcc-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
|
159
drivers/clk/qcom/clk-branch.c
Normal file
159
drivers/clk/qcom/clk-branch.c
Normal file
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* Copyright (c) 2013, 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/delay.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include "clk-branch.h"
|
||||
|
||||
static bool clk_branch_in_hwcg_mode(const struct clk_branch *br)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
if (!br->hwcg_reg)
|
||||
return 0;
|
||||
|
||||
regmap_read(br->clkr.regmap, br->hwcg_reg, &val);
|
||||
|
||||
return !!(val & BIT(br->hwcg_bit));
|
||||
}
|
||||
|
||||
static bool clk_branch_check_halt(const struct clk_branch *br, bool enabling)
|
||||
{
|
||||
bool invert = (br->halt_check == BRANCH_HALT_ENABLE);
|
||||
u32 val;
|
||||
|
||||
regmap_read(br->clkr.regmap, br->halt_reg, &val);
|
||||
|
||||
val &= BIT(br->halt_bit);
|
||||
if (invert)
|
||||
val = !val;
|
||||
|
||||
return !!val == !enabling;
|
||||
}
|
||||
|
||||
#define BRANCH_CLK_OFF BIT(31)
|
||||
#define BRANCH_NOC_FSM_STATUS_SHIFT 28
|
||||
#define BRANCH_NOC_FSM_STATUS_MASK 0x7
|
||||
#define BRANCH_NOC_FSM_STATUS_ON (0x2 << BRANCH_NOC_FSM_STATUS_SHIFT)
|
||||
|
||||
static bool clk_branch2_check_halt(const struct clk_branch *br, bool enabling)
|
||||
{
|
||||
u32 val;
|
||||
u32 mask;
|
||||
|
||||
mask = BRANCH_NOC_FSM_STATUS_MASK << BRANCH_NOC_FSM_STATUS_SHIFT;
|
||||
mask |= BRANCH_CLK_OFF;
|
||||
|
||||
regmap_read(br->clkr.regmap, br->halt_reg, &val);
|
||||
|
||||
if (enabling) {
|
||||
val &= mask;
|
||||
return (val & BRANCH_CLK_OFF) == 0 ||
|
||||
val == BRANCH_NOC_FSM_STATUS_ON;
|
||||
} else {
|
||||
return val & BRANCH_CLK_OFF;
|
||||
}
|
||||
}
|
||||
|
||||
static int clk_branch_wait(const struct clk_branch *br, bool enabling,
|
||||
bool (check_halt)(const struct clk_branch *, bool))
|
||||
{
|
||||
bool voted = br->halt_check & BRANCH_VOTED;
|
||||
const char *name = __clk_get_name(br->clkr.hw.clk);
|
||||
|
||||
/* Skip checking halt bit if the clock is in hardware gated mode */
|
||||
if (clk_branch_in_hwcg_mode(br))
|
||||
return 0;
|
||||
|
||||
if (br->halt_check == BRANCH_HALT_DELAY || (!enabling && voted)) {
|
||||
udelay(10);
|
||||
} else if (br->halt_check == BRANCH_HALT_ENABLE ||
|
||||
br->halt_check == BRANCH_HALT ||
|
||||
(enabling && voted)) {
|
||||
int count = 200;
|
||||
|
||||
while (count-- > 0) {
|
||||
if (check_halt(br, enabling))
|
||||
return 0;
|
||||
udelay(1);
|
||||
}
|
||||
WARN(1, "%s status stuck at 'o%s'", name,
|
||||
enabling ? "ff" : "n");
|
||||
return -EBUSY;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int clk_branch_toggle(struct clk_hw *hw, bool en,
|
||||
bool (check_halt)(const struct clk_branch *, bool))
|
||||
{
|
||||
struct clk_branch *br = to_clk_branch(hw);
|
||||
int ret;
|
||||
|
||||
if (en) {
|
||||
ret = clk_enable_regmap(hw);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
clk_disable_regmap(hw);
|
||||
}
|
||||
|
||||
return clk_branch_wait(br, en, check_halt);
|
||||
}
|
||||
|
||||
static int clk_branch_enable(struct clk_hw *hw)
|
||||
{
|
||||
return clk_branch_toggle(hw, true, clk_branch_check_halt);
|
||||
}
|
||||
|
||||
static void clk_branch_disable(struct clk_hw *hw)
|
||||
{
|
||||
clk_branch_toggle(hw, false, clk_branch_check_halt);
|
||||
}
|
||||
|
||||
const struct clk_ops clk_branch_ops = {
|
||||
.enable = clk_branch_enable,
|
||||
.disable = clk_branch_disable,
|
||||
.is_enabled = clk_is_enabled_regmap,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(clk_branch_ops);
|
||||
|
||||
static int clk_branch2_enable(struct clk_hw *hw)
|
||||
{
|
||||
return clk_branch_toggle(hw, true, clk_branch2_check_halt);
|
||||
}
|
||||
|
||||
static void clk_branch2_disable(struct clk_hw *hw)
|
||||
{
|
||||
clk_branch_toggle(hw, false, clk_branch2_check_halt);
|
||||
}
|
||||
|
||||
const struct clk_ops clk_branch2_ops = {
|
||||
.enable = clk_branch2_enable,
|
||||
.disable = clk_branch2_disable,
|
||||
.is_enabled = clk_is_enabled_regmap,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(clk_branch2_ops);
|
||||
|
||||
const struct clk_ops clk_branch_simple_ops = {
|
||||
.enable = clk_enable_regmap,
|
||||
.disable = clk_disable_regmap,
|
||||
.is_enabled = clk_is_enabled_regmap,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(clk_branch_simple_ops);
|
56
drivers/clk/qcom/clk-branch.h
Normal file
56
drivers/clk/qcom/clk-branch.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright (c) 2013, 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_BRANCH_H__
|
||||
#define __QCOM_CLK_BRANCH_H__
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
|
||||
#include "clk-regmap.h"
|
||||
|
||||
/**
|
||||
* struct clk_branch - gating clock with status bit and dynamic hardware gating
|
||||
*
|
||||
* @hwcg_reg: dynamic hardware clock gating register
|
||||
* @hwcg_bit: ORed with @hwcg_reg to enable dynamic hardware clock gating
|
||||
* @halt_reg: halt register
|
||||
* @halt_bit: ANDed with @halt_reg to test for clock halted
|
||||
* @halt_check: type of halt checking to perform
|
||||
* @clkr: handle between common and hardware-specific interfaces
|
||||
*
|
||||
* Clock which can gate its output.
|
||||
*/
|
||||
struct clk_branch {
|
||||
u32 hwcg_reg;
|
||||
u32 halt_reg;
|
||||
u8 hwcg_bit;
|
||||
u8 halt_bit;
|
||||
u8 halt_check;
|
||||
#define BRANCH_VOTED BIT(7) /* Delay on disable */
|
||||
#define BRANCH_HALT 0 /* pol: 1 = halt */
|
||||
#define BRANCH_HALT_VOTED (BRANCH_HALT | BRANCH_VOTED)
|
||||
#define BRANCH_HALT_ENABLE 1 /* pol: 0 = halt */
|
||||
#define BRANCH_HALT_ENABLE_VOTED (BRANCH_HALT_ENABLE | BRANCH_VOTED)
|
||||
#define BRANCH_HALT_DELAY 2 /* No bit to check; just delay */
|
||||
|
||||
struct clk_regmap clkr;
|
||||
};
|
||||
|
||||
extern const struct clk_ops clk_branch_ops;
|
||||
extern const struct clk_ops clk_branch2_ops;
|
||||
extern const struct clk_ops clk_branch_simple_ops;
|
||||
|
||||
#define to_clk_branch(_hw) \
|
||||
container_of(to_clk_regmap(_hw), struct clk_branch, clkr)
|
||||
|
||||
#endif
|
222
drivers/clk/qcom/clk-pll.c
Normal file
222
drivers/clk/qcom/clk-pll.c
Normal file
|
@ -0,0 +1,222 @@
|
|||
/*
|
||||
* Copyright (c) 2013, 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/bug.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <asm/div64.h>
|
||||
|
||||
#include "clk-pll.h"
|
||||
|
||||
#define PLL_OUTCTRL BIT(0)
|
||||
#define PLL_BYPASSNL BIT(1)
|
||||
#define PLL_RESET_N BIT(2)
|
||||
#define PLL_LOCK_COUNT_SHIFT 8
|
||||
#define PLL_LOCK_COUNT_MASK 0x3f
|
||||
#define PLL_BIAS_COUNT_SHIFT 14
|
||||
#define PLL_BIAS_COUNT_MASK 0x3f
|
||||
#define PLL_VOTE_FSM_ENA BIT(20)
|
||||
#define PLL_VOTE_FSM_RESET BIT(21)
|
||||
|
||||
static int clk_pll_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_pll *pll = to_clk_pll(hw);
|
||||
int ret;
|
||||
u32 mask, val;
|
||||
|
||||
mask = PLL_OUTCTRL | PLL_RESET_N | PLL_BYPASSNL;
|
||||
ret = regmap_read(pll->clkr.regmap, pll->mode_reg, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Skip if already enabled or in FSM mode */
|
||||
if ((val & mask) == mask || val & PLL_VOTE_FSM_ENA)
|
||||
return 0;
|
||||
|
||||
/* Disable PLL bypass mode. */
|
||||
ret = regmap_update_bits(pll->clkr.regmap, pll->mode_reg, PLL_BYPASSNL,
|
||||
PLL_BYPASSNL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* H/W requires a 5us delay between disabling the bypass and
|
||||
* de-asserting the reset. Delay 10us just to be safe.
|
||||
*/
|
||||
udelay(10);
|
||||
|
||||
/* De-assert active-low PLL reset. */
|
||||
ret = regmap_update_bits(pll->clkr.regmap, pll->mode_reg, PLL_RESET_N,
|
||||
PLL_RESET_N);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Wait until PLL is locked. */
|
||||
udelay(50);
|
||||
|
||||
/* Enable PLL output. */
|
||||
ret = regmap_update_bits(pll->clkr.regmap, pll->mode_reg, PLL_OUTCTRL,
|
||||
PLL_OUTCTRL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void clk_pll_disable(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_pll *pll = to_clk_pll(hw);
|
||||
u32 mask;
|
||||
u32 val;
|
||||
|
||||
regmap_read(pll->clkr.regmap, pll->mode_reg, &val);
|
||||
/* Skip if in FSM mode */
|
||||
if (val & PLL_VOTE_FSM_ENA)
|
||||
return;
|
||||
mask = PLL_OUTCTRL | PLL_RESET_N | PLL_BYPASSNL;
|
||||
regmap_update_bits(pll->clkr.regmap, pll->mode_reg, mask, 0);
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
clk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
|
||||
{
|
||||
struct clk_pll *pll = to_clk_pll(hw);
|
||||
u32 l, m, n;
|
||||
unsigned long rate;
|
||||
u64 tmp;
|
||||
|
||||
regmap_read(pll->clkr.regmap, pll->l_reg, &l);
|
||||
regmap_read(pll->clkr.regmap, pll->m_reg, &m);
|
||||
regmap_read(pll->clkr.regmap, pll->n_reg, &n);
|
||||
|
||||
l &= 0x3ff;
|
||||
m &= 0x7ffff;
|
||||
n &= 0x7ffff;
|
||||
|
||||
rate = parent_rate * l;
|
||||
if (n) {
|
||||
tmp = parent_rate;
|
||||
tmp *= m;
|
||||
do_div(tmp, n);
|
||||
rate += tmp;
|
||||
}
|
||||
return rate;
|
||||
}
|
||||
|
||||
const struct clk_ops clk_pll_ops = {
|
||||
.enable = clk_pll_enable,
|
||||
.disable = clk_pll_disable,
|
||||
.recalc_rate = clk_pll_recalc_rate,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(clk_pll_ops);
|
||||
|
||||
static int wait_for_pll(struct clk_pll *pll)
|
||||
{
|
||||
u32 val;
|
||||
int count;
|
||||
int ret;
|
||||
const char *name = __clk_get_name(pll->clkr.hw.clk);
|
||||
|
||||
/* Wait for pll to enable. */
|
||||
for (count = 200; count > 0; count--) {
|
||||
ret = regmap_read(pll->clkr.regmap, pll->status_reg, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (val & BIT(pll->status_bit))
|
||||
return 0;
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
WARN(1, "%s didn't enable after voting for it!\n", name);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int clk_pll_vote_enable(struct clk_hw *hw)
|
||||
{
|
||||
int ret;
|
||||
struct clk_pll *p = to_clk_pll(__clk_get_hw(__clk_get_parent(hw->clk)));
|
||||
|
||||
ret = clk_enable_regmap(hw);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return wait_for_pll(p);
|
||||
}
|
||||
|
||||
const struct clk_ops clk_pll_vote_ops = {
|
||||
.enable = clk_pll_vote_enable,
|
||||
.disable = clk_disable_regmap,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(clk_pll_vote_ops);
|
||||
|
||||
static void
|
||||
clk_pll_set_fsm_mode(struct clk_pll *pll, struct regmap *regmap)
|
||||
{
|
||||
u32 val;
|
||||
u32 mask;
|
||||
|
||||
/* De-assert reset to FSM */
|
||||
regmap_update_bits(regmap, pll->mode_reg, PLL_VOTE_FSM_RESET, 0);
|
||||
|
||||
/* Program bias count and lock count */
|
||||
val = 1 << PLL_BIAS_COUNT_SHIFT;
|
||||
mask = PLL_BIAS_COUNT_MASK << PLL_BIAS_COUNT_SHIFT;
|
||||
mask |= PLL_LOCK_COUNT_MASK << PLL_LOCK_COUNT_SHIFT;
|
||||
regmap_update_bits(regmap, pll->mode_reg, mask, val);
|
||||
|
||||
/* Enable PLL FSM voting */
|
||||
regmap_update_bits(regmap, pll->mode_reg, PLL_VOTE_FSM_ENA,
|
||||
PLL_VOTE_FSM_ENA);
|
||||
}
|
||||
|
||||
static void clk_pll_configure(struct clk_pll *pll, struct regmap *regmap,
|
||||
const struct pll_config *config)
|
||||
{
|
||||
u32 val;
|
||||
u32 mask;
|
||||
|
||||
regmap_write(regmap, pll->l_reg, config->l);
|
||||
regmap_write(regmap, pll->m_reg, config->m);
|
||||
regmap_write(regmap, pll->n_reg, config->n);
|
||||
|
||||
val = config->vco_val;
|
||||
val |= config->pre_div_val;
|
||||
val |= config->post_div_val;
|
||||
val |= config->mn_ena_mask;
|
||||
val |= config->main_output_mask;
|
||||
val |= config->aux_output_mask;
|
||||
|
||||
mask = config->vco_mask;
|
||||
mask |= config->pre_div_mask;
|
||||
mask |= config->post_div_mask;
|
||||
mask |= config->mn_ena_mask;
|
||||
mask |= config->main_output_mask;
|
||||
mask |= config->aux_output_mask;
|
||||
|
||||
regmap_update_bits(regmap, pll->config_reg, mask, val);
|
||||
}
|
||||
|
||||
void clk_pll_configure_sr_hpm_lp(struct clk_pll *pll, struct regmap *regmap,
|
||||
const struct pll_config *config, bool fsm_mode)
|
||||
{
|
||||
clk_pll_configure(pll, regmap, config);
|
||||
if (fsm_mode)
|
||||
clk_pll_set_fsm_mode(pll, regmap);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(clk_pll_configure_sr_hpm_lp);
|
66
drivers/clk/qcom/clk-pll.h
Normal file
66
drivers/clk/qcom/clk-pll.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright (c) 2013, 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_PLL_H__
|
||||
#define __QCOM_CLK_PLL_H__
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include "clk-regmap.h"
|
||||
|
||||
/**
|
||||
* struct clk_pll - phase locked loop (PLL)
|
||||
* @l_reg: L register
|
||||
* @m_reg: M register
|
||||
* @n_reg: N register
|
||||
* @config_reg: config register
|
||||
* @mode_reg: mode register
|
||||
* @status_reg: status register
|
||||
* @status_bit: ANDed with @status_reg to determine if PLL is enabled
|
||||
* @hw: handle between common and hardware-specific interfaces
|
||||
*/
|
||||
struct clk_pll {
|
||||
u32 l_reg;
|
||||
u32 m_reg;
|
||||
u32 n_reg;
|
||||
u32 config_reg;
|
||||
u32 mode_reg;
|
||||
u32 status_reg;
|
||||
u8 status_bit;
|
||||
|
||||
struct clk_regmap clkr;
|
||||
};
|
||||
|
||||
extern const struct clk_ops clk_pll_ops;
|
||||
extern const struct clk_ops clk_pll_vote_ops;
|
||||
|
||||
#define to_clk_pll(_hw) container_of(to_clk_regmap(_hw), struct clk_pll, clkr)
|
||||
|
||||
struct pll_config {
|
||||
u16 l;
|
||||
u32 m;
|
||||
u32 n;
|
||||
u32 vco_val;
|
||||
u32 vco_mask;
|
||||
u32 pre_div_val;
|
||||
u32 pre_div_mask;
|
||||
u32 post_div_val;
|
||||
u32 post_div_mask;
|
||||
u32 mn_ena_mask;
|
||||
u32 main_output_mask;
|
||||
u32 aux_output_mask;
|
||||
};
|
||||
|
||||
void clk_pll_configure_sr_hpm_lp(struct clk_pll *pll, struct regmap *regmap,
|
||||
const struct pll_config *config, bool fsm_mode);
|
||||
|
||||
#endif
|
517
drivers/clk/qcom/clk-rcg.c
Normal file
517
drivers/clk/qcom/clk-rcg.c
Normal file
|
@ -0,0 +1,517 @@
|
|||
/*
|
||||
* Copyright (c) 2013, 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/export.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <asm/div64.h>
|
||||
|
||||
#include "clk-rcg.h"
|
||||
|
||||
static u32 ns_to_src(struct src_sel *s, u32 ns)
|
||||
{
|
||||
ns >>= s->src_sel_shift;
|
||||
ns &= SRC_SEL_MASK;
|
||||
return ns;
|
||||
}
|
||||
|
||||
static u32 src_to_ns(struct src_sel *s, u8 src, u32 ns)
|
||||
{
|
||||
u32 mask;
|
||||
|
||||
mask = SRC_SEL_MASK;
|
||||
mask <<= s->src_sel_shift;
|
||||
ns &= ~mask;
|
||||
|
||||
ns |= src << s->src_sel_shift;
|
||||
return ns;
|
||||
}
|
||||
|
||||
static u8 clk_rcg_get_parent(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_rcg *rcg = to_clk_rcg(hw);
|
||||
int num_parents = __clk_get_num_parents(hw->clk);
|
||||
u32 ns;
|
||||
int i;
|
||||
|
||||
regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns);
|
||||
ns = ns_to_src(&rcg->s, ns);
|
||||
for (i = 0; i < num_parents; i++)
|
||||
if (ns == rcg->s.parent_map[i])
|
||||
return i;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int reg_to_bank(struct clk_dyn_rcg *rcg, u32 bank)
|
||||
{
|
||||
bank &= BIT(rcg->mux_sel_bit);
|
||||
return !!bank;
|
||||
}
|
||||
|
||||
static u8 clk_dyn_rcg_get_parent(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw);
|
||||
int num_parents = __clk_get_num_parents(hw->clk);
|
||||
u32 ns, ctl;
|
||||
int bank;
|
||||
int i;
|
||||
struct src_sel *s;
|
||||
|
||||
regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, &ctl);
|
||||
bank = reg_to_bank(rcg, ctl);
|
||||
s = &rcg->s[bank];
|
||||
|
||||
regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns);
|
||||
ns = ns_to_src(s, ns);
|
||||
|
||||
for (i = 0; i < num_parents; i++)
|
||||
if (ns == s->parent_map[i])
|
||||
return i;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int clk_rcg_set_parent(struct clk_hw *hw, u8 index)
|
||||
{
|
||||
struct clk_rcg *rcg = to_clk_rcg(hw);
|
||||
u32 ns;
|
||||
|
||||
regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns);
|
||||
ns = src_to_ns(&rcg->s, rcg->s.parent_map[index], ns);
|
||||
regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 md_to_m(struct mn *mn, u32 md)
|
||||
{
|
||||
md >>= mn->m_val_shift;
|
||||
md &= BIT(mn->width) - 1;
|
||||
return md;
|
||||
}
|
||||
|
||||
static u32 ns_to_pre_div(struct pre_div *p, u32 ns)
|
||||
{
|
||||
ns >>= p->pre_div_shift;
|
||||
ns &= BIT(p->pre_div_width) - 1;
|
||||
return ns;
|
||||
}
|
||||
|
||||
static u32 pre_div_to_ns(struct pre_div *p, u8 pre_div, u32 ns)
|
||||
{
|
||||
u32 mask;
|
||||
|
||||
mask = BIT(p->pre_div_width) - 1;
|
||||
mask <<= p->pre_div_shift;
|
||||
ns &= ~mask;
|
||||
|
||||
ns |= pre_div << p->pre_div_shift;
|
||||
return ns;
|
||||
}
|
||||
|
||||
static u32 mn_to_md(struct mn *mn, u32 m, u32 n, u32 md)
|
||||
{
|
||||
u32 mask, mask_w;
|
||||
|
||||
mask_w = BIT(mn->width) - 1;
|
||||
mask = (mask_w << mn->m_val_shift) | mask_w;
|
||||
md &= ~mask;
|
||||
|
||||
if (n) {
|
||||
m <<= mn->m_val_shift;
|
||||
md |= m;
|
||||
md |= ~n & mask_w;
|
||||
}
|
||||
|
||||
return md;
|
||||
}
|
||||
|
||||
static u32 ns_m_to_n(struct mn *mn, u32 ns, u32 m)
|
||||
{
|
||||
ns = ~ns >> mn->n_val_shift;
|
||||
ns &= BIT(mn->width) - 1;
|
||||
return ns + m;
|
||||
}
|
||||
|
||||
static u32 reg_to_mnctr_mode(struct mn *mn, u32 val)
|
||||
{
|
||||
val >>= mn->mnctr_mode_shift;
|
||||
val &= MNCTR_MODE_MASK;
|
||||
return val;
|
||||
}
|
||||
|
||||
static u32 mn_to_ns(struct mn *mn, u32 m, u32 n, u32 ns)
|
||||
{
|
||||
u32 mask;
|
||||
|
||||
mask = BIT(mn->width) - 1;
|
||||
mask <<= mn->n_val_shift;
|
||||
ns &= ~mask;
|
||||
|
||||
if (n) {
|
||||
n = n - m;
|
||||
n = ~n;
|
||||
n &= BIT(mn->width) - 1;
|
||||
n <<= mn->n_val_shift;
|
||||
ns |= n;
|
||||
}
|
||||
|
||||
return ns;
|
||||
}
|
||||
|
||||
static u32 mn_to_reg(struct mn *mn, u32 m, u32 n, u32 val)
|
||||
{
|
||||
u32 mask;
|
||||
|
||||
mask = MNCTR_MODE_MASK << mn->mnctr_mode_shift;
|
||||
mask |= BIT(mn->mnctr_en_bit);
|
||||
val &= ~mask;
|
||||
|
||||
if (n) {
|
||||
val |= BIT(mn->mnctr_en_bit);
|
||||
val |= MNCTR_MODE_DUAL << mn->mnctr_mode_shift;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static void configure_bank(struct clk_dyn_rcg *rcg, const struct freq_tbl *f)
|
||||
{
|
||||
u32 ns, md, ctl, *regp;
|
||||
int bank, new_bank;
|
||||
struct mn *mn;
|
||||
struct pre_div *p;
|
||||
struct src_sel *s;
|
||||
bool enabled;
|
||||
u32 md_reg;
|
||||
u32 bank_reg;
|
||||
bool banked_mn = !!rcg->mn[1].width;
|
||||
struct clk_hw *hw = &rcg->clkr.hw;
|
||||
|
||||
enabled = __clk_is_enabled(hw->clk);
|
||||
|
||||
regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns);
|
||||
regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, &ctl);
|
||||
|
||||
if (banked_mn) {
|
||||
regp = &ctl;
|
||||
bank_reg = rcg->clkr.enable_reg;
|
||||
} else {
|
||||
regp = &ns;
|
||||
bank_reg = rcg->ns_reg;
|
||||
}
|
||||
|
||||
bank = reg_to_bank(rcg, *regp);
|
||||
new_bank = enabled ? !bank : bank;
|
||||
|
||||
if (banked_mn) {
|
||||
mn = &rcg->mn[new_bank];
|
||||
md_reg = rcg->md_reg[new_bank];
|
||||
|
||||
ns |= BIT(mn->mnctr_reset_bit);
|
||||
regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns);
|
||||
|
||||
regmap_read(rcg->clkr.regmap, md_reg, &md);
|
||||
md = mn_to_md(mn, f->m, f->n, md);
|
||||
regmap_write(rcg->clkr.regmap, md_reg, md);
|
||||
|
||||
ns = mn_to_ns(mn, f->m, f->n, ns);
|
||||
regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns);
|
||||
|
||||
ctl = mn_to_reg(mn, f->m, f->n, ctl);
|
||||
regmap_write(rcg->clkr.regmap, rcg->clkr.enable_reg, ctl);
|
||||
|
||||
ns &= ~BIT(mn->mnctr_reset_bit);
|
||||
regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns);
|
||||
} else {
|
||||
p = &rcg->p[new_bank];
|
||||
ns = pre_div_to_ns(p, f->pre_div - 1, ns);
|
||||
}
|
||||
|
||||
s = &rcg->s[new_bank];
|
||||
ns = src_to_ns(s, s->parent_map[f->src], ns);
|
||||
regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns);
|
||||
|
||||
if (enabled) {
|
||||
*regp ^= BIT(rcg->mux_sel_bit);
|
||||
regmap_write(rcg->clkr.regmap, bank_reg, *regp);
|
||||
}
|
||||
}
|
||||
|
||||
static int clk_dyn_rcg_set_parent(struct clk_hw *hw, u8 index)
|
||||
{
|
||||
struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw);
|
||||
u32 ns, ctl, md, reg;
|
||||
int bank;
|
||||
struct freq_tbl f = { 0 };
|
||||
bool banked_mn = !!rcg->mn[1].width;
|
||||
|
||||
regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns);
|
||||
regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, &ctl);
|
||||
reg = banked_mn ? ctl : ns;
|
||||
|
||||
bank = reg_to_bank(rcg, reg);
|
||||
|
||||
if (banked_mn) {
|
||||
regmap_read(rcg->clkr.regmap, rcg->md_reg[bank], &md);
|
||||
f.m = md_to_m(&rcg->mn[bank], md);
|
||||
f.n = ns_m_to_n(&rcg->mn[bank], ns, f.m);
|
||||
} else {
|
||||
f.pre_div = ns_to_pre_div(&rcg->p[bank], ns) + 1;
|
||||
}
|
||||
f.src = index;
|
||||
|
||||
configure_bank(rcg, &f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate m/n:d rate
|
||||
*
|
||||
* parent_rate m
|
||||
* rate = ----------- x ---
|
||||
* pre_div n
|
||||
*/
|
||||
static unsigned long
|
||||
calc_rate(unsigned long rate, u32 m, u32 n, u32 mode, u32 pre_div)
|
||||
{
|
||||
if (pre_div)
|
||||
rate /= pre_div + 1;
|
||||
|
||||
if (mode) {
|
||||
u64 tmp = rate;
|
||||
tmp *= m;
|
||||
do_div(tmp, n);
|
||||
rate = tmp;
|
||||
}
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
clk_rcg_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
|
||||
{
|
||||
struct clk_rcg *rcg = to_clk_rcg(hw);
|
||||
u32 pre_div, m = 0, n = 0, ns, md, mode = 0;
|
||||
struct mn *mn = &rcg->mn;
|
||||
|
||||
regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns);
|
||||
pre_div = ns_to_pre_div(&rcg->p, ns);
|
||||
|
||||
if (rcg->mn.width) {
|
||||
regmap_read(rcg->clkr.regmap, rcg->md_reg, &md);
|
||||
m = md_to_m(mn, md);
|
||||
n = ns_m_to_n(mn, ns, m);
|
||||
/* MN counter mode is in hw.enable_reg sometimes */
|
||||
if (rcg->clkr.enable_reg != rcg->ns_reg)
|
||||
regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, &mode);
|
||||
else
|
||||
mode = ns;
|
||||
mode = reg_to_mnctr_mode(mn, mode);
|
||||
}
|
||||
|
||||
return calc_rate(parent_rate, m, n, mode, pre_div);
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
clk_dyn_rcg_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
|
||||
{
|
||||
struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw);
|
||||
u32 m, n, pre_div, ns, md, mode, reg;
|
||||
int bank;
|
||||
struct mn *mn;
|
||||
bool banked_mn = !!rcg->mn[1].width;
|
||||
|
||||
regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns);
|
||||
|
||||
if (banked_mn)
|
||||
regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, ®);
|
||||
else
|
||||
reg = ns;
|
||||
|
||||
bank = reg_to_bank(rcg, reg);
|
||||
|
||||
if (banked_mn) {
|
||||
mn = &rcg->mn[bank];
|
||||
regmap_read(rcg->clkr.regmap, rcg->md_reg[bank], &md);
|
||||
m = md_to_m(mn, md);
|
||||
n = ns_m_to_n(mn, ns, m);
|
||||
mode = reg_to_mnctr_mode(mn, reg);
|
||||
return calc_rate(parent_rate, m, n, mode, 0);
|
||||
} else {
|
||||
pre_div = ns_to_pre_div(&rcg->p[bank], ns);
|
||||
return calc_rate(parent_rate, 0, 0, 0, pre_div);
|
||||
}
|
||||
}
|
||||
|
||||
static const
|
||||
struct freq_tbl *find_freq(const struct freq_tbl *f, unsigned long rate)
|
||||
{
|
||||
if (!f)
|
||||
return NULL;
|
||||
|
||||
for (; f->freq; f++)
|
||||
if (rate <= f->freq)
|
||||
return f;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static long _freq_tbl_determine_rate(struct clk_hw *hw,
|
||||
const struct freq_tbl *f, unsigned long rate,
|
||||
unsigned long *p_rate, struct clk **p)
|
||||
{
|
||||
unsigned long clk_flags;
|
||||
|
||||
f = find_freq(f, rate);
|
||||
if (!f)
|
||||
return -EINVAL;
|
||||
|
||||
clk_flags = __clk_get_flags(hw->clk);
|
||||
*p = clk_get_parent_by_index(hw->clk, f->src);
|
||||
if (clk_flags & CLK_SET_RATE_PARENT) {
|
||||
rate = rate * f->pre_div;
|
||||
if (f->n) {
|
||||
u64 tmp = rate;
|
||||
tmp = tmp * f->n;
|
||||
do_div(tmp, f->m);
|
||||
rate = tmp;
|
||||
}
|
||||
} else {
|
||||
rate = __clk_get_rate(*p);
|
||||
}
|
||||
*p_rate = rate;
|
||||
|
||||
return f->freq;
|
||||
}
|
||||
|
||||
static long clk_rcg_determine_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *p_rate, struct clk **p)
|
||||
{
|
||||
struct clk_rcg *rcg = to_clk_rcg(hw);
|
||||
|
||||
return _freq_tbl_determine_rate(hw, rcg->freq_tbl, rate, p_rate, p);
|
||||
}
|
||||
|
||||
static long clk_dyn_rcg_determine_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *p_rate, struct clk **p)
|
||||
{
|
||||
struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw);
|
||||
|
||||
return _freq_tbl_determine_rate(hw, rcg->freq_tbl, rate, p_rate, p);
|
||||
}
|
||||
|
||||
static int clk_rcg_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_rcg *rcg = to_clk_rcg(hw);
|
||||
const struct freq_tbl *f;
|
||||
u32 ns, md, ctl;
|
||||
struct mn *mn = &rcg->mn;
|
||||
u32 mask = 0;
|
||||
unsigned int reset_reg;
|
||||
|
||||
f = find_freq(rcg->freq_tbl, rate);
|
||||
if (!f)
|
||||
return -EINVAL;
|
||||
|
||||
if (rcg->mn.reset_in_cc)
|
||||
reset_reg = rcg->clkr.enable_reg;
|
||||
else
|
||||
reset_reg = rcg->ns_reg;
|
||||
|
||||
if (rcg->mn.width) {
|
||||
mask = BIT(mn->mnctr_reset_bit);
|
||||
regmap_update_bits(rcg->clkr.regmap, reset_reg, mask, mask);
|
||||
|
||||
regmap_read(rcg->clkr.regmap, rcg->md_reg, &md);
|
||||
md = mn_to_md(mn, f->m, f->n, md);
|
||||
regmap_write(rcg->clkr.regmap, rcg->md_reg, md);
|
||||
|
||||
regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns);
|
||||
/* MN counter mode is in hw.enable_reg sometimes */
|
||||
if (rcg->clkr.enable_reg != rcg->ns_reg) {
|
||||
regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, &ctl);
|
||||
ctl = mn_to_reg(mn, f->m, f->n, ctl);
|
||||
regmap_write(rcg->clkr.regmap, rcg->clkr.enable_reg, ctl);
|
||||
} else {
|
||||
ns = mn_to_reg(mn, f->m, f->n, ns);
|
||||
}
|
||||
ns = mn_to_ns(mn, f->m, f->n, ns);
|
||||
} else {
|
||||
regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns);
|
||||
}
|
||||
|
||||
ns = pre_div_to_ns(&rcg->p, f->pre_div - 1, ns);
|
||||
regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns);
|
||||
|
||||
regmap_update_bits(rcg->clkr.regmap, reset_reg, mask, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __clk_dyn_rcg_set_rate(struct clk_hw *hw, unsigned long rate)
|
||||
{
|
||||
struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw);
|
||||
const struct freq_tbl *f;
|
||||
|
||||
f = find_freq(rcg->freq_tbl, rate);
|
||||
if (!f)
|
||||
return -EINVAL;
|
||||
|
||||
configure_bank(rcg, f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int clk_dyn_rcg_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return __clk_dyn_rcg_set_rate(hw, rate);
|
||||
}
|
||||
|
||||
static int clk_dyn_rcg_set_rate_and_parent(struct clk_hw *hw,
|
||||
unsigned long rate, unsigned long parent_rate, u8 index)
|
||||
{
|
||||
return __clk_dyn_rcg_set_rate(hw, rate);
|
||||
}
|
||||
|
||||
const struct clk_ops clk_rcg_ops = {
|
||||
.enable = clk_enable_regmap,
|
||||
.disable = clk_disable_regmap,
|
||||
.get_parent = clk_rcg_get_parent,
|
||||
.set_parent = clk_rcg_set_parent,
|
||||
.recalc_rate = clk_rcg_recalc_rate,
|
||||
.determine_rate = clk_rcg_determine_rate,
|
||||
.set_rate = clk_rcg_set_rate,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(clk_rcg_ops);
|
||||
|
||||
const struct clk_ops clk_dyn_rcg_ops = {
|
||||
.enable = clk_enable_regmap,
|
||||
.is_enabled = clk_is_enabled_regmap,
|
||||
.disable = clk_disable_regmap,
|
||||
.get_parent = clk_dyn_rcg_get_parent,
|
||||
.set_parent = clk_dyn_rcg_set_parent,
|
||||
.recalc_rate = clk_dyn_rcg_recalc_rate,
|
||||
.determine_rate = clk_dyn_rcg_determine_rate,
|
||||
.set_rate = clk_dyn_rcg_set_rate,
|
||||
.set_rate_and_parent = clk_dyn_rcg_set_rate_and_parent,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(clk_dyn_rcg_ops);
|
159
drivers/clk/qcom/clk-rcg.h
Normal file
159
drivers/clk/qcom/clk-rcg.h
Normal file
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* Copyright (c) 2013, 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_RCG_H__
|
||||
#define __QCOM_CLK_RCG_H__
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include "clk-regmap.h"
|
||||
|
||||
struct freq_tbl {
|
||||
unsigned long freq;
|
||||
u8 src;
|
||||
u8 pre_div;
|
||||
u16 m;
|
||||
u16 n;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mn - M/N:D counter
|
||||
* @mnctr_en_bit: bit to enable mn counter
|
||||
* @mnctr_reset_bit: bit to assert mn counter reset
|
||||
* @mnctr_mode_shift: lowest bit of mn counter mode field
|
||||
* @n_val_shift: lowest bit of n value field
|
||||
* @m_val_shift: lowest bit of m value field
|
||||
* @width: number of bits in m/n/d values
|
||||
* @reset_in_cc: true if the mnctr_reset_bit is in the CC register
|
||||
*/
|
||||
struct mn {
|
||||
u8 mnctr_en_bit;
|
||||
u8 mnctr_reset_bit;
|
||||
u8 mnctr_mode_shift;
|
||||
#define MNCTR_MODE_DUAL 0x2
|
||||
#define MNCTR_MODE_MASK 0x3
|
||||
u8 n_val_shift;
|
||||
u8 m_val_shift;
|
||||
u8 width;
|
||||
bool reset_in_cc;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct pre_div - pre-divider
|
||||
* @pre_div_shift: lowest bit of pre divider field
|
||||
* @pre_div_width: number of bits in predivider
|
||||
*/
|
||||
struct pre_div {
|
||||
u8 pre_div_shift;
|
||||
u8 pre_div_width;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct src_sel - source selector
|
||||
* @src_sel_shift: lowest bit of source selection field
|
||||
* @parent_map: map from software's parent index to hardware's src_sel field
|
||||
*/
|
||||
struct src_sel {
|
||||
u8 src_sel_shift;
|
||||
#define SRC_SEL_MASK 0x7
|
||||
const u8 *parent_map;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct clk_rcg - root clock generator
|
||||
*
|
||||
* @ns_reg: NS register
|
||||
* @md_reg: MD register
|
||||
* @mn: mn counter
|
||||
* @p: pre divider
|
||||
* @s: source selector
|
||||
* @freq_tbl: frequency table
|
||||
* @clkr: regmap clock handle
|
||||
* @lock: register lock
|
||||
*
|
||||
*/
|
||||
struct clk_rcg {
|
||||
u32 ns_reg;
|
||||
u32 md_reg;
|
||||
|
||||
struct mn mn;
|
||||
struct pre_div p;
|
||||
struct src_sel s;
|
||||
|
||||
const struct freq_tbl *freq_tbl;
|
||||
|
||||
struct clk_regmap clkr;
|
||||
};
|
||||
|
||||
extern const struct clk_ops clk_rcg_ops;
|
||||
|
||||
#define to_clk_rcg(_hw) container_of(to_clk_regmap(_hw), struct clk_rcg, clkr)
|
||||
|
||||
/**
|
||||
* struct clk_dyn_rcg - root clock generator with glitch free mux
|
||||
*
|
||||
* @mux_sel_bit: bit to switch glitch free mux
|
||||
* @ns_reg: NS register
|
||||
* @md_reg: MD0 and MD1 register
|
||||
* @mn: mn counter (banked)
|
||||
* @s: source selector (banked)
|
||||
* @freq_tbl: frequency table
|
||||
* @clkr: regmap clock handle
|
||||
* @lock: register lock
|
||||
*
|
||||
*/
|
||||
struct clk_dyn_rcg {
|
||||
u32 ns_reg;
|
||||
u32 md_reg[2];
|
||||
|
||||
u8 mux_sel_bit;
|
||||
|
||||
struct mn mn[2];
|
||||
struct pre_div p[2];
|
||||
struct src_sel s[2];
|
||||
|
||||
const struct freq_tbl *freq_tbl;
|
||||
|
||||
struct clk_regmap clkr;
|
||||
};
|
||||
|
||||
extern const struct clk_ops clk_dyn_rcg_ops;
|
||||
|
||||
#define to_clk_dyn_rcg(_hw) \
|
||||
container_of(to_clk_regmap(_hw), struct clk_dyn_rcg, clkr)
|
||||
|
||||
/**
|
||||
* struct clk_rcg2 - root clock generator
|
||||
*
|
||||
* @cmd_rcgr: corresponds to *_CMD_RCGR
|
||||
* @mnd_width: number of bits in m/n/d values
|
||||
* @hid_width: number of bits in half integer divider
|
||||
* @parent_map: map from software's parent index to hardware's src_sel field
|
||||
* @freq_tbl: frequency table
|
||||
* @clkr: regmap clock handle
|
||||
* @lock: register lock
|
||||
*
|
||||
*/
|
||||
struct clk_rcg2 {
|
||||
u32 cmd_rcgr;
|
||||
u8 mnd_width;
|
||||
u8 hid_width;
|
||||
const u8 *parent_map;
|
||||
const struct freq_tbl *freq_tbl;
|
||||
struct clk_regmap clkr;
|
||||
};
|
||||
|
||||
#define to_clk_rcg2(_hw) container_of(to_clk_regmap(_hw), struct clk_rcg2, clkr)
|
||||
|
||||
extern const struct clk_ops clk_rcg2_ops;
|
||||
|
||||
#endif
|
291
drivers/clk/qcom/clk-rcg2.c
Normal file
291
drivers/clk/qcom/clk-rcg2.c
Normal file
|
@ -0,0 +1,291 @@
|
|||
/*
|
||||
* Copyright (c) 2013, 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/bug.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <asm/div64.h>
|
||||
|
||||
#include "clk-rcg.h"
|
||||
|
||||
#define CMD_REG 0x0
|
||||
#define CMD_UPDATE BIT(0)
|
||||
#define CMD_ROOT_EN BIT(1)
|
||||
#define CMD_DIRTY_CFG BIT(4)
|
||||
#define CMD_DIRTY_N BIT(5)
|
||||
#define CMD_DIRTY_M BIT(6)
|
||||
#define CMD_DIRTY_D BIT(7)
|
||||
#define CMD_ROOT_OFF BIT(31)
|
||||
|
||||
#define CFG_REG 0x4
|
||||
#define CFG_SRC_DIV_SHIFT 0
|
||||
#define CFG_SRC_SEL_SHIFT 8
|
||||
#define CFG_SRC_SEL_MASK (0x7 << CFG_SRC_SEL_SHIFT)
|
||||
#define CFG_MODE_SHIFT 12
|
||||
#define CFG_MODE_MASK (0x3 << CFG_MODE_SHIFT)
|
||||
#define CFG_MODE_DUAL_EDGE (0x2 << CFG_MODE_SHIFT)
|
||||
|
||||
#define M_REG 0x8
|
||||
#define N_REG 0xc
|
||||
#define D_REG 0x10
|
||||
|
||||
static int clk_rcg2_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
|
||||
u32 cmd;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG, &cmd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return (cmd & CMD_ROOT_OFF) != 0;
|
||||
}
|
||||
|
||||
static u8 clk_rcg2_get_parent(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
|
||||
int num_parents = __clk_get_num_parents(hw->clk);
|
||||
u32 cfg;
|
||||
int i, ret;
|
||||
|
||||
ret = regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
cfg &= CFG_SRC_SEL_MASK;
|
||||
cfg >>= CFG_SRC_SEL_SHIFT;
|
||||
|
||||
for (i = 0; i < num_parents; i++)
|
||||
if (cfg == rcg->parent_map[i])
|
||||
return i;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int update_config(struct clk_rcg2 *rcg)
|
||||
{
|
||||
int count, ret;
|
||||
u32 cmd;
|
||||
struct clk_hw *hw = &rcg->clkr.hw;
|
||||
const char *name = __clk_get_name(hw->clk);
|
||||
|
||||
ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG,
|
||||
CMD_UPDATE, CMD_UPDATE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Wait for update to take effect */
|
||||
for (count = 500; count > 0; count--) {
|
||||
ret = regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG, &cmd);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (!(cmd & CMD_UPDATE))
|
||||
return 0;
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
WARN(1, "%s: rcg didn't update its configuration.", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int clk_rcg2_set_parent(struct clk_hw *hw, u8 index)
|
||||
{
|
||||
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
|
||||
int ret;
|
||||
|
||||
ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG,
|
||||
CFG_SRC_SEL_MASK,
|
||||
rcg->parent_map[index] << CFG_SRC_SEL_SHIFT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return update_config(rcg);
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate m/n:d rate
|
||||
*
|
||||
* parent_rate m
|
||||
* rate = ----------- x ---
|
||||
* hid_div n
|
||||
*/
|
||||
static unsigned long
|
||||
calc_rate(unsigned long rate, u32 m, u32 n, u32 mode, u32 hid_div)
|
||||
{
|
||||
if (hid_div) {
|
||||
rate *= 2;
|
||||
rate /= hid_div + 1;
|
||||
}
|
||||
|
||||
if (mode) {
|
||||
u64 tmp = rate;
|
||||
tmp *= m;
|
||||
do_div(tmp, n);
|
||||
rate = tmp;
|
||||
}
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
clk_rcg2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
|
||||
{
|
||||
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
|
||||
u32 cfg, hid_div, m = 0, n = 0, mode = 0, mask;
|
||||
|
||||
regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg);
|
||||
|
||||
if (rcg->mnd_width) {
|
||||
mask = BIT(rcg->mnd_width) - 1;
|
||||
regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + M_REG, &m);
|
||||
m &= mask;
|
||||
regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + N_REG, &n);
|
||||
n = ~n;
|
||||
n &= mask;
|
||||
n += m;
|
||||
mode = cfg & CFG_MODE_MASK;
|
||||
mode >>= CFG_MODE_SHIFT;
|
||||
}
|
||||
|
||||
mask = BIT(rcg->hid_width) - 1;
|
||||
hid_div = cfg >> CFG_SRC_DIV_SHIFT;
|
||||
hid_div &= mask;
|
||||
|
||||
return calc_rate(parent_rate, m, n, mode, hid_div);
|
||||
}
|
||||
|
||||
static const
|
||||
struct freq_tbl *find_freq(const struct freq_tbl *f, unsigned long rate)
|
||||
{
|
||||
if (!f)
|
||||
return NULL;
|
||||
|
||||
for (; f->freq; f++)
|
||||
if (rate <= f->freq)
|
||||
return f;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static long _freq_tbl_determine_rate(struct clk_hw *hw,
|
||||
const struct freq_tbl *f, unsigned long rate,
|
||||
unsigned long *p_rate, struct clk **p)
|
||||
{
|
||||
unsigned long clk_flags;
|
||||
|
||||
f = find_freq(f, rate);
|
||||
if (!f)
|
||||
return -EINVAL;
|
||||
|
||||
clk_flags = __clk_get_flags(hw->clk);
|
||||
*p = clk_get_parent_by_index(hw->clk, f->src);
|
||||
if (clk_flags & CLK_SET_RATE_PARENT) {
|
||||
if (f->pre_div) {
|
||||
rate /= 2;
|
||||
rate *= f->pre_div + 1;
|
||||
}
|
||||
|
||||
if (f->n) {
|
||||
u64 tmp = rate;
|
||||
tmp = tmp * f->n;
|
||||
do_div(tmp, f->m);
|
||||
rate = tmp;
|
||||
}
|
||||
} else {
|
||||
rate = __clk_get_rate(*p);
|
||||
}
|
||||
*p_rate = rate;
|
||||
|
||||
return f->freq;
|
||||
}
|
||||
|
||||
static long clk_rcg2_determine_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *p_rate, struct clk **p)
|
||||
{
|
||||
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
|
||||
|
||||
return _freq_tbl_determine_rate(hw, rcg->freq_tbl, rate, p_rate, p);
|
||||
}
|
||||
|
||||
static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate)
|
||||
{
|
||||
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
|
||||
const struct freq_tbl *f;
|
||||
u32 cfg, mask;
|
||||
int ret;
|
||||
|
||||
f = find_freq(rcg->freq_tbl, rate);
|
||||
if (!f)
|
||||
return -EINVAL;
|
||||
|
||||
if (rcg->mnd_width && f->n) {
|
||||
mask = BIT(rcg->mnd_width) - 1;
|
||||
ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + M_REG,
|
||||
mask, f->m);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + N_REG,
|
||||
mask, ~(f->n - f->m));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + D_REG,
|
||||
mask, ~f->n);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
mask = BIT(rcg->hid_width) - 1;
|
||||
mask |= CFG_SRC_SEL_MASK | CFG_MODE_MASK;
|
||||
cfg = f->pre_div << CFG_SRC_DIV_SHIFT;
|
||||
cfg |= rcg->parent_map[f->src] << CFG_SRC_SEL_SHIFT;
|
||||
if (rcg->mnd_width && f->n)
|
||||
cfg |= CFG_MODE_DUAL_EDGE;
|
||||
ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, mask,
|
||||
cfg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return update_config(rcg);
|
||||
}
|
||||
|
||||
static int clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return __clk_rcg2_set_rate(hw, rate);
|
||||
}
|
||||
|
||||
static int clk_rcg2_set_rate_and_parent(struct clk_hw *hw,
|
||||
unsigned long rate, unsigned long parent_rate, u8 index)
|
||||
{
|
||||
return __clk_rcg2_set_rate(hw, rate);
|
||||
}
|
||||
|
||||
const struct clk_ops clk_rcg2_ops = {
|
||||
.is_enabled = clk_rcg2_is_enabled,
|
||||
.get_parent = clk_rcg2_get_parent,
|
||||
.set_parent = clk_rcg2_set_parent,
|
||||
.recalc_rate = clk_rcg2_recalc_rate,
|
||||
.determine_rate = clk_rcg2_determine_rate,
|
||||
.set_rate = clk_rcg2_set_rate,
|
||||
.set_rate_and_parent = clk_rcg2_set_rate_and_parent,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(clk_rcg2_ops);
|
114
drivers/clk/qcom/clk-regmap.c
Normal file
114
drivers/clk/qcom/clk-regmap.c
Normal file
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* 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/device.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
#include "clk-regmap.h"
|
||||
|
||||
/**
|
||||
* clk_is_enabled_regmap - standard is_enabled() for regmap users
|
||||
*
|
||||
* @hw: clk to operate on
|
||||
*
|
||||
* Clocks that use regmap for their register I/O can set the
|
||||
* enable_reg and enable_mask fields in their struct clk_regmap and then use
|
||||
* this as their is_enabled operation, saving some code.
|
||||
*/
|
||||
int clk_is_enabled_regmap(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_regmap *rclk = to_clk_regmap(hw);
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(rclk->regmap, rclk->enable_reg, &val);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
if (rclk->enable_is_inverted)
|
||||
return (val & rclk->enable_mask) == 0;
|
||||
else
|
||||
return (val & rclk->enable_mask) != 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(clk_is_enabled_regmap);
|
||||
|
||||
/**
|
||||
* clk_enable_regmap - standard enable() for regmap users
|
||||
*
|
||||
* @hw: clk to operate on
|
||||
*
|
||||
* Clocks that use regmap for their register I/O can set the
|
||||
* enable_reg and enable_mask fields in their struct clk_regmap and then use
|
||||
* this as their enable() operation, saving some code.
|
||||
*/
|
||||
int clk_enable_regmap(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_regmap *rclk = to_clk_regmap(hw);
|
||||
unsigned int val;
|
||||
|
||||
if (rclk->enable_is_inverted)
|
||||
val = 0;
|
||||
else
|
||||
val = rclk->enable_mask;
|
||||
|
||||
return regmap_update_bits(rclk->regmap, rclk->enable_reg,
|
||||
rclk->enable_mask, val);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(clk_enable_regmap);
|
||||
|
||||
/**
|
||||
* clk_disable_regmap - standard disable() for regmap users
|
||||
*
|
||||
* @hw: clk to operate on
|
||||
*
|
||||
* Clocks that use regmap for their register I/O can set the
|
||||
* enable_reg and enable_mask fields in their struct clk_regmap and then use
|
||||
* this as their disable() operation, saving some code.
|
||||
*/
|
||||
void clk_disable_regmap(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_regmap *rclk = to_clk_regmap(hw);
|
||||
unsigned int val;
|
||||
|
||||
if (rclk->enable_is_inverted)
|
||||
val = rclk->enable_mask;
|
||||
else
|
||||
val = 0;
|
||||
|
||||
regmap_update_bits(rclk->regmap, rclk->enable_reg, rclk->enable_mask,
|
||||
val);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(clk_disable_regmap);
|
||||
|
||||
/**
|
||||
* devm_clk_register_regmap - register a clk_regmap clock
|
||||
*
|
||||
* @rclk: clk to operate on
|
||||
*
|
||||
* Clocks that use regmap for their register I/O should register their
|
||||
* clk_regmap struct via this function so that the regmap is initialized
|
||||
* and so that the clock is registered with the common clock framework.
|
||||
*/
|
||||
struct clk *devm_clk_register_regmap(struct device *dev,
|
||||
struct clk_regmap *rclk)
|
||||
{
|
||||
if (dev && dev_get_regmap(dev, NULL))
|
||||
rclk->regmap = dev_get_regmap(dev, NULL);
|
||||
else if (dev && dev->parent)
|
||||
rclk->regmap = dev_get_regmap(dev->parent, NULL);
|
||||
|
||||
return devm_clk_register(dev, &rclk->hw);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_clk_register_regmap);
|
45
drivers/clk/qcom/clk-regmap.h
Normal file
45
drivers/clk/qcom/clk-regmap.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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_H__
|
||||
#define __QCOM_CLK_REGMAP_H__
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
|
||||
struct regmap;
|
||||
|
||||
/**
|
||||
* struct clk_regmap - regmap supporting clock
|
||||
* @hw: handle between common and hardware-specific interfaces
|
||||
* @regmap: regmap to use for regmap helpers and/or by providers
|
||||
* @enable_reg: register when using regmap enable/disable ops
|
||||
* @enable_mask: mask when using regmap enable/disable ops
|
||||
* @enable_is_inverted: flag to indicate set enable_mask bits to disable
|
||||
* when using clock_enable_regmap and friends APIs.
|
||||
*/
|
||||
struct clk_regmap {
|
||||
struct clk_hw hw;
|
||||
struct regmap *regmap;
|
||||
unsigned int enable_reg;
|
||||
unsigned int enable_mask;
|
||||
bool enable_is_inverted;
|
||||
};
|
||||
#define to_clk_regmap(_hw) container_of(_hw, struct clk_regmap, hw)
|
||||
|
||||
int clk_is_enabled_regmap(struct clk_hw *hw);
|
||||
int clk_enable_regmap(struct clk_hw *hw);
|
||||
void clk_disable_regmap(struct clk_hw *hw);
|
||||
struct clk *
|
||||
devm_clk_register_regmap(struct device *dev, struct clk_regmap *rclk);
|
||||
|
||||
#endif
|
2819
drivers/clk/qcom/gcc-msm8660.c
Normal file
2819
drivers/clk/qcom/gcc-msm8660.c
Normal file
File diff suppressed because it is too large
Load diff
2993
drivers/clk/qcom/gcc-msm8960.c
Normal file
2993
drivers/clk/qcom/gcc-msm8960.c
Normal file
File diff suppressed because it is too large
Load diff
2694
drivers/clk/qcom/gcc-msm8974.c
Normal file
2694
drivers/clk/qcom/gcc-msm8974.c
Normal file
File diff suppressed because it is too large
Load diff
2321
drivers/clk/qcom/mmcc-msm8960.c
Normal file
2321
drivers/clk/qcom/mmcc-msm8960.c
Normal file
File diff suppressed because it is too large
Load diff
2625
drivers/clk/qcom/mmcc-msm8974.c
Normal file
2625
drivers/clk/qcom/mmcc-msm8974.c
Normal file
File diff suppressed because it is too large
Load diff
63
drivers/clk/qcom/reset.c
Normal file
63
drivers/clk/qcom/reset.c
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright (c) 2013, 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/bitops.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset-controller.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "reset.h"
|
||||
|
||||
static int qcom_reset(struct reset_controller_dev *rcdev, unsigned long id)
|
||||
{
|
||||
rcdev->ops->assert(rcdev, id);
|
||||
udelay(1);
|
||||
rcdev->ops->deassert(rcdev, id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
qcom_reset_assert(struct reset_controller_dev *rcdev, unsigned long id)
|
||||
{
|
||||
struct qcom_reset_controller *rst;
|
||||
const struct qcom_reset_map *map;
|
||||
u32 mask;
|
||||
|
||||
rst = to_qcom_reset_controller(rcdev);
|
||||
map = &rst->reset_map[id];
|
||||
mask = BIT(map->bit);
|
||||
|
||||
return regmap_update_bits(rst->regmap, map->reg, mask, mask);
|
||||
}
|
||||
|
||||
static int
|
||||
qcom_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id)
|
||||
{
|
||||
struct qcom_reset_controller *rst;
|
||||
const struct qcom_reset_map *map;
|
||||
u32 mask;
|
||||
|
||||
rst = to_qcom_reset_controller(rcdev);
|
||||
map = &rst->reset_map[id];
|
||||
mask = BIT(map->bit);
|
||||
|
||||
return regmap_update_bits(rst->regmap, map->reg, mask, 0);
|
||||
}
|
||||
|
||||
struct reset_control_ops qcom_reset_ops = {
|
||||
.reset = qcom_reset,
|
||||
.assert = qcom_reset_assert,
|
||||
.deassert = qcom_reset_deassert,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(qcom_reset_ops);
|
37
drivers/clk/qcom/reset.h
Normal file
37
drivers/clk/qcom/reset.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (c) 2013, 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_RESET_H__
|
||||
#define __QCOM_CLK_RESET_H__
|
||||
|
||||
#include <linux/reset-controller.h>
|
||||
|
||||
struct qcom_reset_map {
|
||||
unsigned int reg;
|
||||
u8 bit;
|
||||
};
|
||||
|
||||
struct regmap;
|
||||
|
||||
struct qcom_reset_controller {
|
||||
const struct qcom_reset_map *reset_map;
|
||||
struct regmap *regmap;
|
||||
struct reset_controller_dev rcdev;
|
||||
};
|
||||
|
||||
#define to_qcom_reset_controller(r) \
|
||||
container_of(r, struct qcom_reset_controller, rcdev);
|
||||
|
||||
extern struct reset_control_ops qcom_reset_ops;
|
||||
|
||||
#endif
|
|
@ -14,9 +14,17 @@
|
|||
#include <linux/clk-provider.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/syscore_ops.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <dt-bindings/clk/exynos-audss-clk.h>
|
||||
|
||||
enum exynos_audss_clk_type {
|
||||
TYPE_EXYNOS4210,
|
||||
TYPE_EXYNOS5250,
|
||||
TYPE_EXYNOS5420,
|
||||
};
|
||||
|
||||
static DEFINE_SPINLOCK(lock);
|
||||
static struct clk **clk_table;
|
||||
static void __iomem *reg_base;
|
||||
|
@ -26,17 +34,13 @@ static struct clk_onecell_data clk_data;
|
|||
#define ASS_CLK_DIV 0x4
|
||||
#define ASS_CLK_GATE 0x8
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static unsigned long reg_save[][2] = {
|
||||
{ASS_CLK_SRC, 0},
|
||||
{ASS_CLK_DIV, 0},
|
||||
{ASS_CLK_GATE, 0},
|
||||
};
|
||||
|
||||
/* list of all parent clock list */
|
||||
static const char *mout_audss_p[] = { "fin_pll", "fout_epll" };
|
||||
static const char *mout_i2s_p[] = { "mout_audss", "cdclk0", "sclk_audio0" };
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int exynos_audss_clk_suspend(void)
|
||||
{
|
||||
int i;
|
||||
|
@ -61,31 +65,69 @@ static struct syscore_ops exynos_audss_clk_syscore_ops = {
|
|||
};
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static const struct of_device_id exynos_audss_clk_of_match[] = {
|
||||
{ .compatible = "samsung,exynos4210-audss-clock",
|
||||
.data = (void *)TYPE_EXYNOS4210, },
|
||||
{ .compatible = "samsung,exynos5250-audss-clock",
|
||||
.data = (void *)TYPE_EXYNOS5250, },
|
||||
{ .compatible = "samsung,exynos5420-audss-clock",
|
||||
.data = (void *)TYPE_EXYNOS5420, },
|
||||
{},
|
||||
};
|
||||
|
||||
/* register exynos_audss clocks */
|
||||
static void __init exynos_audss_clk_init(struct device_node *np)
|
||||
static int exynos_audss_clk_probe(struct platform_device *pdev)
|
||||
{
|
||||
reg_base = of_iomap(np, 0);
|
||||
if (!reg_base) {
|
||||
pr_err("%s: failed to map audss registers\n", __func__);
|
||||
return;
|
||||
int i, ret = 0;
|
||||
struct resource *res;
|
||||
const char *mout_audss_p[] = {"fin_pll", "fout_epll"};
|
||||
const char *mout_i2s_p[] = {"mout_audss", "cdclk0", "sclk_audio0"};
|
||||
const char *sclk_pcm_p = "sclk_pcm0";
|
||||
struct clk *pll_ref, *pll_in, *cdclk, *sclk_audio, *sclk_pcm_in;
|
||||
const struct of_device_id *match;
|
||||
enum exynos_audss_clk_type variant;
|
||||
|
||||
match = of_match_node(exynos_audss_clk_of_match, pdev->dev.of_node);
|
||||
if (!match)
|
||||
return -EINVAL;
|
||||
variant = (enum exynos_audss_clk_type)match->data;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
reg_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(reg_base)) {
|
||||
dev_err(&pdev->dev, "failed to map audss registers\n");
|
||||
return PTR_ERR(reg_base);
|
||||
}
|
||||
|
||||
clk_table = kzalloc(sizeof(struct clk *) * EXYNOS_AUDSS_MAX_CLKS,
|
||||
clk_table = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct clk *) * EXYNOS_AUDSS_MAX_CLKS,
|
||||
GFP_KERNEL);
|
||||
if (!clk_table) {
|
||||
pr_err("%s: could not allocate clk lookup table\n", __func__);
|
||||
return;
|
||||
}
|
||||
if (!clk_table)
|
||||
return -ENOMEM;
|
||||
|
||||
clk_data.clks = clk_table;
|
||||
clk_data.clk_num = EXYNOS_AUDSS_MAX_CLKS;
|
||||
of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
|
||||
if (variant == TYPE_EXYNOS5420)
|
||||
clk_data.clk_num = EXYNOS_AUDSS_MAX_CLKS;
|
||||
else
|
||||
clk_data.clk_num = EXYNOS_AUDSS_MAX_CLKS - 1;
|
||||
|
||||
pll_ref = devm_clk_get(&pdev->dev, "pll_ref");
|
||||
pll_in = devm_clk_get(&pdev->dev, "pll_in");
|
||||
if (!IS_ERR(pll_ref))
|
||||
mout_audss_p[0] = __clk_get_name(pll_ref);
|
||||
if (!IS_ERR(pll_in))
|
||||
mout_audss_p[1] = __clk_get_name(pll_in);
|
||||
clk_table[EXYNOS_MOUT_AUDSS] = clk_register_mux(NULL, "mout_audss",
|
||||
mout_audss_p, ARRAY_SIZE(mout_audss_p),
|
||||
CLK_SET_RATE_NO_REPARENT,
|
||||
reg_base + ASS_CLK_SRC, 0, 1, 0, &lock);
|
||||
|
||||
cdclk = devm_clk_get(&pdev->dev, "cdclk");
|
||||
sclk_audio = devm_clk_get(&pdev->dev, "sclk_audio");
|
||||
if (!IS_ERR(cdclk))
|
||||
mout_i2s_p[1] = __clk_get_name(cdclk);
|
||||
if (!IS_ERR(sclk_audio))
|
||||
mout_i2s_p[2] = __clk_get_name(sclk_audio);
|
||||
clk_table[EXYNOS_MOUT_I2S] = clk_register_mux(NULL, "mout_i2s",
|
||||
mout_i2s_p, ARRAY_SIZE(mout_i2s_p),
|
||||
CLK_SET_RATE_NO_REPARENT,
|
||||
|
@ -119,17 +161,88 @@ static void __init exynos_audss_clk_init(struct device_node *np)
|
|||
"sclk_pcm", CLK_SET_RATE_PARENT,
|
||||
reg_base + ASS_CLK_GATE, 4, 0, &lock);
|
||||
|
||||
sclk_pcm_in = devm_clk_get(&pdev->dev, "sclk_pcm_in");
|
||||
if (!IS_ERR(sclk_pcm_in))
|
||||
sclk_pcm_p = __clk_get_name(sclk_pcm_in);
|
||||
clk_table[EXYNOS_SCLK_PCM] = clk_register_gate(NULL, "sclk_pcm",
|
||||
"div_pcm0", CLK_SET_RATE_PARENT,
|
||||
sclk_pcm_p, CLK_SET_RATE_PARENT,
|
||||
reg_base + ASS_CLK_GATE, 5, 0, &lock);
|
||||
|
||||
if (variant == TYPE_EXYNOS5420) {
|
||||
clk_table[EXYNOS_ADMA] = clk_register_gate(NULL, "adma",
|
||||
"dout_srp", CLK_SET_RATE_PARENT,
|
||||
reg_base + ASS_CLK_GATE, 9, 0, &lock);
|
||||
}
|
||||
|
||||
for (i = 0; i < clk_data.clk_num; i++) {
|
||||
if (IS_ERR(clk_table[i])) {
|
||||
dev_err(&pdev->dev, "failed to register clock %d\n", i);
|
||||
ret = PTR_ERR(clk_table[i]);
|
||||
goto unregister;
|
||||
}
|
||||
}
|
||||
|
||||
ret = of_clk_add_provider(pdev->dev.of_node, of_clk_src_onecell_get,
|
||||
&clk_data);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add clock provider\n");
|
||||
goto unregister;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
register_syscore_ops(&exynos_audss_clk_syscore_ops);
|
||||
#endif
|
||||
|
||||
pr_info("Exynos: Audss: clock setup completed\n");
|
||||
dev_info(&pdev->dev, "setup completed\n");
|
||||
|
||||
return 0;
|
||||
|
||||
unregister:
|
||||
for (i = 0; i < clk_data.clk_num; i++) {
|
||||
if (!IS_ERR(clk_table[i]))
|
||||
clk_unregister(clk_table[i]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
CLK_OF_DECLARE(exynos4210_audss_clk, "samsung,exynos4210-audss-clock",
|
||||
exynos_audss_clk_init);
|
||||
CLK_OF_DECLARE(exynos5250_audss_clk, "samsung,exynos5250-audss-clock",
|
||||
exynos_audss_clk_init);
|
||||
|
||||
static int exynos_audss_clk_remove(struct platform_device *pdev)
|
||||
{
|
||||
int i;
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver exynos_audss_clk_driver = {
|
||||
.driver = {
|
||||
.name = "exynos-audss-clk",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = exynos_audss_clk_of_match,
|
||||
},
|
||||
.probe = exynos_audss_clk_probe,
|
||||
.remove = exynos_audss_clk_remove,
|
||||
};
|
||||
|
||||
static int __init exynos_audss_clk_init(void)
|
||||
{
|
||||
return platform_driver_register(&exynos_audss_clk_driver);
|
||||
}
|
||||
core_initcall(exynos_audss_clk_init);
|
||||
|
||||
static void __exit exynos_audss_clk_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&exynos_audss_clk_driver);
|
||||
}
|
||||
module_exit(exynos_audss_clk_exit);
|
||||
|
||||
MODULE_AUTHOR("Padmavathi Venna <padma.v@samsung.com>");
|
||||
MODULE_DESCRIPTION("Exynos Audio Subsystem Clock Controller");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:exynos-audss-clk");
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -10,6 +10,7 @@
|
|||
* Common Clock Framework support for Exynos5250 SoC.
|
||||
*/
|
||||
|
||||
#include <dt-bindings/clock/exynos5250.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/clk-provider.h>
|
||||
|
@ -25,6 +26,7 @@
|
|||
#define MPLL_LOCK 0x4000
|
||||
#define MPLL_CON0 0x4100
|
||||
#define SRC_CORE1 0x4204
|
||||
#define GATE_IP_ACP 0x8800
|
||||
#define CPLL_LOCK 0x10020
|
||||
#define EPLL_LOCK 0x10030
|
||||
#define VPLL_LOCK 0x10040
|
||||
|
@ -35,6 +37,7 @@
|
|||
#define GPLL_CON0 0x10150
|
||||
#define SRC_TOP0 0x10210
|
||||
#define SRC_TOP2 0x10218
|
||||
#define SRC_TOP3 0x1021c
|
||||
#define SRC_GSCL 0x10220
|
||||
#define SRC_DISP1_0 0x1022c
|
||||
#define SRC_MAU 0x10240
|
||||
|
@ -65,6 +68,7 @@
|
|||
#define DIV_PERIC4 0x10568
|
||||
#define DIV_PERIC5 0x1056c
|
||||
#define GATE_IP_GSCL 0x10920
|
||||
#define GATE_IP_DISP1 0x10928
|
||||
#define GATE_IP_MFC 0x1092c
|
||||
#define GATE_IP_GEN 0x10934
|
||||
#define GATE_IP_FSYS 0x10944
|
||||
|
@ -74,8 +78,6 @@
|
|||
#define BPLL_CON0 0x20110
|
||||
#define SRC_CDREX 0x20200
|
||||
#define PLL_DIV2_SEL 0x20a24
|
||||
#define GATE_IP_DISP1 0x10928
|
||||
#define GATE_IP_ACP 0x10000
|
||||
|
||||
/* list of PLLs to be registered */
|
||||
enum exynos5250_plls {
|
||||
|
@ -83,51 +85,6 @@ enum exynos5250_plls {
|
|||
nr_plls /* number of PLLs */
|
||||
};
|
||||
|
||||
/*
|
||||
* Let each supported clock get a unique id. This id is used to lookup the clock
|
||||
* for device tree based platforms. The clocks are categorized into three
|
||||
* sections: core, sclk gate and bus interface gate clocks.
|
||||
*
|
||||
* When adding a new clock to this list, it is advised to choose a clock
|
||||
* category and add it to the end of that category. That is because the the
|
||||
* device tree source file is referring to these ids and any change in the
|
||||
* sequence number of existing clocks will require corresponding change in the
|
||||
* device tree files. This limitation would go away when pre-processor support
|
||||
* for dtc would be available.
|
||||
*/
|
||||
enum exynos5250_clks {
|
||||
none,
|
||||
|
||||
/* core clocks */
|
||||
fin_pll, fout_apll, fout_mpll, fout_bpll, fout_gpll, fout_cpll,
|
||||
fout_epll, fout_vpll,
|
||||
|
||||
/* gate for special clocks (sclk) */
|
||||
sclk_cam_bayer = 128, sclk_cam0, sclk_cam1, sclk_gscl_wa, sclk_gscl_wb,
|
||||
sclk_fimd1, sclk_mipi1, sclk_dp, sclk_hdmi, sclk_pixel, sclk_audio0,
|
||||
sclk_mmc0, sclk_mmc1, sclk_mmc2, sclk_mmc3, sclk_sata, sclk_usb3,
|
||||
sclk_jpeg, sclk_uart0, sclk_uart1, sclk_uart2, sclk_uart3, sclk_pwm,
|
||||
sclk_audio1, sclk_audio2, sclk_spdif, sclk_spi0, sclk_spi1, sclk_spi2,
|
||||
div_i2s1, div_i2s2, sclk_hdmiphy,
|
||||
|
||||
/* gate clocks */
|
||||
gscl0 = 256, gscl1, gscl2, gscl3, gscl_wa, gscl_wb, smmu_gscl0,
|
||||
smmu_gscl1, smmu_gscl2, smmu_gscl3, mfc, smmu_mfcl, smmu_mfcr, rotator,
|
||||
jpeg, mdma1, smmu_rotator, smmu_jpeg, smmu_mdma1, pdma0, pdma1, sata,
|
||||
usbotg, mipi_hsi, sdmmc0, sdmmc1, sdmmc2, sdmmc3, sromc, usb2, usb3,
|
||||
sata_phyctrl, sata_phyi2c, uart0, uart1, uart2, uart3, uart4, i2c0,
|
||||
i2c1, i2c2, i2c3, i2c4, i2c5, i2c6, i2c7, i2c_hdmi, adc, spi0, spi1,
|
||||
spi2, i2s1, i2s2, pcm1, pcm2, pwm, spdif, ac97, hsi2c0, hsi2c1, hsi2c2,
|
||||
hsi2c3, chipid, sysreg, pmu, cmu_top, cmu_core, cmu_mem, tzpc0, tzpc1,
|
||||
tzpc2, tzpc3, tzpc4, tzpc5, tzpc6, tzpc7, tzpc8, tzpc9, hdmi_cec, mct,
|
||||
wdt, rtc, tmu, fimd1, mie1, dsim0, dp, mixer, hdmi, g2d,
|
||||
|
||||
/* mux clocks */
|
||||
mout_hdmi = 1024,
|
||||
|
||||
nr_clks,
|
||||
};
|
||||
|
||||
/*
|
||||
* list of controller registers to be saved and restored during a
|
||||
* suspend/resume cycle.
|
||||
|
@ -138,6 +95,7 @@ static unsigned long exynos5250_clk_regs[] __initdata = {
|
|||
SRC_CORE1,
|
||||
SRC_TOP0,
|
||||
SRC_TOP2,
|
||||
SRC_TOP3,
|
||||
SRC_GSCL,
|
||||
SRC_DISP1_0,
|
||||
SRC_MAU,
|
||||
|
@ -181,7 +139,7 @@ static unsigned long exynos5250_clk_regs[] __initdata = {
|
|||
|
||||
/* list of all parent clock list */
|
||||
PNAME(mout_apll_p) = { "fin_pll", "fout_apll", };
|
||||
PNAME(mout_cpu_p) = { "mout_apll", "sclk_mpll", };
|
||||
PNAME(mout_cpu_p) = { "mout_apll", "mout_mpll", };
|
||||
PNAME(mout_mpll_fout_p) = { "fout_mplldiv2", "fout_mpll" };
|
||||
PNAME(mout_mpll_p) = { "fin_pll", "mout_mpll_fout" };
|
||||
PNAME(mout_bpll_fout_p) = { "fout_bplldiv2", "fout_bpll" };
|
||||
|
@ -190,308 +148,432 @@ PNAME(mout_vpllsrc_p) = { "fin_pll", "sclk_hdmi27m" };
|
|||
PNAME(mout_vpll_p) = { "mout_vpllsrc", "fout_vpll" };
|
||||
PNAME(mout_cpll_p) = { "fin_pll", "fout_cpll" };
|
||||
PNAME(mout_epll_p) = { "fin_pll", "fout_epll" };
|
||||
PNAME(mout_mpll_user_p) = { "fin_pll", "sclk_mpll" };
|
||||
PNAME(mout_bpll_user_p) = { "fin_pll", "sclk_bpll" };
|
||||
PNAME(mout_aclk166_p) = { "sclk_cpll", "sclk_mpll_user" };
|
||||
PNAME(mout_aclk200_p) = { "sclk_mpll_user", "sclk_bpll_user" };
|
||||
PNAME(mout_mpll_user_p) = { "fin_pll", "mout_mpll" };
|
||||
PNAME(mout_bpll_user_p) = { "fin_pll", "mout_bpll" };
|
||||
PNAME(mout_aclk166_p) = { "mout_cpll", "mout_mpll_user" };
|
||||
PNAME(mout_aclk200_p) = { "mout_mpll_user", "mout_bpll_user" };
|
||||
PNAME(mout_aclk200_sub_p) = { "fin_pll", "div_aclk200" };
|
||||
PNAME(mout_aclk266_sub_p) = { "fin_pll", "div_aclk266" };
|
||||
PNAME(mout_aclk333_sub_p) = { "fin_pll", "div_aclk333" };
|
||||
PNAME(mout_hdmi_p) = { "div_hdmi_pixel", "sclk_hdmiphy" };
|
||||
PNAME(mout_usb3_p) = { "sclk_mpll_user", "sclk_cpll" };
|
||||
PNAME(mout_usb3_p) = { "mout_mpll_user", "mout_cpll" };
|
||||
PNAME(mout_group1_p) = { "fin_pll", "fin_pll", "sclk_hdmi27m",
|
||||
"sclk_dptxphy", "sclk_uhostphy", "sclk_hdmiphy",
|
||||
"sclk_mpll_user", "sclk_epll", "sclk_vpll",
|
||||
"sclk_cpll" };
|
||||
"mout_mpll_user", "mout_epll", "mout_vpll",
|
||||
"mout_cpll", "none", "none",
|
||||
"none", "none", "none",
|
||||
"none" };
|
||||
PNAME(mout_audio0_p) = { "cdclk0", "fin_pll", "sclk_hdmi27m", "sclk_dptxphy",
|
||||
"sclk_uhostphy", "sclk_hdmiphy",
|
||||
"sclk_mpll_user", "sclk_epll", "sclk_vpll",
|
||||
"sclk_cpll" };
|
||||
"sclk_uhostphy", "fin_pll",
|
||||
"mout_mpll_user", "mout_epll", "mout_vpll",
|
||||
"mout_cpll", "none", "none",
|
||||
"none", "none", "none",
|
||||
"none" };
|
||||
PNAME(mout_audio1_p) = { "cdclk1", "fin_pll", "sclk_hdmi27m", "sclk_dptxphy",
|
||||
"sclk_uhostphy", "sclk_hdmiphy",
|
||||
"sclk_mpll_user", "sclk_epll", "sclk_vpll",
|
||||
"sclk_cpll" };
|
||||
"sclk_uhostphy", "fin_pll",
|
||||
"mout_mpll_user", "mout_epll", "mout_vpll",
|
||||
"mout_cpll", "none", "none",
|
||||
"none", "none", "none",
|
||||
"none" };
|
||||
PNAME(mout_audio2_p) = { "cdclk2", "fin_pll", "sclk_hdmi27m", "sclk_dptxphy",
|
||||
"sclk_uhostphy", "sclk_hdmiphy",
|
||||
"sclk_mpll_user", "sclk_epll", "sclk_vpll",
|
||||
"sclk_cpll" };
|
||||
"sclk_uhostphy", "fin_pll",
|
||||
"mout_mpll_user", "mout_epll", "mout_vpll",
|
||||
"mout_cpll", "none", "none",
|
||||
"none", "none", "none",
|
||||
"none" };
|
||||
PNAME(mout_spdif_p) = { "sclk_audio0", "sclk_audio1", "sclk_audio2",
|
||||
"spdif_extclk" };
|
||||
|
||||
/* fixed rate clocks generated outside the soc */
|
||||
static struct samsung_fixed_rate_clock exynos5250_fixed_rate_ext_clks[] __initdata = {
|
||||
FRATE(fin_pll, "fin_pll", NULL, CLK_IS_ROOT, 0),
|
||||
FRATE(CLK_FIN_PLL, "fin_pll", NULL, CLK_IS_ROOT, 0),
|
||||
};
|
||||
|
||||
/* fixed rate clocks generated inside the soc */
|
||||
static struct samsung_fixed_rate_clock exynos5250_fixed_rate_clks[] __initdata = {
|
||||
FRATE(sclk_hdmiphy, "sclk_hdmiphy", NULL, CLK_IS_ROOT, 24000000),
|
||||
FRATE(none, "sclk_hdmi27m", NULL, CLK_IS_ROOT, 27000000),
|
||||
FRATE(none, "sclk_dptxphy", NULL, CLK_IS_ROOT, 24000000),
|
||||
FRATE(none, "sclk_uhostphy", NULL, CLK_IS_ROOT, 48000000),
|
||||
FRATE(CLK_SCLK_HDMIPHY, "sclk_hdmiphy", NULL, CLK_IS_ROOT, 24000000),
|
||||
FRATE(0, "sclk_hdmi27m", NULL, CLK_IS_ROOT, 27000000),
|
||||
FRATE(0, "sclk_dptxphy", NULL, CLK_IS_ROOT, 24000000),
|
||||
FRATE(0, "sclk_uhostphy", NULL, CLK_IS_ROOT, 48000000),
|
||||
};
|
||||
|
||||
static struct samsung_fixed_factor_clock exynos5250_fixed_factor_clks[] __initdata = {
|
||||
FFACTOR(none, "fout_mplldiv2", "fout_mpll", 1, 2, 0),
|
||||
FFACTOR(none, "fout_bplldiv2", "fout_bpll", 1, 2, 0),
|
||||
FFACTOR(0, "fout_mplldiv2", "fout_mpll", 1, 2, 0),
|
||||
FFACTOR(0, "fout_bplldiv2", "fout_bpll", 1, 2, 0),
|
||||
};
|
||||
|
||||
static struct samsung_mux_clock exynos5250_pll_pmux_clks[] __initdata = {
|
||||
MUX(none, "mout_vpllsrc", mout_vpllsrc_p, SRC_TOP2, 0, 1),
|
||||
MUX(0, "mout_vpllsrc", mout_vpllsrc_p, SRC_TOP2, 0, 1),
|
||||
};
|
||||
|
||||
static struct samsung_mux_clock exynos5250_mux_clks[] __initdata = {
|
||||
MUX_A(none, "mout_apll", mout_apll_p, SRC_CPU, 0, 1, "mout_apll"),
|
||||
MUX_A(none, "mout_cpu", mout_cpu_p, SRC_CPU, 16, 1, "mout_cpu"),
|
||||
MUX(none, "mout_mpll_fout", mout_mpll_fout_p, PLL_DIV2_SEL, 4, 1),
|
||||
MUX_A(none, "sclk_mpll", mout_mpll_p, SRC_CORE1, 8, 1, "mout_mpll"),
|
||||
MUX(none, "mout_bpll_fout", mout_bpll_fout_p, PLL_DIV2_SEL, 0, 1),
|
||||
MUX(none, "sclk_bpll", mout_bpll_p, SRC_CDREX, 0, 1),
|
||||
MUX(none, "sclk_vpll", mout_vpll_p, SRC_TOP2, 16, 1),
|
||||
MUX(none, "sclk_epll", mout_epll_p, SRC_TOP2, 12, 1),
|
||||
MUX(none, "sclk_cpll", mout_cpll_p, SRC_TOP2, 8, 1),
|
||||
MUX(none, "sclk_mpll_user", mout_mpll_user_p, SRC_TOP2, 20, 1),
|
||||
MUX(none, "sclk_bpll_user", mout_bpll_user_p, SRC_TOP2, 24, 1),
|
||||
MUX(none, "mout_aclk166", mout_aclk166_p, SRC_TOP0, 8, 1),
|
||||
MUX(none, "mout_aclk333", mout_aclk166_p, SRC_TOP0, 16, 1),
|
||||
MUX(none, "mout_aclk200", mout_aclk200_p, SRC_TOP0, 12, 1),
|
||||
MUX(none, "mout_cam_bayer", mout_group1_p, SRC_GSCL, 12, 4),
|
||||
MUX(none, "mout_cam0", mout_group1_p, SRC_GSCL, 16, 4),
|
||||
MUX(none, "mout_cam1", mout_group1_p, SRC_GSCL, 20, 4),
|
||||
MUX(none, "mout_gscl_wa", mout_group1_p, SRC_GSCL, 24, 4),
|
||||
MUX(none, "mout_gscl_wb", mout_group1_p, SRC_GSCL, 28, 4),
|
||||
MUX(none, "mout_fimd1", mout_group1_p, SRC_DISP1_0, 0, 4),
|
||||
MUX(none, "mout_mipi1", mout_group1_p, SRC_DISP1_0, 12, 4),
|
||||
MUX(none, "mout_dp", mout_group1_p, SRC_DISP1_0, 16, 4),
|
||||
MUX(mout_hdmi, "mout_hdmi", mout_hdmi_p, SRC_DISP1_0, 20, 1),
|
||||
MUX(none, "mout_audio0", mout_audio0_p, SRC_MAU, 0, 4),
|
||||
MUX(none, "mout_mmc0", mout_group1_p, SRC_FSYS, 0, 4),
|
||||
MUX(none, "mout_mmc1", mout_group1_p, SRC_FSYS, 4, 4),
|
||||
MUX(none, "mout_mmc2", mout_group1_p, SRC_FSYS, 8, 4),
|
||||
MUX(none, "mout_mmc3", mout_group1_p, SRC_FSYS, 12, 4),
|
||||
MUX(none, "mout_sata", mout_aclk200_p, SRC_FSYS, 24, 1),
|
||||
MUX(none, "mout_usb3", mout_usb3_p, SRC_FSYS, 28, 1),
|
||||
MUX(none, "mout_jpeg", mout_group1_p, SRC_GEN, 0, 4),
|
||||
MUX(none, "mout_uart0", mout_group1_p, SRC_PERIC0, 0, 4),
|
||||
MUX(none, "mout_uart1", mout_group1_p, SRC_PERIC0, 4, 4),
|
||||
MUX(none, "mout_uart2", mout_group1_p, SRC_PERIC0, 8, 4),
|
||||
MUX(none, "mout_uart3", mout_group1_p, SRC_PERIC0, 12, 4),
|
||||
MUX(none, "mout_pwm", mout_group1_p, SRC_PERIC0, 24, 4),
|
||||
MUX(none, "mout_audio1", mout_audio1_p, SRC_PERIC1, 0, 4),
|
||||
MUX(none, "mout_audio2", mout_audio2_p, SRC_PERIC1, 4, 4),
|
||||
MUX(none, "mout_spdif", mout_spdif_p, SRC_PERIC1, 8, 2),
|
||||
MUX(none, "mout_spi0", mout_group1_p, SRC_PERIC1, 16, 4),
|
||||
MUX(none, "mout_spi1", mout_group1_p, SRC_PERIC1, 20, 4),
|
||||
MUX(none, "mout_spi2", mout_group1_p, SRC_PERIC1, 24, 4),
|
||||
/*
|
||||
* NOTE: Following table is sorted by (clock domain, register address,
|
||||
* bitfield shift) triplet in ascending order. When adding new entries,
|
||||
* please make sure that the order is kept, to avoid merge conflicts
|
||||
* and make further work with defined data easier.
|
||||
*/
|
||||
|
||||
/*
|
||||
* CMU_CPU
|
||||
*/
|
||||
MUX_FA(0, "mout_apll", mout_apll_p, SRC_CPU, 0, 1,
|
||||
CLK_SET_RATE_PARENT, 0, "mout_apll"),
|
||||
MUX_A(0, "mout_cpu", mout_cpu_p, SRC_CPU, 16, 1, "mout_cpu"),
|
||||
|
||||
/*
|
||||
* CMU_CORE
|
||||
*/
|
||||
MUX_A(0, "mout_mpll", mout_mpll_p, SRC_CORE1, 8, 1, "mout_mpll"),
|
||||
|
||||
/*
|
||||
* CMU_TOP
|
||||
*/
|
||||
MUX(0, "mout_aclk166", mout_aclk166_p, SRC_TOP0, 8, 1),
|
||||
MUX(0, "mout_aclk200", mout_aclk200_p, SRC_TOP0, 12, 1),
|
||||
MUX(0, "mout_aclk333", mout_aclk166_p, SRC_TOP0, 16, 1),
|
||||
|
||||
MUX(0, "mout_cpll", mout_cpll_p, SRC_TOP2, 8, 1),
|
||||
MUX(0, "mout_epll", mout_epll_p, SRC_TOP2, 12, 1),
|
||||
MUX(0, "mout_vpll", mout_vpll_p, SRC_TOP2, 16, 1),
|
||||
MUX(0, "mout_mpll_user", mout_mpll_user_p, SRC_TOP2, 20, 1),
|
||||
MUX(0, "mout_bpll_user", mout_bpll_user_p, SRC_TOP2, 24, 1),
|
||||
|
||||
MUX(0, "mout_aclk200_disp1_sub", mout_aclk200_sub_p, SRC_TOP3, 4, 1),
|
||||
MUX(0, "mout_aclk266_gscl_sub", mout_aclk266_sub_p, SRC_TOP3, 8, 1),
|
||||
MUX(0, "mout_aclk333_sub", mout_aclk333_sub_p, SRC_TOP3, 24, 1),
|
||||
|
||||
MUX(0, "mout_cam_bayer", mout_group1_p, SRC_GSCL, 12, 4),
|
||||
MUX(0, "mout_cam0", mout_group1_p, SRC_GSCL, 16, 4),
|
||||
MUX(0, "mout_cam1", mout_group1_p, SRC_GSCL, 20, 4),
|
||||
MUX(0, "mout_gscl_wa", mout_group1_p, SRC_GSCL, 24, 4),
|
||||
MUX(0, "mout_gscl_wb", mout_group1_p, SRC_GSCL, 28, 4),
|
||||
|
||||
MUX(0, "mout_fimd1", mout_group1_p, SRC_DISP1_0, 0, 4),
|
||||
MUX(0, "mout_mipi1", mout_group1_p, SRC_DISP1_0, 12, 4),
|
||||
MUX(0, "mout_dp", mout_group1_p, SRC_DISP1_0, 16, 4),
|
||||
MUX(CLK_MOUT_HDMI, "mout_hdmi", mout_hdmi_p, SRC_DISP1_0, 20, 1),
|
||||
|
||||
MUX(0, "mout_audio0", mout_audio0_p, SRC_MAU, 0, 4),
|
||||
|
||||
MUX(0, "mout_mmc0", mout_group1_p, SRC_FSYS, 0, 4),
|
||||
MUX(0, "mout_mmc1", mout_group1_p, SRC_FSYS, 4, 4),
|
||||
MUX(0, "mout_mmc2", mout_group1_p, SRC_FSYS, 8, 4),
|
||||
MUX(0, "mout_mmc3", mout_group1_p, SRC_FSYS, 12, 4),
|
||||
MUX(0, "mout_sata", mout_aclk200_p, SRC_FSYS, 24, 1),
|
||||
MUX(0, "mout_usb3", mout_usb3_p, SRC_FSYS, 28, 1),
|
||||
|
||||
MUX(0, "mout_jpeg", mout_group1_p, SRC_GEN, 0, 4),
|
||||
|
||||
MUX(0, "mout_uart0", mout_group1_p, SRC_PERIC0, 0, 4),
|
||||
MUX(0, "mout_uart1", mout_group1_p, SRC_PERIC0, 4, 4),
|
||||
MUX(0, "mout_uart2", mout_group1_p, SRC_PERIC0, 8, 4),
|
||||
MUX(0, "mout_uart3", mout_group1_p, SRC_PERIC0, 12, 4),
|
||||
MUX(0, "mout_pwm", mout_group1_p, SRC_PERIC0, 24, 4),
|
||||
|
||||
MUX(0, "mout_audio1", mout_audio1_p, SRC_PERIC1, 0, 4),
|
||||
MUX(0, "mout_audio2", mout_audio2_p, SRC_PERIC1, 4, 4),
|
||||
MUX(0, "mout_spdif", mout_spdif_p, SRC_PERIC1, 8, 2),
|
||||
MUX(0, "mout_spi0", mout_group1_p, SRC_PERIC1, 16, 4),
|
||||
MUX(0, "mout_spi1", mout_group1_p, SRC_PERIC1, 20, 4),
|
||||
MUX(0, "mout_spi2", mout_group1_p, SRC_PERIC1, 24, 4),
|
||||
|
||||
/*
|
||||
* CMU_CDREX
|
||||
*/
|
||||
MUX(0, "mout_bpll", mout_bpll_p, SRC_CDREX, 0, 1),
|
||||
|
||||
MUX(0, "mout_mpll_fout", mout_mpll_fout_p, PLL_DIV2_SEL, 4, 1),
|
||||
MUX(0, "mout_bpll_fout", mout_bpll_fout_p, PLL_DIV2_SEL, 0, 1),
|
||||
};
|
||||
|
||||
static struct samsung_div_clock exynos5250_div_clks[] __initdata = {
|
||||
DIV(none, "div_arm", "mout_cpu", DIV_CPU0, 0, 3),
|
||||
DIV(none, "sclk_apll", "mout_apll", DIV_CPU0, 24, 3),
|
||||
DIV(none, "aclk66_pre", "sclk_mpll_user", DIV_TOP1, 24, 3),
|
||||
DIV(none, "aclk66", "aclk66_pre", DIV_TOP0, 0, 3),
|
||||
DIV(none, "aclk266", "sclk_mpll_user", DIV_TOP0, 16, 3),
|
||||
DIV(none, "aclk166", "mout_aclk166", DIV_TOP0, 8, 3),
|
||||
DIV(none, "aclk333", "mout_aclk333", DIV_TOP0, 20, 3),
|
||||
DIV(none, "aclk200", "mout_aclk200", DIV_TOP0, 12, 3),
|
||||
DIV(none, "div_cam_bayer", "mout_cam_bayer", DIV_GSCL, 12, 4),
|
||||
DIV(none, "div_cam0", "mout_cam0", DIV_GSCL, 16, 4),
|
||||
DIV(none, "div_cam1", "mout_cam1", DIV_GSCL, 20, 4),
|
||||
DIV(none, "div_gscl_wa", "mout_gscl_wa", DIV_GSCL, 24, 4),
|
||||
DIV(none, "div_gscl_wb", "mout_gscl_wb", DIV_GSCL, 28, 4),
|
||||
DIV(none, "div_fimd1", "mout_fimd1", DIV_DISP1_0, 0, 4),
|
||||
DIV(none, "div_mipi1", "mout_mipi1", DIV_DISP1_0, 16, 4),
|
||||
DIV(none, "div_dp", "mout_dp", DIV_DISP1_0, 24, 4),
|
||||
DIV(none, "div_jpeg", "mout_jpeg", DIV_GEN, 4, 4),
|
||||
DIV(none, "div_audio0", "mout_audio0", DIV_MAU, 0, 4),
|
||||
DIV(none, "div_pcm0", "sclk_audio0", DIV_MAU, 4, 8),
|
||||
DIV(none, "div_sata", "mout_sata", DIV_FSYS0, 20, 4),
|
||||
DIV(none, "div_usb3", "mout_usb3", DIV_FSYS0, 24, 4),
|
||||
DIV(none, "div_mmc0", "mout_mmc0", DIV_FSYS1, 0, 4),
|
||||
DIV(none, "div_mmc1", "mout_mmc1", DIV_FSYS1, 16, 4),
|
||||
DIV(none, "div_mmc2", "mout_mmc2", DIV_FSYS2, 0, 4),
|
||||
DIV(none, "div_mmc3", "mout_mmc3", DIV_FSYS2, 16, 4),
|
||||
DIV(none, "div_uart0", "mout_uart0", DIV_PERIC0, 0, 4),
|
||||
DIV(none, "div_uart1", "mout_uart1", DIV_PERIC0, 4, 4),
|
||||
DIV(none, "div_uart2", "mout_uart2", DIV_PERIC0, 8, 4),
|
||||
DIV(none, "div_uart3", "mout_uart3", DIV_PERIC0, 12, 4),
|
||||
DIV(none, "div_spi0", "mout_spi0", DIV_PERIC1, 0, 4),
|
||||
DIV(none, "div_spi1", "mout_spi1", DIV_PERIC1, 16, 4),
|
||||
DIV(none, "div_spi2", "mout_spi2", DIV_PERIC2, 0, 4),
|
||||
DIV(none, "div_pwm", "mout_pwm", DIV_PERIC3, 0, 4),
|
||||
DIV(none, "div_audio1", "mout_audio1", DIV_PERIC4, 0, 4),
|
||||
DIV(none, "div_pcm1", "sclk_audio1", DIV_PERIC4, 4, 8),
|
||||
DIV(none, "div_audio2", "mout_audio2", DIV_PERIC4, 16, 4),
|
||||
DIV(none, "div_pcm2", "sclk_audio2", DIV_PERIC4, 20, 8),
|
||||
DIV(div_i2s1, "div_i2s1", "sclk_audio1", DIV_PERIC5, 0, 6),
|
||||
DIV(div_i2s2, "div_i2s2", "sclk_audio2", DIV_PERIC5, 8, 6),
|
||||
DIV(sclk_pixel, "div_hdmi_pixel", "sclk_vpll", DIV_DISP1_0, 28, 4),
|
||||
DIV_A(none, "armclk", "div_arm", DIV_CPU0, 28, 3, "armclk"),
|
||||
DIV_F(none, "div_mipi1_pre", "div_mipi1",
|
||||
/*
|
||||
* NOTE: Following table is sorted by (clock domain, register address,
|
||||
* bitfield shift) triplet in ascending order. When adding new entries,
|
||||
* please make sure that the order is kept, to avoid merge conflicts
|
||||
* and make further work with defined data easier.
|
||||
*/
|
||||
|
||||
/*
|
||||
* CMU_CPU
|
||||
*/
|
||||
DIV(0, "div_arm", "mout_cpu", DIV_CPU0, 0, 3),
|
||||
DIV(0, "div_apll", "mout_apll", DIV_CPU0, 24, 3),
|
||||
DIV_A(0, "div_arm2", "div_arm", DIV_CPU0, 28, 3, "armclk"),
|
||||
|
||||
/*
|
||||
* CMU_TOP
|
||||
*/
|
||||
DIV(0, "div_aclk66", "div_aclk66_pre", DIV_TOP0, 0, 3),
|
||||
DIV(0, "div_aclk166", "mout_aclk166", DIV_TOP0, 8, 3),
|
||||
DIV(0, "div_aclk200", "mout_aclk200", DIV_TOP0, 12, 3),
|
||||
DIV(0, "div_aclk266", "mout_mpll_user", DIV_TOP0, 16, 3),
|
||||
DIV(0, "div_aclk333", "mout_aclk333", DIV_TOP0, 20, 3),
|
||||
|
||||
DIV(0, "div_aclk66_pre", "mout_mpll_user", DIV_TOP1, 24, 3),
|
||||
|
||||
DIV(0, "div_cam_bayer", "mout_cam_bayer", DIV_GSCL, 12, 4),
|
||||
DIV(0, "div_cam0", "mout_cam0", DIV_GSCL, 16, 4),
|
||||
DIV(0, "div_cam1", "mout_cam1", DIV_GSCL, 20, 4),
|
||||
DIV(0, "div_gscl_wa", "mout_gscl_wa", DIV_GSCL, 24, 4),
|
||||
DIV(0, "div_gscl_wb", "mout_gscl_wb", DIV_GSCL, 28, 4),
|
||||
|
||||
DIV(0, "div_fimd1", "mout_fimd1", DIV_DISP1_0, 0, 4),
|
||||
DIV(0, "div_mipi1", "mout_mipi1", DIV_DISP1_0, 16, 4),
|
||||
DIV_F(0, "div_mipi1_pre", "div_mipi1",
|
||||
DIV_DISP1_0, 20, 4, CLK_SET_RATE_PARENT, 0),
|
||||
DIV_F(none, "div_mmc_pre0", "div_mmc0",
|
||||
DIV(0, "div_dp", "mout_dp", DIV_DISP1_0, 24, 4),
|
||||
DIV(CLK_SCLK_PIXEL, "div_hdmi_pixel", "mout_vpll", DIV_DISP1_0, 28, 4),
|
||||
|
||||
DIV(0, "div_jpeg", "mout_jpeg", DIV_GEN, 4, 4),
|
||||
|
||||
DIV(0, "div_audio0", "mout_audio0", DIV_MAU, 0, 4),
|
||||
DIV(CLK_DIV_PCM0, "div_pcm0", "sclk_audio0", DIV_MAU, 4, 8),
|
||||
|
||||
DIV(0, "div_sata", "mout_sata", DIV_FSYS0, 20, 4),
|
||||
DIV(0, "div_usb3", "mout_usb3", DIV_FSYS0, 24, 4),
|
||||
|
||||
DIV(0, "div_mmc0", "mout_mmc0", DIV_FSYS1, 0, 4),
|
||||
DIV_F(0, "div_mmc_pre0", "div_mmc0",
|
||||
DIV_FSYS1, 8, 8, CLK_SET_RATE_PARENT, 0),
|
||||
DIV_F(none, "div_mmc_pre1", "div_mmc1",
|
||||
DIV(0, "div_mmc1", "mout_mmc1", DIV_FSYS1, 16, 4),
|
||||
DIV_F(0, "div_mmc_pre1", "div_mmc1",
|
||||
DIV_FSYS1, 24, 8, CLK_SET_RATE_PARENT, 0),
|
||||
DIV_F(none, "div_mmc_pre2", "div_mmc2",
|
||||
|
||||
DIV(0, "div_mmc2", "mout_mmc2", DIV_FSYS2, 0, 4),
|
||||
DIV_F(0, "div_mmc_pre2", "div_mmc2",
|
||||
DIV_FSYS2, 8, 8, CLK_SET_RATE_PARENT, 0),
|
||||
DIV_F(none, "div_mmc_pre3", "div_mmc3",
|
||||
DIV(0, "div_mmc3", "mout_mmc3", DIV_FSYS2, 16, 4),
|
||||
DIV_F(0, "div_mmc_pre3", "div_mmc3",
|
||||
DIV_FSYS2, 24, 8, CLK_SET_RATE_PARENT, 0),
|
||||
DIV_F(none, "div_spi_pre0", "div_spi0",
|
||||
|
||||
DIV(0, "div_uart0", "mout_uart0", DIV_PERIC0, 0, 4),
|
||||
DIV(0, "div_uart1", "mout_uart1", DIV_PERIC0, 4, 4),
|
||||
DIV(0, "div_uart2", "mout_uart2", DIV_PERIC0, 8, 4),
|
||||
DIV(0, "div_uart3", "mout_uart3", DIV_PERIC0, 12, 4),
|
||||
|
||||
DIV(0, "div_spi0", "mout_spi0", DIV_PERIC1, 0, 4),
|
||||
DIV_F(0, "div_spi_pre0", "div_spi0",
|
||||
DIV_PERIC1, 8, 8, CLK_SET_RATE_PARENT, 0),
|
||||
DIV_F(none, "div_spi_pre1", "div_spi1",
|
||||
DIV(0, "div_spi1", "mout_spi1", DIV_PERIC1, 16, 4),
|
||||
DIV_F(0, "div_spi_pre1", "div_spi1",
|
||||
DIV_PERIC1, 24, 8, CLK_SET_RATE_PARENT, 0),
|
||||
DIV_F(none, "div_spi_pre2", "div_spi2",
|
||||
|
||||
DIV(0, "div_spi2", "mout_spi2", DIV_PERIC2, 0, 4),
|
||||
DIV_F(0, "div_spi_pre2", "div_spi2",
|
||||
DIV_PERIC2, 8, 8, CLK_SET_RATE_PARENT, 0),
|
||||
|
||||
DIV(0, "div_pwm", "mout_pwm", DIV_PERIC3, 0, 4),
|
||||
|
||||
DIV(0, "div_audio1", "mout_audio1", DIV_PERIC4, 0, 4),
|
||||
DIV(0, "div_pcm1", "sclk_audio1", DIV_PERIC4, 4, 8),
|
||||
DIV(0, "div_audio2", "mout_audio2", DIV_PERIC4, 16, 4),
|
||||
DIV(0, "div_pcm2", "sclk_audio2", DIV_PERIC4, 20, 8),
|
||||
|
||||
DIV(CLK_DIV_I2S1, "div_i2s1", "sclk_audio1", DIV_PERIC5, 0, 6),
|
||||
DIV(CLK_DIV_I2S2, "div_i2s2", "sclk_audio2", DIV_PERIC5, 8, 6),
|
||||
};
|
||||
|
||||
static struct samsung_gate_clock exynos5250_gate_clks[] __initdata = {
|
||||
GATE(gscl0, "gscl0", "none", GATE_IP_GSCL, 0, 0, 0),
|
||||
GATE(gscl1, "gscl1", "none", GATE_IP_GSCL, 1, 0, 0),
|
||||
GATE(gscl2, "gscl2", "aclk266", GATE_IP_GSCL, 2, 0, 0),
|
||||
GATE(gscl3, "gscl3", "aclk266", GATE_IP_GSCL, 3, 0, 0),
|
||||
GATE(gscl_wa, "gscl_wa", "div_gscl_wa", GATE_IP_GSCL, 5, 0, 0),
|
||||
GATE(gscl_wb, "gscl_wb", "div_gscl_wb", GATE_IP_GSCL, 6, 0, 0),
|
||||
GATE(smmu_gscl0, "smmu_gscl0", "aclk266", GATE_IP_GSCL, 7, 0, 0),
|
||||
GATE(smmu_gscl1, "smmu_gscl1", "aclk266", GATE_IP_GSCL, 8, 0, 0),
|
||||
GATE(smmu_gscl2, "smmu_gscl2", "aclk266", GATE_IP_GSCL, 9, 0, 0),
|
||||
GATE(smmu_gscl3, "smmu_gscl3", "aclk266", GATE_IP_GSCL, 10, 0, 0),
|
||||
GATE(mfc, "mfc", "aclk333", GATE_IP_MFC, 0, 0, 0),
|
||||
GATE(smmu_mfcl, "smmu_mfcl", "aclk333", GATE_IP_MFC, 1, 0, 0),
|
||||
GATE(smmu_mfcr, "smmu_mfcr", "aclk333", GATE_IP_MFC, 2, 0, 0),
|
||||
GATE(rotator, "rotator", "aclk266", GATE_IP_GEN, 1, 0, 0),
|
||||
GATE(jpeg, "jpeg", "aclk166", GATE_IP_GEN, 2, 0, 0),
|
||||
GATE(mdma1, "mdma1", "aclk266", GATE_IP_GEN, 4, 0, 0),
|
||||
GATE(smmu_rotator, "smmu_rotator", "aclk266", GATE_IP_GEN, 6, 0, 0),
|
||||
GATE(smmu_jpeg, "smmu_jpeg", "aclk166", GATE_IP_GEN, 7, 0, 0),
|
||||
GATE(smmu_mdma1, "smmu_mdma1", "aclk266", GATE_IP_GEN, 9, 0, 0),
|
||||
GATE(pdma0, "pdma0", "aclk200", GATE_IP_FSYS, 1, 0, 0),
|
||||
GATE(pdma1, "pdma1", "aclk200", GATE_IP_FSYS, 2, 0, 0),
|
||||
GATE(sata, "sata", "aclk200", GATE_IP_FSYS, 6, 0, 0),
|
||||
GATE(usbotg, "usbotg", "aclk200", GATE_IP_FSYS, 7, 0, 0),
|
||||
GATE(mipi_hsi, "mipi_hsi", "aclk200", GATE_IP_FSYS, 8, 0, 0),
|
||||
GATE(sdmmc0, "sdmmc0", "aclk200", GATE_IP_FSYS, 12, 0, 0),
|
||||
GATE(sdmmc1, "sdmmc1", "aclk200", GATE_IP_FSYS, 13, 0, 0),
|
||||
GATE(sdmmc2, "sdmmc2", "aclk200", GATE_IP_FSYS, 14, 0, 0),
|
||||
GATE(sdmmc3, "sdmmc3", "aclk200", GATE_IP_FSYS, 15, 0, 0),
|
||||
GATE(sromc, "sromc", "aclk200", GATE_IP_FSYS, 17, 0, 0),
|
||||
GATE(usb2, "usb2", "aclk200", GATE_IP_FSYS, 18, 0, 0),
|
||||
GATE(usb3, "usb3", "aclk200", GATE_IP_FSYS, 19, 0, 0),
|
||||
GATE(sata_phyctrl, "sata_phyctrl", "aclk200", GATE_IP_FSYS, 24, 0, 0),
|
||||
GATE(sata_phyi2c, "sata_phyi2c", "aclk200", GATE_IP_FSYS, 25, 0, 0),
|
||||
GATE(uart0, "uart0", "aclk66", GATE_IP_PERIC, 0, 0, 0),
|
||||
GATE(uart1, "uart1", "aclk66", GATE_IP_PERIC, 1, 0, 0),
|
||||
GATE(uart2, "uart2", "aclk66", GATE_IP_PERIC, 2, 0, 0),
|
||||
GATE(uart3, "uart3", "aclk66", GATE_IP_PERIC, 3, 0, 0),
|
||||
GATE(uart4, "uart4", "aclk66", GATE_IP_PERIC, 4, 0, 0),
|
||||
GATE(i2c0, "i2c0", "aclk66", GATE_IP_PERIC, 6, 0, 0),
|
||||
GATE(i2c1, "i2c1", "aclk66", GATE_IP_PERIC, 7, 0, 0),
|
||||
GATE(i2c2, "i2c2", "aclk66", GATE_IP_PERIC, 8, 0, 0),
|
||||
GATE(i2c3, "i2c3", "aclk66", GATE_IP_PERIC, 9, 0, 0),
|
||||
GATE(i2c4, "i2c4", "aclk66", GATE_IP_PERIC, 10, 0, 0),
|
||||
GATE(i2c5, "i2c5", "aclk66", GATE_IP_PERIC, 11, 0, 0),
|
||||
GATE(i2c6, "i2c6", "aclk66", GATE_IP_PERIC, 12, 0, 0),
|
||||
GATE(i2c7, "i2c7", "aclk66", GATE_IP_PERIC, 13, 0, 0),
|
||||
GATE(i2c_hdmi, "i2c_hdmi", "aclk66", GATE_IP_PERIC, 14, 0, 0),
|
||||
GATE(adc, "adc", "aclk66", GATE_IP_PERIC, 15, 0, 0),
|
||||
GATE(spi0, "spi0", "aclk66", GATE_IP_PERIC, 16, 0, 0),
|
||||
GATE(spi1, "spi1", "aclk66", GATE_IP_PERIC, 17, 0, 0),
|
||||
GATE(spi2, "spi2", "aclk66", GATE_IP_PERIC, 18, 0, 0),
|
||||
GATE(i2s1, "i2s1", "aclk66", GATE_IP_PERIC, 20, 0, 0),
|
||||
GATE(i2s2, "i2s2", "aclk66", GATE_IP_PERIC, 21, 0, 0),
|
||||
GATE(pcm1, "pcm1", "aclk66", GATE_IP_PERIC, 22, 0, 0),
|
||||
GATE(pcm2, "pcm2", "aclk66", GATE_IP_PERIC, 23, 0, 0),
|
||||
GATE(pwm, "pwm", "aclk66", GATE_IP_PERIC, 24, 0, 0),
|
||||
GATE(spdif, "spdif", "aclk66", GATE_IP_PERIC, 26, 0, 0),
|
||||
GATE(ac97, "ac97", "aclk66", GATE_IP_PERIC, 27, 0, 0),
|
||||
GATE(hsi2c0, "hsi2c0", "aclk66", GATE_IP_PERIC, 28, 0, 0),
|
||||
GATE(hsi2c1, "hsi2c1", "aclk66", GATE_IP_PERIC, 29, 0, 0),
|
||||
GATE(hsi2c2, "hsi2c2", "aclk66", GATE_IP_PERIC, 30, 0, 0),
|
||||
GATE(hsi2c3, "hsi2c3", "aclk66", GATE_IP_PERIC, 31, 0, 0),
|
||||
GATE(chipid, "chipid", "aclk66", GATE_IP_PERIS, 0, 0, 0),
|
||||
GATE(sysreg, "sysreg", "aclk66", GATE_IP_PERIS, 1, 0, 0),
|
||||
GATE(pmu, "pmu", "aclk66", GATE_IP_PERIS, 2, CLK_IGNORE_UNUSED, 0),
|
||||
GATE(tzpc0, "tzpc0", "aclk66", GATE_IP_PERIS, 6, 0, 0),
|
||||
GATE(tzpc1, "tzpc1", "aclk66", GATE_IP_PERIS, 7, 0, 0),
|
||||
GATE(tzpc2, "tzpc2", "aclk66", GATE_IP_PERIS, 8, 0, 0),
|
||||
GATE(tzpc3, "tzpc3", "aclk66", GATE_IP_PERIS, 9, 0, 0),
|
||||
GATE(tzpc4, "tzpc4", "aclk66", GATE_IP_PERIS, 10, 0, 0),
|
||||
GATE(tzpc5, "tzpc5", "aclk66", GATE_IP_PERIS, 11, 0, 0),
|
||||
GATE(tzpc6, "tzpc6", "aclk66", GATE_IP_PERIS, 12, 0, 0),
|
||||
GATE(tzpc7, "tzpc7", "aclk66", GATE_IP_PERIS, 13, 0, 0),
|
||||
GATE(tzpc8, "tzpc8", "aclk66", GATE_IP_PERIS, 14, 0, 0),
|
||||
GATE(tzpc9, "tzpc9", "aclk66", GATE_IP_PERIS, 15, 0, 0),
|
||||
GATE(hdmi_cec, "hdmi_cec", "aclk66", GATE_IP_PERIS, 16, 0, 0),
|
||||
GATE(mct, "mct", "aclk66", GATE_IP_PERIS, 18, 0, 0),
|
||||
GATE(wdt, "wdt", "aclk66", GATE_IP_PERIS, 19, 0, 0),
|
||||
GATE(rtc, "rtc", "aclk66", GATE_IP_PERIS, 20, 0, 0),
|
||||
GATE(tmu, "tmu", "aclk66", GATE_IP_PERIS, 21, 0, 0),
|
||||
GATE(cmu_top, "cmu_top", "aclk66",
|
||||
GATE_IP_PERIS, 3, CLK_IGNORE_UNUSED, 0),
|
||||
GATE(cmu_core, "cmu_core", "aclk66",
|
||||
GATE_IP_PERIS, 4, CLK_IGNORE_UNUSED, 0),
|
||||
GATE(cmu_mem, "cmu_mem", "aclk66",
|
||||
GATE_IP_PERIS, 5, CLK_IGNORE_UNUSED, 0),
|
||||
GATE(sclk_cam_bayer, "sclk_cam_bayer", "div_cam_bayer",
|
||||
/*
|
||||
* NOTE: Following table is sorted by (clock domain, register address,
|
||||
* bitfield shift) triplet in ascending order. When adding new entries,
|
||||
* please make sure that the order is kept, to avoid merge conflicts
|
||||
* and make further work with defined data easier.
|
||||
*/
|
||||
|
||||
/*
|
||||
* CMU_ACP
|
||||
*/
|
||||
GATE(CLK_MDMA0, "mdma0", "div_aclk266", GATE_IP_ACP, 1, 0, 0),
|
||||
GATE(CLK_G2D, "g2d", "div_aclk200", GATE_IP_ACP, 3, 0, 0),
|
||||
GATE(CLK_SMMU_MDMA0, "smmu_mdma0", "div_aclk266", GATE_IP_ACP, 5, 0, 0),
|
||||
|
||||
/*
|
||||
* CMU_TOP
|
||||
*/
|
||||
GATE(CLK_SCLK_CAM_BAYER, "sclk_cam_bayer", "div_cam_bayer",
|
||||
SRC_MASK_GSCL, 12, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_cam0, "sclk_cam0", "div_cam0",
|
||||
GATE(CLK_SCLK_CAM0, "sclk_cam0", "div_cam0",
|
||||
SRC_MASK_GSCL, 16, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_cam1, "sclk_cam1", "div_cam1",
|
||||
GATE(CLK_SCLK_CAM1, "sclk_cam1", "div_cam1",
|
||||
SRC_MASK_GSCL, 20, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_gscl_wa, "sclk_gscl_wa", "div_gscl_wa",
|
||||
GATE(CLK_SCLK_GSCL_WA, "sclk_gscl_wa", "div_gscl_wa",
|
||||
SRC_MASK_GSCL, 24, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_gscl_wb, "sclk_gscl_wb", "div_gscl_wb",
|
||||
GATE(CLK_SCLK_GSCL_WB, "sclk_gscl_wb", "div_gscl_wb",
|
||||
SRC_MASK_GSCL, 28, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_fimd1, "sclk_fimd1", "div_fimd1",
|
||||
|
||||
GATE(CLK_SCLK_FIMD1, "sclk_fimd1", "div_fimd1",
|
||||
SRC_MASK_DISP1_0, 0, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_mipi1, "sclk_mipi1", "div_mipi1",
|
||||
GATE(CLK_SCLK_MIPI1, "sclk_mipi1", "div_mipi1",
|
||||
SRC_MASK_DISP1_0, 12, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_dp, "sclk_dp", "div_dp",
|
||||
GATE(CLK_SCLK_DP, "sclk_dp", "div_dp",
|
||||
SRC_MASK_DISP1_0, 16, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_hdmi, "sclk_hdmi", "mout_hdmi",
|
||||
GATE(CLK_SCLK_HDMI, "sclk_hdmi", "mout_hdmi",
|
||||
SRC_MASK_DISP1_0, 20, 0, 0),
|
||||
GATE(sclk_audio0, "sclk_audio0", "div_audio0",
|
||||
|
||||
GATE(CLK_SCLK_AUDIO0, "sclk_audio0", "div_audio0",
|
||||
SRC_MASK_MAU, 0, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_mmc0, "sclk_mmc0", "div_mmc_pre0",
|
||||
|
||||
GATE(CLK_SCLK_MMC0, "sclk_mmc0", "div_mmc_pre0",
|
||||
SRC_MASK_FSYS, 0, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_mmc1, "sclk_mmc1", "div_mmc_pre1",
|
||||
GATE(CLK_SCLK_MMC1, "sclk_mmc1", "div_mmc_pre1",
|
||||
SRC_MASK_FSYS, 4, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_mmc2, "sclk_mmc2", "div_mmc_pre2",
|
||||
GATE(CLK_SCLK_MMC2, "sclk_mmc2", "div_mmc_pre2",
|
||||
SRC_MASK_FSYS, 8, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_mmc3, "sclk_mmc3", "div_mmc_pre3",
|
||||
GATE(CLK_SCLK_MMC3, "sclk_mmc3", "div_mmc_pre3",
|
||||
SRC_MASK_FSYS, 12, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_sata, "sclk_sata", "div_sata",
|
||||
GATE(CLK_SCLK_SATA, "sclk_sata", "div_sata",
|
||||
SRC_MASK_FSYS, 24, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_usb3, "sclk_usb3", "div_usb3",
|
||||
GATE(CLK_SCLK_USB3, "sclk_usb3", "div_usb3",
|
||||
SRC_MASK_FSYS, 28, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_jpeg, "sclk_jpeg", "div_jpeg",
|
||||
|
||||
GATE(CLK_SCLK_JPEG, "sclk_jpeg", "div_jpeg",
|
||||
SRC_MASK_GEN, 0, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_uart0, "sclk_uart0", "div_uart0",
|
||||
|
||||
GATE(CLK_SCLK_UART0, "sclk_uart0", "div_uart0",
|
||||
SRC_MASK_PERIC0, 0, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_uart1, "sclk_uart1", "div_uart1",
|
||||
GATE(CLK_SCLK_UART1, "sclk_uart1", "div_uart1",
|
||||
SRC_MASK_PERIC0, 4, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_uart2, "sclk_uart2", "div_uart2",
|
||||
GATE(CLK_SCLK_UART2, "sclk_uart2", "div_uart2",
|
||||
SRC_MASK_PERIC0, 8, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_uart3, "sclk_uart3", "div_uart3",
|
||||
GATE(CLK_SCLK_UART3, "sclk_uart3", "div_uart3",
|
||||
SRC_MASK_PERIC0, 12, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_pwm, "sclk_pwm", "div_pwm",
|
||||
GATE(CLK_SCLK_PWM, "sclk_pwm", "div_pwm",
|
||||
SRC_MASK_PERIC0, 24, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_audio1, "sclk_audio1", "div_audio1",
|
||||
|
||||
GATE(CLK_SCLK_AUDIO1, "sclk_audio1", "div_audio1",
|
||||
SRC_MASK_PERIC1, 0, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_audio2, "sclk_audio2", "div_audio2",
|
||||
GATE(CLK_SCLK_AUDIO2, "sclk_audio2", "div_audio2",
|
||||
SRC_MASK_PERIC1, 4, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_spdif, "sclk_spdif", "mout_spdif",
|
||||
GATE(CLK_SCLK_SPDIF, "sclk_spdif", "mout_spdif",
|
||||
SRC_MASK_PERIC1, 4, 0, 0),
|
||||
GATE(sclk_spi0, "sclk_spi0", "div_spi_pre0",
|
||||
GATE(CLK_SCLK_SPI0, "sclk_spi0", "div_spi_pre0",
|
||||
SRC_MASK_PERIC1, 16, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_spi1, "sclk_spi1", "div_spi_pre1",
|
||||
GATE(CLK_SCLK_SPI1, "sclk_spi1", "div_spi_pre1",
|
||||
SRC_MASK_PERIC1, 20, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_spi2, "sclk_spi2", "div_spi_pre2",
|
||||
GATE(CLK_SCLK_SPI2, "sclk_spi2", "div_spi_pre2",
|
||||
SRC_MASK_PERIC1, 24, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(fimd1, "fimd1", "aclk200", GATE_IP_DISP1, 0, 0, 0),
|
||||
GATE(mie1, "mie1", "aclk200", GATE_IP_DISP1, 1, 0, 0),
|
||||
GATE(dsim0, "dsim0", "aclk200", GATE_IP_DISP1, 3, 0, 0),
|
||||
GATE(dp, "dp", "aclk200", GATE_IP_DISP1, 4, 0, 0),
|
||||
GATE(mixer, "mixer", "mout_aclk200_disp1", GATE_IP_DISP1, 5, 0, 0),
|
||||
GATE(hdmi, "hdmi", "mout_aclk200_disp1", GATE_IP_DISP1, 6, 0, 0),
|
||||
GATE(g2d, "g2d", "aclk200", GATE_IP_ACP, 3, 0, 0),
|
||||
|
||||
GATE(CLK_GSCL0, "gscl0", "mout_aclk266_gscl_sub", GATE_IP_GSCL, 0, 0,
|
||||
0),
|
||||
GATE(CLK_GSCL1, "gscl1", "mout_aclk266_gscl_sub", GATE_IP_GSCL, 1, 0,
|
||||
0),
|
||||
GATE(CLK_GSCL2, "gscl2", "mout_aclk266_gscl_sub", GATE_IP_GSCL, 2, 0,
|
||||
0),
|
||||
GATE(CLK_GSCL3, "gscl3", "mout_aclk266_gscl_sub", GATE_IP_GSCL, 3, 0,
|
||||
0),
|
||||
GATE(CLK_GSCL_WA, "gscl_wa", "div_gscl_wa", GATE_IP_GSCL, 5, 0, 0),
|
||||
GATE(CLK_GSCL_WB, "gscl_wb", "div_gscl_wb", GATE_IP_GSCL, 6, 0, 0),
|
||||
GATE(CLK_SMMU_GSCL0, "smmu_gscl0", "mout_aclk266_gscl_sub",
|
||||
GATE_IP_GSCL, 7, 0, 0),
|
||||
GATE(CLK_SMMU_GSCL1, "smmu_gscl1", "mout_aclk266_gscl_sub",
|
||||
GATE_IP_GSCL, 8, 0, 0),
|
||||
GATE(CLK_SMMU_GSCL2, "smmu_gscl2", "mout_aclk266_gscl_sub",
|
||||
GATE_IP_GSCL, 9, 0, 0),
|
||||
GATE(CLK_SMMU_GSCL3, "smmu_gscl3", "mout_aclk266_gscl_sub",
|
||||
GATE_IP_GSCL, 10, 0, 0),
|
||||
|
||||
GATE(CLK_FIMD1, "fimd1", "mout_aclk200_disp1_sub", GATE_IP_DISP1, 0, 0,
|
||||
0),
|
||||
GATE(CLK_MIE1, "mie1", "mout_aclk200_disp1_sub", GATE_IP_DISP1, 1, 0,
|
||||
0),
|
||||
GATE(CLK_DSIM0, "dsim0", "mout_aclk200_disp1_sub", GATE_IP_DISP1, 3, 0,
|
||||
0),
|
||||
GATE(CLK_DP, "dp", "mout_aclk200_disp1_sub", GATE_IP_DISP1, 4, 0, 0),
|
||||
GATE(CLK_MIXER, "mixer", "mout_aclk200_disp1_sub", GATE_IP_DISP1, 5, 0,
|
||||
0),
|
||||
GATE(CLK_HDMI, "hdmi", "mout_aclk200_disp1_sub", GATE_IP_DISP1, 6, 0,
|
||||
0),
|
||||
|
||||
GATE(CLK_MFC, "mfc", "mout_aclk333_sub", GATE_IP_MFC, 0, 0, 0),
|
||||
GATE(CLK_SMMU_MFCR, "smmu_mfcr", "mout_aclk333_sub", GATE_IP_MFC, 1, 0,
|
||||
0),
|
||||
GATE(CLK_SMMU_MFCL, "smmu_mfcl", "mout_aclk333_sub", GATE_IP_MFC, 2, 0,
|
||||
0),
|
||||
|
||||
GATE(CLK_ROTATOR, "rotator", "div_aclk266", GATE_IP_GEN, 1, 0, 0),
|
||||
GATE(CLK_JPEG, "jpeg", "div_aclk166", GATE_IP_GEN, 2, 0, 0),
|
||||
GATE(CLK_MDMA1, "mdma1", "div_aclk266", GATE_IP_GEN, 4, 0, 0),
|
||||
GATE(CLK_SMMU_ROTATOR, "smmu_rotator", "div_aclk266", GATE_IP_GEN, 6, 0,
|
||||
0),
|
||||
GATE(CLK_SMMU_JPEG, "smmu_jpeg", "div_aclk166", GATE_IP_GEN, 7, 0, 0),
|
||||
GATE(CLK_SMMU_MDMA1, "smmu_mdma1", "div_aclk266", GATE_IP_GEN, 9, 0, 0),
|
||||
|
||||
GATE(CLK_PDMA0, "pdma0", "div_aclk200", GATE_IP_FSYS, 1, 0, 0),
|
||||
GATE(CLK_PDMA1, "pdma1", "div_aclk200", GATE_IP_FSYS, 2, 0, 0),
|
||||
GATE(CLK_SATA, "sata", "div_aclk200", GATE_IP_FSYS, 6, 0, 0),
|
||||
GATE(CLK_USBOTG, "usbotg", "div_aclk200", GATE_IP_FSYS, 7, 0, 0),
|
||||
GATE(CLK_MIPI_HSI, "mipi_hsi", "div_aclk200", GATE_IP_FSYS, 8, 0, 0),
|
||||
GATE(CLK_SDMMC0, "sdmmc0", "div_aclk200", GATE_IP_FSYS, 12, 0, 0),
|
||||
GATE(CLK_SDMMC1, "sdmmc1", "div_aclk200", GATE_IP_FSYS, 13, 0, 0),
|
||||
GATE(CLK_SDMMC2, "sdmmc2", "div_aclk200", GATE_IP_FSYS, 14, 0, 0),
|
||||
GATE(CLK_SDMMC3, "sdmmc3", "div_aclk200", GATE_IP_FSYS, 15, 0, 0),
|
||||
GATE(CLK_SROMC, "sromc", "div_aclk200", GATE_IP_FSYS, 17, 0, 0),
|
||||
GATE(CLK_USB2, "usb2", "div_aclk200", GATE_IP_FSYS, 18, 0, 0),
|
||||
GATE(CLK_USB3, "usb3", "div_aclk200", GATE_IP_FSYS, 19, 0, 0),
|
||||
GATE(CLK_SATA_PHYCTRL, "sata_phyctrl", "div_aclk200",
|
||||
GATE_IP_FSYS, 24, 0, 0),
|
||||
GATE(CLK_SATA_PHYI2C, "sata_phyi2c", "div_aclk200", GATE_IP_FSYS, 25, 0,
|
||||
0),
|
||||
|
||||
GATE(CLK_UART0, "uart0", "div_aclk66", GATE_IP_PERIC, 0, 0, 0),
|
||||
GATE(CLK_UART1, "uart1", "div_aclk66", GATE_IP_PERIC, 1, 0, 0),
|
||||
GATE(CLK_UART2, "uart2", "div_aclk66", GATE_IP_PERIC, 2, 0, 0),
|
||||
GATE(CLK_UART3, "uart3", "div_aclk66", GATE_IP_PERIC, 3, 0, 0),
|
||||
GATE(CLK_UART4, "uart4", "div_aclk66", GATE_IP_PERIC, 4, 0, 0),
|
||||
GATE(CLK_I2C0, "i2c0", "div_aclk66", GATE_IP_PERIC, 6, 0, 0),
|
||||
GATE(CLK_I2C1, "i2c1", "div_aclk66", GATE_IP_PERIC, 7, 0, 0),
|
||||
GATE(CLK_I2C2, "i2c2", "div_aclk66", GATE_IP_PERIC, 8, 0, 0),
|
||||
GATE(CLK_I2C3, "i2c3", "div_aclk66", GATE_IP_PERIC, 9, 0, 0),
|
||||
GATE(CLK_I2C4, "i2c4", "div_aclk66", GATE_IP_PERIC, 10, 0, 0),
|
||||
GATE(CLK_I2C5, "i2c5", "div_aclk66", GATE_IP_PERIC, 11, 0, 0),
|
||||
GATE(CLK_I2C6, "i2c6", "div_aclk66", GATE_IP_PERIC, 12, 0, 0),
|
||||
GATE(CLK_I2C7, "i2c7", "div_aclk66", GATE_IP_PERIC, 13, 0, 0),
|
||||
GATE(CLK_I2C_HDMI, "i2c_hdmi", "div_aclk66", GATE_IP_PERIC, 14, 0, 0),
|
||||
GATE(CLK_ADC, "adc", "div_aclk66", GATE_IP_PERIC, 15, 0, 0),
|
||||
GATE(CLK_SPI0, "spi0", "div_aclk66", GATE_IP_PERIC, 16, 0, 0),
|
||||
GATE(CLK_SPI1, "spi1", "div_aclk66", GATE_IP_PERIC, 17, 0, 0),
|
||||
GATE(CLK_SPI2, "spi2", "div_aclk66", GATE_IP_PERIC, 18, 0, 0),
|
||||
GATE(CLK_I2S1, "i2s1", "div_aclk66", GATE_IP_PERIC, 20, 0, 0),
|
||||
GATE(CLK_I2S2, "i2s2", "div_aclk66", GATE_IP_PERIC, 21, 0, 0),
|
||||
GATE(CLK_PCM1, "pcm1", "div_aclk66", GATE_IP_PERIC, 22, 0, 0),
|
||||
GATE(CLK_PCM2, "pcm2", "div_aclk66", GATE_IP_PERIC, 23, 0, 0),
|
||||
GATE(CLK_PWM, "pwm", "div_aclk66", GATE_IP_PERIC, 24, 0, 0),
|
||||
GATE(CLK_SPDIF, "spdif", "div_aclk66", GATE_IP_PERIC, 26, 0, 0),
|
||||
GATE(CLK_AC97, "ac97", "div_aclk66", GATE_IP_PERIC, 27, 0, 0),
|
||||
GATE(CLK_HSI2C0, "hsi2c0", "div_aclk66", GATE_IP_PERIC, 28, 0, 0),
|
||||
GATE(CLK_HSI2C1, "hsi2c1", "div_aclk66", GATE_IP_PERIC, 29, 0, 0),
|
||||
GATE(CLK_HSI2C2, "hsi2c2", "div_aclk66", GATE_IP_PERIC, 30, 0, 0),
|
||||
GATE(CLK_HSI2C3, "hsi2c3", "div_aclk66", GATE_IP_PERIC, 31, 0, 0),
|
||||
|
||||
GATE(CLK_CHIPID, "chipid", "div_aclk66", GATE_IP_PERIS, 0, 0, 0),
|
||||
GATE(CLK_SYSREG, "sysreg", "div_aclk66",
|
||||
GATE_IP_PERIS, 1, CLK_IGNORE_UNUSED, 0),
|
||||
GATE(CLK_PMU, "pmu", "div_aclk66", GATE_IP_PERIS, 2, CLK_IGNORE_UNUSED,
|
||||
0),
|
||||
GATE(CLK_CMU_TOP, "cmu_top", "div_aclk66",
|
||||
GATE_IP_PERIS, 3, CLK_IGNORE_UNUSED, 0),
|
||||
GATE(CLK_CMU_CORE, "cmu_core", "div_aclk66",
|
||||
GATE_IP_PERIS, 4, CLK_IGNORE_UNUSED, 0),
|
||||
GATE(CLK_CMU_MEM, "cmu_mem", "div_aclk66",
|
||||
GATE_IP_PERIS, 5, CLK_IGNORE_UNUSED, 0),
|
||||
GATE(CLK_TZPC0, "tzpc0", "div_aclk66", GATE_IP_PERIS, 6, 0, 0),
|
||||
GATE(CLK_TZPC1, "tzpc1", "div_aclk66", GATE_IP_PERIS, 7, 0, 0),
|
||||
GATE(CLK_TZPC2, "tzpc2", "div_aclk66", GATE_IP_PERIS, 8, 0, 0),
|
||||
GATE(CLK_TZPC3, "tzpc3", "div_aclk66", GATE_IP_PERIS, 9, 0, 0),
|
||||
GATE(CLK_TZPC4, "tzpc4", "div_aclk66", GATE_IP_PERIS, 10, 0, 0),
|
||||
GATE(CLK_TZPC5, "tzpc5", "div_aclk66", GATE_IP_PERIS, 11, 0, 0),
|
||||
GATE(CLK_TZPC6, "tzpc6", "div_aclk66", GATE_IP_PERIS, 12, 0, 0),
|
||||
GATE(CLK_TZPC7, "tzpc7", "div_aclk66", GATE_IP_PERIS, 13, 0, 0),
|
||||
GATE(CLK_TZPC8, "tzpc8", "div_aclk66", GATE_IP_PERIS, 14, 0, 0),
|
||||
GATE(CLK_TZPC9, "tzpc9", "div_aclk66", GATE_IP_PERIS, 15, 0, 0),
|
||||
GATE(CLK_HDMI_CEC, "hdmi_cec", "div_aclk66", GATE_IP_PERIS, 16, 0, 0),
|
||||
GATE(CLK_MCT, "mct", "div_aclk66", GATE_IP_PERIS, 18, 0, 0),
|
||||
GATE(CLK_WDT, "wdt", "div_aclk66", GATE_IP_PERIS, 19, 0, 0),
|
||||
GATE(CLK_RTC, "rtc", "div_aclk66", GATE_IP_PERIS, 20, 0, 0),
|
||||
GATE(CLK_TMU, "tmu", "div_aclk66", GATE_IP_PERIS, 21, 0, 0),
|
||||
};
|
||||
|
||||
static struct samsung_pll_rate_table vpll_24mhz_tbl[] __initdata = {
|
||||
|
@ -517,20 +599,41 @@ static struct samsung_pll_rate_table epll_24mhz_tbl[] __initdata = {
|
|||
{ },
|
||||
};
|
||||
|
||||
static struct samsung_pll_rate_table apll_24mhz_tbl[] __initdata = {
|
||||
/* sorted in descending order */
|
||||
/* PLL_35XX_RATE(rate, m, p, s) */
|
||||
PLL_35XX_RATE(1700000000, 425, 6, 0),
|
||||
PLL_35XX_RATE(1600000000, 200, 3, 0),
|
||||
PLL_35XX_RATE(1500000000, 250, 4, 0),
|
||||
PLL_35XX_RATE(1400000000, 175, 3, 0),
|
||||
PLL_35XX_RATE(1300000000, 325, 6, 0),
|
||||
PLL_35XX_RATE(1200000000, 200, 4, 0),
|
||||
PLL_35XX_RATE(1100000000, 275, 6, 0),
|
||||
PLL_35XX_RATE(1000000000, 125, 3, 0),
|
||||
PLL_35XX_RATE(900000000, 150, 4, 0),
|
||||
PLL_35XX_RATE(800000000, 100, 3, 0),
|
||||
PLL_35XX_RATE(700000000, 175, 3, 1),
|
||||
PLL_35XX_RATE(600000000, 200, 4, 1),
|
||||
PLL_35XX_RATE(500000000, 125, 3, 1),
|
||||
PLL_35XX_RATE(400000000, 100, 3, 1),
|
||||
PLL_35XX_RATE(300000000, 200, 4, 2),
|
||||
PLL_35XX_RATE(200000000, 100, 3, 2),
|
||||
};
|
||||
|
||||
static struct samsung_pll_clock exynos5250_plls[nr_plls] __initdata = {
|
||||
[apll] = PLL_A(pll_35xx, fout_apll, "fout_apll", "fin_pll", APLL_LOCK,
|
||||
APLL_CON0, "fout_apll", NULL),
|
||||
[mpll] = PLL_A(pll_35xx, fout_mpll, "fout_mpll", "fin_pll", MPLL_LOCK,
|
||||
MPLL_CON0, "fout_mpll", NULL),
|
||||
[bpll] = PLL(pll_35xx, fout_bpll, "fout_bpll", "fin_pll", BPLL_LOCK,
|
||||
[apll] = PLL_A(pll_35xx, CLK_FOUT_APLL, "fout_apll", "fin_pll",
|
||||
APLL_LOCK, APLL_CON0, "fout_apll", NULL),
|
||||
[mpll] = PLL_A(pll_35xx, CLK_FOUT_MPLL, "fout_mpll", "fin_pll",
|
||||
MPLL_LOCK, MPLL_CON0, "fout_mpll", NULL),
|
||||
[bpll] = PLL(pll_35xx, CLK_FOUT_BPLL, "fout_bpll", "fin_pll", BPLL_LOCK,
|
||||
BPLL_CON0, NULL),
|
||||
[gpll] = PLL(pll_35xx, fout_gpll, "fout_gpll", "fin_pll", GPLL_LOCK,
|
||||
[gpll] = PLL(pll_35xx, CLK_FOUT_GPLL, "fout_gpll", "fin_pll", GPLL_LOCK,
|
||||
GPLL_CON0, NULL),
|
||||
[cpll] = PLL(pll_35xx, fout_cpll, "fout_cpll", "fin_pll", CPLL_LOCK,
|
||||
[cpll] = PLL(pll_35xx, CLK_FOUT_CPLL, "fout_cpll", "fin_pll", CPLL_LOCK,
|
||||
CPLL_CON0, NULL),
|
||||
[epll] = PLL(pll_36xx, fout_epll, "fout_epll", "fin_pll", EPLL_LOCK,
|
||||
[epll] = PLL(pll_36xx, CLK_FOUT_EPLL, "fout_epll", "fin_pll", EPLL_LOCK,
|
||||
EPLL_CON0, NULL),
|
||||
[vpll] = PLL(pll_36xx, fout_vpll, "fout_vpll", "mout_vpllsrc",
|
||||
[vpll] = PLL(pll_36xx, CLK_FOUT_VPLL, "fout_vpll", "mout_vpllsrc",
|
||||
VPLL_LOCK, VPLL_CON0, NULL),
|
||||
};
|
||||
|
||||
|
@ -552,7 +655,7 @@ static void __init exynos5250_clk_init(struct device_node *np)
|
|||
panic("%s: unable to determine soc\n", __func__);
|
||||
}
|
||||
|
||||
samsung_clk_init(np, reg_base, nr_clks,
|
||||
samsung_clk_init(np, reg_base, CLK_NR_CLKS,
|
||||
exynos5250_clk_regs, ARRAY_SIZE(exynos5250_clk_regs),
|
||||
NULL, 0);
|
||||
samsung_clk_of_register_fixed_ext(exynos5250_fixed_rate_ext_clks,
|
||||
|
@ -561,8 +664,10 @@ static void __init exynos5250_clk_init(struct device_node *np)
|
|||
samsung_clk_register_mux(exynos5250_pll_pmux_clks,
|
||||
ARRAY_SIZE(exynos5250_pll_pmux_clks));
|
||||
|
||||
if (_get_rate("fin_pll") == 24 * MHZ)
|
||||
if (_get_rate("fin_pll") == 24 * MHZ) {
|
||||
exynos5250_plls[epll].rate_table = epll_24mhz_tbl;
|
||||
exynos5250_plls[apll].rate_table = apll_24mhz_tbl;
|
||||
}
|
||||
|
||||
if (_get_rate("mout_vpllsrc") == 24 * MHZ)
|
||||
exynos5250_plls[vpll].rate_table = vpll_24mhz_tbl;
|
||||
|
@ -581,6 +686,6 @@ static void __init exynos5250_clk_init(struct device_node *np)
|
|||
ARRAY_SIZE(exynos5250_gate_clks));
|
||||
|
||||
pr_info("Exynos5250: clock setup completed, armclk=%ld\n",
|
||||
_get_rate("armclk"));
|
||||
_get_rate("div_arm2"));
|
||||
}
|
||||
CLK_OF_DECLARE(exynos5250_clk, "samsung,exynos5250-clock", exynos5250_clk_init);
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
* Common Clock Framework support for Exynos5420 SoC.
|
||||
*/
|
||||
|
||||
#include <dt-bindings/clock/exynos5420.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/clk-provider.h>
|
||||
|
@ -107,48 +108,6 @@ enum exynos5420_plls {
|
|||
nr_plls /* number of PLLs */
|
||||
};
|
||||
|
||||
enum exynos5420_clks {
|
||||
none,
|
||||
|
||||
/* core clocks */
|
||||
fin_pll, fout_apll, fout_cpll, fout_dpll, fout_epll, fout_rpll,
|
||||
fout_ipll, fout_spll, fout_vpll, fout_mpll, fout_bpll, fout_kpll,
|
||||
|
||||
/* gate for special clocks (sclk) */
|
||||
sclk_uart0 = 128, sclk_uart1, sclk_uart2, sclk_uart3, sclk_mmc0,
|
||||
sclk_mmc1, sclk_mmc2, sclk_spi0, sclk_spi1, sclk_spi2, sclk_i2s1,
|
||||
sclk_i2s2, sclk_pcm1, sclk_pcm2, sclk_spdif, sclk_hdmi, sclk_pixel,
|
||||
sclk_dp1, sclk_mipi1, sclk_fimd1, sclk_maudio0, sclk_maupcm0,
|
||||
sclk_usbd300, sclk_usbd301, sclk_usbphy300, sclk_usbphy301, sclk_unipro,
|
||||
sclk_pwm, sclk_gscl_wa, sclk_gscl_wb, sclk_hdmiphy,
|
||||
|
||||
/* gate clocks */
|
||||
aclk66_peric = 256, uart0, uart1, uart2, uart3, i2c0, i2c1, i2c2, i2c3,
|
||||
i2c4, i2c5, i2c6, i2c7, i2c_hdmi, tsadc, spi0, spi1, spi2, keyif, i2s1,
|
||||
i2s2, pcm1, pcm2, pwm, spdif, i2c8, i2c9, i2c10, aclk66_psgen = 300,
|
||||
chipid, sysreg, tzpc0, tzpc1, tzpc2, tzpc3, tzpc4, tzpc5, tzpc6, tzpc7,
|
||||
tzpc8, tzpc9, hdmi_cec, seckey, mct, wdt, rtc, tmu, tmu_gpu,
|
||||
pclk66_gpio = 330, aclk200_fsys2 = 350, mmc0, mmc1, mmc2, sromc, ufs,
|
||||
aclk200_fsys = 360, tsi, pdma0, pdma1, rtic, usbh20, usbd300, usbd301,
|
||||
aclk400_mscl = 380, mscl0, mscl1, mscl2, smmu_mscl0, smmu_mscl1,
|
||||
smmu_mscl2, aclk333 = 400, mfc, smmu_mfcl, smmu_mfcr,
|
||||
aclk200_disp1 = 410, dsim1, dp1, hdmi, aclk300_disp1 = 420, fimd1,
|
||||
smmu_fimd1, aclk166 = 430, mixer, aclk266 = 440, rotator, mdma1,
|
||||
smmu_rotator, smmu_mdma1, aclk300_jpeg = 450, jpeg, jpeg2, smmu_jpeg,
|
||||
aclk300_gscl = 460, smmu_gscl0, smmu_gscl1, gscl_wa, gscl_wb, gscl0,
|
||||
gscl1, clk_3aa, aclk266_g2d = 470, sss, slim_sss, mdma0,
|
||||
aclk333_g2d = 480, g2d, aclk333_432_gscl = 490, smmu_3aa, smmu_fimcl0,
|
||||
smmu_fimcl1, smmu_fimcl3, fimc_lite3, aclk_g3d = 500, g3d, smmu_mixer,
|
||||
|
||||
/* mux clocks */
|
||||
mout_hdmi = 640,
|
||||
|
||||
/* divider clocks */
|
||||
dout_pixel = 768,
|
||||
|
||||
nr_clks,
|
||||
};
|
||||
|
||||
/*
|
||||
* list of controller registers to be saved and restored during a
|
||||
* suspend/resume cycle.
|
||||
|
@ -298,225 +257,226 @@ PNAME(maudio0_p) = { "fin_pll", "maudio_clk", "sclk_dpll", "sclk_mpll",
|
|||
|
||||
/* fixed rate clocks generated outside the soc */
|
||||
static struct samsung_fixed_rate_clock exynos5420_fixed_rate_ext_clks[] __initdata = {
|
||||
FRATE(fin_pll, "fin_pll", NULL, CLK_IS_ROOT, 0),
|
||||
FRATE(CLK_FIN_PLL, "fin_pll", NULL, CLK_IS_ROOT, 0),
|
||||
};
|
||||
|
||||
/* fixed rate clocks generated inside the soc */
|
||||
static struct samsung_fixed_rate_clock exynos5420_fixed_rate_clks[] __initdata = {
|
||||
FRATE(sclk_hdmiphy, "sclk_hdmiphy", NULL, CLK_IS_ROOT, 24000000),
|
||||
FRATE(none, "sclk_pwi", NULL, CLK_IS_ROOT, 24000000),
|
||||
FRATE(none, "sclk_usbh20", NULL, CLK_IS_ROOT, 48000000),
|
||||
FRATE(none, "mphy_refclk_ixtal24", NULL, CLK_IS_ROOT, 48000000),
|
||||
FRATE(none, "sclk_usbh20_scan_clk", NULL, CLK_IS_ROOT, 480000000),
|
||||
FRATE(CLK_SCLK_HDMIPHY, "sclk_hdmiphy", NULL, CLK_IS_ROOT, 24000000),
|
||||
FRATE(0, "sclk_pwi", NULL, CLK_IS_ROOT, 24000000),
|
||||
FRATE(0, "sclk_usbh20", NULL, CLK_IS_ROOT, 48000000),
|
||||
FRATE(0, "mphy_refclk_ixtal24", NULL, CLK_IS_ROOT, 48000000),
|
||||
FRATE(0, "sclk_usbh20_scan_clk", NULL, CLK_IS_ROOT, 480000000),
|
||||
};
|
||||
|
||||
static struct samsung_fixed_factor_clock exynos5420_fixed_factor_clks[] __initdata = {
|
||||
FFACTOR(none, "sclk_hsic_12m", "fin_pll", 1, 2, 0),
|
||||
FFACTOR(0, "sclk_hsic_12m", "fin_pll", 1, 2, 0),
|
||||
};
|
||||
|
||||
static struct samsung_mux_clock exynos5420_mux_clks[] __initdata = {
|
||||
MUX(none, "mout_mspll_kfc", mspll_cpu_p, SRC_TOP7, 8, 2),
|
||||
MUX(none, "mout_mspll_cpu", mspll_cpu_p, SRC_TOP7, 12, 2),
|
||||
MUX(none, "mout_apll", apll_p, SRC_CPU, 0, 1),
|
||||
MUX(none, "mout_cpu", cpu_p, SRC_CPU, 16, 1),
|
||||
MUX(none, "mout_kpll", kpll_p, SRC_KFC, 0, 1),
|
||||
MUX(none, "mout_cpu_kfc", kfc_p, SRC_KFC, 16, 1),
|
||||
MUX(0, "mout_mspll_kfc", mspll_cpu_p, SRC_TOP7, 8, 2),
|
||||
MUX(0, "mout_mspll_cpu", mspll_cpu_p, SRC_TOP7, 12, 2),
|
||||
MUX(0, "mout_apll", apll_p, SRC_CPU, 0, 1),
|
||||
MUX(0, "mout_cpu", cpu_p, SRC_CPU, 16, 1),
|
||||
MUX(0, "mout_kpll", kpll_p, SRC_KFC, 0, 1),
|
||||
MUX(0, "mout_cpu_kfc", kfc_p, SRC_KFC, 16, 1),
|
||||
|
||||
MUX(none, "sclk_bpll", bpll_p, SRC_CDREX, 0, 1),
|
||||
MUX(0, "sclk_bpll", bpll_p, SRC_CDREX, 0, 1),
|
||||
|
||||
MUX_A(none, "mout_aclk400_mscl", group1_p,
|
||||
MUX_A(0, "mout_aclk400_mscl", group1_p,
|
||||
SRC_TOP0, 4, 2, "aclk400_mscl"),
|
||||
MUX(none, "mout_aclk200", group1_p, SRC_TOP0, 8, 2),
|
||||
MUX(none, "mout_aclk200_fsys2", group1_p, SRC_TOP0, 12, 2),
|
||||
MUX(none, "mout_aclk200_fsys", group1_p, SRC_TOP0, 28, 2),
|
||||
MUX(0, "mout_aclk200", group1_p, SRC_TOP0, 8, 2),
|
||||
MUX(0, "mout_aclk200_fsys2", group1_p, SRC_TOP0, 12, 2),
|
||||
MUX(0, "mout_aclk200_fsys", group1_p, SRC_TOP0, 28, 2),
|
||||
|
||||
MUX(none, "mout_aclk333_432_gscl", group4_p, SRC_TOP1, 0, 2),
|
||||
MUX(none, "mout_aclk66", group1_p, SRC_TOP1, 8, 2),
|
||||
MUX(none, "mout_aclk266", group1_p, SRC_TOP1, 20, 2),
|
||||
MUX(none, "mout_aclk166", group1_p, SRC_TOP1, 24, 2),
|
||||
MUX(none, "mout_aclk333", group1_p, SRC_TOP1, 28, 2),
|
||||
MUX(0, "mout_aclk333_432_gscl", group4_p, SRC_TOP1, 0, 2),
|
||||
MUX(0, "mout_aclk66", group1_p, SRC_TOP1, 8, 2),
|
||||
MUX(0, "mout_aclk266", group1_p, SRC_TOP1, 20, 2),
|
||||
MUX(0, "mout_aclk166", group1_p, SRC_TOP1, 24, 2),
|
||||
MUX(0, "mout_aclk333", group1_p, SRC_TOP1, 28, 2),
|
||||
|
||||
MUX(none, "mout_aclk333_g2d", group1_p, SRC_TOP2, 8, 2),
|
||||
MUX(none, "mout_aclk266_g2d", group1_p, SRC_TOP2, 12, 2),
|
||||
MUX(none, "mout_aclk_g3d", group5_p, SRC_TOP2, 16, 1),
|
||||
MUX(none, "mout_aclk300_jpeg", group1_p, SRC_TOP2, 20, 2),
|
||||
MUX(none, "mout_aclk300_disp1", group1_p, SRC_TOP2, 24, 2),
|
||||
MUX(none, "mout_aclk300_gscl", group1_p, SRC_TOP2, 28, 2),
|
||||
MUX(0, "mout_aclk333_g2d", group1_p, SRC_TOP2, 8, 2),
|
||||
MUX(0, "mout_aclk266_g2d", group1_p, SRC_TOP2, 12, 2),
|
||||
MUX(0, "mout_aclk_g3d", group5_p, SRC_TOP2, 16, 1),
|
||||
MUX(0, "mout_aclk300_jpeg", group1_p, SRC_TOP2, 20, 2),
|
||||
MUX(0, "mout_aclk300_disp1", group1_p, SRC_TOP2, 24, 2),
|
||||
MUX(0, "mout_aclk300_gscl", group1_p, SRC_TOP2, 28, 2),
|
||||
|
||||
MUX(none, "mout_user_aclk400_mscl", user_aclk400_mscl_p,
|
||||
MUX(0, "mout_user_aclk400_mscl", user_aclk400_mscl_p,
|
||||
SRC_TOP3, 4, 1),
|
||||
MUX_A(none, "mout_aclk200_disp1", aclk200_disp1_p,
|
||||
MUX_A(0, "mout_aclk200_disp1", aclk200_disp1_p,
|
||||
SRC_TOP3, 8, 1, "aclk200_disp1"),
|
||||
MUX(none, "mout_user_aclk200_fsys2", user_aclk200_fsys2_p,
|
||||
MUX(0, "mout_user_aclk200_fsys2", user_aclk200_fsys2_p,
|
||||
SRC_TOP3, 12, 1),
|
||||
MUX(none, "mout_user_aclk200_fsys", user_aclk200_fsys_p,
|
||||
MUX(0, "mout_user_aclk200_fsys", user_aclk200_fsys_p,
|
||||
SRC_TOP3, 28, 1),
|
||||
|
||||
MUX(none, "mout_user_aclk333_432_gscl", user_aclk333_432_gscl_p,
|
||||
MUX(0, "mout_user_aclk333_432_gscl", user_aclk333_432_gscl_p,
|
||||
SRC_TOP4, 0, 1),
|
||||
MUX(none, "mout_aclk66_peric", aclk66_peric_p, SRC_TOP4, 8, 1),
|
||||
MUX(none, "mout_user_aclk266", user_aclk266_p, SRC_TOP4, 20, 1),
|
||||
MUX(none, "mout_user_aclk166", user_aclk166_p, SRC_TOP4, 24, 1),
|
||||
MUX(none, "mout_user_aclk333", user_aclk333_p, SRC_TOP4, 28, 1),
|
||||
MUX(0, "mout_aclk66_peric", aclk66_peric_p, SRC_TOP4, 8, 1),
|
||||
MUX(0, "mout_user_aclk266", user_aclk266_p, SRC_TOP4, 20, 1),
|
||||
MUX(0, "mout_user_aclk166", user_aclk166_p, SRC_TOP4, 24, 1),
|
||||
MUX(0, "mout_user_aclk333", user_aclk333_p, SRC_TOP4, 28, 1),
|
||||
|
||||
MUX(none, "mout_aclk66_psgen", aclk66_peric_p, SRC_TOP5, 4, 1),
|
||||
MUX(none, "mout_user_aclk333_g2d", user_aclk333_g2d_p, SRC_TOP5, 8, 1),
|
||||
MUX(none, "mout_user_aclk266_g2d", user_aclk266_g2d_p, SRC_TOP5, 12, 1),
|
||||
MUX_A(none, "mout_user_aclk_g3d", user_aclk_g3d_p,
|
||||
MUX(0, "mout_aclk66_psgen", aclk66_peric_p, SRC_TOP5, 4, 1),
|
||||
MUX(0, "mout_user_aclk333_g2d", user_aclk333_g2d_p, SRC_TOP5, 8, 1),
|
||||
MUX(0, "mout_user_aclk266_g2d", user_aclk266_g2d_p, SRC_TOP5, 12, 1),
|
||||
MUX_A(0, "mout_user_aclk_g3d", user_aclk_g3d_p,
|
||||
SRC_TOP5, 16, 1, "aclkg3d"),
|
||||
MUX(none, "mout_user_aclk300_jpeg", user_aclk300_jpeg_p,
|
||||
MUX(0, "mout_user_aclk300_jpeg", user_aclk300_jpeg_p,
|
||||
SRC_TOP5, 20, 1),
|
||||
MUX(none, "mout_user_aclk300_disp1", user_aclk300_disp1_p,
|
||||
MUX(0, "mout_user_aclk300_disp1", user_aclk300_disp1_p,
|
||||
SRC_TOP5, 24, 1),
|
||||
MUX(none, "mout_user_aclk300_gscl", user_aclk300_gscl_p,
|
||||
MUX(0, "mout_user_aclk300_gscl", user_aclk300_gscl_p,
|
||||
SRC_TOP5, 28, 1),
|
||||
|
||||
MUX(none, "sclk_mpll", mpll_p, SRC_TOP6, 0, 1),
|
||||
MUX(none, "sclk_vpll", vpll_p, SRC_TOP6, 4, 1),
|
||||
MUX(none, "sclk_spll", spll_p, SRC_TOP6, 8, 1),
|
||||
MUX(none, "sclk_ipll", ipll_p, SRC_TOP6, 12, 1),
|
||||
MUX(none, "sclk_rpll", rpll_p, SRC_TOP6, 16, 1),
|
||||
MUX(none, "sclk_epll", epll_p, SRC_TOP6, 20, 1),
|
||||
MUX(none, "sclk_dpll", dpll_p, SRC_TOP6, 24, 1),
|
||||
MUX(none, "sclk_cpll", cpll_p, SRC_TOP6, 28, 1),
|
||||
MUX(0, "sclk_mpll", mpll_p, SRC_TOP6, 0, 1),
|
||||
MUX(0, "sclk_vpll", vpll_p, SRC_TOP6, 4, 1),
|
||||
MUX(0, "sclk_spll", spll_p, SRC_TOP6, 8, 1),
|
||||
MUX(0, "sclk_ipll", ipll_p, SRC_TOP6, 12, 1),
|
||||
MUX(0, "sclk_rpll", rpll_p, SRC_TOP6, 16, 1),
|
||||
MUX(0, "sclk_epll", epll_p, SRC_TOP6, 20, 1),
|
||||
MUX(0, "sclk_dpll", dpll_p, SRC_TOP6, 24, 1),
|
||||
MUX(0, "sclk_cpll", cpll_p, SRC_TOP6, 28, 1),
|
||||
|
||||
MUX(none, "mout_sw_aclk400_mscl", sw_aclk400_mscl_p, SRC_TOP10, 4, 1),
|
||||
MUX(none, "mout_sw_aclk200", sw_aclk200_p, SRC_TOP10, 8, 1),
|
||||
MUX(none, "mout_sw_aclk200_fsys2", sw_aclk200_fsys2_p,
|
||||
MUX(0, "mout_sw_aclk400_mscl", sw_aclk400_mscl_p, SRC_TOP10, 4, 1),
|
||||
MUX(0, "mout_sw_aclk200", sw_aclk200_p, SRC_TOP10, 8, 1),
|
||||
MUX(0, "mout_sw_aclk200_fsys2", sw_aclk200_fsys2_p,
|
||||
SRC_TOP10, 12, 1),
|
||||
MUX(none, "mout_sw_aclk200_fsys", sw_aclk200_fsys_p, SRC_TOP10, 28, 1),
|
||||
MUX(0, "mout_sw_aclk200_fsys", sw_aclk200_fsys_p, SRC_TOP10, 28, 1),
|
||||
|
||||
MUX(none, "mout_sw_aclk333_432_gscl", sw_aclk333_432_gscl_p,
|
||||
MUX(0, "mout_sw_aclk333_432_gscl", sw_aclk333_432_gscl_p,
|
||||
SRC_TOP11, 0, 1),
|
||||
MUX(none, "mout_sw_aclk66", sw_aclk66_p, SRC_TOP11, 8, 1),
|
||||
MUX(none, "mout_sw_aclk266", sw_aclk266_p, SRC_TOP11, 20, 1),
|
||||
MUX(none, "mout_sw_aclk166", sw_aclk166_p, SRC_TOP11, 24, 1),
|
||||
MUX(none, "mout_sw_aclk333", sw_aclk333_p, SRC_TOP11, 28, 1),
|
||||
MUX(0, "mout_sw_aclk66", sw_aclk66_p, SRC_TOP11, 8, 1),
|
||||
MUX(0, "mout_sw_aclk266", sw_aclk266_p, SRC_TOP11, 20, 1),
|
||||
MUX(0, "mout_sw_aclk166", sw_aclk166_p, SRC_TOP11, 24, 1),
|
||||
MUX(0, "mout_sw_aclk333", sw_aclk333_p, SRC_TOP11, 28, 1),
|
||||
|
||||
MUX(none, "mout_sw_aclk333_g2d", sw_aclk333_g2d_p, SRC_TOP12, 8, 1),
|
||||
MUX(none, "mout_sw_aclk266_g2d", sw_aclk266_g2d_p, SRC_TOP12, 12, 1),
|
||||
MUX(none, "mout_sw_aclk_g3d", sw_aclk_g3d_p, SRC_TOP12, 16, 1),
|
||||
MUX(none, "mout_sw_aclk300_jpeg", sw_aclk300_jpeg_p, SRC_TOP12, 20, 1),
|
||||
MUX(none, "mout_sw_aclk300_disp1", sw_aclk300_disp1_p,
|
||||
MUX(0, "mout_sw_aclk333_g2d", sw_aclk333_g2d_p, SRC_TOP12, 8, 1),
|
||||
MUX(0, "mout_sw_aclk266_g2d", sw_aclk266_g2d_p, SRC_TOP12, 12, 1),
|
||||
MUX(0, "mout_sw_aclk_g3d", sw_aclk_g3d_p, SRC_TOP12, 16, 1),
|
||||
MUX(0, "mout_sw_aclk300_jpeg", sw_aclk300_jpeg_p, SRC_TOP12, 20, 1),
|
||||
MUX(0, "mout_sw_aclk300_disp1", sw_aclk300_disp1_p,
|
||||
SRC_TOP12, 24, 1),
|
||||
MUX(none, "mout_sw_aclk300_gscl", sw_aclk300_gscl_p, SRC_TOP12, 28, 1),
|
||||
MUX(0, "mout_sw_aclk300_gscl", sw_aclk300_gscl_p, SRC_TOP12, 28, 1),
|
||||
|
||||
/* DISP1 Block */
|
||||
MUX(none, "mout_fimd1", group3_p, SRC_DISP10, 4, 1),
|
||||
MUX(none, "mout_mipi1", group2_p, SRC_DISP10, 16, 3),
|
||||
MUX(none, "mout_dp1", group2_p, SRC_DISP10, 20, 3),
|
||||
MUX(none, "mout_pixel", group2_p, SRC_DISP10, 24, 3),
|
||||
MUX(mout_hdmi, "mout_hdmi", hdmi_p, SRC_DISP10, 28, 1),
|
||||
MUX(0, "mout_fimd1", group3_p, SRC_DISP10, 4, 1),
|
||||
MUX(0, "mout_mipi1", group2_p, SRC_DISP10, 16, 3),
|
||||
MUX(0, "mout_dp1", group2_p, SRC_DISP10, 20, 3),
|
||||
MUX(0, "mout_pixel", group2_p, SRC_DISP10, 24, 3),
|
||||
MUX(CLK_MOUT_HDMI, "mout_hdmi", hdmi_p, SRC_DISP10, 28, 1),
|
||||
|
||||
/* MAU Block */
|
||||
MUX(none, "mout_maudio0", maudio0_p, SRC_MAU, 28, 3),
|
||||
MUX(0, "mout_maudio0", maudio0_p, SRC_MAU, 28, 3),
|
||||
|
||||
/* FSYS Block */
|
||||
MUX(none, "mout_usbd301", group2_p, SRC_FSYS, 4, 3),
|
||||
MUX(none, "mout_mmc0", group2_p, SRC_FSYS, 8, 3),
|
||||
MUX(none, "mout_mmc1", group2_p, SRC_FSYS, 12, 3),
|
||||
MUX(none, "mout_mmc2", group2_p, SRC_FSYS, 16, 3),
|
||||
MUX(none, "mout_usbd300", group2_p, SRC_FSYS, 20, 3),
|
||||
MUX(none, "mout_unipro", group2_p, SRC_FSYS, 24, 3),
|
||||
MUX(0, "mout_usbd301", group2_p, SRC_FSYS, 4, 3),
|
||||
MUX(0, "mout_mmc0", group2_p, SRC_FSYS, 8, 3),
|
||||
MUX(0, "mout_mmc1", group2_p, SRC_FSYS, 12, 3),
|
||||
MUX(0, "mout_mmc2", group2_p, SRC_FSYS, 16, 3),
|
||||
MUX(0, "mout_usbd300", group2_p, SRC_FSYS, 20, 3),
|
||||
MUX(0, "mout_unipro", group2_p, SRC_FSYS, 24, 3),
|
||||
|
||||
/* PERIC Block */
|
||||
MUX(none, "mout_uart0", group2_p, SRC_PERIC0, 4, 3),
|
||||
MUX(none, "mout_uart1", group2_p, SRC_PERIC0, 8, 3),
|
||||
MUX(none, "mout_uart2", group2_p, SRC_PERIC0, 12, 3),
|
||||
MUX(none, "mout_uart3", group2_p, SRC_PERIC0, 16, 3),
|
||||
MUX(none, "mout_pwm", group2_p, SRC_PERIC0, 24, 3),
|
||||
MUX(none, "mout_spdif", spdif_p, SRC_PERIC0, 28, 3),
|
||||
MUX(none, "mout_audio0", audio0_p, SRC_PERIC1, 8, 3),
|
||||
MUX(none, "mout_audio1", audio1_p, SRC_PERIC1, 12, 3),
|
||||
MUX(none, "mout_audio2", audio2_p, SRC_PERIC1, 16, 3),
|
||||
MUX(none, "mout_spi0", group2_p, SRC_PERIC1, 20, 3),
|
||||
MUX(none, "mout_spi1", group2_p, SRC_PERIC1, 24, 3),
|
||||
MUX(none, "mout_spi2", group2_p, SRC_PERIC1, 28, 3),
|
||||
MUX(0, "mout_uart0", group2_p, SRC_PERIC0, 4, 3),
|
||||
MUX(0, "mout_uart1", group2_p, SRC_PERIC0, 8, 3),
|
||||
MUX(0, "mout_uart2", group2_p, SRC_PERIC0, 12, 3),
|
||||
MUX(0, "mout_uart3", group2_p, SRC_PERIC0, 16, 3),
|
||||
MUX(0, "mout_pwm", group2_p, SRC_PERIC0, 24, 3),
|
||||
MUX(0, "mout_spdif", spdif_p, SRC_PERIC0, 28, 3),
|
||||
MUX(0, "mout_audio0", audio0_p, SRC_PERIC1, 8, 3),
|
||||
MUX(0, "mout_audio1", audio1_p, SRC_PERIC1, 12, 3),
|
||||
MUX(0, "mout_audio2", audio2_p, SRC_PERIC1, 16, 3),
|
||||
MUX(0, "mout_spi0", group2_p, SRC_PERIC1, 20, 3),
|
||||
MUX(0, "mout_spi1", group2_p, SRC_PERIC1, 24, 3),
|
||||
MUX(0, "mout_spi2", group2_p, SRC_PERIC1, 28, 3),
|
||||
};
|
||||
|
||||
static struct samsung_div_clock exynos5420_div_clks[] __initdata = {
|
||||
DIV(none, "div_arm", "mout_cpu", DIV_CPU0, 0, 3),
|
||||
DIV(none, "sclk_apll", "mout_apll", DIV_CPU0, 24, 3),
|
||||
DIV(none, "armclk2", "div_arm", DIV_CPU0, 28, 3),
|
||||
DIV(none, "div_kfc", "mout_cpu_kfc", DIV_KFC0, 0, 3),
|
||||
DIV(none, "sclk_kpll", "mout_kpll", DIV_KFC0, 24, 3),
|
||||
DIV(0, "div_arm", "mout_cpu", DIV_CPU0, 0, 3),
|
||||
DIV(0, "sclk_apll", "mout_apll", DIV_CPU0, 24, 3),
|
||||
DIV(0, "armclk2", "div_arm", DIV_CPU0, 28, 3),
|
||||
DIV(0, "div_kfc", "mout_cpu_kfc", DIV_KFC0, 0, 3),
|
||||
DIV(0, "sclk_kpll", "mout_kpll", DIV_KFC0, 24, 3),
|
||||
|
||||
DIV(none, "dout_aclk400_mscl", "mout_aclk400_mscl", DIV_TOP0, 4, 3),
|
||||
DIV(none, "dout_aclk200", "mout_aclk200", DIV_TOP0, 8, 3),
|
||||
DIV(none, "dout_aclk200_fsys2", "mout_aclk200_fsys2", DIV_TOP0, 12, 3),
|
||||
DIV(none, "dout_pclk200_fsys", "mout_pclk200_fsys", DIV_TOP0, 24, 3),
|
||||
DIV(none, "dout_aclk200_fsys", "mout_aclk200_fsys", DIV_TOP0, 28, 3),
|
||||
DIV(0, "dout_aclk400_mscl", "mout_aclk400_mscl", DIV_TOP0, 4, 3),
|
||||
DIV(0, "dout_aclk200", "mout_aclk200", DIV_TOP0, 8, 3),
|
||||
DIV(0, "dout_aclk200_fsys2", "mout_aclk200_fsys2", DIV_TOP0, 12, 3),
|
||||
DIV(0, "dout_pclk200_fsys", "mout_pclk200_fsys", DIV_TOP0, 24, 3),
|
||||
DIV(0, "dout_aclk200_fsys", "mout_aclk200_fsys", DIV_TOP0, 28, 3),
|
||||
|
||||
DIV(none, "dout_aclk333_432_gscl", "mout_aclk333_432_gscl",
|
||||
DIV(0, "dout_aclk333_432_gscl", "mout_aclk333_432_gscl",
|
||||
DIV_TOP1, 0, 3),
|
||||
DIV(none, "dout_aclk66", "mout_aclk66", DIV_TOP1, 8, 6),
|
||||
DIV(none, "dout_aclk266", "mout_aclk266", DIV_TOP1, 20, 3),
|
||||
DIV(none, "dout_aclk166", "mout_aclk166", DIV_TOP1, 24, 3),
|
||||
DIV(none, "dout_aclk333", "mout_aclk333", DIV_TOP1, 28, 3),
|
||||
DIV(0, "dout_aclk66", "mout_aclk66", DIV_TOP1, 8, 6),
|
||||
DIV(0, "dout_aclk266", "mout_aclk266", DIV_TOP1, 20, 3),
|
||||
DIV(0, "dout_aclk166", "mout_aclk166", DIV_TOP1, 24, 3),
|
||||
DIV(0, "dout_aclk333", "mout_aclk333", DIV_TOP1, 28, 3),
|
||||
|
||||
DIV(none, "dout_aclk333_g2d", "mout_aclk333_g2d", DIV_TOP2, 8, 3),
|
||||
DIV(none, "dout_aclk266_g2d", "mout_aclk266_g2d", DIV_TOP2, 12, 3),
|
||||
DIV(none, "dout_aclk_g3d", "mout_aclk_g3d", DIV_TOP2, 16, 3),
|
||||
DIV(none, "dout_aclk300_jpeg", "mout_aclk300_jpeg", DIV_TOP2, 20, 3),
|
||||
DIV_A(none, "dout_aclk300_disp1", "mout_aclk300_disp1",
|
||||
DIV(0, "dout_aclk333_g2d", "mout_aclk333_g2d", DIV_TOP2, 8, 3),
|
||||
DIV(0, "dout_aclk266_g2d", "mout_aclk266_g2d", DIV_TOP2, 12, 3),
|
||||
DIV(0, "dout_aclk_g3d", "mout_aclk_g3d", DIV_TOP2, 16, 3),
|
||||
DIV(0, "dout_aclk300_jpeg", "mout_aclk300_jpeg", DIV_TOP2, 20, 3),
|
||||
DIV_A(0, "dout_aclk300_disp1", "mout_aclk300_disp1",
|
||||
DIV_TOP2, 24, 3, "aclk300_disp1"),
|
||||
DIV(none, "dout_aclk300_gscl", "mout_aclk300_gscl", DIV_TOP2, 28, 3),
|
||||
DIV(0, "dout_aclk300_gscl", "mout_aclk300_gscl", DIV_TOP2, 28, 3),
|
||||
|
||||
/* DISP1 Block */
|
||||
DIV(none, "dout_fimd1", "mout_fimd1", DIV_DISP10, 0, 4),
|
||||
DIV(none, "dout_mipi1", "mout_mipi1", DIV_DISP10, 16, 8),
|
||||
DIV(none, "dout_dp1", "mout_dp1", DIV_DISP10, 24, 4),
|
||||
DIV(dout_pixel, "dout_hdmi_pixel", "mout_pixel", DIV_DISP10, 28, 4),
|
||||
DIV(0, "dout_fimd1", "mout_fimd1", DIV_DISP10, 0, 4),
|
||||
DIV(0, "dout_mipi1", "mout_mipi1", DIV_DISP10, 16, 8),
|
||||
DIV(0, "dout_dp1", "mout_dp1", DIV_DISP10, 24, 4),
|
||||
DIV(CLK_DOUT_PIXEL, "dout_hdmi_pixel", "mout_pixel", DIV_DISP10, 28, 4),
|
||||
|
||||
/* Audio Block */
|
||||
DIV(none, "dout_maudio0", "mout_maudio0", DIV_MAU, 20, 4),
|
||||
DIV(none, "dout_maupcm0", "dout_maudio0", DIV_MAU, 24, 8),
|
||||
DIV(0, "dout_maudio0", "mout_maudio0", DIV_MAU, 20, 4),
|
||||
DIV(0, "dout_maupcm0", "dout_maudio0", DIV_MAU, 24, 8),
|
||||
|
||||
/* USB3.0 */
|
||||
DIV(none, "dout_usbphy301", "mout_usbd301", DIV_FSYS0, 12, 4),
|
||||
DIV(none, "dout_usbphy300", "mout_usbd300", DIV_FSYS0, 16, 4),
|
||||
DIV(none, "dout_usbd301", "mout_usbd301", DIV_FSYS0, 20, 4),
|
||||
DIV(none, "dout_usbd300", "mout_usbd300", DIV_FSYS0, 24, 4),
|
||||
DIV(0, "dout_usbphy301", "mout_usbd301", DIV_FSYS0, 12, 4),
|
||||
DIV(0, "dout_usbphy300", "mout_usbd300", DIV_FSYS0, 16, 4),
|
||||
DIV(0, "dout_usbd301", "mout_usbd301", DIV_FSYS0, 20, 4),
|
||||
DIV(0, "dout_usbd300", "mout_usbd300", DIV_FSYS0, 24, 4),
|
||||
|
||||
/* MMC */
|
||||
DIV(none, "dout_mmc0", "mout_mmc0", DIV_FSYS1, 0, 10),
|
||||
DIV(none, "dout_mmc1", "mout_mmc1", DIV_FSYS1, 10, 10),
|
||||
DIV(none, "dout_mmc2", "mout_mmc2", DIV_FSYS1, 20, 10),
|
||||
DIV(0, "dout_mmc0", "mout_mmc0", DIV_FSYS1, 0, 10),
|
||||
DIV(0, "dout_mmc1", "mout_mmc1", DIV_FSYS1, 10, 10),
|
||||
DIV(0, "dout_mmc2", "mout_mmc2", DIV_FSYS1, 20, 10),
|
||||
|
||||
DIV(none, "dout_unipro", "mout_unipro", DIV_FSYS2, 24, 8),
|
||||
DIV(0, "dout_unipro", "mout_unipro", DIV_FSYS2, 24, 8),
|
||||
|
||||
/* UART and PWM */
|
||||
DIV(none, "dout_uart0", "mout_uart0", DIV_PERIC0, 8, 4),
|
||||
DIV(none, "dout_uart1", "mout_uart1", DIV_PERIC0, 12, 4),
|
||||
DIV(none, "dout_uart2", "mout_uart2", DIV_PERIC0, 16, 4),
|
||||
DIV(none, "dout_uart3", "mout_uart3", DIV_PERIC0, 20, 4),
|
||||
DIV(none, "dout_pwm", "mout_pwm", DIV_PERIC0, 28, 4),
|
||||
DIV(0, "dout_uart0", "mout_uart0", DIV_PERIC0, 8, 4),
|
||||
DIV(0, "dout_uart1", "mout_uart1", DIV_PERIC0, 12, 4),
|
||||
DIV(0, "dout_uart2", "mout_uart2", DIV_PERIC0, 16, 4),
|
||||
DIV(0, "dout_uart3", "mout_uart3", DIV_PERIC0, 20, 4),
|
||||
DIV(0, "dout_pwm", "mout_pwm", DIV_PERIC0, 28, 4),
|
||||
|
||||
/* SPI */
|
||||
DIV(none, "dout_spi0", "mout_spi0", DIV_PERIC1, 20, 4),
|
||||
DIV(none, "dout_spi1", "mout_spi1", DIV_PERIC1, 24, 4),
|
||||
DIV(none, "dout_spi2", "mout_spi2", DIV_PERIC1, 28, 4),
|
||||
DIV(0, "dout_spi0", "mout_spi0", DIV_PERIC1, 20, 4),
|
||||
DIV(0, "dout_spi1", "mout_spi1", DIV_PERIC1, 24, 4),
|
||||
DIV(0, "dout_spi2", "mout_spi2", DIV_PERIC1, 28, 4),
|
||||
|
||||
/* PCM */
|
||||
DIV(none, "dout_pcm1", "dout_audio1", DIV_PERIC2, 16, 8),
|
||||
DIV(none, "dout_pcm2", "dout_audio2", DIV_PERIC2, 24, 8),
|
||||
DIV(0, "dout_pcm1", "dout_audio1", DIV_PERIC2, 16, 8),
|
||||
DIV(0, "dout_pcm2", "dout_audio2", DIV_PERIC2, 24, 8),
|
||||
|
||||
/* Audio - I2S */
|
||||
DIV(none, "dout_i2s1", "dout_audio1", DIV_PERIC3, 6, 6),
|
||||
DIV(none, "dout_i2s2", "dout_audio2", DIV_PERIC3, 12, 6),
|
||||
DIV(none, "dout_audio0", "mout_audio0", DIV_PERIC3, 20, 4),
|
||||
DIV(none, "dout_audio1", "mout_audio1", DIV_PERIC3, 24, 4),
|
||||
DIV(none, "dout_audio2", "mout_audio2", DIV_PERIC3, 28, 4),
|
||||
DIV(0, "dout_i2s1", "dout_audio1", DIV_PERIC3, 6, 6),
|
||||
DIV(0, "dout_i2s2", "dout_audio2", DIV_PERIC3, 12, 6),
|
||||
DIV(0, "dout_audio0", "mout_audio0", DIV_PERIC3, 20, 4),
|
||||
DIV(0, "dout_audio1", "mout_audio1", DIV_PERIC3, 24, 4),
|
||||
DIV(0, "dout_audio2", "mout_audio2", DIV_PERIC3, 28, 4),
|
||||
|
||||
/* SPI Pre-Ratio */
|
||||
DIV(none, "dout_pre_spi0", "dout_spi0", DIV_PERIC4, 8, 8),
|
||||
DIV(none, "dout_pre_spi1", "dout_spi1", DIV_PERIC4, 16, 8),
|
||||
DIV(none, "dout_pre_spi2", "dout_spi2", DIV_PERIC4, 24, 8),
|
||||
DIV(0, "dout_pre_spi0", "dout_spi0", DIV_PERIC4, 8, 8),
|
||||
DIV(0, "dout_pre_spi1", "dout_spi1", DIV_PERIC4, 16, 8),
|
||||
DIV(0, "dout_pre_spi2", "dout_spi2", DIV_PERIC4, 24, 8),
|
||||
};
|
||||
|
||||
static struct samsung_gate_clock exynos5420_gate_clks[] __initdata = {
|
||||
/* TODO: Re-verify the CG bits for all the gate clocks */
|
||||
GATE_A(mct, "pclk_st", "aclk66_psgen", GATE_BUS_PERIS1, 2, 0, 0, "mct"),
|
||||
GATE_A(CLK_MCT, "pclk_st", "aclk66_psgen", GATE_BUS_PERIS1, 2, 0, 0,
|
||||
"mct"),
|
||||
|
||||
GATE(0, "aclk200_fsys", "mout_user_aclk200_fsys",
|
||||
GATE_BUS_FSYS0, 9, CLK_IGNORE_UNUSED, 0),
|
||||
|
@ -545,217 +505,227 @@ static struct samsung_gate_clock exynos5420_gate_clks[] __initdata = {
|
|||
GATE_BUS_TOP, 15, CLK_IGNORE_UNUSED, 0),
|
||||
|
||||
/* sclk */
|
||||
GATE(sclk_uart0, "sclk_uart0", "dout_uart0",
|
||||
GATE(CLK_SCLK_UART0, "sclk_uart0", "dout_uart0",
|
||||
GATE_TOP_SCLK_PERIC, 0, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_uart1, "sclk_uart1", "dout_uart1",
|
||||
GATE(CLK_SCLK_UART1, "sclk_uart1", "dout_uart1",
|
||||
GATE_TOP_SCLK_PERIC, 1, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_uart2, "sclk_uart2", "dout_uart2",
|
||||
GATE(CLK_SCLK_UART2, "sclk_uart2", "dout_uart2",
|
||||
GATE_TOP_SCLK_PERIC, 2, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_uart3, "sclk_uart3", "dout_uart3",
|
||||
GATE(CLK_SCLK_UART3, "sclk_uart3", "dout_uart3",
|
||||
GATE_TOP_SCLK_PERIC, 3, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_spi0, "sclk_spi0", "dout_pre_spi0",
|
||||
GATE(CLK_SCLK_SPI0, "sclk_spi0", "dout_pre_spi0",
|
||||
GATE_TOP_SCLK_PERIC, 6, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_spi1, "sclk_spi1", "dout_pre_spi1",
|
||||
GATE(CLK_SCLK_SPI1, "sclk_spi1", "dout_pre_spi1",
|
||||
GATE_TOP_SCLK_PERIC, 7, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_spi2, "sclk_spi2", "dout_pre_spi2",
|
||||
GATE(CLK_SCLK_SPI2, "sclk_spi2", "dout_pre_spi2",
|
||||
GATE_TOP_SCLK_PERIC, 8, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_spdif, "sclk_spdif", "mout_spdif",
|
||||
GATE(CLK_SCLK_SPDIF, "sclk_spdif", "mout_spdif",
|
||||
GATE_TOP_SCLK_PERIC, 9, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_pwm, "sclk_pwm", "dout_pwm",
|
||||
GATE(CLK_SCLK_PWM, "sclk_pwm", "dout_pwm",
|
||||
GATE_TOP_SCLK_PERIC, 11, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_pcm1, "sclk_pcm1", "dout_pcm1",
|
||||
GATE(CLK_SCLK_PCM1, "sclk_pcm1", "dout_pcm1",
|
||||
GATE_TOP_SCLK_PERIC, 15, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_pcm2, "sclk_pcm2", "dout_pcm2",
|
||||
GATE(CLK_SCLK_PCM2, "sclk_pcm2", "dout_pcm2",
|
||||
GATE_TOP_SCLK_PERIC, 16, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_i2s1, "sclk_i2s1", "dout_i2s1",
|
||||
GATE(CLK_SCLK_I2S1, "sclk_i2s1", "dout_i2s1",
|
||||
GATE_TOP_SCLK_PERIC, 17, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_i2s2, "sclk_i2s2", "dout_i2s2",
|
||||
GATE(CLK_SCLK_I2S2, "sclk_i2s2", "dout_i2s2",
|
||||
GATE_TOP_SCLK_PERIC, 18, CLK_SET_RATE_PARENT, 0),
|
||||
|
||||
GATE(sclk_mmc0, "sclk_mmc0", "dout_mmc0",
|
||||
GATE(CLK_SCLK_MMC0, "sclk_mmc0", "dout_mmc0",
|
||||
GATE_TOP_SCLK_FSYS, 0, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_mmc1, "sclk_mmc1", "dout_mmc1",
|
||||
GATE(CLK_SCLK_MMC1, "sclk_mmc1", "dout_mmc1",
|
||||
GATE_TOP_SCLK_FSYS, 1, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_mmc2, "sclk_mmc2", "dout_mmc2",
|
||||
GATE(CLK_SCLK_MMC2, "sclk_mmc2", "dout_mmc2",
|
||||
GATE_TOP_SCLK_FSYS, 2, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_usbphy301, "sclk_usbphy301", "dout_usbphy301",
|
||||
GATE(CLK_SCLK_USBPHY301, "sclk_usbphy301", "dout_usbphy301",
|
||||
GATE_TOP_SCLK_FSYS, 7, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_usbphy300, "sclk_usbphy300", "dout_usbphy300",
|
||||
GATE(CLK_SCLK_USBPHY300, "sclk_usbphy300", "dout_usbphy300",
|
||||
GATE_TOP_SCLK_FSYS, 8, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_usbd300, "sclk_usbd300", "dout_usbd300",
|
||||
GATE(CLK_SCLK_USBD300, "sclk_usbd300", "dout_usbd300",
|
||||
GATE_TOP_SCLK_FSYS, 9, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_usbd301, "sclk_usbd301", "dout_usbd301",
|
||||
GATE(CLK_SCLK_USBD301, "sclk_usbd301", "dout_usbd301",
|
||||
GATE_TOP_SCLK_FSYS, 10, CLK_SET_RATE_PARENT, 0),
|
||||
|
||||
GATE(sclk_usbd301, "sclk_unipro", "dout_unipro",
|
||||
GATE(CLK_SCLK_USBD301, "sclk_unipro", "dout_unipro",
|
||||
SRC_MASK_FSYS, 24, CLK_SET_RATE_PARENT, 0),
|
||||
|
||||
GATE(sclk_gscl_wa, "sclk_gscl_wa", "aclK333_432_gscl",
|
||||
GATE(CLK_SCLK_GSCL_WA, "sclk_gscl_wa", "aclK333_432_gscl",
|
||||
GATE_TOP_SCLK_GSCL, 6, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_gscl_wb, "sclk_gscl_wb", "aclk333_432_gscl",
|
||||
GATE(CLK_SCLK_GSCL_WB, "sclk_gscl_wb", "aclk333_432_gscl",
|
||||
GATE_TOP_SCLK_GSCL, 7, CLK_SET_RATE_PARENT, 0),
|
||||
|
||||
/* Display */
|
||||
GATE(sclk_fimd1, "sclk_fimd1", "dout_fimd1",
|
||||
GATE(CLK_SCLK_FIMD1, "sclk_fimd1", "dout_fimd1",
|
||||
GATE_TOP_SCLK_DISP1, 0, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_mipi1, "sclk_mipi1", "dout_mipi1",
|
||||
GATE(CLK_SCLK_MIPI1, "sclk_mipi1", "dout_mipi1",
|
||||
GATE_TOP_SCLK_DISP1, 3, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_hdmi, "sclk_hdmi", "mout_hdmi",
|
||||
GATE(CLK_SCLK_HDMI, "sclk_hdmi", "mout_hdmi",
|
||||
GATE_TOP_SCLK_DISP1, 9, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_pixel, "sclk_pixel", "dout_hdmi_pixel",
|
||||
GATE(CLK_SCLK_PIXEL, "sclk_pixel", "dout_hdmi_pixel",
|
||||
GATE_TOP_SCLK_DISP1, 10, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_dp1, "sclk_dp1", "dout_dp1",
|
||||
GATE(CLK_SCLK_DP1, "sclk_dp1", "dout_dp1",
|
||||
GATE_TOP_SCLK_DISP1, 20, CLK_SET_RATE_PARENT, 0),
|
||||
|
||||
/* Maudio Block */
|
||||
GATE(sclk_maudio0, "sclk_maudio0", "dout_maudio0",
|
||||
GATE(CLK_SCLK_MAUDIO0, "sclk_maudio0", "dout_maudio0",
|
||||
GATE_TOP_SCLK_MAU, 0, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(sclk_maupcm0, "sclk_maupcm0", "dout_maupcm0",
|
||||
GATE(CLK_SCLK_MAUPCM0, "sclk_maupcm0", "dout_maupcm0",
|
||||
GATE_TOP_SCLK_MAU, 1, CLK_SET_RATE_PARENT, 0),
|
||||
/* FSYS */
|
||||
GATE(tsi, "tsi", "aclk200_fsys", GATE_BUS_FSYS0, 0, 0, 0),
|
||||
GATE(pdma0, "pdma0", "aclk200_fsys", GATE_BUS_FSYS0, 1, 0, 0),
|
||||
GATE(pdma1, "pdma1", "aclk200_fsys", GATE_BUS_FSYS0, 2, 0, 0),
|
||||
GATE(ufs, "ufs", "aclk200_fsys2", GATE_BUS_FSYS0, 3, 0, 0),
|
||||
GATE(rtic, "rtic", "aclk200_fsys", GATE_BUS_FSYS0, 5, 0, 0),
|
||||
GATE(mmc0, "mmc0", "aclk200_fsys2", GATE_BUS_FSYS0, 12, 0, 0),
|
||||
GATE(mmc1, "mmc1", "aclk200_fsys2", GATE_BUS_FSYS0, 13, 0, 0),
|
||||
GATE(mmc2, "mmc2", "aclk200_fsys2", GATE_BUS_FSYS0, 14, 0, 0),
|
||||
GATE(sromc, "sromc", "aclk200_fsys2",
|
||||
GATE(CLK_TSI, "tsi", "aclk200_fsys", GATE_BUS_FSYS0, 0, 0, 0),
|
||||
GATE(CLK_PDMA0, "pdma0", "aclk200_fsys", GATE_BUS_FSYS0, 1, 0, 0),
|
||||
GATE(CLK_PDMA1, "pdma1", "aclk200_fsys", GATE_BUS_FSYS0, 2, 0, 0),
|
||||
GATE(CLK_UFS, "ufs", "aclk200_fsys2", GATE_BUS_FSYS0, 3, 0, 0),
|
||||
GATE(CLK_RTIC, "rtic", "aclk200_fsys", GATE_BUS_FSYS0, 5, 0, 0),
|
||||
GATE(CLK_MMC0, "mmc0", "aclk200_fsys2", GATE_BUS_FSYS0, 12, 0, 0),
|
||||
GATE(CLK_MMC1, "mmc1", "aclk200_fsys2", GATE_BUS_FSYS0, 13, 0, 0),
|
||||
GATE(CLK_MMC2, "mmc2", "aclk200_fsys2", GATE_BUS_FSYS0, 14, 0, 0),
|
||||
GATE(CLK_SROMC, "sromc", "aclk200_fsys2",
|
||||
GATE_BUS_FSYS0, 19, CLK_IGNORE_UNUSED, 0),
|
||||
GATE(usbh20, "usbh20", "aclk200_fsys", GATE_BUS_FSYS0, 20, 0, 0),
|
||||
GATE(usbd300, "usbd300", "aclk200_fsys", GATE_BUS_FSYS0, 21, 0, 0),
|
||||
GATE(usbd301, "usbd301", "aclk200_fsys", GATE_BUS_FSYS0, 28, 0, 0),
|
||||
GATE(CLK_USBH20, "usbh20", "aclk200_fsys", GATE_BUS_FSYS0, 20, 0, 0),
|
||||
GATE(CLK_USBD300, "usbd300", "aclk200_fsys", GATE_BUS_FSYS0, 21, 0, 0),
|
||||
GATE(CLK_USBD301, "usbd301", "aclk200_fsys", GATE_BUS_FSYS0, 28, 0, 0),
|
||||
|
||||
/* UART */
|
||||
GATE(uart0, "uart0", "aclk66_peric", GATE_BUS_PERIC, 4, 0, 0),
|
||||
GATE(uart1, "uart1", "aclk66_peric", GATE_BUS_PERIC, 5, 0, 0),
|
||||
GATE_A(uart2, "uart2", "aclk66_peric",
|
||||
GATE(CLK_UART0, "uart0", "aclk66_peric", GATE_BUS_PERIC, 4, 0, 0),
|
||||
GATE(CLK_UART1, "uart1", "aclk66_peric", GATE_BUS_PERIC, 5, 0, 0),
|
||||
GATE_A(CLK_UART2, "uart2", "aclk66_peric",
|
||||
GATE_BUS_PERIC, 6, CLK_IGNORE_UNUSED, 0, "uart2"),
|
||||
GATE(uart3, "uart3", "aclk66_peric", GATE_BUS_PERIC, 7, 0, 0),
|
||||
GATE(CLK_UART3, "uart3", "aclk66_peric", GATE_BUS_PERIC, 7, 0, 0),
|
||||
/* I2C */
|
||||
GATE(i2c0, "i2c0", "aclk66_peric", GATE_BUS_PERIC, 9, 0, 0),
|
||||
GATE(i2c1, "i2c1", "aclk66_peric", GATE_BUS_PERIC, 10, 0, 0),
|
||||
GATE(i2c2, "i2c2", "aclk66_peric", GATE_BUS_PERIC, 11, 0, 0),
|
||||
GATE(i2c3, "i2c3", "aclk66_peric", GATE_BUS_PERIC, 12, 0, 0),
|
||||
GATE(i2c4, "i2c4", "aclk66_peric", GATE_BUS_PERIC, 13, 0, 0),
|
||||
GATE(i2c5, "i2c5", "aclk66_peric", GATE_BUS_PERIC, 14, 0, 0),
|
||||
GATE(i2c6, "i2c6", "aclk66_peric", GATE_BUS_PERIC, 15, 0, 0),
|
||||
GATE(i2c7, "i2c7", "aclk66_peric", GATE_BUS_PERIC, 16, 0, 0),
|
||||
GATE(i2c_hdmi, "i2c_hdmi", "aclk66_peric", GATE_BUS_PERIC, 17, 0, 0),
|
||||
GATE(tsadc, "tsadc", "aclk66_peric", GATE_BUS_PERIC, 18, 0, 0),
|
||||
GATE(CLK_I2C0, "i2c0", "aclk66_peric", GATE_BUS_PERIC, 9, 0, 0),
|
||||
GATE(CLK_I2C1, "i2c1", "aclk66_peric", GATE_BUS_PERIC, 10, 0, 0),
|
||||
GATE(CLK_I2C2, "i2c2", "aclk66_peric", GATE_BUS_PERIC, 11, 0, 0),
|
||||
GATE(CLK_I2C3, "i2c3", "aclk66_peric", GATE_BUS_PERIC, 12, 0, 0),
|
||||
GATE(CLK_I2C4, "i2c4", "aclk66_peric", GATE_BUS_PERIC, 13, 0, 0),
|
||||
GATE(CLK_I2C5, "i2c5", "aclk66_peric", GATE_BUS_PERIC, 14, 0, 0),
|
||||
GATE(CLK_I2C6, "i2c6", "aclk66_peric", GATE_BUS_PERIC, 15, 0, 0),
|
||||
GATE(CLK_I2C7, "i2c7", "aclk66_peric", GATE_BUS_PERIC, 16, 0, 0),
|
||||
GATE(CLK_I2C_HDMI, "i2c_hdmi", "aclk66_peric", GATE_BUS_PERIC, 17, 0,
|
||||
0),
|
||||
GATE(CLK_TSADC, "tsadc", "aclk66_peric", GATE_BUS_PERIC, 18, 0, 0),
|
||||
/* SPI */
|
||||
GATE(spi0, "spi0", "aclk66_peric", GATE_BUS_PERIC, 19, 0, 0),
|
||||
GATE(spi1, "spi1", "aclk66_peric", GATE_BUS_PERIC, 20, 0, 0),
|
||||
GATE(spi2, "spi2", "aclk66_peric", GATE_BUS_PERIC, 21, 0, 0),
|
||||
GATE(keyif, "keyif", "aclk66_peric", GATE_BUS_PERIC, 22, 0, 0),
|
||||
GATE(CLK_SPI0, "spi0", "aclk66_peric", GATE_BUS_PERIC, 19, 0, 0),
|
||||
GATE(CLK_SPI1, "spi1", "aclk66_peric", GATE_BUS_PERIC, 20, 0, 0),
|
||||
GATE(CLK_SPI2, "spi2", "aclk66_peric", GATE_BUS_PERIC, 21, 0, 0),
|
||||
GATE(CLK_KEYIF, "keyif", "aclk66_peric", GATE_BUS_PERIC, 22, 0, 0),
|
||||
/* I2S */
|
||||
GATE(i2s1, "i2s1", "aclk66_peric", GATE_BUS_PERIC, 23, 0, 0),
|
||||
GATE(i2s2, "i2s2", "aclk66_peric", GATE_BUS_PERIC, 24, 0, 0),
|
||||
GATE(CLK_I2S1, "i2s1", "aclk66_peric", GATE_BUS_PERIC, 23, 0, 0),
|
||||
GATE(CLK_I2S2, "i2s2", "aclk66_peric", GATE_BUS_PERIC, 24, 0, 0),
|
||||
/* PCM */
|
||||
GATE(pcm1, "pcm1", "aclk66_peric", GATE_BUS_PERIC, 25, 0, 0),
|
||||
GATE(pcm2, "pcm2", "aclk66_peric", GATE_BUS_PERIC, 26, 0, 0),
|
||||
GATE(CLK_PCM1, "pcm1", "aclk66_peric", GATE_BUS_PERIC, 25, 0, 0),
|
||||
GATE(CLK_PCM2, "pcm2", "aclk66_peric", GATE_BUS_PERIC, 26, 0, 0),
|
||||
/* PWM */
|
||||
GATE(pwm, "pwm", "aclk66_peric", GATE_BUS_PERIC, 27, 0, 0),
|
||||
GATE(CLK_PWM, "pwm", "aclk66_peric", GATE_BUS_PERIC, 27, 0, 0),
|
||||
/* SPDIF */
|
||||
GATE(spdif, "spdif", "aclk66_peric", GATE_BUS_PERIC, 29, 0, 0),
|
||||
GATE(CLK_SPDIF, "spdif", "aclk66_peric", GATE_BUS_PERIC, 29, 0, 0),
|
||||
|
||||
GATE(i2c8, "i2c8", "aclk66_peric", GATE_BUS_PERIC1, 0, 0, 0),
|
||||
GATE(i2c9, "i2c9", "aclk66_peric", GATE_BUS_PERIC1, 1, 0, 0),
|
||||
GATE(i2c10, "i2c10", "aclk66_peric", GATE_BUS_PERIC1, 2, 0, 0),
|
||||
GATE(CLK_I2C8, "i2c8", "aclk66_peric", GATE_BUS_PERIC1, 0, 0, 0),
|
||||
GATE(CLK_I2C9, "i2c9", "aclk66_peric", GATE_BUS_PERIC1, 1, 0, 0),
|
||||
GATE(CLK_I2C10, "i2c10", "aclk66_peric", GATE_BUS_PERIC1, 2, 0, 0),
|
||||
|
||||
GATE(chipid, "chipid", "aclk66_psgen",
|
||||
GATE(CLK_CHIPID, "chipid", "aclk66_psgen",
|
||||
GATE_BUS_PERIS0, 12, CLK_IGNORE_UNUSED, 0),
|
||||
GATE(sysreg, "sysreg", "aclk66_psgen",
|
||||
GATE(CLK_SYSREG, "sysreg", "aclk66_psgen",
|
||||
GATE_BUS_PERIS0, 13, CLK_IGNORE_UNUSED, 0),
|
||||
GATE(tzpc0, "tzpc0", "aclk66_psgen", GATE_BUS_PERIS0, 18, 0, 0),
|
||||
GATE(tzpc1, "tzpc1", "aclk66_psgen", GATE_BUS_PERIS0, 19, 0, 0),
|
||||
GATE(tzpc2, "tzpc2", "aclk66_psgen", GATE_BUS_PERIS0, 20, 0, 0),
|
||||
GATE(tzpc3, "tzpc3", "aclk66_psgen", GATE_BUS_PERIS0, 21, 0, 0),
|
||||
GATE(tzpc4, "tzpc4", "aclk66_psgen", GATE_BUS_PERIS0, 22, 0, 0),
|
||||
GATE(tzpc5, "tzpc5", "aclk66_psgen", GATE_BUS_PERIS0, 23, 0, 0),
|
||||
GATE(tzpc6, "tzpc6", "aclk66_psgen", GATE_BUS_PERIS0, 24, 0, 0),
|
||||
GATE(tzpc7, "tzpc7", "aclk66_psgen", GATE_BUS_PERIS0, 25, 0, 0),
|
||||
GATE(tzpc8, "tzpc8", "aclk66_psgen", GATE_BUS_PERIS0, 26, 0, 0),
|
||||
GATE(tzpc9, "tzpc9", "aclk66_psgen", GATE_BUS_PERIS0, 27, 0, 0),
|
||||
GATE(CLK_TZPC0, "tzpc0", "aclk66_psgen", GATE_BUS_PERIS0, 18, 0, 0),
|
||||
GATE(CLK_TZPC1, "tzpc1", "aclk66_psgen", GATE_BUS_PERIS0, 19, 0, 0),
|
||||
GATE(CLK_TZPC2, "tzpc2", "aclk66_psgen", GATE_BUS_PERIS0, 20, 0, 0),
|
||||
GATE(CLK_TZPC3, "tzpc3", "aclk66_psgen", GATE_BUS_PERIS0, 21, 0, 0),
|
||||
GATE(CLK_TZPC4, "tzpc4", "aclk66_psgen", GATE_BUS_PERIS0, 22, 0, 0),
|
||||
GATE(CLK_TZPC5, "tzpc5", "aclk66_psgen", GATE_BUS_PERIS0, 23, 0, 0),
|
||||
GATE(CLK_TZPC6, "tzpc6", "aclk66_psgen", GATE_BUS_PERIS0, 24, 0, 0),
|
||||
GATE(CLK_TZPC7, "tzpc7", "aclk66_psgen", GATE_BUS_PERIS0, 25, 0, 0),
|
||||
GATE(CLK_TZPC8, "tzpc8", "aclk66_psgen", GATE_BUS_PERIS0, 26, 0, 0),
|
||||
GATE(CLK_TZPC9, "tzpc9", "aclk66_psgen", GATE_BUS_PERIS0, 27, 0, 0),
|
||||
|
||||
GATE(hdmi_cec, "hdmi_cec", "aclk66_psgen", GATE_BUS_PERIS1, 0, 0, 0),
|
||||
GATE(seckey, "seckey", "aclk66_psgen", GATE_BUS_PERIS1, 1, 0, 0),
|
||||
GATE(wdt, "wdt", "aclk66_psgen", GATE_BUS_PERIS1, 3, 0, 0),
|
||||
GATE(rtc, "rtc", "aclk66_psgen", GATE_BUS_PERIS1, 4, 0, 0),
|
||||
GATE(tmu, "tmu", "aclk66_psgen", GATE_BUS_PERIS1, 5, 0, 0),
|
||||
GATE(tmu_gpu, "tmu_gpu", "aclk66_psgen", GATE_BUS_PERIS1, 6, 0, 0),
|
||||
GATE(CLK_HDMI_CEC, "hdmi_cec", "aclk66_psgen", GATE_BUS_PERIS1, 0, 0,
|
||||
0),
|
||||
GATE(CLK_SECKEY, "seckey", "aclk66_psgen", GATE_BUS_PERIS1, 1, 0, 0),
|
||||
GATE(CLK_WDT, "wdt", "aclk66_psgen", GATE_BUS_PERIS1, 3, 0, 0),
|
||||
GATE(CLK_RTC, "rtc", "aclk66_psgen", GATE_BUS_PERIS1, 4, 0, 0),
|
||||
GATE(CLK_TMU, "tmu", "aclk66_psgen", GATE_BUS_PERIS1, 5, 0, 0),
|
||||
GATE(CLK_TMU_GPU, "tmu_gpu", "aclk66_psgen", GATE_BUS_PERIS1, 6, 0, 0),
|
||||
|
||||
GATE(gscl0, "gscl0", "aclk300_gscl", GATE_IP_GSCL0, 0, 0, 0),
|
||||
GATE(gscl1, "gscl1", "aclk300_gscl", GATE_IP_GSCL0, 1, 0, 0),
|
||||
GATE(clk_3aa, "clk_3aa", "aclk300_gscl", GATE_IP_GSCL0, 4, 0, 0),
|
||||
GATE(CLK_GSCL0, "gscl0", "aclk300_gscl", GATE_IP_GSCL0, 0, 0, 0),
|
||||
GATE(CLK_GSCL1, "gscl1", "aclk300_gscl", GATE_IP_GSCL0, 1, 0, 0),
|
||||
GATE(CLK_CLK_3AA, "clk_3aa", "aclk300_gscl", GATE_IP_GSCL0, 4, 0, 0),
|
||||
|
||||
GATE(smmu_3aa, "smmu_3aa", "aclk333_432_gscl", GATE_IP_GSCL1, 2, 0, 0),
|
||||
GATE(smmu_fimcl0, "smmu_fimcl0", "aclk333_432_gscl",
|
||||
GATE(CLK_SMMU_3AA, "smmu_3aa", "aclk333_432_gscl", GATE_IP_GSCL1, 2, 0,
|
||||
0),
|
||||
GATE(CLK_SMMU_FIMCL0, "smmu_fimcl0", "aclk333_432_gscl",
|
||||
GATE_IP_GSCL1, 3, 0, 0),
|
||||
GATE(smmu_fimcl1, "smmu_fimcl1", "aclk333_432_gscl",
|
||||
GATE(CLK_SMMU_FIMCL1, "smmu_fimcl1", "aclk333_432_gscl",
|
||||
GATE_IP_GSCL1, 4, 0, 0),
|
||||
GATE(smmu_gscl0, "smmu_gscl0", "aclk300_gscl", GATE_IP_GSCL1, 6, 0, 0),
|
||||
GATE(smmu_gscl1, "smmu_gscl1", "aclk300_gscl", GATE_IP_GSCL1, 7, 0, 0),
|
||||
GATE(gscl_wa, "gscl_wa", "aclk300_gscl", GATE_IP_GSCL1, 12, 0, 0),
|
||||
GATE(gscl_wb, "gscl_wb", "aclk300_gscl", GATE_IP_GSCL1, 13, 0, 0),
|
||||
GATE(smmu_fimcl3, "smmu_fimcl3,", "aclk333_432_gscl",
|
||||
GATE(CLK_SMMU_GSCL0, "smmu_gscl0", "aclk300_gscl", GATE_IP_GSCL1, 6, 0,
|
||||
0),
|
||||
GATE(CLK_SMMU_GSCL1, "smmu_gscl1", "aclk300_gscl", GATE_IP_GSCL1, 7, 0,
|
||||
0),
|
||||
GATE(CLK_GSCL_WA, "gscl_wa", "aclk300_gscl", GATE_IP_GSCL1, 12, 0, 0),
|
||||
GATE(CLK_GSCL_WB, "gscl_wb", "aclk300_gscl", GATE_IP_GSCL1, 13, 0, 0),
|
||||
GATE(CLK_SMMU_FIMCL3, "smmu_fimcl3,", "aclk333_432_gscl",
|
||||
GATE_IP_GSCL1, 16, 0, 0),
|
||||
GATE(fimc_lite3, "fimc_lite3", "aclk333_432_gscl",
|
||||
GATE(CLK_FIMC_LITE3, "fimc_lite3", "aclk333_432_gscl",
|
||||
GATE_IP_GSCL1, 17, 0, 0),
|
||||
|
||||
GATE(fimd1, "fimd1", "aclk300_disp1", GATE_IP_DISP1, 0, 0, 0),
|
||||
GATE(dsim1, "dsim1", "aclk200_disp1", GATE_IP_DISP1, 3, 0, 0),
|
||||
GATE(dp1, "dp1", "aclk200_disp1", GATE_IP_DISP1, 4, 0, 0),
|
||||
GATE(mixer, "mixer", "aclk166", GATE_IP_DISP1, 5, 0, 0),
|
||||
GATE(hdmi, "hdmi", "aclk200_disp1", GATE_IP_DISP1, 6, 0, 0),
|
||||
GATE(smmu_fimd1, "smmu_fimd1", "aclk300_disp1", GATE_IP_DISP1, 8, 0, 0),
|
||||
GATE(CLK_FIMD1, "fimd1", "aclk300_disp1", GATE_IP_DISP1, 0, 0, 0),
|
||||
GATE(CLK_DSIM1, "dsim1", "aclk200_disp1", GATE_IP_DISP1, 3, 0, 0),
|
||||
GATE(CLK_DP1, "dp1", "aclk200_disp1", GATE_IP_DISP1, 4, 0, 0),
|
||||
GATE(CLK_MIXER, "mixer", "aclk166", GATE_IP_DISP1, 5, 0, 0),
|
||||
GATE(CLK_HDMI, "hdmi", "aclk200_disp1", GATE_IP_DISP1, 6, 0, 0),
|
||||
GATE(CLK_SMMU_FIMD1, "smmu_fimd1", "aclk300_disp1", GATE_IP_DISP1, 8, 0,
|
||||
0),
|
||||
|
||||
GATE(mfc, "mfc", "aclk333", GATE_IP_MFC, 0, 0, 0),
|
||||
GATE(smmu_mfcl, "smmu_mfcl", "aclk333", GATE_IP_MFC, 1, 0, 0),
|
||||
GATE(smmu_mfcr, "smmu_mfcr", "aclk333", GATE_IP_MFC, 2, 0, 0),
|
||||
GATE(CLK_MFC, "mfc", "aclk333", GATE_IP_MFC, 0, 0, 0),
|
||||
GATE(CLK_SMMU_MFCL, "smmu_mfcl", "aclk333", GATE_IP_MFC, 1, 0, 0),
|
||||
GATE(CLK_SMMU_MFCR, "smmu_mfcr", "aclk333", GATE_IP_MFC, 2, 0, 0),
|
||||
|
||||
GATE(g3d, "g3d", "aclkg3d", GATE_IP_G3D, 9, 0, 0),
|
||||
GATE(CLK_G3D, "g3d", "aclkg3d", GATE_IP_G3D, 9, 0, 0),
|
||||
|
||||
GATE(rotator, "rotator", "aclk266", GATE_IP_GEN, 1, 0, 0),
|
||||
GATE(jpeg, "jpeg", "aclk300_jpeg", GATE_IP_GEN, 2, 0, 0),
|
||||
GATE(jpeg2, "jpeg2", "aclk300_jpeg", GATE_IP_GEN, 3, 0, 0),
|
||||
GATE(mdma1, "mdma1", "aclk266", GATE_IP_GEN, 4, 0, 0),
|
||||
GATE(smmu_rotator, "smmu_rotator", "aclk266", GATE_IP_GEN, 6, 0, 0),
|
||||
GATE(smmu_jpeg, "smmu_jpeg", "aclk300_jpeg", GATE_IP_GEN, 7, 0, 0),
|
||||
GATE(smmu_mdma1, "smmu_mdma1", "aclk266", GATE_IP_GEN, 9, 0, 0),
|
||||
GATE(CLK_ROTATOR, "rotator", "aclk266", GATE_IP_GEN, 1, 0, 0),
|
||||
GATE(CLK_JPEG, "jpeg", "aclk300_jpeg", GATE_IP_GEN, 2, 0, 0),
|
||||
GATE(CLK_JPEG2, "jpeg2", "aclk300_jpeg", GATE_IP_GEN, 3, 0, 0),
|
||||
GATE(CLK_MDMA1, "mdma1", "aclk266", GATE_IP_GEN, 4, 0, 0),
|
||||
GATE(CLK_SMMU_ROTATOR, "smmu_rotator", "aclk266", GATE_IP_GEN, 6, 0, 0),
|
||||
GATE(CLK_SMMU_JPEG, "smmu_jpeg", "aclk300_jpeg", GATE_IP_GEN, 7, 0, 0),
|
||||
GATE(CLK_SMMU_MDMA1, "smmu_mdma1", "aclk266", GATE_IP_GEN, 9, 0, 0),
|
||||
|
||||
GATE(mscl0, "mscl0", "aclk400_mscl", GATE_IP_MSCL, 0, 0, 0),
|
||||
GATE(mscl1, "mscl1", "aclk400_mscl", GATE_IP_MSCL, 1, 0, 0),
|
||||
GATE(mscl2, "mscl2", "aclk400_mscl", GATE_IP_MSCL, 2, 0, 0),
|
||||
GATE(smmu_mscl0, "smmu_mscl0", "aclk400_mscl", GATE_IP_MSCL, 8, 0, 0),
|
||||
GATE(smmu_mscl1, "smmu_mscl1", "aclk400_mscl", GATE_IP_MSCL, 9, 0, 0),
|
||||
GATE(smmu_mscl2, "smmu_mscl2", "aclk400_mscl", GATE_IP_MSCL, 10, 0, 0),
|
||||
GATE(smmu_mixer, "smmu_mixer", "aclk200_disp1", GATE_IP_DISP1, 9, 0, 0),
|
||||
GATE(CLK_MSCL0, "mscl0", "aclk400_mscl", GATE_IP_MSCL, 0, 0, 0),
|
||||
GATE(CLK_MSCL1, "mscl1", "aclk400_mscl", GATE_IP_MSCL, 1, 0, 0),
|
||||
GATE(CLK_MSCL2, "mscl2", "aclk400_mscl", GATE_IP_MSCL, 2, 0, 0),
|
||||
GATE(CLK_SMMU_MSCL0, "smmu_mscl0", "aclk400_mscl", GATE_IP_MSCL, 8, 0,
|
||||
0),
|
||||
GATE(CLK_SMMU_MSCL1, "smmu_mscl1", "aclk400_mscl", GATE_IP_MSCL, 9, 0,
|
||||
0),
|
||||
GATE(CLK_SMMU_MSCL2, "smmu_mscl2", "aclk400_mscl", GATE_IP_MSCL, 10, 0,
|
||||
0),
|
||||
GATE(CLK_SMMU_MIXER, "smmu_mixer", "aclk200_disp1", GATE_IP_DISP1, 9, 0,
|
||||
0),
|
||||
};
|
||||
|
||||
static struct samsung_pll_clock exynos5420_plls[nr_plls] __initdata = {
|
||||
[apll] = PLL(pll_2550, fout_apll, "fout_apll", "fin_pll", APLL_LOCK,
|
||||
[apll] = PLL(pll_2550, CLK_FOUT_APLL, "fout_apll", "fin_pll", APLL_LOCK,
|
||||
APLL_CON0, NULL),
|
||||
[cpll] = PLL(pll_2550, fout_mpll, "fout_mpll", "fin_pll", MPLL_LOCK,
|
||||
MPLL_CON0, NULL),
|
||||
[dpll] = PLL(pll_2550, fout_dpll, "fout_dpll", "fin_pll", DPLL_LOCK,
|
||||
[cpll] = PLL(pll_2550, CLK_FOUT_CPLL, "fout_cpll", "fin_pll", CPLL_LOCK,
|
||||
CPLL_CON0, NULL),
|
||||
[dpll] = PLL(pll_2550, CLK_FOUT_DPLL, "fout_dpll", "fin_pll", DPLL_LOCK,
|
||||
DPLL_CON0, NULL),
|
||||
[epll] = PLL(pll_2650, fout_epll, "fout_epll", "fin_pll", EPLL_LOCK,
|
||||
[epll] = PLL(pll_2650, CLK_FOUT_EPLL, "fout_epll", "fin_pll", EPLL_LOCK,
|
||||
EPLL_CON0, NULL),
|
||||
[rpll] = PLL(pll_2650, fout_rpll, "fout_rpll", "fin_pll", RPLL_LOCK,
|
||||
[rpll] = PLL(pll_2650, CLK_FOUT_RPLL, "fout_rpll", "fin_pll", RPLL_LOCK,
|
||||
RPLL_CON0, NULL),
|
||||
[ipll] = PLL(pll_2550, fout_ipll, "fout_ipll", "fin_pll", IPLL_LOCK,
|
||||
[ipll] = PLL(pll_2550, CLK_FOUT_IPLL, "fout_ipll", "fin_pll", IPLL_LOCK,
|
||||
IPLL_CON0, NULL),
|
||||
[spll] = PLL(pll_2550, fout_spll, "fout_spll", "fin_pll", SPLL_LOCK,
|
||||
[spll] = PLL(pll_2550, CLK_FOUT_SPLL, "fout_spll", "fin_pll", SPLL_LOCK,
|
||||
SPLL_CON0, NULL),
|
||||
[vpll] = PLL(pll_2550, fout_vpll, "fout_vpll", "fin_pll", VPLL_LOCK,
|
||||
[vpll] = PLL(pll_2550, CLK_FOUT_VPLL, "fout_vpll", "fin_pll", VPLL_LOCK,
|
||||
VPLL_CON0, NULL),
|
||||
[mpll] = PLL(pll_2550, fout_mpll, "fout_mpll", "fin_pll", MPLL_LOCK,
|
||||
[mpll] = PLL(pll_2550, CLK_FOUT_MPLL, "fout_mpll", "fin_pll", MPLL_LOCK,
|
||||
MPLL_CON0, NULL),
|
||||
[bpll] = PLL(pll_2550, fout_bpll, "fout_bpll", "fin_pll", BPLL_LOCK,
|
||||
[bpll] = PLL(pll_2550, CLK_FOUT_BPLL, "fout_bpll", "fin_pll", BPLL_LOCK,
|
||||
BPLL_CON0, NULL),
|
||||
[kpll] = PLL(pll_2550, fout_kpll, "fout_kpll", "fin_pll", KPLL_LOCK,
|
||||
[kpll] = PLL(pll_2550, CLK_FOUT_KPLL, "fout_kpll", "fin_pll", KPLL_LOCK,
|
||||
KPLL_CON0, NULL),
|
||||
};
|
||||
|
||||
|
@ -777,7 +747,7 @@ static void __init exynos5420_clk_init(struct device_node *np)
|
|||
panic("%s: unable to determine soc\n", __func__);
|
||||
}
|
||||
|
||||
samsung_clk_init(np, reg_base, nr_clks,
|
||||
samsung_clk_init(np, reg_base, CLK_NR_CLKS,
|
||||
exynos5420_clk_regs, ARRAY_SIZE(exynos5420_clk_regs),
|
||||
NULL, 0);
|
||||
samsung_clk_of_register_fixed_ext(exynos5420_fixed_rate_ext_clks,
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
* Common Clock Framework support for Exynos5440 SoC.
|
||||
*/
|
||||
|
||||
#include <dt-bindings/clock/exynos5440.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/clk-provider.h>
|
||||
|
@ -22,79 +23,65 @@
|
|||
#define CPU_CLK_STATUS 0xfc
|
||||
#define MISC_DOUT1 0x558
|
||||
|
||||
/*
|
||||
* Let each supported clock get a unique id. This id is used to lookup the clock
|
||||
* for device tree based platforms.
|
||||
*/
|
||||
enum exynos5440_clks {
|
||||
none, xtal, arm_clk,
|
||||
|
||||
spi_baud = 16, pb0_250, pr0_250, pr1_250, b_250, b_125, b_200, sata,
|
||||
usb, gmac0, cs250, pb0_250_o, pr0_250_o, pr1_250_o, b_250_o, b_125_o,
|
||||
b_200_o, sata_o, usb_o, gmac0_o, cs250_o,
|
||||
|
||||
nr_clks,
|
||||
};
|
||||
|
||||
/* parent clock name list */
|
||||
PNAME(mout_armclk_p) = { "cplla", "cpllb" };
|
||||
PNAME(mout_spi_p) = { "div125", "div200" };
|
||||
|
||||
/* fixed rate clocks generated outside the soc */
|
||||
static struct samsung_fixed_rate_clock exynos5440_fixed_rate_ext_clks[] __initdata = {
|
||||
FRATE(none, "xtal", NULL, CLK_IS_ROOT, 0),
|
||||
FRATE(0, "xtal", NULL, CLK_IS_ROOT, 0),
|
||||
};
|
||||
|
||||
/* fixed rate clocks */
|
||||
static struct samsung_fixed_rate_clock exynos5440_fixed_rate_clks[] __initdata = {
|
||||
FRATE(none, "ppll", NULL, CLK_IS_ROOT, 1000000000),
|
||||
FRATE(none, "usb_phy0", NULL, CLK_IS_ROOT, 60000000),
|
||||
FRATE(none, "usb_phy1", NULL, CLK_IS_ROOT, 60000000),
|
||||
FRATE(none, "usb_ohci12", NULL, CLK_IS_ROOT, 12000000),
|
||||
FRATE(none, "usb_ohci48", NULL, CLK_IS_ROOT, 48000000),
|
||||
FRATE(0, "ppll", NULL, CLK_IS_ROOT, 1000000000),
|
||||
FRATE(0, "usb_phy0", NULL, CLK_IS_ROOT, 60000000),
|
||||
FRATE(0, "usb_phy1", NULL, CLK_IS_ROOT, 60000000),
|
||||
FRATE(0, "usb_ohci12", NULL, CLK_IS_ROOT, 12000000),
|
||||
FRATE(0, "usb_ohci48", NULL, CLK_IS_ROOT, 48000000),
|
||||
};
|
||||
|
||||
/* fixed factor clocks */
|
||||
static struct samsung_fixed_factor_clock exynos5440_fixed_factor_clks[] __initdata = {
|
||||
FFACTOR(none, "div250", "ppll", 1, 4, 0),
|
||||
FFACTOR(none, "div200", "ppll", 1, 5, 0),
|
||||
FFACTOR(none, "div125", "div250", 1, 2, 0),
|
||||
FFACTOR(0, "div250", "ppll", 1, 4, 0),
|
||||
FFACTOR(0, "div200", "ppll", 1, 5, 0),
|
||||
FFACTOR(0, "div125", "div250", 1, 2, 0),
|
||||
};
|
||||
|
||||
/* mux clocks */
|
||||
static struct samsung_mux_clock exynos5440_mux_clks[] __initdata = {
|
||||
MUX(none, "mout_spi", mout_spi_p, MISC_DOUT1, 5, 1),
|
||||
MUX_A(arm_clk, "arm_clk", mout_armclk_p,
|
||||
MUX(0, "mout_spi", mout_spi_p, MISC_DOUT1, 5, 1),
|
||||
MUX_A(CLK_ARM_CLK, "arm_clk", mout_armclk_p,
|
||||
CPU_CLK_STATUS, 0, 1, "armclk"),
|
||||
};
|
||||
|
||||
/* divider clocks */
|
||||
static struct samsung_div_clock exynos5440_div_clks[] __initdata = {
|
||||
DIV(spi_baud, "div_spi", "mout_spi", MISC_DOUT1, 3, 2),
|
||||
DIV(CLK_SPI_BAUD, "div_spi", "mout_spi", MISC_DOUT1, 3, 2),
|
||||
};
|
||||
|
||||
/* gate clocks */
|
||||
static struct samsung_gate_clock exynos5440_gate_clks[] __initdata = {
|
||||
GATE(pb0_250, "pb0_250", "div250", CLKEN_OV_VAL, 3, 0, 0),
|
||||
GATE(pr0_250, "pr0_250", "div250", CLKEN_OV_VAL, 4, 0, 0),
|
||||
GATE(pr1_250, "pr1_250", "div250", CLKEN_OV_VAL, 5, 0, 0),
|
||||
GATE(b_250, "b_250", "div250", CLKEN_OV_VAL, 9, 0, 0),
|
||||
GATE(b_125, "b_125", "div125", CLKEN_OV_VAL, 10, 0, 0),
|
||||
GATE(b_200, "b_200", "div200", CLKEN_OV_VAL, 11, 0, 0),
|
||||
GATE(sata, "sata", "div200", CLKEN_OV_VAL, 12, 0, 0),
|
||||
GATE(usb, "usb", "div200", CLKEN_OV_VAL, 13, 0, 0),
|
||||
GATE(gmac0, "gmac0", "div200", CLKEN_OV_VAL, 14, 0, 0),
|
||||
GATE(cs250, "cs250", "div250", CLKEN_OV_VAL, 19, 0, 0),
|
||||
GATE(pb0_250_o, "pb0_250_o", "pb0_250", CLKEN_OV_VAL, 3, 0, 0),
|
||||
GATE(pr0_250_o, "pr0_250_o", "pr0_250", CLKEN_OV_VAL, 4, 0, 0),
|
||||
GATE(pr1_250_o, "pr1_250_o", "pr1_250", CLKEN_OV_VAL, 5, 0, 0),
|
||||
GATE(b_250_o, "b_250_o", "b_250", CLKEN_OV_VAL, 9, 0, 0),
|
||||
GATE(b_125_o, "b_125_o", "b_125", CLKEN_OV_VAL, 10, 0, 0),
|
||||
GATE(b_200_o, "b_200_o", "b_200", CLKEN_OV_VAL, 11, 0, 0),
|
||||
GATE(sata_o, "sata_o", "sata", CLKEN_OV_VAL, 12, 0, 0),
|
||||
GATE(usb_o, "usb_o", "usb", CLKEN_OV_VAL, 13, 0, 0),
|
||||
GATE(gmac0_o, "gmac0_o", "gmac", CLKEN_OV_VAL, 14, 0, 0),
|
||||
GATE(cs250_o, "cs250_o", "cs250", CLKEN_OV_VAL, 19, 0, 0),
|
||||
GATE(CLK_PB0_250, "pb0_250", "div250", CLKEN_OV_VAL, 3, 0, 0),
|
||||
GATE(CLK_PR0_250, "pr0_250", "div250", CLKEN_OV_VAL, 4, 0, 0),
|
||||
GATE(CLK_PR1_250, "pr1_250", "div250", CLKEN_OV_VAL, 5, 0, 0),
|
||||
GATE(CLK_B_250, "b_250", "div250", CLKEN_OV_VAL, 9, 0, 0),
|
||||
GATE(CLK_B_125, "b_125", "div125", CLKEN_OV_VAL, 10, 0, 0),
|
||||
GATE(CLK_B_200, "b_200", "div200", CLKEN_OV_VAL, 11, 0, 0),
|
||||
GATE(CLK_SATA, "sata", "div200", CLKEN_OV_VAL, 12, 0, 0),
|
||||
GATE(CLK_USB, "usb", "div200", CLKEN_OV_VAL, 13, 0, 0),
|
||||
GATE(CLK_GMAC0, "gmac0", "div200", CLKEN_OV_VAL, 14, 0, 0),
|
||||
GATE(CLK_CS250, "cs250", "div250", CLKEN_OV_VAL, 19, 0, 0),
|
||||
GATE(CLK_PB0_250_O, "pb0_250_o", "pb0_250", CLKEN_OV_VAL, 3, 0, 0),
|
||||
GATE(CLK_PR0_250_O, "pr0_250_o", "pr0_250", CLKEN_OV_VAL, 4, 0, 0),
|
||||
GATE(CLK_PR1_250_O, "pr1_250_o", "pr1_250", CLKEN_OV_VAL, 5, 0, 0),
|
||||
GATE(CLK_B_250_O, "b_250_o", "b_250", CLKEN_OV_VAL, 9, 0, 0),
|
||||
GATE(CLK_B_125_O, "b_125_o", "b_125", CLKEN_OV_VAL, 10, 0, 0),
|
||||
GATE(CLK_B_200_O, "b_200_o", "b_200", CLKEN_OV_VAL, 11, 0, 0),
|
||||
GATE(CLK_SATA_O, "sata_o", "sata", CLKEN_OV_VAL, 12, 0, 0),
|
||||
GATE(CLK_USB_O, "usb_o", "usb", CLKEN_OV_VAL, 13, 0, 0),
|
||||
GATE(CLK_GMAC0_O, "gmac0_o", "gmac", CLKEN_OV_VAL, 14, 0, 0),
|
||||
GATE(CLK_CS250_O, "cs250_o", "cs250", CLKEN_OV_VAL, 19, 0, 0),
|
||||
};
|
||||
|
||||
static struct of_device_id ext_clk_match[] __initdata = {
|
||||
|
@ -114,7 +101,7 @@ static void __init exynos5440_clk_init(struct device_node *np)
|
|||
return;
|
||||
}
|
||||
|
||||
samsung_clk_init(np, reg_base, nr_clks, NULL, 0, NULL, 0);
|
||||
samsung_clk_init(np, reg_base, CLK_NR_CLKS, NULL, 0, NULL, 0);
|
||||
samsung_clk_of_register_fixed_ext(exynos5440_fixed_rate_ext_clks,
|
||||
ARRAY_SIZE(exynos5440_fixed_rate_ext_clks), ext_clk_match);
|
||||
|
||||
|
|
7
drivers/clk/shmobile/Makefile
Normal file
7
drivers/clk/shmobile/Makefile
Normal file
|
@ -0,0 +1,7 @@
|
|||
obj-$(CONFIG_ARCH_EMEV2) += clk-emev2.o
|
||||
obj-$(CONFIG_ARCH_R8A7790) += clk-rcar-gen2.o
|
||||
obj-$(CONFIG_ARCH_R8A7791) += clk-rcar-gen2.o
|
||||
obj-$(CONFIG_ARCH_SHMOBILE_MULTI) += clk-div6.o
|
||||
obj-$(CONFIG_ARCH_SHMOBILE_MULTI) += clk-mstp.o
|
||||
# for emply built-in.o
|
||||
obj-n := dummy
|
185
drivers/clk/shmobile/clk-div6.c
Normal file
185
drivers/clk/shmobile/clk-div6.c
Normal file
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
* r8a7790 Common Clock Framework support
|
||||
*
|
||||
* Copyright (C) 2013 Renesas Solutions Corp.
|
||||
*
|
||||
* Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.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 of the License.
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
|
||||
#define CPG_DIV6_CKSTP BIT(8)
|
||||
#define CPG_DIV6_DIV(d) ((d) & 0x3f)
|
||||
#define CPG_DIV6_DIV_MASK 0x3f
|
||||
|
||||
/**
|
||||
* struct div6_clock - MSTP gating clock
|
||||
* @hw: handle between common and hardware-specific interfaces
|
||||
* @reg: IO-remapped register
|
||||
* @div: divisor value (1-64)
|
||||
*/
|
||||
struct div6_clock {
|
||||
struct clk_hw hw;
|
||||
void __iomem *reg;
|
||||
unsigned int div;
|
||||
};
|
||||
|
||||
#define to_div6_clock(_hw) container_of(_hw, struct div6_clock, hw)
|
||||
|
||||
static int cpg_div6_clock_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct div6_clock *clock = to_div6_clock(hw);
|
||||
|
||||
clk_writel(CPG_DIV6_DIV(clock->div - 1), clock->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cpg_div6_clock_disable(struct clk_hw *hw)
|
||||
{
|
||||
struct div6_clock *clock = to_div6_clock(hw);
|
||||
|
||||
/* DIV6 clocks require the divisor field to be non-zero when stopping
|
||||
* the clock.
|
||||
*/
|
||||
clk_writel(CPG_DIV6_CKSTP | CPG_DIV6_DIV(CPG_DIV6_DIV_MASK),
|
||||
clock->reg);
|
||||
}
|
||||
|
||||
static int cpg_div6_clock_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct div6_clock *clock = to_div6_clock(hw);
|
||||
|
||||
return !(clk_readl(clock->reg) & CPG_DIV6_CKSTP);
|
||||
}
|
||||
|
||||
static unsigned long cpg_div6_clock_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct div6_clock *clock = to_div6_clock(hw);
|
||||
unsigned int div = (clk_readl(clock->reg) & CPG_DIV6_DIV_MASK) + 1;
|
||||
|
||||
return parent_rate / div;
|
||||
}
|
||||
|
||||
static unsigned int cpg_div6_clock_calc_div(unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
unsigned int div;
|
||||
|
||||
div = DIV_ROUND_CLOSEST(parent_rate, rate);
|
||||
return clamp_t(unsigned int, div, 1, 64);
|
||||
}
|
||||
|
||||
static long cpg_div6_clock_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
unsigned int div = cpg_div6_clock_calc_div(rate, *parent_rate);
|
||||
|
||||
return *parent_rate / div;
|
||||
}
|
||||
|
||||
static int cpg_div6_clock_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct div6_clock *clock = to_div6_clock(hw);
|
||||
unsigned int div = cpg_div6_clock_calc_div(rate, parent_rate);
|
||||
|
||||
clock->div = div;
|
||||
|
||||
/* Only program the new divisor if the clock isn't stopped. */
|
||||
if (!(clk_readl(clock->reg) & CPG_DIV6_CKSTP))
|
||||
clk_writel(CPG_DIV6_DIV(clock->div - 1), clock->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops cpg_div6_clock_ops = {
|
||||
.enable = cpg_div6_clock_enable,
|
||||
.disable = cpg_div6_clock_disable,
|
||||
.is_enabled = cpg_div6_clock_is_enabled,
|
||||
.recalc_rate = cpg_div6_clock_recalc_rate,
|
||||
.round_rate = cpg_div6_clock_round_rate,
|
||||
.set_rate = cpg_div6_clock_set_rate,
|
||||
};
|
||||
|
||||
static void __init cpg_div6_clock_init(struct device_node *np)
|
||||
{
|
||||
struct clk_init_data init;
|
||||
struct div6_clock *clock;
|
||||
const char *parent_name;
|
||||
const char *name;
|
||||
struct clk *clk;
|
||||
int ret;
|
||||
|
||||
clock = kzalloc(sizeof(*clock), GFP_KERNEL);
|
||||
if (!clock) {
|
||||
pr_err("%s: failed to allocate %s DIV6 clock\n",
|
||||
__func__, np->name);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Remap the clock register and read the divisor. Disabling the
|
||||
* clock overwrites the divisor, so we need to cache its value for the
|
||||
* enable operation.
|
||||
*/
|
||||
clock->reg = of_iomap(np, 0);
|
||||
if (clock->reg == NULL) {
|
||||
pr_err("%s: failed to map %s DIV6 clock register\n",
|
||||
__func__, np->name);
|
||||
goto error;
|
||||
}
|
||||
|
||||
clock->div = (clk_readl(clock->reg) & CPG_DIV6_DIV_MASK) + 1;
|
||||
|
||||
/* Parse the DT properties. */
|
||||
ret = of_property_read_string(np, "clock-output-names", &name);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: failed to get %s DIV6 clock output name\n",
|
||||
__func__, np->name);
|
||||
goto error;
|
||||
}
|
||||
|
||||
parent_name = of_clk_get_parent_name(np, 0);
|
||||
if (parent_name == NULL) {
|
||||
pr_err("%s: failed to get %s DIV6 clock parent name\n",
|
||||
__func__, np->name);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Register the clock. */
|
||||
init.name = name;
|
||||
init.ops = &cpg_div6_clock_ops;
|
||||
init.flags = CLK_IS_BASIC;
|
||||
init.parent_names = &parent_name;
|
||||
init.num_parents = 1;
|
||||
|
||||
clock->hw.init = &init;
|
||||
|
||||
clk = clk_register(NULL, &clock->hw);
|
||||
if (IS_ERR(clk)) {
|
||||
pr_err("%s: failed to register %s DIV6 clock (%ld)\n",
|
||||
__func__, np->name, PTR_ERR(clk));
|
||||
goto error;
|
||||
}
|
||||
|
||||
of_clk_add_provider(np, of_clk_src_simple_get, clk);
|
||||
|
||||
return;
|
||||
|
||||
error:
|
||||
if (clock->reg)
|
||||
iounmap(clock->reg);
|
||||
kfree(clock);
|
||||
}
|
||||
CLK_OF_DECLARE(cpg_div6_clk, "renesas,cpg-div6-clock", cpg_div6_clock_init);
|
104
drivers/clk/shmobile/clk-emev2.c
Normal file
104
drivers/clk/shmobile/clk-emev2.c
Normal file
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* EMMA Mobile EV2 common clock framework support
|
||||
*
|
||||
* Copyright (C) 2013 Takashi Yoshii <takashi.yoshii.ze@renesas.com>
|
||||
* Copyright (C) 2012 Magnus Damm
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
|
||||
/* EMEV2 SMU registers */
|
||||
#define USIAU0_RSTCTRL 0x094
|
||||
#define USIBU1_RSTCTRL 0x0ac
|
||||
#define USIBU2_RSTCTRL 0x0b0
|
||||
#define USIBU3_RSTCTRL 0x0b4
|
||||
#define STI_RSTCTRL 0x124
|
||||
#define STI_CLKSEL 0x688
|
||||
|
||||
static DEFINE_SPINLOCK(lock);
|
||||
|
||||
/* not pretty, but hey */
|
||||
void __iomem *smu_base;
|
||||
|
||||
static void __init emev2_smu_write(unsigned long value, int offs)
|
||||
{
|
||||
BUG_ON(!smu_base || (offs >= PAGE_SIZE));
|
||||
writel_relaxed(value, smu_base + offs);
|
||||
}
|
||||
|
||||
static const struct of_device_id smu_id[] __initconst = {
|
||||
{ .compatible = "renesas,emev2-smu", },
|
||||
{},
|
||||
};
|
||||
|
||||
static void __init emev2_smu_init(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
|
||||
np = of_find_matching_node(NULL, smu_id);
|
||||
BUG_ON(!np);
|
||||
smu_base = of_iomap(np, 0);
|
||||
BUG_ON(!smu_base);
|
||||
of_node_put(np);
|
||||
|
||||
/* setup STI timer to run on 32.768 kHz and deassert reset */
|
||||
emev2_smu_write(0, STI_CLKSEL);
|
||||
emev2_smu_write(1, STI_RSTCTRL);
|
||||
|
||||
/* deassert reset for UART0->UART3 */
|
||||
emev2_smu_write(2, USIAU0_RSTCTRL);
|
||||
emev2_smu_write(2, USIBU1_RSTCTRL);
|
||||
emev2_smu_write(2, USIBU2_RSTCTRL);
|
||||
emev2_smu_write(2, USIBU3_RSTCTRL);
|
||||
}
|
||||
|
||||
static void __init emev2_smu_clkdiv_init(struct device_node *np)
|
||||
{
|
||||
u32 reg[2];
|
||||
struct clk *clk;
|
||||
const char *parent_name = of_clk_get_parent_name(np, 0);
|
||||
if (WARN_ON(of_property_read_u32_array(np, "reg", reg, 2)))
|
||||
return;
|
||||
if (!smu_base)
|
||||
emev2_smu_init();
|
||||
clk = clk_register_divider(NULL, np->name, parent_name, 0,
|
||||
smu_base + reg[0], reg[1], 8, 0, &lock);
|
||||
of_clk_add_provider(np, of_clk_src_simple_get, clk);
|
||||
clk_register_clkdev(clk, np->name, NULL);
|
||||
pr_debug("## %s %s %p\n", __func__, np->name, clk);
|
||||
}
|
||||
CLK_OF_DECLARE(emev2_smu_clkdiv, "renesas,emev2-smu-clkdiv",
|
||||
emev2_smu_clkdiv_init);
|
||||
|
||||
static void __init emev2_smu_gclk_init(struct device_node *np)
|
||||
{
|
||||
u32 reg[2];
|
||||
struct clk *clk;
|
||||
const char *parent_name = of_clk_get_parent_name(np, 0);
|
||||
if (WARN_ON(of_property_read_u32_array(np, "reg", reg, 2)))
|
||||
return;
|
||||
if (!smu_base)
|
||||
emev2_smu_init();
|
||||
clk = clk_register_gate(NULL, np->name, parent_name, 0,
|
||||
smu_base + reg[0], reg[1], 0, &lock);
|
||||
of_clk_add_provider(np, of_clk_src_simple_get, clk);
|
||||
clk_register_clkdev(clk, np->name, NULL);
|
||||
pr_debug("## %s %s %p\n", __func__, np->name, clk);
|
||||
}
|
||||
CLK_OF_DECLARE(emev2_smu_gclk, "renesas,emev2-smu-gclk", emev2_smu_gclk_init);
|
233
drivers/clk/shmobile/clk-mstp.c
Normal file
233
drivers/clk/shmobile/clk-mstp.c
Normal file
|
@ -0,0 +1,233 @@
|
|||
/*
|
||||
* R-Car MSTP clocks
|
||||
*
|
||||
* Copyright (C) 2013 Ideas On Board SPRL
|
||||
*
|
||||
* Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.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 of the License.
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
/*
|
||||
* MSTP clocks. We can't use standard gate clocks as we need to poll on the
|
||||
* status register when enabling the clock.
|
||||
*/
|
||||
|
||||
#define MSTP_MAX_CLOCKS 32
|
||||
|
||||
/**
|
||||
* struct mstp_clock_group - MSTP gating clocks group
|
||||
*
|
||||
* @data: clocks in this group
|
||||
* @smstpcr: module stop control register
|
||||
* @mstpsr: module stop status register (optional)
|
||||
* @lock: protects writes to SMSTPCR
|
||||
*/
|
||||
struct mstp_clock_group {
|
||||
struct clk_onecell_data data;
|
||||
void __iomem *smstpcr;
|
||||
void __iomem *mstpsr;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mstp_clock - MSTP gating clock
|
||||
* @hw: handle between common and hardware-specific interfaces
|
||||
* @bit_index: control bit index
|
||||
* @group: MSTP clocks group
|
||||
*/
|
||||
struct mstp_clock {
|
||||
struct clk_hw hw;
|
||||
u32 bit_index;
|
||||
struct mstp_clock_group *group;
|
||||
};
|
||||
|
||||
#define to_mstp_clock(_hw) container_of(_hw, struct mstp_clock, hw)
|
||||
|
||||
static int cpg_mstp_clock_endisable(struct clk_hw *hw, bool enable)
|
||||
{
|
||||
struct mstp_clock *clock = to_mstp_clock(hw);
|
||||
struct mstp_clock_group *group = clock->group;
|
||||
u32 bitmask = BIT(clock->bit_index);
|
||||
unsigned long flags;
|
||||
unsigned int i;
|
||||
u32 value;
|
||||
|
||||
spin_lock_irqsave(&group->lock, flags);
|
||||
|
||||
value = clk_readl(group->smstpcr);
|
||||
if (enable)
|
||||
value &= ~bitmask;
|
||||
else
|
||||
value |= bitmask;
|
||||
clk_writel(value, group->smstpcr);
|
||||
|
||||
spin_unlock_irqrestore(&group->lock, flags);
|
||||
|
||||
if (!enable || !group->mstpsr)
|
||||
return 0;
|
||||
|
||||
for (i = 1000; i > 0; --i) {
|
||||
if (!(clk_readl(group->mstpsr) & bitmask))
|
||||
break;
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
if (!i) {
|
||||
pr_err("%s: failed to enable %p[%d]\n", __func__,
|
||||
group->smstpcr, clock->bit_index);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cpg_mstp_clock_enable(struct clk_hw *hw)
|
||||
{
|
||||
return cpg_mstp_clock_endisable(hw, true);
|
||||
}
|
||||
|
||||
static void cpg_mstp_clock_disable(struct clk_hw *hw)
|
||||
{
|
||||
cpg_mstp_clock_endisable(hw, false);
|
||||
}
|
||||
|
||||
static int cpg_mstp_clock_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct mstp_clock *clock = to_mstp_clock(hw);
|
||||
struct mstp_clock_group *group = clock->group;
|
||||
u32 value;
|
||||
|
||||
if (group->mstpsr)
|
||||
value = clk_readl(group->mstpsr);
|
||||
else
|
||||
value = clk_readl(group->smstpcr);
|
||||
|
||||
return !!(value & BIT(clock->bit_index));
|
||||
}
|
||||
|
||||
static const struct clk_ops cpg_mstp_clock_ops = {
|
||||
.enable = cpg_mstp_clock_enable,
|
||||
.disable = cpg_mstp_clock_disable,
|
||||
.is_enabled = cpg_mstp_clock_is_enabled,
|
||||
};
|
||||
|
||||
static struct clk * __init
|
||||
cpg_mstp_clock_register(const char *name, const char *parent_name,
|
||||
unsigned int index, struct mstp_clock_group *group)
|
||||
{
|
||||
struct clk_init_data init;
|
||||
struct mstp_clock *clock;
|
||||
struct clk *clk;
|
||||
|
||||
clock = kzalloc(sizeof(*clock), GFP_KERNEL);
|
||||
if (!clock) {
|
||||
pr_err("%s: failed to allocate MSTP clock.\n", __func__);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
init.name = name;
|
||||
init.ops = &cpg_mstp_clock_ops;
|
||||
init.flags = CLK_IS_BASIC;
|
||||
init.parent_names = &parent_name;
|
||||
init.num_parents = 1;
|
||||
|
||||
clock->bit_index = index;
|
||||
clock->group = group;
|
||||
clock->hw.init = &init;
|
||||
|
||||
clk = clk_register(NULL, &clock->hw);
|
||||
|
||||
if (IS_ERR(clk))
|
||||
kfree(clock);
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
static void __init cpg_mstp_clocks_init(struct device_node *np)
|
||||
{
|
||||
struct mstp_clock_group *group;
|
||||
struct clk **clks;
|
||||
unsigned int i;
|
||||
|
||||
group = kzalloc(sizeof(*group), GFP_KERNEL);
|
||||
clks = kmalloc(MSTP_MAX_CLOCKS * sizeof(*clks), GFP_KERNEL);
|
||||
if (group == NULL || clks == NULL) {
|
||||
kfree(group);
|
||||
kfree(clks);
|
||||
pr_err("%s: failed to allocate group\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_init(&group->lock);
|
||||
group->data.clks = clks;
|
||||
|
||||
group->smstpcr = of_iomap(np, 0);
|
||||
group->mstpsr = of_iomap(np, 1);
|
||||
|
||||
if (group->smstpcr == NULL) {
|
||||
pr_err("%s: failed to remap SMSTPCR\n", __func__);
|
||||
kfree(group);
|
||||
kfree(clks);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < MSTP_MAX_CLOCKS; ++i)
|
||||
clks[i] = ERR_PTR(-ENOENT);
|
||||
|
||||
for (i = 0; i < MSTP_MAX_CLOCKS; ++i) {
|
||||
const char *parent_name;
|
||||
const char *name;
|
||||
u32 clkidx;
|
||||
int ret;
|
||||
|
||||
/* Skip clocks with no name. */
|
||||
ret = of_property_read_string_index(np, "clock-output-names",
|
||||
i, &name);
|
||||
if (ret < 0 || strlen(name) == 0)
|
||||
continue;
|
||||
|
||||
parent_name = of_clk_get_parent_name(np, i);
|
||||
ret = of_property_read_u32_index(np, "renesas,clock-indices", i,
|
||||
&clkidx);
|
||||
if (parent_name == NULL || ret < 0)
|
||||
break;
|
||||
|
||||
if (clkidx >= MSTP_MAX_CLOCKS) {
|
||||
pr_err("%s: invalid clock %s %s index %u)\n",
|
||||
__func__, np->name, name, clkidx);
|
||||
continue;
|
||||
}
|
||||
|
||||
clks[clkidx] = cpg_mstp_clock_register(name, parent_name,
|
||||
clkidx, group);
|
||||
if (!IS_ERR(clks[clkidx])) {
|
||||
group->data.clk_num = max(group->data.clk_num,
|
||||
clkidx + 1);
|
||||
/*
|
||||
* Register a clkdev to let board code retrieve the
|
||||
* clock by name and register aliases for non-DT
|
||||
* devices.
|
||||
*
|
||||
* FIXME: Remove this when all devices that require a
|
||||
* clock will be instantiated from DT.
|
||||
*/
|
||||
clk_register_clkdev(clks[clkidx], name, NULL);
|
||||
} else {
|
||||
pr_err("%s: failed to register %s %s clock (%ld)\n",
|
||||
__func__, np->name, name, PTR_ERR(clks[clkidx]));
|
||||
}
|
||||
}
|
||||
|
||||
of_clk_add_provider(np, of_clk_src_onecell_get, &group->data);
|
||||
}
|
||||
CLK_OF_DECLARE(cpg_mstp_clks, "renesas,cpg-mstp-clocks", cpg_mstp_clocks_init);
|
298
drivers/clk/shmobile/clk-rcar-gen2.c
Normal file
298
drivers/clk/shmobile/clk-rcar-gen2.c
Normal file
|
@ -0,0 +1,298 @@
|
|||
/*
|
||||
* rcar_gen2 Core CPG Clocks
|
||||
*
|
||||
* Copyright (C) 2013 Ideas On Board SPRL
|
||||
*
|
||||
* Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.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 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/math64.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
struct rcar_gen2_cpg {
|
||||
struct clk_onecell_data data;
|
||||
spinlock_t lock;
|
||||
void __iomem *reg;
|
||||
};
|
||||
|
||||
#define CPG_SDCKCR 0x00000074
|
||||
#define CPG_PLL0CR 0x000000d8
|
||||
#define CPG_FRQCRC 0x000000e0
|
||||
#define CPG_FRQCRC_ZFC_MASK (0x1f << 8)
|
||||
#define CPG_FRQCRC_ZFC_SHIFT 8
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Z Clock
|
||||
*
|
||||
* Traits of this clock:
|
||||
* prepare - clk_prepare only ensures that parents are prepared
|
||||
* enable - clk_enable only ensures that parents are enabled
|
||||
* rate - rate is adjustable. clk->rate = parent->rate * mult / 32
|
||||
* parent - fixed parent. No clk_set_parent support
|
||||
*/
|
||||
|
||||
struct cpg_z_clk {
|
||||
struct clk_hw hw;
|
||||
void __iomem *reg;
|
||||
};
|
||||
|
||||
#define to_z_clk(_hw) container_of(_hw, struct cpg_z_clk, hw)
|
||||
|
||||
static unsigned long cpg_z_clk_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct cpg_z_clk *zclk = to_z_clk(hw);
|
||||
unsigned int mult;
|
||||
unsigned int val;
|
||||
|
||||
val = (clk_readl(zclk->reg) & CPG_FRQCRC_ZFC_MASK)
|
||||
>> CPG_FRQCRC_ZFC_SHIFT;
|
||||
mult = 32 - val;
|
||||
|
||||
return div_u64((u64)parent_rate * mult, 32);
|
||||
}
|
||||
|
||||
static long cpg_z_clk_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
unsigned long prate = *parent_rate;
|
||||
unsigned int mult;
|
||||
|
||||
if (!prate)
|
||||
prate = 1;
|
||||
|
||||
mult = div_u64((u64)rate * 32, prate);
|
||||
mult = clamp(mult, 1U, 32U);
|
||||
|
||||
return *parent_rate / 32 * mult;
|
||||
}
|
||||
|
||||
static int cpg_z_clk_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct cpg_z_clk *zclk = to_z_clk(hw);
|
||||
unsigned int mult;
|
||||
u32 val;
|
||||
|
||||
mult = div_u64((u64)rate * 32, parent_rate);
|
||||
mult = clamp(mult, 1U, 32U);
|
||||
|
||||
val = clk_readl(zclk->reg);
|
||||
val &= ~CPG_FRQCRC_ZFC_MASK;
|
||||
val |= (32 - mult) << CPG_FRQCRC_ZFC_SHIFT;
|
||||
clk_writel(val, zclk->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops cpg_z_clk_ops = {
|
||||
.recalc_rate = cpg_z_clk_recalc_rate,
|
||||
.round_rate = cpg_z_clk_round_rate,
|
||||
.set_rate = cpg_z_clk_set_rate,
|
||||
};
|
||||
|
||||
static struct clk * __init cpg_z_clk_register(struct rcar_gen2_cpg *cpg)
|
||||
{
|
||||
static const char *parent_name = "pll0";
|
||||
struct clk_init_data init;
|
||||
struct cpg_z_clk *zclk;
|
||||
struct clk *clk;
|
||||
|
||||
zclk = kzalloc(sizeof(*zclk), GFP_KERNEL);
|
||||
if (!zclk)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = "z";
|
||||
init.ops = &cpg_z_clk_ops;
|
||||
init.flags = 0;
|
||||
init.parent_names = &parent_name;
|
||||
init.num_parents = 1;
|
||||
|
||||
zclk->reg = cpg->reg + CPG_FRQCRC;
|
||||
zclk->hw.init = &init;
|
||||
|
||||
clk = clk_register(NULL, &zclk->hw);
|
||||
if (IS_ERR(clk))
|
||||
kfree(zclk);
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* CPG Clock Data
|
||||
*/
|
||||
|
||||
/*
|
||||
* MD EXTAL PLL0 PLL1 PLL3
|
||||
* 14 13 19 (MHz) *1 *1
|
||||
*---------------------------------------------------
|
||||
* 0 0 0 15 x 1 x172/2 x208/2 x106
|
||||
* 0 0 1 15 x 1 x172/2 x208/2 x88
|
||||
* 0 1 0 20 x 1 x130/2 x156/2 x80
|
||||
* 0 1 1 20 x 1 x130/2 x156/2 x66
|
||||
* 1 0 0 26 / 2 x200/2 x240/2 x122
|
||||
* 1 0 1 26 / 2 x200/2 x240/2 x102
|
||||
* 1 1 0 30 / 2 x172/2 x208/2 x106
|
||||
* 1 1 1 30 / 2 x172/2 x208/2 x88
|
||||
*
|
||||
* *1 : Table 7.6 indicates VCO ouput (PLLx = VCO/2)
|
||||
*/
|
||||
#define CPG_PLL_CONFIG_INDEX(md) ((((md) & BIT(14)) >> 12) | \
|
||||
(((md) & BIT(13)) >> 12) | \
|
||||
(((md) & BIT(19)) >> 19))
|
||||
struct cpg_pll_config {
|
||||
unsigned int extal_div;
|
||||
unsigned int pll1_mult;
|
||||
unsigned int pll3_mult;
|
||||
};
|
||||
|
||||
static const struct cpg_pll_config cpg_pll_configs[8] __initconst = {
|
||||
{ 1, 208, 106 }, { 1, 208, 88 }, { 1, 156, 80 }, { 1, 156, 66 },
|
||||
{ 2, 240, 122 }, { 2, 240, 102 }, { 2, 208, 106 }, { 2, 208, 88 },
|
||||
};
|
||||
|
||||
/* SDHI divisors */
|
||||
static const struct clk_div_table cpg_sdh_div_table[] = {
|
||||
{ 0, 2 }, { 1, 3 }, { 2, 4 }, { 3, 6 },
|
||||
{ 4, 8 }, { 5, 12 }, { 6, 16 }, { 7, 18 },
|
||||
{ 8, 24 }, { 10, 36 }, { 11, 48 }, { 0, 0 },
|
||||
};
|
||||
|
||||
static const struct clk_div_table cpg_sd01_div_table[] = {
|
||||
{ 5, 12 }, { 6, 16 }, { 7, 18 }, { 8, 24 },
|
||||
{ 10, 36 }, { 11, 48 }, { 12, 10 }, { 0, 0 },
|
||||
};
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Initialization
|
||||
*/
|
||||
|
||||
static u32 cpg_mode __initdata;
|
||||
|
||||
static struct clk * __init
|
||||
rcar_gen2_cpg_register_clock(struct device_node *np, struct rcar_gen2_cpg *cpg,
|
||||
const struct cpg_pll_config *config,
|
||||
const char *name)
|
||||
{
|
||||
const struct clk_div_table *table = NULL;
|
||||
const char *parent_name = "main";
|
||||
unsigned int shift;
|
||||
unsigned int mult = 1;
|
||||
unsigned int div = 1;
|
||||
|
||||
if (!strcmp(name, "main")) {
|
||||
parent_name = of_clk_get_parent_name(np, 0);
|
||||
div = config->extal_div;
|
||||
} else if (!strcmp(name, "pll0")) {
|
||||
/* PLL0 is a configurable multiplier clock. Register it as a
|
||||
* fixed factor clock 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);
|
||||
mult = ((value >> 24) & ((1 << 7) - 1)) + 1;
|
||||
} else if (!strcmp(name, "pll1")) {
|
||||
mult = config->pll1_mult / 2;
|
||||
} else if (!strcmp(name, "pll3")) {
|
||||
mult = config->pll3_mult;
|
||||
} else if (!strcmp(name, "lb")) {
|
||||
div = cpg_mode & BIT(18) ? 36 : 24;
|
||||
} else if (!strcmp(name, "qspi")) {
|
||||
div = (cpg_mode & (BIT(3) | BIT(2) | BIT(1))) == BIT(2)
|
||||
? 16 : 20;
|
||||
} else if (!strcmp(name, "sdh")) {
|
||||
table = cpg_sdh_div_table;
|
||||
shift = 8;
|
||||
} else if (!strcmp(name, "sd0")) {
|
||||
table = cpg_sd01_div_table;
|
||||
shift = 4;
|
||||
} else if (!strcmp(name, "sd1")) {
|
||||
table = cpg_sd01_div_table;
|
||||
shift = 0;
|
||||
} else if (!strcmp(name, "z")) {
|
||||
return cpg_z_clk_register(cpg);
|
||||
} else {
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
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 + CPG_SDCKCR, shift,
|
||||
4, 0, table, &cpg->lock);
|
||||
}
|
||||
|
||||
static void __init rcar_gen2_cpg_clocks_init(struct device_node *np)
|
||||
{
|
||||
const struct cpg_pll_config *config;
|
||||
struct rcar_gen2_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 = kzalloc(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.
|
||||
*/
|
||||
pr_err("%s: failed to allocate cpg\n", __func__);
|
||||
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;
|
||||
|
||||
config = &cpg_pll_configs[CPG_PLL_CONFIG_INDEX(cpg_mode)];
|
||||
|
||||
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 = rcar_gen2_cpg_register_clock(np, cpg, config, 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(rcar_gen2_cpg_clks, "renesas,rcar-gen2-cpg-clocks",
|
||||
rcar_gen2_cpg_clocks_init);
|
||||
|
||||
void __init rcar_gen2_clocks_init(u32 mode)
|
||||
{
|
||||
cpg_mode = mode;
|
||||
|
||||
of_clk_init(NULL);
|
||||
}
|
5
drivers/clk/sirf/Makefile
Normal file
5
drivers/clk/sirf/Makefile
Normal file
|
@ -0,0 +1,5 @@
|
|||
#
|
||||
# Makefile for sirf specific clk
|
||||
#
|
||||
|
||||
obj-$(CONFIG_ARCH_SIRF) += clk-prima2.o clk-atlas6.o
|
31
drivers/clk/sirf/atlas6.h
Normal file
31
drivers/clk/sirf/atlas6.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
#define SIRFSOC_CLKC_CLK_EN0 0x0000
|
||||
#define SIRFSOC_CLKC_CLK_EN1 0x0004
|
||||
#define SIRFSOC_CLKC_REF_CFG 0x0020
|
||||
#define SIRFSOC_CLKC_CPU_CFG 0x0024
|
||||
#define SIRFSOC_CLKC_MEM_CFG 0x0028
|
||||
#define SIRFSOC_CLKC_MEMDIV_CFG 0x002C
|
||||
#define SIRFSOC_CLKC_SYS_CFG 0x0030
|
||||
#define SIRFSOC_CLKC_IO_CFG 0x0034
|
||||
#define SIRFSOC_CLKC_DSP_CFG 0x0038
|
||||
#define SIRFSOC_CLKC_GFX_CFG 0x003c
|
||||
#define SIRFSOC_CLKC_MM_CFG 0x0040
|
||||
#define SIRFSOC_CLKC_GFX2D_CFG 0x0040
|
||||
#define SIRFSOC_CLKC_LCD_CFG 0x0044
|
||||
#define SIRFSOC_CLKC_MMC01_CFG 0x0048
|
||||
#define SIRFSOC_CLKC_MMC23_CFG 0x004C
|
||||
#define SIRFSOC_CLKC_MMC45_CFG 0x0050
|
||||
#define SIRFSOC_CLKC_NAND_CFG 0x0054
|
||||
#define SIRFSOC_CLKC_NANDDIV_CFG 0x0058
|
||||
#define SIRFSOC_CLKC_PLL1_CFG0 0x0080
|
||||
#define SIRFSOC_CLKC_PLL2_CFG0 0x0084
|
||||
#define SIRFSOC_CLKC_PLL3_CFG0 0x0088
|
||||
#define SIRFSOC_CLKC_PLL1_CFG1 0x008c
|
||||
#define SIRFSOC_CLKC_PLL2_CFG1 0x0090
|
||||
#define SIRFSOC_CLKC_PLL3_CFG1 0x0094
|
||||
#define SIRFSOC_CLKC_PLL1_CFG2 0x0098
|
||||
#define SIRFSOC_CLKC_PLL2_CFG2 0x009c
|
||||
#define SIRFSOC_CLKC_PLL3_CFG2 0x00A0
|
||||
#define SIRFSOC_USBPHY_PLL_CTRL 0x0008
|
||||
#define SIRFSOC_USBPHY_PLL_POWERDOWN BIT(1)
|
||||
#define SIRFSOC_USBPHY_PLL_BYPASS BIT(2)
|
||||
#define SIRFSOC_USBPHY_PLL_LOCK BIT(3)
|
152
drivers/clk/sirf/clk-atlas6.c
Normal file
152
drivers/clk/sirf/clk-atlas6.c
Normal file
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* Clock tree for CSR SiRFatlasVI
|
||||
*
|
||||
* Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
|
||||
*
|
||||
* Licensed under GPLv2 or later.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/syscore_ops.h>
|
||||
|
||||
#include "atlas6.h"
|
||||
#include "clk-common.c"
|
||||
|
||||
static struct clk_dmn clk_mmc01 = {
|
||||
.regofs = SIRFSOC_CLKC_MMC01_CFG,
|
||||
.enable_bit = 59,
|
||||
.hw = {
|
||||
.init = &clk_mmc01_init,
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_dmn clk_mmc23 = {
|
||||
.regofs = SIRFSOC_CLKC_MMC23_CFG,
|
||||
.enable_bit = 60,
|
||||
.hw = {
|
||||
.init = &clk_mmc23_init,
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_dmn clk_mmc45 = {
|
||||
.regofs = SIRFSOC_CLKC_MMC45_CFG,
|
||||
.enable_bit = 61,
|
||||
.hw = {
|
||||
.init = &clk_mmc45_init,
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_init_data clk_nand_init = {
|
||||
.name = "nand",
|
||||
.ops = &dmn_ops,
|
||||
.parent_names = dmn_clk_parents,
|
||||
.num_parents = ARRAY_SIZE(dmn_clk_parents),
|
||||
};
|
||||
|
||||
static struct clk_dmn clk_nand = {
|
||||
.regofs = SIRFSOC_CLKC_NAND_CFG,
|
||||
.enable_bit = 34,
|
||||
.hw = {
|
||||
.init = &clk_nand_init,
|
||||
},
|
||||
};
|
||||
|
||||
enum atlas6_clk_index {
|
||||
/* 0 1 2 3 4 5 6 7 8 9 */
|
||||
rtc, osc, pll1, pll2, pll3, mem, sys, security, dsp, gps,
|
||||
mf, io, cpu, uart0, uart1, uart2, tsc, i2c0, i2c1, spi0,
|
||||
spi1, pwmc, efuse, pulse, dmac0, dmac1, nand, audio, usp0, usp1,
|
||||
usp2, vip, gfx, gfx2d, lcd, vpp, mmc01, mmc23, mmc45, usbpll,
|
||||
usb0, usb1, cphif, maxclk,
|
||||
};
|
||||
|
||||
static __initdata struct clk_hw *atlas6_clk_hw_array[maxclk] = {
|
||||
NULL, /* dummy */
|
||||
NULL,
|
||||
&clk_pll1.hw,
|
||||
&clk_pll2.hw,
|
||||
&clk_pll3.hw,
|
||||
&clk_mem.hw,
|
||||
&clk_sys.hw,
|
||||
&clk_security.hw,
|
||||
&clk_dsp.hw,
|
||||
&clk_gps.hw,
|
||||
&clk_mf.hw,
|
||||
&clk_io.hw,
|
||||
&clk_cpu.hw,
|
||||
&clk_uart0.hw,
|
||||
&clk_uart1.hw,
|
||||
&clk_uart2.hw,
|
||||
&clk_tsc.hw,
|
||||
&clk_i2c0.hw,
|
||||
&clk_i2c1.hw,
|
||||
&clk_spi0.hw,
|
||||
&clk_spi1.hw,
|
||||
&clk_pwmc.hw,
|
||||
&clk_efuse.hw,
|
||||
&clk_pulse.hw,
|
||||
&clk_dmac0.hw,
|
||||
&clk_dmac1.hw,
|
||||
&clk_nand.hw,
|
||||
&clk_audio.hw,
|
||||
&clk_usp0.hw,
|
||||
&clk_usp1.hw,
|
||||
&clk_usp2.hw,
|
||||
&clk_vip.hw,
|
||||
&clk_gfx.hw,
|
||||
&clk_gfx2d.hw,
|
||||
&clk_lcd.hw,
|
||||
&clk_vpp.hw,
|
||||
&clk_mmc01.hw,
|
||||
&clk_mmc23.hw,
|
||||
&clk_mmc45.hw,
|
||||
&usb_pll_clk_hw,
|
||||
&clk_usb0.hw,
|
||||
&clk_usb1.hw,
|
||||
&clk_cphif.hw,
|
||||
};
|
||||
|
||||
static struct clk *atlas6_clks[maxclk];
|
||||
|
||||
static void __init atlas6_clk_init(struct device_node *np)
|
||||
{
|
||||
struct device_node *rscnp;
|
||||
int i;
|
||||
|
||||
rscnp = of_find_compatible_node(NULL, NULL, "sirf,prima2-rsc");
|
||||
sirfsoc_rsc_vbase = of_iomap(rscnp, 0);
|
||||
if (!sirfsoc_rsc_vbase)
|
||||
panic("unable to map rsc registers\n");
|
||||
of_node_put(rscnp);
|
||||
|
||||
sirfsoc_clk_vbase = of_iomap(np, 0);
|
||||
if (!sirfsoc_clk_vbase)
|
||||
panic("unable to map clkc registers\n");
|
||||
|
||||
/* These are always available (RTC and 26MHz OSC)*/
|
||||
atlas6_clks[rtc] = clk_register_fixed_rate(NULL, "rtc", NULL,
|
||||
CLK_IS_ROOT, 32768);
|
||||
atlas6_clks[osc] = clk_register_fixed_rate(NULL, "osc", NULL,
|
||||
CLK_IS_ROOT, 26000000);
|
||||
|
||||
for (i = pll1; i < maxclk; i++) {
|
||||
atlas6_clks[i] = clk_register(NULL, atlas6_clk_hw_array[i]);
|
||||
BUG_ON(!atlas6_clks[i]);
|
||||
}
|
||||
clk_register_clkdev(atlas6_clks[cpu], NULL, "cpu");
|
||||
clk_register_clkdev(atlas6_clks[io], NULL, "io");
|
||||
clk_register_clkdev(atlas6_clks[mem], NULL, "mem");
|
||||
clk_register_clkdev(atlas6_clks[mem], NULL, "osc");
|
||||
|
||||
clk_data.clks = atlas6_clks;
|
||||
clk_data.clk_num = maxclk;
|
||||
|
||||
of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
|
||||
}
|
||||
CLK_OF_DECLARE(atlas6_clk, "sirf,atlas6-clkc", atlas6_clk_init);
|
|
@ -1,51 +1,18 @@
|
|||
/*
|
||||
* Clock tree for CSR SiRFprimaII
|
||||
* common clks module for all SiRF SoCs
|
||||
*
|
||||
* Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
|
||||
*
|
||||
* Licensed under GPLv2 or later.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/syscore_ops.h>
|
||||
|
||||
#define SIRFSOC_CLKC_CLK_EN0 0x0000
|
||||
#define SIRFSOC_CLKC_CLK_EN1 0x0004
|
||||
#define SIRFSOC_CLKC_REF_CFG 0x0014
|
||||
#define SIRFSOC_CLKC_CPU_CFG 0x0018
|
||||
#define SIRFSOC_CLKC_MEM_CFG 0x001c
|
||||
#define SIRFSOC_CLKC_SYS_CFG 0x0020
|
||||
#define SIRFSOC_CLKC_IO_CFG 0x0024
|
||||
#define SIRFSOC_CLKC_DSP_CFG 0x0028
|
||||
#define SIRFSOC_CLKC_GFX_CFG 0x002c
|
||||
#define SIRFSOC_CLKC_MM_CFG 0x0030
|
||||
#define SIRFSOC_CLKC_LCD_CFG 0x0034
|
||||
#define SIRFSOC_CLKC_MMC_CFG 0x0038
|
||||
#define SIRFSOC_CLKC_PLL1_CFG0 0x0040
|
||||
#define SIRFSOC_CLKC_PLL2_CFG0 0x0044
|
||||
#define SIRFSOC_CLKC_PLL3_CFG0 0x0048
|
||||
#define SIRFSOC_CLKC_PLL1_CFG1 0x004c
|
||||
#define SIRFSOC_CLKC_PLL2_CFG1 0x0050
|
||||
#define SIRFSOC_CLKC_PLL3_CFG1 0x0054
|
||||
#define SIRFSOC_CLKC_PLL1_CFG2 0x0058
|
||||
#define SIRFSOC_CLKC_PLL2_CFG2 0x005c
|
||||
#define SIRFSOC_CLKC_PLL3_CFG2 0x0060
|
||||
#define SIRFSOC_USBPHY_PLL_CTRL 0x0008
|
||||
#define SIRFSOC_USBPHY_PLL_POWERDOWN BIT(1)
|
||||
#define SIRFSOC_USBPHY_PLL_BYPASS BIT(2)
|
||||
#define SIRFSOC_USBPHY_PLL_LOCK BIT(3)
|
||||
|
||||
static void *sirfsoc_clk_vbase, *sirfsoc_rsc_vbase;
|
||||
|
||||
#define KHZ 1000
|
||||
#define MHZ (KHZ * KHZ)
|
||||
|
||||
static void *sirfsoc_clk_vbase;
|
||||
static void *sirfsoc_rsc_vbase;
|
||||
static struct clk_onecell_data clk_data;
|
||||
|
||||
/*
|
||||
* SiRFprimaII clock controller
|
||||
* - 2 oscillators: osc-26MHz, rtc-32.768KHz
|
||||
|
@ -127,6 +94,7 @@ static long pll_clk_round_rate(struct clk_hw *hw, unsigned long rate,
|
|||
unsigned long *parent_rate)
|
||||
{
|
||||
unsigned long fin, nf, nr, od;
|
||||
u64 dividend;
|
||||
|
||||
/*
|
||||
* fout = fin * nf / (nr * od);
|
||||
|
@ -147,7 +115,10 @@ static long pll_clk_round_rate(struct clk_hw *hw, unsigned long rate,
|
|||
nr = BIT(6);
|
||||
od = 1;
|
||||
|
||||
return fin * nf / (nr * od);
|
||||
dividend = (u64)fin * nf;
|
||||
do_div(dividend, nr * od);
|
||||
|
||||
return (long)dividend;
|
||||
}
|
||||
|
||||
static int pll_clk_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
|
@ -186,6 +157,30 @@ static int pll_clk_set_rate(struct clk_hw *hw, unsigned long rate,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static long cpu_clk_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
/*
|
||||
* SiRF SoC has not cpu clock control,
|
||||
* So bypass to it's parent pll.
|
||||
*/
|
||||
struct clk *parent_clk = clk_get_parent(hw->clk);
|
||||
struct clk *pll_parent_clk = clk_get_parent(parent_clk);
|
||||
unsigned long pll_parent_rate = clk_get_rate(pll_parent_clk);
|
||||
return pll_clk_round_rate(__clk_get_hw(parent_clk), rate, &pll_parent_rate);
|
||||
}
|
||||
|
||||
static unsigned long cpu_clk_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
/*
|
||||
* SiRF SoC has not cpu clock control,
|
||||
* So return the parent pll rate.
|
||||
*/
|
||||
struct clk *parent_clk = clk_get_parent(hw->clk);
|
||||
return __clk_get_rate(parent_clk);
|
||||
}
|
||||
|
||||
static struct clk_ops std_pll_ops = {
|
||||
.recalc_rate = pll_clk_recalc_rate,
|
||||
.round_rate = pll_clk_round_rate,
|
||||
|
@ -403,6 +398,42 @@ static int dmn_clk_set_rate(struct clk_hw *hw, unsigned long rate,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int cpu_clk_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
int ret1, ret2;
|
||||
struct clk *cur_parent;
|
||||
|
||||
if (rate == clk_get_rate(clk_pll1.hw.clk)) {
|
||||
ret1 = clk_set_parent(hw->clk, clk_pll1.hw.clk);
|
||||
return ret1;
|
||||
}
|
||||
|
||||
if (rate == clk_get_rate(clk_pll2.hw.clk)) {
|
||||
ret1 = clk_set_parent(hw->clk, clk_pll2.hw.clk);
|
||||
return ret1;
|
||||
}
|
||||
|
||||
if (rate == clk_get_rate(clk_pll3.hw.clk)) {
|
||||
ret1 = clk_set_parent(hw->clk, clk_pll3.hw.clk);
|
||||
return ret1;
|
||||
}
|
||||
|
||||
cur_parent = clk_get_parent(hw->clk);
|
||||
|
||||
/* switch to tmp pll before setting parent clock's rate */
|
||||
if (cur_parent == clk_pll1.hw.clk) {
|
||||
ret1 = clk_set_parent(hw->clk, clk_pll2.hw.clk);
|
||||
BUG_ON(ret1);
|
||||
}
|
||||
|
||||
ret2 = clk_set_rate(clk_pll1.hw.clk, rate);
|
||||
|
||||
ret1 = clk_set_parent(hw->clk, clk_pll1.hw.clk);
|
||||
|
||||
return ret2 ? ret2 : ret1;
|
||||
}
|
||||
|
||||
static struct clk_ops msi_ops = {
|
||||
.set_rate = dmn_clk_set_rate,
|
||||
.round_rate = dmn_clk_round_rate,
|
||||
|
@ -457,6 +488,9 @@ static struct clk_dmn clk_io = {
|
|||
static struct clk_ops cpu_ops = {
|
||||
.set_parent = dmn_clk_set_parent,
|
||||
.get_parent = dmn_clk_get_parent,
|
||||
.set_rate = cpu_clk_set_rate,
|
||||
.round_rate = cpu_clk_round_rate,
|
||||
.recalc_rate = cpu_clk_recalc_rate,
|
||||
};
|
||||
|
||||
static struct clk_init_data clk_cpu_init = {
|
||||
|
@ -532,6 +566,11 @@ static struct clk_dmn clk_mm = {
|
|||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* for atlas6, gfx2d holds the bit of prima2's clk_mm
|
||||
*/
|
||||
#define clk_gfx2d clk_mm
|
||||
|
||||
static struct clk_init_data clk_lcd_init = {
|
||||
.name = "lcd",
|
||||
.ops = &dmn_ops,
|
||||
|
@ -569,14 +608,6 @@ static struct clk_init_data clk_mmc01_init = {
|
|||
.num_parents = ARRAY_SIZE(dmn_clk_parents),
|
||||
};
|
||||
|
||||
static struct clk_dmn clk_mmc01 = {
|
||||
.regofs = SIRFSOC_CLKC_MMC_CFG,
|
||||
.enable_bit = 59,
|
||||
.hw = {
|
||||
.init = &clk_mmc01_init,
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_init_data clk_mmc23_init = {
|
||||
.name = "mmc23",
|
||||
.ops = &dmn_ops,
|
||||
|
@ -584,14 +615,6 @@ static struct clk_init_data clk_mmc23_init = {
|
|||
.num_parents = ARRAY_SIZE(dmn_clk_parents),
|
||||
};
|
||||
|
||||
static struct clk_dmn clk_mmc23 = {
|
||||
.regofs = SIRFSOC_CLKC_MMC_CFG,
|
||||
.enable_bit = 60,
|
||||
.hw = {
|
||||
.init = &clk_mmc23_init,
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_init_data clk_mmc45_init = {
|
||||
.name = "mmc45",
|
||||
.ops = &dmn_ops,
|
||||
|
@ -599,14 +622,6 @@ static struct clk_init_data clk_mmc45_init = {
|
|||
.num_parents = ARRAY_SIZE(dmn_clk_parents),
|
||||
};
|
||||
|
||||
static struct clk_dmn clk_mmc45 = {
|
||||
.regofs = SIRFSOC_CLKC_MMC_CFG,
|
||||
.enable_bit = 61,
|
||||
.hw = {
|
||||
.init = &clk_mmc45_init,
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* peripheral controllers in io domain
|
||||
*/
|
||||
|
@ -667,6 +682,20 @@ static struct clk_ops ios_ops = {
|
|||
.disable = std_clk_disable,
|
||||
};
|
||||
|
||||
static struct clk_init_data clk_cphif_init = {
|
||||
.name = "cphif",
|
||||
.ops = &ios_ops,
|
||||
.parent_names = std_clk_io_parents,
|
||||
.num_parents = ARRAY_SIZE(std_clk_io_parents),
|
||||
};
|
||||
|
||||
static struct clk_std clk_cphif = {
|
||||
.enable_bit = 20,
|
||||
.hw = {
|
||||
.init = &clk_cphif_init,
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_init_data clk_dmac0_init = {
|
||||
.name = "dmac0",
|
||||
.ops = &ios_ops,
|
||||
|
@ -695,20 +724,6 @@ static struct clk_std clk_dmac1 = {
|
|||
},
|
||||
};
|
||||
|
||||
static struct clk_init_data clk_nand_init = {
|
||||
.name = "nand",
|
||||
.ops = &ios_ops,
|
||||
.parent_names = std_clk_io_parents,
|
||||
.num_parents = ARRAY_SIZE(std_clk_io_parents),
|
||||
};
|
||||
|
||||
static struct clk_std clk_nand = {
|
||||
.enable_bit = 34,
|
||||
.hw = {
|
||||
.init = &clk_nand_init,
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_init_data clk_audio_init = {
|
||||
.name = "audio",
|
||||
.ops = &ios_ops,
|
||||
|
@ -970,7 +985,7 @@ static const char *std_clk_sys_parents[] = {
|
|||
};
|
||||
|
||||
static struct clk_init_data clk_security_init = {
|
||||
.name = "mf",
|
||||
.name = "security",
|
||||
.ops = &ios_ops,
|
||||
.parent_names = std_clk_sys_parents,
|
||||
.num_parents = ARRAY_SIZE(std_clk_sys_parents),
|
||||
|
@ -1014,96 +1029,3 @@ static struct clk_std clk_usb1 = {
|
|||
.init = &clk_usb1_init,
|
||||
},
|
||||
};
|
||||
|
||||
enum prima2_clk_index {
|
||||
/* 0 1 2 3 4 5 6 7 8 9 */
|
||||
rtc, osc, pll1, pll2, pll3, mem, sys, security, dsp, gps,
|
||||
mf, io, cpu, uart0, uart1, uart2, tsc, i2c0, i2c1, spi0,
|
||||
spi1, pwmc, efuse, pulse, dmac0, dmac1, nand, audio, usp0, usp1,
|
||||
usp2, vip, gfx, mm, lcd, vpp, mmc01, mmc23, mmc45, usbpll,
|
||||
usb0, usb1, maxclk,
|
||||
};
|
||||
|
||||
static struct clk_hw *prima2_clk_hw_array[maxclk] __initdata = {
|
||||
NULL, /* dummy */
|
||||
NULL,
|
||||
&clk_pll1.hw,
|
||||
&clk_pll2.hw,
|
||||
&clk_pll3.hw,
|
||||
&clk_mem.hw,
|
||||
&clk_sys.hw,
|
||||
&clk_security.hw,
|
||||
&clk_dsp.hw,
|
||||
&clk_gps.hw,
|
||||
&clk_mf.hw,
|
||||
&clk_io.hw,
|
||||
&clk_cpu.hw,
|
||||
&clk_uart0.hw,
|
||||
&clk_uart1.hw,
|
||||
&clk_uart2.hw,
|
||||
&clk_tsc.hw,
|
||||
&clk_i2c0.hw,
|
||||
&clk_i2c1.hw,
|
||||
&clk_spi0.hw,
|
||||
&clk_spi1.hw,
|
||||
&clk_pwmc.hw,
|
||||
&clk_efuse.hw,
|
||||
&clk_pulse.hw,
|
||||
&clk_dmac0.hw,
|
||||
&clk_dmac1.hw,
|
||||
&clk_nand.hw,
|
||||
&clk_audio.hw,
|
||||
&clk_usp0.hw,
|
||||
&clk_usp1.hw,
|
||||
&clk_usp2.hw,
|
||||
&clk_vip.hw,
|
||||
&clk_gfx.hw,
|
||||
&clk_mm.hw,
|
||||
&clk_lcd.hw,
|
||||
&clk_vpp.hw,
|
||||
&clk_mmc01.hw,
|
||||
&clk_mmc23.hw,
|
||||
&clk_mmc45.hw,
|
||||
&usb_pll_clk_hw,
|
||||
&clk_usb0.hw,
|
||||
&clk_usb1.hw,
|
||||
};
|
||||
|
||||
static struct clk *prima2_clks[maxclk];
|
||||
static struct clk_onecell_data clk_data;
|
||||
|
||||
static void __init sirfsoc_clk_init(struct device_node *np)
|
||||
{
|
||||
struct device_node *rscnp;
|
||||
int i;
|
||||
|
||||
rscnp = of_find_compatible_node(NULL, NULL, "sirf,prima2-rsc");
|
||||
sirfsoc_rsc_vbase = of_iomap(rscnp, 0);
|
||||
if (!sirfsoc_rsc_vbase)
|
||||
panic("unable to map rsc registers\n");
|
||||
of_node_put(rscnp);
|
||||
|
||||
sirfsoc_clk_vbase = of_iomap(np, 0);
|
||||
if (!sirfsoc_clk_vbase)
|
||||
panic("unable to map clkc registers\n");
|
||||
|
||||
/* These are always available (RTC and 26MHz OSC)*/
|
||||
prima2_clks[rtc] = clk_register_fixed_rate(NULL, "rtc", NULL,
|
||||
CLK_IS_ROOT, 32768);
|
||||
prima2_clks[osc]= clk_register_fixed_rate(NULL, "osc", NULL,
|
||||
CLK_IS_ROOT, 26000000);
|
||||
|
||||
for (i = pll1; i < maxclk; i++) {
|
||||
prima2_clks[i] = clk_register(NULL, prima2_clk_hw_array[i]);
|
||||
BUG_ON(IS_ERR(prima2_clks[i]));
|
||||
}
|
||||
clk_register_clkdev(prima2_clks[cpu], NULL, "cpu");
|
||||
clk_register_clkdev(prima2_clks[io], NULL, "io");
|
||||
clk_register_clkdev(prima2_clks[mem], NULL, "mem");
|
||||
|
||||
clk_data.clks = prima2_clks;
|
||||
clk_data.clk_num = maxclk;
|
||||
|
||||
of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
|
||||
}
|
||||
CLK_OF_DECLARE(sirfsoc_clk, "sirf,prima2-clkc", sirfsoc_clk_init);
|
151
drivers/clk/sirf/clk-prima2.c
Normal file
151
drivers/clk/sirf/clk-prima2.c
Normal file
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* Clock tree for CSR SiRFprimaII
|
||||
*
|
||||
* Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
|
||||
*
|
||||
* Licensed under GPLv2 or later.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/syscore_ops.h>
|
||||
|
||||
#include "prima2.h"
|
||||
#include "clk-common.c"
|
||||
|
||||
static struct clk_dmn clk_mmc01 = {
|
||||
.regofs = SIRFSOC_CLKC_MMC_CFG,
|
||||
.enable_bit = 59,
|
||||
.hw = {
|
||||
.init = &clk_mmc01_init,
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_dmn clk_mmc23 = {
|
||||
.regofs = SIRFSOC_CLKC_MMC_CFG,
|
||||
.enable_bit = 60,
|
||||
.hw = {
|
||||
.init = &clk_mmc23_init,
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_dmn clk_mmc45 = {
|
||||
.regofs = SIRFSOC_CLKC_MMC_CFG,
|
||||
.enable_bit = 61,
|
||||
.hw = {
|
||||
.init = &clk_mmc45_init,
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_init_data clk_nand_init = {
|
||||
.name = "nand",
|
||||
.ops = &ios_ops,
|
||||
.parent_names = std_clk_io_parents,
|
||||
.num_parents = ARRAY_SIZE(std_clk_io_parents),
|
||||
};
|
||||
|
||||
static struct clk_std clk_nand = {
|
||||
.enable_bit = 34,
|
||||
.hw = {
|
||||
.init = &clk_nand_init,
|
||||
},
|
||||
};
|
||||
|
||||
enum prima2_clk_index {
|
||||
/* 0 1 2 3 4 5 6 7 8 9 */
|
||||
rtc, osc, pll1, pll2, pll3, mem, sys, security, dsp, gps,
|
||||
mf, io, cpu, uart0, uart1, uart2, tsc, i2c0, i2c1, spi0,
|
||||
spi1, pwmc, efuse, pulse, dmac0, dmac1, nand, audio, usp0, usp1,
|
||||
usp2, vip, gfx, mm, lcd, vpp, mmc01, mmc23, mmc45, usbpll,
|
||||
usb0, usb1, cphif, maxclk,
|
||||
};
|
||||
|
||||
static __initdata struct clk_hw *prima2_clk_hw_array[maxclk] = {
|
||||
NULL, /* dummy */
|
||||
NULL,
|
||||
&clk_pll1.hw,
|
||||
&clk_pll2.hw,
|
||||
&clk_pll3.hw,
|
||||
&clk_mem.hw,
|
||||
&clk_sys.hw,
|
||||
&clk_security.hw,
|
||||
&clk_dsp.hw,
|
||||
&clk_gps.hw,
|
||||
&clk_mf.hw,
|
||||
&clk_io.hw,
|
||||
&clk_cpu.hw,
|
||||
&clk_uart0.hw,
|
||||
&clk_uart1.hw,
|
||||
&clk_uart2.hw,
|
||||
&clk_tsc.hw,
|
||||
&clk_i2c0.hw,
|
||||
&clk_i2c1.hw,
|
||||
&clk_spi0.hw,
|
||||
&clk_spi1.hw,
|
||||
&clk_pwmc.hw,
|
||||
&clk_efuse.hw,
|
||||
&clk_pulse.hw,
|
||||
&clk_dmac0.hw,
|
||||
&clk_dmac1.hw,
|
||||
&clk_nand.hw,
|
||||
&clk_audio.hw,
|
||||
&clk_usp0.hw,
|
||||
&clk_usp1.hw,
|
||||
&clk_usp2.hw,
|
||||
&clk_vip.hw,
|
||||
&clk_gfx.hw,
|
||||
&clk_mm.hw,
|
||||
&clk_lcd.hw,
|
||||
&clk_vpp.hw,
|
||||
&clk_mmc01.hw,
|
||||
&clk_mmc23.hw,
|
||||
&clk_mmc45.hw,
|
||||
&usb_pll_clk_hw,
|
||||
&clk_usb0.hw,
|
||||
&clk_usb1.hw,
|
||||
&clk_cphif.hw,
|
||||
};
|
||||
|
||||
static struct clk *prima2_clks[maxclk];
|
||||
|
||||
static void __init prima2_clk_init(struct device_node *np)
|
||||
{
|
||||
struct device_node *rscnp;
|
||||
int i;
|
||||
|
||||
rscnp = of_find_compatible_node(NULL, NULL, "sirf,prima2-rsc");
|
||||
sirfsoc_rsc_vbase = of_iomap(rscnp, 0);
|
||||
if (!sirfsoc_rsc_vbase)
|
||||
panic("unable to map rsc registers\n");
|
||||
of_node_put(rscnp);
|
||||
|
||||
sirfsoc_clk_vbase = of_iomap(np, 0);
|
||||
if (!sirfsoc_clk_vbase)
|
||||
panic("unable to map clkc registers\n");
|
||||
|
||||
/* These are always available (RTC and 26MHz OSC)*/
|
||||
prima2_clks[rtc] = clk_register_fixed_rate(NULL, "rtc", NULL,
|
||||
CLK_IS_ROOT, 32768);
|
||||
prima2_clks[osc] = clk_register_fixed_rate(NULL, "osc", NULL,
|
||||
CLK_IS_ROOT, 26000000);
|
||||
|
||||
for (i = pll1; i < maxclk; i++) {
|
||||
prima2_clks[i] = clk_register(NULL, prima2_clk_hw_array[i]);
|
||||
BUG_ON(!prima2_clks[i]);
|
||||
}
|
||||
clk_register_clkdev(prima2_clks[cpu], NULL, "cpu");
|
||||
clk_register_clkdev(prima2_clks[io], NULL, "io");
|
||||
clk_register_clkdev(prima2_clks[mem], NULL, "mem");
|
||||
clk_register_clkdev(prima2_clks[mem], NULL, "osc");
|
||||
|
||||
clk_data.clks = prima2_clks;
|
||||
clk_data.clk_num = maxclk;
|
||||
|
||||
of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
|
||||
}
|
||||
CLK_OF_DECLARE(prima2_clk, "sirf,prima2-clkc", prima2_clk_init);
|
25
drivers/clk/sirf/prima2.h
Normal file
25
drivers/clk/sirf/prima2.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
#define SIRFSOC_CLKC_CLK_EN0 0x0000
|
||||
#define SIRFSOC_CLKC_CLK_EN1 0x0004
|
||||
#define SIRFSOC_CLKC_REF_CFG 0x0014
|
||||
#define SIRFSOC_CLKC_CPU_CFG 0x0018
|
||||
#define SIRFSOC_CLKC_MEM_CFG 0x001c
|
||||
#define SIRFSOC_CLKC_SYS_CFG 0x0020
|
||||
#define SIRFSOC_CLKC_IO_CFG 0x0024
|
||||
#define SIRFSOC_CLKC_DSP_CFG 0x0028
|
||||
#define SIRFSOC_CLKC_GFX_CFG 0x002c
|
||||
#define SIRFSOC_CLKC_MM_CFG 0x0030
|
||||
#define SIRFSOC_CLKC_LCD_CFG 0x0034
|
||||
#define SIRFSOC_CLKC_MMC_CFG 0x0038
|
||||
#define SIRFSOC_CLKC_PLL1_CFG0 0x0040
|
||||
#define SIRFSOC_CLKC_PLL2_CFG0 0x0044
|
||||
#define SIRFSOC_CLKC_PLL3_CFG0 0x0048
|
||||
#define SIRFSOC_CLKC_PLL1_CFG1 0x004c
|
||||
#define SIRFSOC_CLKC_PLL2_CFG1 0x0050
|
||||
#define SIRFSOC_CLKC_PLL3_CFG1 0x0054
|
||||
#define SIRFSOC_CLKC_PLL1_CFG2 0x0058
|
||||
#define SIRFSOC_CLKC_PLL2_CFG2 0x005c
|
||||
#define SIRFSOC_CLKC_PLL3_CFG2 0x0060
|
||||
#define SIRFSOC_USBPHY_PLL_CTRL 0x0008
|
||||
#define SIRFSOC_USBPHY_PLL_POWERDOWN BIT(1)
|
||||
#define SIRFSOC_USBPHY_PLL_BYPASS BIT(2)
|
||||
#define SIRFSOC_USBPHY_PLL_LOCK BIT(3)
|
|
@ -121,9 +121,7 @@ static __init struct clk *socfpga_clk_init(struct device_node *node,
|
|||
int rc;
|
||||
u32 fixed_div;
|
||||
|
||||
rc = of_property_read_u32(node, "reg", ®);
|
||||
if (WARN_ON(rc))
|
||||
return NULL;
|
||||
of_property_read_u32(node, "reg", ®);
|
||||
|
||||
socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL);
|
||||
if (WARN_ON(!socfpga_clk))
|
||||
|
@ -292,7 +290,7 @@ static void __init socfpga_gate_clk_init(struct device_node *node,
|
|||
socfpga_clk->shift = div_reg[1];
|
||||
socfpga_clk->width = div_reg[2];
|
||||
} else {
|
||||
socfpga_clk->div_reg = 0;
|
||||
socfpga_clk->div_reg = NULL;
|
||||
}
|
||||
|
||||
of_property_read_string(node, "clock-output-names", &clk_name);
|
||||
|
|
|
@ -116,7 +116,7 @@ static int clk_frac_set_rate(struct clk_hw *hw, unsigned long drate,
|
|||
return 0;
|
||||
}
|
||||
|
||||
struct clk_ops clk_frac_ops = {
|
||||
static struct clk_ops clk_frac_ops = {
|
||||
.recalc_rate = clk_frac_recalc_rate,
|
||||
.round_rate = clk_frac_round_rate,
|
||||
.set_rate = clk_frac_set_rate,
|
||||
|
|
|
@ -30,17 +30,9 @@
|
|||
* parent - fixed parent. No clk_set_parent support
|
||||
*/
|
||||
|
||||
struct clk_factors {
|
||||
struct clk_hw hw;
|
||||
void __iomem *reg;
|
||||
struct clk_factors_config *config;
|
||||
void (*get_factors) (u32 *rate, u32 parent, u8 *n, u8 *k, u8 *m, u8 *p);
|
||||
spinlock_t *lock;
|
||||
};
|
||||
|
||||
#define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw)
|
||||
|
||||
#define SETMASK(len, pos) (((-1U) >> (31-len)) << (pos))
|
||||
#define SETMASK(len, pos) (((1U << (len)) - 1) << (pos))
|
||||
#define CLRMASK(len, pos) (~(SETMASK(len, pos)))
|
||||
#define FACTOR_GET(bit, len, reg) (((reg) & SETMASK(len, bit)) >> (bit))
|
||||
|
||||
|
@ -88,7 +80,7 @@ static long clk_factors_round_rate(struct clk_hw *hw, unsigned long rate,
|
|||
static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
u8 n, k, m, p;
|
||||
u8 n = 0, k = 0, m = 0, p = 0;
|
||||
u32 reg;
|
||||
struct clk_factors *factors = to_clk_factors(hw);
|
||||
struct clk_factors_config *config = factors->config;
|
||||
|
@ -120,61 +112,8 @@ static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops clk_factors_ops = {
|
||||
const struct clk_ops clk_factors_ops = {
|
||||
.recalc_rate = clk_factors_recalc_rate,
|
||||
.round_rate = clk_factors_round_rate,
|
||||
.set_rate = clk_factors_set_rate,
|
||||
};
|
||||
|
||||
/**
|
||||
* clk_register_factors - register a factors clock with
|
||||
* the clock framework
|
||||
* @dev: device registering this clock
|
||||
* @name: name of this clock
|
||||
* @parent_name: name of clock's parent
|
||||
* @flags: framework-specific flags
|
||||
* @reg: register address to adjust factors
|
||||
* @config: shift and width of factors n, k, m and p
|
||||
* @get_factors: function to calculate the factors for a given frequency
|
||||
* @lock: shared register lock for this clock
|
||||
*/
|
||||
struct clk *clk_register_factors(struct device *dev, const char *name,
|
||||
const char *parent_name,
|
||||
unsigned long flags, void __iomem *reg,
|
||||
struct clk_factors_config *config,
|
||||
void (*get_factors)(u32 *rate, u32 parent,
|
||||
u8 *n, u8 *k, u8 *m, u8 *p),
|
||||
spinlock_t *lock)
|
||||
{
|
||||
struct clk_factors *factors;
|
||||
struct clk *clk;
|
||||
struct clk_init_data init;
|
||||
|
||||
/* allocate the factors */
|
||||
factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL);
|
||||
if (!factors) {
|
||||
pr_err("%s: could not allocate factors clk\n", __func__);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
init.name = name;
|
||||
init.ops = &clk_factors_ops;
|
||||
init.flags = flags;
|
||||
init.parent_names = (parent_name ? &parent_name : NULL);
|
||||
init.num_parents = (parent_name ? 1 : 0);
|
||||
|
||||
/* struct clk_factors assignments */
|
||||
factors->reg = reg;
|
||||
factors->config = config;
|
||||
factors->lock = lock;
|
||||
factors->hw.init = &init;
|
||||
factors->get_factors = get_factors;
|
||||
|
||||
/* register the clock */
|
||||
clk = clk_register(dev, &factors->hw);
|
||||
|
||||
if (IS_ERR(clk))
|
||||
kfree(factors);
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
|
|
@ -17,11 +17,13 @@ struct clk_factors_config {
|
|||
u8 pwidth;
|
||||
};
|
||||
|
||||
struct clk *clk_register_factors(struct device *dev, const char *name,
|
||||
const char *parent_name,
|
||||
unsigned long flags, void __iomem *reg,
|
||||
struct clk_factors_config *config,
|
||||
void (*get_factors) (u32 *rate, u32 parent_rate,
|
||||
u8 *n, u8 *k, u8 *m, u8 *p),
|
||||
spinlock_t *lock);
|
||||
struct clk_factors {
|
||||
struct clk_hw hw;
|
||||
void __iomem *reg;
|
||||
struct clk_factors_config *config;
|
||||
void (*get_factors) (u32 *rate, u32 parent, u8 *n, u8 *k, u8 *m, u8 *p);
|
||||
spinlock_t *lock;
|
||||
};
|
||||
|
||||
extern const struct clk_ops clk_factors_ops;
|
||||
#endif
|
||||
|
|
|
@ -23,6 +23,9 @@
|
|||
|
||||
static DEFINE_SPINLOCK(clk_lock);
|
||||
|
||||
/* Maximum number of parents our clocks have */
|
||||
#define SUNXI_MAX_PARENTS 5
|
||||
|
||||
/**
|
||||
* sun4i_osc_clk_setup() - Setup function for gatable oscillator
|
||||
*/
|
||||
|
@ -37,18 +40,16 @@ static void __init sun4i_osc_clk_setup(struct device_node *node)
|
|||
const char *clk_name = node->name;
|
||||
u32 rate;
|
||||
|
||||
if (of_property_read_u32(node, "clock-frequency", &rate))
|
||||
return;
|
||||
|
||||
/* allocate fixed-rate and gate clock structs */
|
||||
fixed = kzalloc(sizeof(struct clk_fixed_rate), GFP_KERNEL);
|
||||
if (!fixed)
|
||||
return;
|
||||
gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
|
||||
if (!gate) {
|
||||
kfree(fixed);
|
||||
return;
|
||||
}
|
||||
|
||||
if (of_property_read_u32(node, "clock-frequency", &rate))
|
||||
return;
|
||||
if (!gate)
|
||||
goto err_free_fixed;
|
||||
|
||||
/* set up gate and fixed rate properties */
|
||||
gate->reg = of_iomap(node, 0);
|
||||
|
@ -63,10 +64,18 @@ static void __init sun4i_osc_clk_setup(struct device_node *node)
|
|||
&gate->hw, &clk_gate_ops,
|
||||
CLK_IS_ROOT);
|
||||
|
||||
if (!IS_ERR(clk)) {
|
||||
of_clk_add_provider(node, of_clk_src_simple_get, clk);
|
||||
clk_register_clkdev(clk, clk_name, NULL);
|
||||
}
|
||||
if (IS_ERR(clk))
|
||||
goto err_free_gate;
|
||||
|
||||
of_clk_add_provider(node, of_clk_src_simple_get, clk);
|
||||
clk_register_clkdev(clk, clk_name, NULL);
|
||||
|
||||
return;
|
||||
|
||||
err_free_gate:
|
||||
kfree(gate);
|
||||
err_free_fixed:
|
||||
kfree(fixed);
|
||||
}
|
||||
CLK_OF_DECLARE(sun4i_osc, "allwinner,sun4i-osc-clk", sun4i_osc_clk_setup);
|
||||
|
||||
|
@ -208,6 +217,40 @@ static void sun6i_a31_get_pll1_factors(u32 *freq, u32 parent_rate,
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* sun4i_get_pll5_factors() - calculates n, k factors for PLL5
|
||||
* PLL5 rate is calculated as follows
|
||||
* rate = parent_rate * n * (k + 1)
|
||||
* parent_rate is always 24Mhz
|
||||
*/
|
||||
|
||||
static void sun4i_get_pll5_factors(u32 *freq, u32 parent_rate,
|
||||
u8 *n, u8 *k, u8 *m, u8 *p)
|
||||
{
|
||||
u8 div;
|
||||
|
||||
/* Normalize value to a parent_rate multiple (24M) */
|
||||
div = *freq / parent_rate;
|
||||
*freq = parent_rate * div;
|
||||
|
||||
/* we were called to round the frequency, we can now return */
|
||||
if (n == NULL)
|
||||
return;
|
||||
|
||||
if (div < 31)
|
||||
*k = 0;
|
||||
else if (div / 2 < 31)
|
||||
*k = 1;
|
||||
else if (div / 3 < 31)
|
||||
*k = 2;
|
||||
else
|
||||
*k = 3;
|
||||
|
||||
*n = DIV_ROUND_UP(div, (*k+1));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* sun4i_get_apb1_factors() - calculates m, p factors for APB1
|
||||
* APB1 rate is calculated as follows
|
||||
|
@ -251,11 +294,97 @@ static void sun4i_get_apb1_factors(u32 *freq, u32 parent_rate,
|
|||
|
||||
|
||||
|
||||
/**
|
||||
* sun4i_get_mod0_factors() - calculates m, n factors for MOD0-style clocks
|
||||
* MMC rate is calculated as follows
|
||||
* rate = (parent_rate >> p) / (m + 1);
|
||||
*/
|
||||
|
||||
static void sun4i_get_mod0_factors(u32 *freq, u32 parent_rate,
|
||||
u8 *n, u8 *k, u8 *m, u8 *p)
|
||||
{
|
||||
u8 div, calcm, calcp;
|
||||
|
||||
/* These clocks can only divide, so we will never be able to achieve
|
||||
* frequencies higher than the parent frequency */
|
||||
if (*freq > parent_rate)
|
||||
*freq = parent_rate;
|
||||
|
||||
div = parent_rate / *freq;
|
||||
|
||||
if (div < 16)
|
||||
calcp = 0;
|
||||
else if (div / 2 < 16)
|
||||
calcp = 1;
|
||||
else if (div / 4 < 16)
|
||||
calcp = 2;
|
||||
else
|
||||
calcp = 3;
|
||||
|
||||
calcm = DIV_ROUND_UP(div, 1 << calcp);
|
||||
|
||||
*freq = (parent_rate >> calcp) / calcm;
|
||||
|
||||
/* we were called to round the frequency, we can now return */
|
||||
if (n == NULL)
|
||||
return;
|
||||
|
||||
*m = calcm - 1;
|
||||
*p = calcp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* sun7i_a20_get_out_factors() - calculates m, p factors for CLK_OUT_A/B
|
||||
* CLK_OUT rate is calculated as follows
|
||||
* rate = (parent_rate >> p) / (m + 1);
|
||||
*/
|
||||
|
||||
static void sun7i_a20_get_out_factors(u32 *freq, u32 parent_rate,
|
||||
u8 *n, u8 *k, u8 *m, u8 *p)
|
||||
{
|
||||
u8 div, calcm, calcp;
|
||||
|
||||
/* These clocks can only divide, so we will never be able to achieve
|
||||
* frequencies higher than the parent frequency */
|
||||
if (*freq > parent_rate)
|
||||
*freq = parent_rate;
|
||||
|
||||
div = parent_rate / *freq;
|
||||
|
||||
if (div < 32)
|
||||
calcp = 0;
|
||||
else if (div / 2 < 32)
|
||||
calcp = 1;
|
||||
else if (div / 4 < 32)
|
||||
calcp = 2;
|
||||
else
|
||||
calcp = 3;
|
||||
|
||||
calcm = DIV_ROUND_UP(div, 1 << calcp);
|
||||
|
||||
*freq = (parent_rate >> calcp) / calcm;
|
||||
|
||||
/* we were called to round the frequency, we can now return */
|
||||
if (n == NULL)
|
||||
return;
|
||||
|
||||
*m = calcm - 1;
|
||||
*p = calcp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* sunxi_factors_clk_setup() - Setup function for factor clocks
|
||||
*/
|
||||
|
||||
#define SUNXI_FACTORS_MUX_MASK 0x3
|
||||
|
||||
struct factors_data {
|
||||
int enable;
|
||||
int mux;
|
||||
struct clk_factors_config *table;
|
||||
void (*getter) (u32 *rate, u32 parent_rate, u8 *n, u8 *k, u8 *m, u8 *p);
|
||||
};
|
||||
|
@ -280,6 +409,13 @@ static struct clk_factors_config sun6i_a31_pll1_config = {
|
|||
.mwidth = 2,
|
||||
};
|
||||
|
||||
static struct clk_factors_config sun4i_pll5_config = {
|
||||
.nshift = 8,
|
||||
.nwidth = 5,
|
||||
.kshift = 4,
|
||||
.kwidth = 2,
|
||||
};
|
||||
|
||||
static struct clk_factors_config sun4i_apb1_config = {
|
||||
.mshift = 0,
|
||||
.mwidth = 5,
|
||||
|
@ -287,40 +423,143 @@ static struct clk_factors_config sun4i_apb1_config = {
|
|||
.pwidth = 2,
|
||||
};
|
||||
|
||||
/* user manual says "n" but it's really "p" */
|
||||
static struct clk_factors_config sun4i_mod0_config = {
|
||||
.mshift = 0,
|
||||
.mwidth = 4,
|
||||
.pshift = 16,
|
||||
.pwidth = 2,
|
||||
};
|
||||
|
||||
/* user manual says "n" but it's really "p" */
|
||||
static struct clk_factors_config sun7i_a20_out_config = {
|
||||
.mshift = 8,
|
||||
.mwidth = 5,
|
||||
.pshift = 20,
|
||||
.pwidth = 2,
|
||||
};
|
||||
|
||||
static const struct factors_data sun4i_pll1_data __initconst = {
|
||||
.enable = 31,
|
||||
.table = &sun4i_pll1_config,
|
||||
.getter = sun4i_get_pll1_factors,
|
||||
};
|
||||
|
||||
static const struct factors_data sun6i_a31_pll1_data __initconst = {
|
||||
.enable = 31,
|
||||
.table = &sun6i_a31_pll1_config,
|
||||
.getter = sun6i_a31_get_pll1_factors,
|
||||
};
|
||||
|
||||
static const struct factors_data sun4i_pll5_data __initconst = {
|
||||
.enable = 31,
|
||||
.table = &sun4i_pll5_config,
|
||||
.getter = sun4i_get_pll5_factors,
|
||||
};
|
||||
|
||||
static const struct factors_data sun4i_apb1_data __initconst = {
|
||||
.table = &sun4i_apb1_config,
|
||||
.getter = sun4i_get_apb1_factors,
|
||||
};
|
||||
|
||||
static void __init sunxi_factors_clk_setup(struct device_node *node,
|
||||
struct factors_data *data)
|
||||
static const struct factors_data sun4i_mod0_data __initconst = {
|
||||
.enable = 31,
|
||||
.mux = 24,
|
||||
.table = &sun4i_mod0_config,
|
||||
.getter = sun4i_get_mod0_factors,
|
||||
};
|
||||
|
||||
static const struct factors_data sun7i_a20_out_data __initconst = {
|
||||
.enable = 31,
|
||||
.mux = 24,
|
||||
.table = &sun7i_a20_out_config,
|
||||
.getter = sun7i_a20_get_out_factors,
|
||||
};
|
||||
|
||||
static struct clk * __init sunxi_factors_clk_setup(struct device_node *node,
|
||||
const struct factors_data *data)
|
||||
{
|
||||
struct clk *clk;
|
||||
struct clk_factors *factors;
|
||||
struct clk_gate *gate = NULL;
|
||||
struct clk_mux *mux = NULL;
|
||||
struct clk_hw *gate_hw = NULL;
|
||||
struct clk_hw *mux_hw = NULL;
|
||||
const char *clk_name = node->name;
|
||||
const char *parent;
|
||||
const char *parents[SUNXI_MAX_PARENTS];
|
||||
void *reg;
|
||||
int i = 0;
|
||||
|
||||
reg = of_iomap(node, 0);
|
||||
|
||||
parent = of_clk_get_parent_name(node, 0);
|
||||
/* if we have a mux, we will have >1 parents */
|
||||
while (i < SUNXI_MAX_PARENTS &&
|
||||
(parents[i] = of_clk_get_parent_name(node, i)) != NULL)
|
||||
i++;
|
||||
|
||||
clk = clk_register_factors(NULL, clk_name, parent, 0, reg,
|
||||
data->table, data->getter, &clk_lock);
|
||||
/* Nodes should be providing the name via clock-output-names
|
||||
* but originally our dts didn't, and so we used node->name.
|
||||
* The new, better nodes look like clk@deadbeef, so we pull the
|
||||
* name just in this case */
|
||||
if (!strcmp("clk", clk_name)) {
|
||||
of_property_read_string_index(node, "clock-output-names",
|
||||
0, &clk_name);
|
||||
}
|
||||
|
||||
factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL);
|
||||
if (!factors)
|
||||
return NULL;
|
||||
|
||||
/* Add a gate if this factor clock can be gated */
|
||||
if (data->enable) {
|
||||
gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
|
||||
if (!gate) {
|
||||
kfree(factors);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* set up gate properties */
|
||||
gate->reg = reg;
|
||||
gate->bit_idx = data->enable;
|
||||
gate->lock = &clk_lock;
|
||||
gate_hw = &gate->hw;
|
||||
}
|
||||
|
||||
/* Add a mux if this factor clock can be muxed */
|
||||
if (data->mux) {
|
||||
mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL);
|
||||
if (!mux) {
|
||||
kfree(factors);
|
||||
kfree(gate);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* set up gate properties */
|
||||
mux->reg = reg;
|
||||
mux->shift = data->mux;
|
||||
mux->mask = SUNXI_FACTORS_MUX_MASK;
|
||||
mux->lock = &clk_lock;
|
||||
mux_hw = &mux->hw;
|
||||
}
|
||||
|
||||
/* set up factors properties */
|
||||
factors->reg = reg;
|
||||
factors->config = data->table;
|
||||
factors->get_factors = data->getter;
|
||||
factors->lock = &clk_lock;
|
||||
|
||||
clk = clk_register_composite(NULL, clk_name,
|
||||
parents, i,
|
||||
mux_hw, &clk_mux_ops,
|
||||
&factors->hw, &clk_factors_ops,
|
||||
gate_hw, &clk_gate_ops, 0);
|
||||
|
||||
if (!IS_ERR(clk)) {
|
||||
of_clk_add_provider(node, of_clk_src_simple_get, clk);
|
||||
clk_register_clkdev(clk, clk_name, NULL);
|
||||
}
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
|
||||
|
@ -352,13 +591,14 @@ static void __init sunxi_mux_clk_setup(struct device_node *node,
|
|||
{
|
||||
struct clk *clk;
|
||||
const char *clk_name = node->name;
|
||||
const char *parents[5];
|
||||
const char *parents[SUNXI_MAX_PARENTS];
|
||||
void *reg;
|
||||
int i = 0;
|
||||
|
||||
reg = of_iomap(node, 0);
|
||||
|
||||
while (i < 5 && (parents[i] = of_clk_get_parent_name(node, i)) != NULL)
|
||||
while (i < SUNXI_MAX_PARENTS &&
|
||||
(parents[i] = of_clk_get_parent_name(node, i)) != NULL)
|
||||
i++;
|
||||
|
||||
clk = clk_register_mux(NULL, clk_name, parents, i,
|
||||
|
@ -555,11 +795,186 @@ static void __init sunxi_gates_clk_setup(struct device_node *node,
|
|||
of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* sunxi_divs_clk_setup() helper data
|
||||
*/
|
||||
|
||||
#define SUNXI_DIVS_MAX_QTY 2
|
||||
#define SUNXI_DIVISOR_WIDTH 2
|
||||
|
||||
struct divs_data {
|
||||
const struct factors_data *factors; /* data for the factor clock */
|
||||
struct {
|
||||
u8 fixed; /* is it a fixed divisor? if not... */
|
||||
struct clk_div_table *table; /* is it a table based divisor? */
|
||||
u8 shift; /* otherwise it's a normal divisor with this shift */
|
||||
u8 pow; /* is it power-of-two based? */
|
||||
u8 gate; /* is it independently gateable? */
|
||||
} div[SUNXI_DIVS_MAX_QTY];
|
||||
};
|
||||
|
||||
static struct clk_div_table pll6_sata_tbl[] = {
|
||||
{ .val = 0, .div = 6, },
|
||||
{ .val = 1, .div = 12, },
|
||||
{ .val = 2, .div = 18, },
|
||||
{ .val = 3, .div = 24, },
|
||||
{ } /* sentinel */
|
||||
};
|
||||
|
||||
static const struct divs_data pll5_divs_data __initconst = {
|
||||
.factors = &sun4i_pll5_data,
|
||||
.div = {
|
||||
{ .shift = 0, .pow = 0, }, /* M, DDR */
|
||||
{ .shift = 16, .pow = 1, }, /* P, other */
|
||||
}
|
||||
};
|
||||
|
||||
static const struct divs_data pll6_divs_data __initconst = {
|
||||
.factors = &sun4i_pll5_data,
|
||||
.div = {
|
||||
{ .shift = 0, .table = pll6_sata_tbl, .gate = 14 }, /* M, SATA */
|
||||
{ .fixed = 2 }, /* P, other */
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* sunxi_divs_clk_setup() - Setup function for leaf divisors on clocks
|
||||
*
|
||||
* These clocks look something like this
|
||||
* ________________________
|
||||
* | ___divisor 1---|----> to consumer
|
||||
* parent >--| pll___/___divisor 2---|----> to consumer
|
||||
* | \_______________|____> to consumer
|
||||
* |________________________|
|
||||
*/
|
||||
|
||||
static void __init sunxi_divs_clk_setup(struct device_node *node,
|
||||
struct divs_data *data)
|
||||
{
|
||||
struct clk_onecell_data *clk_data;
|
||||
const char *parent = node->name;
|
||||
const char *clk_name;
|
||||
struct clk **clks, *pclk;
|
||||
struct clk_hw *gate_hw, *rate_hw;
|
||||
const struct clk_ops *rate_ops;
|
||||
struct clk_gate *gate = NULL;
|
||||
struct clk_fixed_factor *fix_factor;
|
||||
struct clk_divider *divider;
|
||||
void *reg;
|
||||
int i = 0;
|
||||
int flags, clkflags;
|
||||
|
||||
/* Set up factor clock that we will be dividing */
|
||||
pclk = sunxi_factors_clk_setup(node, data->factors);
|
||||
|
||||
reg = of_iomap(node, 0);
|
||||
|
||||
clk_data = kmalloc(sizeof(struct clk_onecell_data), GFP_KERNEL);
|
||||
if (!clk_data)
|
||||
return;
|
||||
|
||||
clks = kzalloc(SUNXI_DIVS_MAX_QTY * sizeof(struct clk *), GFP_KERNEL);
|
||||
if (!clks)
|
||||
goto free_clkdata;
|
||||
|
||||
clk_data->clks = clks;
|
||||
|
||||
/* It's not a good idea to have automatic reparenting changing
|
||||
* our RAM clock! */
|
||||
clkflags = !strcmp("pll5", parent) ? 0 : CLK_SET_RATE_PARENT;
|
||||
|
||||
for (i = 0; i < SUNXI_DIVS_MAX_QTY; i++) {
|
||||
if (of_property_read_string_index(node, "clock-output-names",
|
||||
i, &clk_name) != 0)
|
||||
break;
|
||||
|
||||
gate_hw = NULL;
|
||||
rate_hw = NULL;
|
||||
rate_ops = NULL;
|
||||
|
||||
/* If this leaf clock can be gated, create a gate */
|
||||
if (data->div[i].gate) {
|
||||
gate = kzalloc(sizeof(*gate), GFP_KERNEL);
|
||||
if (!gate)
|
||||
goto free_clks;
|
||||
|
||||
gate->reg = reg;
|
||||
gate->bit_idx = data->div[i].gate;
|
||||
gate->lock = &clk_lock;
|
||||
|
||||
gate_hw = &gate->hw;
|
||||
}
|
||||
|
||||
/* Leaves can be fixed or configurable divisors */
|
||||
if (data->div[i].fixed) {
|
||||
fix_factor = kzalloc(sizeof(*fix_factor), GFP_KERNEL);
|
||||
if (!fix_factor)
|
||||
goto free_gate;
|
||||
|
||||
fix_factor->mult = 1;
|
||||
fix_factor->div = data->div[i].fixed;
|
||||
|
||||
rate_hw = &fix_factor->hw;
|
||||
rate_ops = &clk_fixed_factor_ops;
|
||||
} else {
|
||||
divider = kzalloc(sizeof(*divider), GFP_KERNEL);
|
||||
if (!divider)
|
||||
goto free_gate;
|
||||
|
||||
flags = data->div[i].pow ? CLK_DIVIDER_POWER_OF_TWO : 0;
|
||||
|
||||
divider->reg = reg;
|
||||
divider->shift = data->div[i].shift;
|
||||
divider->width = SUNXI_DIVISOR_WIDTH;
|
||||
divider->flags = flags;
|
||||
divider->lock = &clk_lock;
|
||||
divider->table = data->div[i].table;
|
||||
|
||||
rate_hw = ÷r->hw;
|
||||
rate_ops = &clk_divider_ops;
|
||||
}
|
||||
|
||||
/* Wrap the (potential) gate and the divisor on a composite
|
||||
* clock to unify them */
|
||||
clks[i] = clk_register_composite(NULL, clk_name, &parent, 1,
|
||||
NULL, NULL,
|
||||
rate_hw, rate_ops,
|
||||
gate_hw, &clk_gate_ops,
|
||||
clkflags);
|
||||
|
||||
WARN_ON(IS_ERR(clk_data->clks[i]));
|
||||
clk_register_clkdev(clks[i], clk_name, NULL);
|
||||
}
|
||||
|
||||
/* The last clock available on the getter is the parent */
|
||||
clks[i++] = pclk;
|
||||
|
||||
/* Adjust to the real max */
|
||||
clk_data->clk_num = i;
|
||||
|
||||
of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
|
||||
|
||||
return;
|
||||
|
||||
free_gate:
|
||||
kfree(gate);
|
||||
free_clks:
|
||||
kfree(clks);
|
||||
free_clkdata:
|
||||
kfree(clk_data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Matches for factors clocks */
|
||||
static const struct of_device_id clk_factors_match[] __initconst = {
|
||||
{.compatible = "allwinner,sun4i-pll1-clk", .data = &sun4i_pll1_data,},
|
||||
{.compatible = "allwinner,sun6i-a31-pll1-clk", .data = &sun6i_a31_pll1_data,},
|
||||
{.compatible = "allwinner,sun4i-apb1-clk", .data = &sun4i_apb1_data,},
|
||||
{.compatible = "allwinner,sun4i-mod0-clk", .data = &sun4i_mod0_data,},
|
||||
{.compatible = "allwinner,sun7i-a20-out-clk", .data = &sun7i_a20_out_data,},
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -572,6 +987,13 @@ static const struct of_device_id clk_div_match[] __initconst = {
|
|||
{}
|
||||
};
|
||||
|
||||
/* Matches for divided outputs */
|
||||
static const struct of_device_id clk_divs_match[] __initconst = {
|
||||
{.compatible = "allwinner,sun4i-pll5-clk", .data = &pll5_divs_data,},
|
||||
{.compatible = "allwinner,sun4i-pll6-clk", .data = &pll6_divs_data,},
|
||||
{}
|
||||
};
|
||||
|
||||
/* Matches for mux clocks */
|
||||
static const struct of_device_id clk_mux_match[] __initconst = {
|
||||
{.compatible = "allwinner,sun4i-cpu-clk", .data = &sun4i_cpu_mux_data,},
|
||||
|
@ -616,7 +1038,32 @@ static void __init of_sunxi_table_clock_setup(const struct of_device_id *clk_mat
|
|||
}
|
||||
}
|
||||
|
||||
static void __init sunxi_init_clocks(struct device_node *np)
|
||||
/**
|
||||
* System clock protection
|
||||
*
|
||||
* By enabling these critical clocks, we prevent their accidental gating
|
||||
* by the framework
|
||||
*/
|
||||
static void __init sunxi_clock_protect(void)
|
||||
{
|
||||
struct clk *clk;
|
||||
|
||||
/* memory bus clock - sun5i+ */
|
||||
clk = clk_get(NULL, "mbus");
|
||||
if (!IS_ERR(clk)) {
|
||||
clk_prepare_enable(clk);
|
||||
clk_put(clk);
|
||||
}
|
||||
|
||||
/* DDR clock - sun4i+ */
|
||||
clk = clk_get(NULL, "pll5_ddr");
|
||||
if (!IS_ERR(clk)) {
|
||||
clk_prepare_enable(clk);
|
||||
clk_put(clk);
|
||||
}
|
||||
}
|
||||
|
||||
static void __init sunxi_init_clocks(void)
|
||||
{
|
||||
/* Register factor clocks */
|
||||
of_sunxi_table_clock_setup(clk_factors_match, sunxi_factors_clk_setup);
|
||||
|
@ -624,11 +1071,17 @@ static void __init sunxi_init_clocks(struct device_node *np)
|
|||
/* Register divider clocks */
|
||||
of_sunxi_table_clock_setup(clk_div_match, sunxi_divider_clk_setup);
|
||||
|
||||
/* Register divided output clocks */
|
||||
of_sunxi_table_clock_setup(clk_divs_match, sunxi_divs_clk_setup);
|
||||
|
||||
/* Register mux clocks */
|
||||
of_sunxi_table_clock_setup(clk_mux_match, sunxi_mux_clk_setup);
|
||||
|
||||
/* Register gate clocks */
|
||||
of_sunxi_table_clock_setup(clk_gates_match, sunxi_gates_clk_setup);
|
||||
|
||||
/* Enable core system clocks */
|
||||
sunxi_clock_protect();
|
||||
}
|
||||
CLK_OF_DECLARE(sun4i_a10_clk_init, "allwinner,sun4i-a10", sunxi_init_clocks);
|
||||
CLK_OF_DECLARE(sun5i_a10s_clk_init, "allwinner,sun5i-a10s", sunxi_init_clocks);
|
||||
|
|
|
@ -6,7 +6,12 @@ obj-y += clk-periph-gate.o
|
|||
obj-y += clk-pll.o
|
||||
obj-y += clk-pll-out.o
|
||||
obj-y += clk-super.o
|
||||
|
||||
obj-y += clk-tegra-audio.o
|
||||
obj-y += clk-tegra-periph.o
|
||||
obj-y += clk-tegra-pmc.o
|
||||
obj-y += clk-tegra-fixed.o
|
||||
obj-y += clk-tegra-super-gen4.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clk-tegra20.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += clk-tegra30.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_114_SOC) += clk-tegra114.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_124_SOC) += clk-tegra124.o
|
||||
|
|
235
drivers/clk/tegra/clk-id.h
Normal file
235
drivers/clk/tegra/clk-id.h
Normal file
|
@ -0,0 +1,235 @@
|
|||
/*
|
||||
* This header provides IDs for clocks common between several Tegra SoCs
|
||||
*/
|
||||
#ifndef _TEGRA_CLK_ID_H
|
||||
#define _TEGRA_CLK_ID_H
|
||||
|
||||
enum clk_id {
|
||||
tegra_clk_actmon,
|
||||
tegra_clk_adx,
|
||||
tegra_clk_adx1,
|
||||
tegra_clk_afi,
|
||||
tegra_clk_amx,
|
||||
tegra_clk_amx1,
|
||||
tegra_clk_apbdma,
|
||||
tegra_clk_apbif,
|
||||
tegra_clk_audio0,
|
||||
tegra_clk_audio0_2x,
|
||||
tegra_clk_audio0_mux,
|
||||
tegra_clk_audio1,
|
||||
tegra_clk_audio1_2x,
|
||||
tegra_clk_audio1_mux,
|
||||
tegra_clk_audio2,
|
||||
tegra_clk_audio2_2x,
|
||||
tegra_clk_audio2_mux,
|
||||
tegra_clk_audio3,
|
||||
tegra_clk_audio3_2x,
|
||||
tegra_clk_audio3_mux,
|
||||
tegra_clk_audio4,
|
||||
tegra_clk_audio4_2x,
|
||||
tegra_clk_audio4_mux,
|
||||
tegra_clk_blink,
|
||||
tegra_clk_bsea,
|
||||
tegra_clk_bsev,
|
||||
tegra_clk_cclk_g,
|
||||
tegra_clk_cclk_lp,
|
||||
tegra_clk_cilab,
|
||||
tegra_clk_cilcd,
|
||||
tegra_clk_cile,
|
||||
tegra_clk_clk_32k,
|
||||
tegra_clk_clk72Mhz,
|
||||
tegra_clk_clk_m,
|
||||
tegra_clk_clk_m_div2,
|
||||
tegra_clk_clk_m_div4,
|
||||
tegra_clk_clk_out_1,
|
||||
tegra_clk_clk_out_1_mux,
|
||||
tegra_clk_clk_out_2,
|
||||
tegra_clk_clk_out_2_mux,
|
||||
tegra_clk_clk_out_3,
|
||||
tegra_clk_clk_out_3_mux,
|
||||
tegra_clk_cml0,
|
||||
tegra_clk_cml1,
|
||||
tegra_clk_csi,
|
||||
tegra_clk_csite,
|
||||
tegra_clk_csus,
|
||||
tegra_clk_cve,
|
||||
tegra_clk_dam0,
|
||||
tegra_clk_dam1,
|
||||
tegra_clk_dam2,
|
||||
tegra_clk_d_audio,
|
||||
tegra_clk_dds,
|
||||
tegra_clk_dfll_ref,
|
||||
tegra_clk_dfll_soc,
|
||||
tegra_clk_disp1,
|
||||
tegra_clk_disp2,
|
||||
tegra_clk_dp2,
|
||||
tegra_clk_dpaux,
|
||||
tegra_clk_dsia,
|
||||
tegra_clk_dsialp,
|
||||
tegra_clk_dsia_mux,
|
||||
tegra_clk_dsib,
|
||||
tegra_clk_dsiblp,
|
||||
tegra_clk_dsib_mux,
|
||||
tegra_clk_dtv,
|
||||
tegra_clk_emc,
|
||||
tegra_clk_entropy,
|
||||
tegra_clk_epp,
|
||||
tegra_clk_epp_8,
|
||||
tegra_clk_extern1,
|
||||
tegra_clk_extern2,
|
||||
tegra_clk_extern3,
|
||||
tegra_clk_fuse,
|
||||
tegra_clk_fuse_burn,
|
||||
tegra_clk_gpu,
|
||||
tegra_clk_gr2d,
|
||||
tegra_clk_gr2d_8,
|
||||
tegra_clk_gr3d,
|
||||
tegra_clk_gr3d_8,
|
||||
tegra_clk_hclk,
|
||||
tegra_clk_hda,
|
||||
tegra_clk_hda2codec_2x,
|
||||
tegra_clk_hda2hdmi,
|
||||
tegra_clk_hdmi,
|
||||
tegra_clk_hdmi_audio,
|
||||
tegra_clk_host1x,
|
||||
tegra_clk_host1x_8,
|
||||
tegra_clk_i2c1,
|
||||
tegra_clk_i2c2,
|
||||
tegra_clk_i2c3,
|
||||
tegra_clk_i2c4,
|
||||
tegra_clk_i2c5,
|
||||
tegra_clk_i2c6,
|
||||
tegra_clk_i2cslow,
|
||||
tegra_clk_i2s0,
|
||||
tegra_clk_i2s0_sync,
|
||||
tegra_clk_i2s1,
|
||||
tegra_clk_i2s1_sync,
|
||||
tegra_clk_i2s2,
|
||||
tegra_clk_i2s2_sync,
|
||||
tegra_clk_i2s3,
|
||||
tegra_clk_i2s3_sync,
|
||||
tegra_clk_i2s4,
|
||||
tegra_clk_i2s4_sync,
|
||||
tegra_clk_isp,
|
||||
tegra_clk_isp_8,
|
||||
tegra_clk_ispb,
|
||||
tegra_clk_kbc,
|
||||
tegra_clk_kfuse,
|
||||
tegra_clk_la,
|
||||
tegra_clk_mipi,
|
||||
tegra_clk_mipi_cal,
|
||||
tegra_clk_mpe,
|
||||
tegra_clk_mselect,
|
||||
tegra_clk_msenc,
|
||||
tegra_clk_ndflash,
|
||||
tegra_clk_ndflash_8,
|
||||
tegra_clk_ndspeed,
|
||||
tegra_clk_ndspeed_8,
|
||||
tegra_clk_nor,
|
||||
tegra_clk_owr,
|
||||
tegra_clk_pcie,
|
||||
tegra_clk_pclk,
|
||||
tegra_clk_pll_a,
|
||||
tegra_clk_pll_a_out0,
|
||||
tegra_clk_pll_c,
|
||||
tegra_clk_pll_c2,
|
||||
tegra_clk_pll_c3,
|
||||
tegra_clk_pll_c4,
|
||||
tegra_clk_pll_c_out1,
|
||||
tegra_clk_pll_d,
|
||||
tegra_clk_pll_d2,
|
||||
tegra_clk_pll_d2_out0,
|
||||
tegra_clk_pll_d_out0,
|
||||
tegra_clk_pll_dp,
|
||||
tegra_clk_pll_e_out0,
|
||||
tegra_clk_pll_m,
|
||||
tegra_clk_pll_m_out1,
|
||||
tegra_clk_pll_p,
|
||||
tegra_clk_pll_p_out1,
|
||||
tegra_clk_pll_p_out2,
|
||||
tegra_clk_pll_p_out2_int,
|
||||
tegra_clk_pll_p_out3,
|
||||
tegra_clk_pll_p_out4,
|
||||
tegra_clk_pll_p_out5,
|
||||
tegra_clk_pll_ref,
|
||||
tegra_clk_pll_re_out,
|
||||
tegra_clk_pll_re_vco,
|
||||
tegra_clk_pll_u,
|
||||
tegra_clk_pll_u_12m,
|
||||
tegra_clk_pll_u_480m,
|
||||
tegra_clk_pll_u_48m,
|
||||
tegra_clk_pll_u_60m,
|
||||
tegra_clk_pll_x,
|
||||
tegra_clk_pll_x_out0,
|
||||
tegra_clk_pwm,
|
||||
tegra_clk_rtc,
|
||||
tegra_clk_sata,
|
||||
tegra_clk_sata_cold,
|
||||
tegra_clk_sata_oob,
|
||||
tegra_clk_sbc1,
|
||||
tegra_clk_sbc1_8,
|
||||
tegra_clk_sbc2,
|
||||
tegra_clk_sbc2_8,
|
||||
tegra_clk_sbc3,
|
||||
tegra_clk_sbc3_8,
|
||||
tegra_clk_sbc4,
|
||||
tegra_clk_sbc4_8,
|
||||
tegra_clk_sbc5,
|
||||
tegra_clk_sbc5_8,
|
||||
tegra_clk_sbc6,
|
||||
tegra_clk_sbc6_8,
|
||||
tegra_clk_sclk,
|
||||
tegra_clk_sdmmc1,
|
||||
tegra_clk_sdmmc2,
|
||||
tegra_clk_sdmmc3,
|
||||
tegra_clk_sdmmc4,
|
||||
tegra_clk_se,
|
||||
tegra_clk_soc_therm,
|
||||
tegra_clk_sor0,
|
||||
tegra_clk_sor0_lvds,
|
||||
tegra_clk_spdif,
|
||||
tegra_clk_spdif_2x,
|
||||
tegra_clk_spdif_in,
|
||||
tegra_clk_spdif_in_sync,
|
||||
tegra_clk_spdif_mux,
|
||||
tegra_clk_spdif_out,
|
||||
tegra_clk_timer,
|
||||
tegra_clk_trace,
|
||||
tegra_clk_tsec,
|
||||
tegra_clk_tsensor,
|
||||
tegra_clk_tvdac,
|
||||
tegra_clk_tvo,
|
||||
tegra_clk_uarta,
|
||||
tegra_clk_uartb,
|
||||
tegra_clk_uartc,
|
||||
tegra_clk_uartd,
|
||||
tegra_clk_uarte,
|
||||
tegra_clk_usb2,
|
||||
tegra_clk_usb3,
|
||||
tegra_clk_usbd,
|
||||
tegra_clk_vcp,
|
||||
tegra_clk_vde,
|
||||
tegra_clk_vde_8,
|
||||
tegra_clk_vfir,
|
||||
tegra_clk_vi,
|
||||
tegra_clk_vi_8,
|
||||
tegra_clk_vi_9,
|
||||
tegra_clk_vic03,
|
||||
tegra_clk_vim2_clk,
|
||||
tegra_clk_vimclk_sync,
|
||||
tegra_clk_vi_sensor,
|
||||
tegra_clk_vi_sensor2,
|
||||
tegra_clk_vi_sensor_8,
|
||||
tegra_clk_xusb_dev,
|
||||
tegra_clk_xusb_dev_src,
|
||||
tegra_clk_xusb_falcon_src,
|
||||
tegra_clk_xusb_fs_src,
|
||||
tegra_clk_xusb_host,
|
||||
tegra_clk_xusb_host_src,
|
||||
tegra_clk_xusb_hs_src,
|
||||
tegra_clk_xusb_ss,
|
||||
tegra_clk_xusb_ss_src,
|
||||
tegra_clk_max,
|
||||
};
|
||||
|
||||
#endif /* _TEGRA_CLK_ID_H */
|
|
@ -151,12 +151,16 @@ const struct clk_ops tegra_clk_periph_gate_ops = {
|
|||
|
||||
struct clk *tegra_clk_register_periph_gate(const char *name,
|
||||
const char *parent_name, u8 gate_flags, void __iomem *clk_base,
|
||||
unsigned long flags, int clk_num,
|
||||
struct tegra_clk_periph_regs *pregs, int *enable_refcnt)
|
||||
unsigned long flags, int clk_num, int *enable_refcnt)
|
||||
{
|
||||
struct tegra_clk_periph_gate *gate;
|
||||
struct clk *clk;
|
||||
struct clk_init_data init;
|
||||
struct tegra_clk_periph_regs *pregs;
|
||||
|
||||
pregs = get_reg_bank(clk_num);
|
||||
if (!pregs)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
gate = kzalloc(sizeof(*gate), GFP_KERNEL);
|
||||
if (!gate) {
|
||||
|
|
|
@ -162,7 +162,7 @@ const struct clk_ops tegra_clk_periph_ops = {
|
|||
.disable = clk_periph_disable,
|
||||
};
|
||||
|
||||
const struct clk_ops tegra_clk_periph_nodiv_ops = {
|
||||
static const struct clk_ops tegra_clk_periph_nodiv_ops = {
|
||||
.get_parent = clk_periph_get_parent,
|
||||
.set_parent = clk_periph_set_parent,
|
||||
.is_enabled = clk_periph_is_enabled,
|
||||
|
@ -170,27 +170,50 @@ const struct clk_ops tegra_clk_periph_nodiv_ops = {
|
|||
.disable = clk_periph_disable,
|
||||
};
|
||||
|
||||
const struct clk_ops tegra_clk_periph_no_gate_ops = {
|
||||
.get_parent = clk_periph_get_parent,
|
||||
.set_parent = clk_periph_set_parent,
|
||||
.recalc_rate = clk_periph_recalc_rate,
|
||||
.round_rate = clk_periph_round_rate,
|
||||
.set_rate = clk_periph_set_rate,
|
||||
};
|
||||
|
||||
static struct clk *_tegra_clk_register_periph(const char *name,
|
||||
const char **parent_names, int num_parents,
|
||||
struct tegra_clk_periph *periph,
|
||||
void __iomem *clk_base, u32 offset, bool div,
|
||||
void __iomem *clk_base, u32 offset,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct clk *clk;
|
||||
struct clk_init_data init;
|
||||
struct tegra_clk_periph_regs *bank;
|
||||
bool div = !(periph->gate.flags & TEGRA_PERIPH_NO_DIV);
|
||||
|
||||
if (periph->gate.flags & TEGRA_PERIPH_NO_DIV) {
|
||||
flags |= CLK_SET_RATE_PARENT;
|
||||
init.ops = &tegra_clk_periph_nodiv_ops;
|
||||
} else if (periph->gate.flags & TEGRA_PERIPH_NO_GATE)
|
||||
init.ops = &tegra_clk_periph_no_gate_ops;
|
||||
else
|
||||
init.ops = &tegra_clk_periph_ops;
|
||||
|
||||
init.name = name;
|
||||
init.ops = div ? &tegra_clk_periph_ops : &tegra_clk_periph_nodiv_ops;
|
||||
init.flags = flags;
|
||||
init.parent_names = parent_names;
|
||||
init.num_parents = num_parents;
|
||||
|
||||
bank = get_reg_bank(periph->gate.clk_num);
|
||||
if (!bank)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
/* Data in .init is copied by clk_register(), so stack variable OK */
|
||||
periph->hw.init = &init;
|
||||
periph->magic = TEGRA_CLK_PERIPH_MAGIC;
|
||||
periph->mux.reg = clk_base + offset;
|
||||
periph->divider.reg = div ? (clk_base + offset) : NULL;
|
||||
periph->gate.clk_base = clk_base;
|
||||
periph->gate.regs = bank;
|
||||
periph->gate.enable_refcnt = periph_clk_enb_refcnt;
|
||||
|
||||
clk = clk_register(NULL, &periph->hw);
|
||||
if (IS_ERR(clk))
|
||||
|
@ -209,7 +232,7 @@ struct clk *tegra_clk_register_periph(const char *name,
|
|||
u32 offset, unsigned long flags)
|
||||
{
|
||||
return _tegra_clk_register_periph(name, parent_names, num_parents,
|
||||
periph, clk_base, offset, true, flags);
|
||||
periph, clk_base, offset, flags);
|
||||
}
|
||||
|
||||
struct clk *tegra_clk_register_periph_nodiv(const char *name,
|
||||
|
@ -217,6 +240,7 @@ struct clk *tegra_clk_register_periph_nodiv(const char *name,
|
|||
struct tegra_clk_periph *periph, void __iomem *clk_base,
|
||||
u32 offset)
|
||||
{
|
||||
periph->gate.flags |= TEGRA_PERIPH_NO_DIV;
|
||||
return _tegra_clk_register_periph(name, parent_names, num_parents,
|
||||
periph, clk_base, offset, false, CLK_SET_RATE_PARENT);
|
||||
periph, clk_base, offset, CLK_SET_RATE_PARENT);
|
||||
}
|
||||
|
|
|
@ -77,7 +77,23 @@
|
|||
#define PLLE_MISC_SETUP_VALUE (7 << PLLE_MISC_SETUP_BASE_SHIFT)
|
||||
|
||||
#define PLLE_SS_CTRL 0x68
|
||||
#define PLLE_SS_DISABLE (7 << 10)
|
||||
#define PLLE_SS_CNTL_BYPASS_SS BIT(10)
|
||||
#define PLLE_SS_CNTL_INTERP_RESET BIT(11)
|
||||
#define PLLE_SS_CNTL_SSC_BYP BIT(12)
|
||||
#define PLLE_SS_CNTL_CENTER BIT(14)
|
||||
#define PLLE_SS_CNTL_INVERT BIT(15)
|
||||
#define PLLE_SS_DISABLE (PLLE_SS_CNTL_BYPASS_SS | PLLE_SS_CNTL_INTERP_RESET |\
|
||||
PLLE_SS_CNTL_SSC_BYP)
|
||||
#define PLLE_SS_MAX_MASK 0x1ff
|
||||
#define PLLE_SS_MAX_VAL 0x25
|
||||
#define PLLE_SS_INC_MASK (0xff << 16)
|
||||
#define PLLE_SS_INC_VAL (0x1 << 16)
|
||||
#define PLLE_SS_INCINTRV_MASK (0x3f << 24)
|
||||
#define PLLE_SS_INCINTRV_VAL (0x20 << 24)
|
||||
#define PLLE_SS_COEFFICIENTS_MASK \
|
||||
(PLLE_SS_MAX_MASK | PLLE_SS_INC_MASK | PLLE_SS_INCINTRV_MASK)
|
||||
#define PLLE_SS_COEFFICIENTS_VAL \
|
||||
(PLLE_SS_MAX_VAL | PLLE_SS_INC_VAL | PLLE_SS_INCINTRV_VAL)
|
||||
|
||||
#define PLLE_AUX_PLLP_SEL BIT(2)
|
||||
#define PLLE_AUX_ENABLE_SWCTL BIT(4)
|
||||
|
@ -121,6 +137,36 @@
|
|||
#define PMC_SATA_PWRGT_PLLE_IDDQ_VALUE BIT(5)
|
||||
#define PMC_SATA_PWRGT_PLLE_IDDQ_SWCTL BIT(4)
|
||||
|
||||
#define PLLSS_MISC_KCP 0
|
||||
#define PLLSS_MISC_KVCO 0
|
||||
#define PLLSS_MISC_SETUP 0
|
||||
#define PLLSS_EN_SDM 0
|
||||
#define PLLSS_EN_SSC 0
|
||||
#define PLLSS_EN_DITHER2 0
|
||||
#define PLLSS_EN_DITHER 1
|
||||
#define PLLSS_SDM_RESET 0
|
||||
#define PLLSS_CLAMP 0
|
||||
#define PLLSS_SDM_SSC_MAX 0
|
||||
#define PLLSS_SDM_SSC_MIN 0
|
||||
#define PLLSS_SDM_SSC_STEP 0
|
||||
#define PLLSS_SDM_DIN 0
|
||||
#define PLLSS_MISC_DEFAULT ((PLLSS_MISC_KCP << 25) | \
|
||||
(PLLSS_MISC_KVCO << 24) | \
|
||||
PLLSS_MISC_SETUP)
|
||||
#define PLLSS_CFG_DEFAULT ((PLLSS_EN_SDM << 31) | \
|
||||
(PLLSS_EN_SSC << 30) | \
|
||||
(PLLSS_EN_DITHER2 << 29) | \
|
||||
(PLLSS_EN_DITHER << 28) | \
|
||||
(PLLSS_SDM_RESET) << 27 | \
|
||||
(PLLSS_CLAMP << 22))
|
||||
#define PLLSS_CTRL1_DEFAULT \
|
||||
((PLLSS_SDM_SSC_MAX << 16) | PLLSS_SDM_SSC_MIN)
|
||||
#define PLLSS_CTRL2_DEFAULT \
|
||||
((PLLSS_SDM_SSC_STEP << 16) | PLLSS_SDM_DIN)
|
||||
#define PLLSS_LOCK_OVERRIDE BIT(24)
|
||||
#define PLLSS_REF_SRC_SEL_SHIFT 25
|
||||
#define PLLSS_REF_SRC_SEL_MASK (3 << PLLSS_REF_SRC_SEL_SHIFT)
|
||||
|
||||
#define pll_readl(offset, p) readl_relaxed(p->clk_base + offset)
|
||||
#define pll_readl_base(p) pll_readl(p->params->base_reg, p)
|
||||
#define pll_readl_misc(p) pll_readl(p->params->misc_reg, p)
|
||||
|
@ -134,7 +180,7 @@
|
|||
#define mask(w) ((1 << (w)) - 1)
|
||||
#define divm_mask(p) mask(p->params->div_nmp->divm_width)
|
||||
#define divn_mask(p) mask(p->params->div_nmp->divn_width)
|
||||
#define divp_mask(p) (p->flags & TEGRA_PLLU ? PLLU_POST_DIVP_MASK : \
|
||||
#define divp_mask(p) (p->params->flags & TEGRA_PLLU ? PLLU_POST_DIVP_MASK :\
|
||||
mask(p->params->div_nmp->divp_width))
|
||||
|
||||
#define divm_max(p) (divm_mask(p))
|
||||
|
@ -154,10 +200,10 @@ static void clk_pll_enable_lock(struct tegra_clk_pll *pll)
|
|||
{
|
||||
u32 val;
|
||||
|
||||
if (!(pll->flags & TEGRA_PLL_USE_LOCK))
|
||||
if (!(pll->params->flags & TEGRA_PLL_USE_LOCK))
|
||||
return;
|
||||
|
||||
if (!(pll->flags & TEGRA_PLL_HAS_LOCK_ENABLE))
|
||||
if (!(pll->params->flags & TEGRA_PLL_HAS_LOCK_ENABLE))
|
||||
return;
|
||||
|
||||
val = pll_readl_misc(pll);
|
||||
|
@ -171,13 +217,13 @@ static int clk_pll_wait_for_lock(struct tegra_clk_pll *pll)
|
|||
u32 val, lock_mask;
|
||||
void __iomem *lock_addr;
|
||||
|
||||
if (!(pll->flags & TEGRA_PLL_USE_LOCK)) {
|
||||
if (!(pll->params->flags & TEGRA_PLL_USE_LOCK)) {
|
||||
udelay(pll->params->lock_delay);
|
||||
return 0;
|
||||
}
|
||||
|
||||
lock_addr = pll->clk_base;
|
||||
if (pll->flags & TEGRA_PLL_LOCK_MISC)
|
||||
if (pll->params->flags & TEGRA_PLL_LOCK_MISC)
|
||||
lock_addr += pll->params->misc_reg;
|
||||
else
|
||||
lock_addr += pll->params->base_reg;
|
||||
|
@ -204,7 +250,7 @@ static int clk_pll_is_enabled(struct clk_hw *hw)
|
|||
struct tegra_clk_pll *pll = to_clk_pll(hw);
|
||||
u32 val;
|
||||
|
||||
if (pll->flags & TEGRA_PLLM) {
|
||||
if (pll->params->flags & TEGRA_PLLM) {
|
||||
val = readl_relaxed(pll->pmc + PMC_PLLP_WB0_OVERRIDE);
|
||||
if (val & PMC_PLLP_WB0_OVERRIDE_PLLM_OVERRIDE)
|
||||
return val & PMC_PLLP_WB0_OVERRIDE_PLLM_ENABLE ? 1 : 0;
|
||||
|
@ -223,12 +269,12 @@ static void _clk_pll_enable(struct clk_hw *hw)
|
|||
clk_pll_enable_lock(pll);
|
||||
|
||||
val = pll_readl_base(pll);
|
||||
if (pll->flags & TEGRA_PLL_BYPASS)
|
||||
if (pll->params->flags & TEGRA_PLL_BYPASS)
|
||||
val &= ~PLL_BASE_BYPASS;
|
||||
val |= PLL_BASE_ENABLE;
|
||||
pll_writel_base(val, pll);
|
||||
|
||||
if (pll->flags & TEGRA_PLLM) {
|
||||
if (pll->params->flags & TEGRA_PLLM) {
|
||||
val = readl_relaxed(pll->pmc + PMC_PLLP_WB0_OVERRIDE);
|
||||
val |= PMC_PLLP_WB0_OVERRIDE_PLLM_ENABLE;
|
||||
writel_relaxed(val, pll->pmc + PMC_PLLP_WB0_OVERRIDE);
|
||||
|
@ -241,12 +287,12 @@ static void _clk_pll_disable(struct clk_hw *hw)
|
|||
u32 val;
|
||||
|
||||
val = pll_readl_base(pll);
|
||||
if (pll->flags & TEGRA_PLL_BYPASS)
|
||||
if (pll->params->flags & TEGRA_PLL_BYPASS)
|
||||
val &= ~PLL_BASE_BYPASS;
|
||||
val &= ~PLL_BASE_ENABLE;
|
||||
pll_writel_base(val, pll);
|
||||
|
||||
if (pll->flags & TEGRA_PLLM) {
|
||||
if (pll->params->flags & TEGRA_PLLM) {
|
||||
val = readl_relaxed(pll->pmc + PMC_PLLP_WB0_OVERRIDE);
|
||||
val &= ~PMC_PLLP_WB0_OVERRIDE_PLLM_ENABLE;
|
||||
writel_relaxed(val, pll->pmc + PMC_PLLP_WB0_OVERRIDE);
|
||||
|
@ -326,7 +372,7 @@ static int _get_table_rate(struct clk_hw *hw,
|
|||
struct tegra_clk_pll *pll = to_clk_pll(hw);
|
||||
struct tegra_clk_pll_freq_table *sel;
|
||||
|
||||
for (sel = pll->freq_table; sel->input_rate != 0; sel++)
|
||||
for (sel = pll->params->freq_table; sel->input_rate != 0; sel++)
|
||||
if (sel->input_rate == parent_rate &&
|
||||
sel->output_rate == rate)
|
||||
break;
|
||||
|
@ -389,12 +435,11 @@ static int _calc_rate(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg,
|
|||
if (cfg->m > divm_max(pll) || cfg->n > divn_max(pll) ||
|
||||
(1 << p_div) > divp_max(pll)
|
||||
|| cfg->output_rate > pll->params->vco_max) {
|
||||
pr_err("%s: Failed to set %s rate %lu\n",
|
||||
__func__, __clk_get_name(hw->clk), rate);
|
||||
WARN_ON(1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cfg->output_rate >>= p_div;
|
||||
|
||||
if (pll->params->pdiv_tohw) {
|
||||
ret = _p_div_to_hw(hw, 1 << p_div);
|
||||
if (ret < 0)
|
||||
|
@ -414,7 +459,7 @@ static void _update_pll_mnp(struct tegra_clk_pll *pll,
|
|||
struct tegra_clk_pll_params *params = pll->params;
|
||||
struct div_nmp *div_nmp = params->div_nmp;
|
||||
|
||||
if ((pll->flags & TEGRA_PLLM) &&
|
||||
if ((params->flags & TEGRA_PLLM) &&
|
||||
(pll_override_readl(PMC_PLLP_WB0_OVERRIDE, pll) &
|
||||
PMC_PLLP_WB0_OVERRIDE_PLLM_OVERRIDE)) {
|
||||
val = pll_override_readl(params->pmc_divp_reg, pll);
|
||||
|
@ -450,7 +495,7 @@ static void _get_pll_mnp(struct tegra_clk_pll *pll,
|
|||
struct tegra_clk_pll_params *params = pll->params;
|
||||
struct div_nmp *div_nmp = params->div_nmp;
|
||||
|
||||
if ((pll->flags & TEGRA_PLLM) &&
|
||||
if ((params->flags & TEGRA_PLLM) &&
|
||||
(pll_override_readl(PMC_PLLP_WB0_OVERRIDE, pll) &
|
||||
PMC_PLLP_WB0_OVERRIDE_PLLM_OVERRIDE)) {
|
||||
val = pll_override_readl(params->pmc_divp_reg, pll);
|
||||
|
@ -479,11 +524,11 @@ static void _update_pll_cpcon(struct tegra_clk_pll *pll,
|
|||
val &= ~(PLL_MISC_CPCON_MASK << PLL_MISC_CPCON_SHIFT);
|
||||
val |= cfg->cpcon << PLL_MISC_CPCON_SHIFT;
|
||||
|
||||
if (pll->flags & TEGRA_PLL_SET_LFCON) {
|
||||
if (pll->params->flags & TEGRA_PLL_SET_LFCON) {
|
||||
val &= ~(PLL_MISC_LFCON_MASK << PLL_MISC_LFCON_SHIFT);
|
||||
if (cfg->n >= PLLDU_LFCON_SET_DIVN)
|
||||
val |= 1 << PLL_MISC_LFCON_SHIFT;
|
||||
} else if (pll->flags & TEGRA_PLL_SET_DCCON) {
|
||||
} else if (pll->params->flags & TEGRA_PLL_SET_DCCON) {
|
||||
val &= ~(1 << PLL_MISC_DCCON_SHIFT);
|
||||
if (rate >= (pll->params->vco_max >> 1))
|
||||
val |= 1 << PLL_MISC_DCCON_SHIFT;
|
||||
|
@ -505,7 +550,7 @@ static int _program_pll(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg,
|
|||
|
||||
_update_pll_mnp(pll, cfg);
|
||||
|
||||
if (pll->flags & TEGRA_PLL_HAS_CPCON)
|
||||
if (pll->params->flags & TEGRA_PLL_HAS_CPCON)
|
||||
_update_pll_cpcon(pll, cfg, rate);
|
||||
|
||||
if (state) {
|
||||
|
@ -524,11 +569,11 @@ static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
|
|||
unsigned long flags = 0;
|
||||
int ret = 0;
|
||||
|
||||
if (pll->flags & TEGRA_PLL_FIXED) {
|
||||
if (rate != pll->fixed_rate) {
|
||||
if (pll->params->flags & TEGRA_PLL_FIXED) {
|
||||
if (rate != pll->params->fixed_rate) {
|
||||
pr_err("%s: Can not change %s fixed rate %lu to %lu\n",
|
||||
__func__, __clk_get_name(hw->clk),
|
||||
pll->fixed_rate, rate);
|
||||
pll->params->fixed_rate, rate);
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
|
@ -536,6 +581,8 @@ static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
|
|||
|
||||
if (_get_table_rate(hw, &cfg, rate, parent_rate) &&
|
||||
_calc_rate(hw, &cfg, rate, parent_rate)) {
|
||||
pr_err("%s: Failed to set %s rate %lu\n", __func__,
|
||||
__clk_get_name(hw->clk), rate);
|
||||
WARN_ON(1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -559,18 +606,16 @@ static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
|
|||
struct tegra_clk_pll *pll = to_clk_pll(hw);
|
||||
struct tegra_clk_pll_freq_table cfg;
|
||||
|
||||
if (pll->flags & TEGRA_PLL_FIXED)
|
||||
return pll->fixed_rate;
|
||||
if (pll->params->flags & TEGRA_PLL_FIXED)
|
||||
return pll->params->fixed_rate;
|
||||
|
||||
/* PLLM is used for memory; we do not change rate */
|
||||
if (pll->flags & TEGRA_PLLM)
|
||||
if (pll->params->flags & TEGRA_PLLM)
|
||||
return __clk_get_rate(hw->clk);
|
||||
|
||||
if (_get_table_rate(hw, &cfg, rate, *prate) &&
|
||||
_calc_rate(hw, &cfg, rate, *prate)) {
|
||||
WARN_ON(1);
|
||||
_calc_rate(hw, &cfg, rate, *prate))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return cfg.output_rate;
|
||||
}
|
||||
|
@ -586,17 +631,19 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw,
|
|||
|
||||
val = pll_readl_base(pll);
|
||||
|
||||
if ((pll->flags & TEGRA_PLL_BYPASS) && (val & PLL_BASE_BYPASS))
|
||||
if ((pll->params->flags & TEGRA_PLL_BYPASS) && (val & PLL_BASE_BYPASS))
|
||||
return parent_rate;
|
||||
|
||||
if ((pll->flags & TEGRA_PLL_FIXED) && !(val & PLL_BASE_OVERRIDE)) {
|
||||
if ((pll->params->flags & TEGRA_PLL_FIXED) &&
|
||||
!(val & PLL_BASE_OVERRIDE)) {
|
||||
struct tegra_clk_pll_freq_table sel;
|
||||
if (_get_table_rate(hw, &sel, pll->fixed_rate, parent_rate)) {
|
||||
if (_get_table_rate(hw, &sel, pll->params->fixed_rate,
|
||||
parent_rate)) {
|
||||
pr_err("Clock %s has unknown fixed frequency\n",
|
||||
__clk_get_name(hw->clk));
|
||||
BUG();
|
||||
}
|
||||
return pll->fixed_rate;
|
||||
return pll->params->fixed_rate;
|
||||
}
|
||||
|
||||
_get_pll_mnp(pll, &cfg);
|
||||
|
@ -664,7 +711,7 @@ static int clk_plle_enable(struct clk_hw *hw)
|
|||
u32 val;
|
||||
int err;
|
||||
|
||||
if (_get_table_rate(hw, &sel, pll->fixed_rate, input_rate))
|
||||
if (_get_table_rate(hw, &sel, pll->params->fixed_rate, input_rate))
|
||||
return -EINVAL;
|
||||
|
||||
clk_pll_disable(hw);
|
||||
|
@ -680,7 +727,7 @@ static int clk_plle_enable(struct clk_hw *hw)
|
|||
return err;
|
||||
}
|
||||
|
||||
if (pll->flags & TEGRA_PLLE_CONFIGURE) {
|
||||
if (pll->params->flags & TEGRA_PLLE_CONFIGURE) {
|
||||
/* configure dividers */
|
||||
val = pll_readl_base(pll);
|
||||
val &= ~(divm_mask(pll) | divn_mask(pll) | divp_mask(pll));
|
||||
|
@ -744,7 +791,7 @@ const struct clk_ops tegra_clk_plle_ops = {
|
|||
.enable = clk_plle_enable,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_ARCH_TEGRA_114_SOC
|
||||
#if defined(CONFIG_ARCH_TEGRA_114_SOC) || defined(CONFIG_ARCH_TEGRA_124_SOC)
|
||||
|
||||
static int _pll_fixed_mdiv(struct tegra_clk_pll_params *pll_params,
|
||||
unsigned long parent_rate)
|
||||
|
@ -755,6 +802,48 @@ static int _pll_fixed_mdiv(struct tegra_clk_pll_params *pll_params,
|
|||
return 1;
|
||||
}
|
||||
|
||||
static unsigned long _clip_vco_min(unsigned long vco_min,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return DIV_ROUND_UP(vco_min, parent_rate) * parent_rate;
|
||||
}
|
||||
|
||||
static int _setup_dynamic_ramp(struct tegra_clk_pll_params *pll_params,
|
||||
void __iomem *clk_base,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
u32 val;
|
||||
u32 step_a, step_b;
|
||||
|
||||
switch (parent_rate) {
|
||||
case 12000000:
|
||||
case 13000000:
|
||||
case 26000000:
|
||||
step_a = 0x2B;
|
||||
step_b = 0x0B;
|
||||
break;
|
||||
case 16800000:
|
||||
step_a = 0x1A;
|
||||
step_b = 0x09;
|
||||
break;
|
||||
case 19200000:
|
||||
step_a = 0x12;
|
||||
step_b = 0x08;
|
||||
break;
|
||||
default:
|
||||
pr_err("%s: Unexpected reference rate %lu\n",
|
||||
__func__, parent_rate);
|
||||
WARN_ON(1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
val = step_a << pll_params->stepa_shift;
|
||||
val |= step_b << pll_params->stepb_shift;
|
||||
writel_relaxed(val, clk_base + pll_params->dyn_ramp_reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int clk_pll_iddq_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct tegra_clk_pll *pll = to_clk_pll(hw);
|
||||
|
@ -1173,7 +1262,7 @@ static int clk_plle_tegra114_enable(struct clk_hw *hw)
|
|||
unsigned long flags = 0;
|
||||
unsigned long input_rate = clk_get_rate(clk_get_parent(hw->clk));
|
||||
|
||||
if (_get_table_rate(hw, &sel, pll->fixed_rate, input_rate))
|
||||
if (_get_table_rate(hw, &sel, pll->params->fixed_rate, input_rate))
|
||||
return -EINVAL;
|
||||
|
||||
if (pll->lock)
|
||||
|
@ -1217,6 +1306,18 @@ static int clk_plle_tegra114_enable(struct clk_hw *hw)
|
|||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
val = pll_readl(PLLE_SS_CTRL, pll);
|
||||
val &= ~(PLLE_SS_CNTL_CENTER | PLLE_SS_CNTL_INVERT);
|
||||
val &= ~PLLE_SS_COEFFICIENTS_MASK;
|
||||
val |= PLLE_SS_COEFFICIENTS_VAL;
|
||||
pll_writel(val, PLLE_SS_CTRL, pll);
|
||||
val &= ~(PLLE_SS_CNTL_SSC_BYP | PLLE_SS_CNTL_BYPASS_SS);
|
||||
pll_writel(val, PLLE_SS_CTRL, pll);
|
||||
udelay(1);
|
||||
val &= ~PLLE_SS_CNTL_INTERP_RESET;
|
||||
pll_writel(val, PLLE_SS_CTRL, pll);
|
||||
udelay(1);
|
||||
|
||||
/* TODO: enable hw control of xusb brick pll */
|
||||
|
||||
out:
|
||||
|
@ -1248,9 +1349,8 @@ static void clk_plle_tegra114_disable(struct clk_hw *hw)
|
|||
#endif
|
||||
|
||||
static struct tegra_clk_pll *_tegra_init_pll(void __iomem *clk_base,
|
||||
void __iomem *pmc, unsigned long fixed_rate,
|
||||
struct tegra_clk_pll_params *pll_params, u32 pll_flags,
|
||||
struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock)
|
||||
void __iomem *pmc, struct tegra_clk_pll_params *pll_params,
|
||||
spinlock_t *lock)
|
||||
{
|
||||
struct tegra_clk_pll *pll;
|
||||
|
||||
|
@ -1261,10 +1361,7 @@ static struct tegra_clk_pll *_tegra_init_pll(void __iomem *clk_base,
|
|||
pll->clk_base = clk_base;
|
||||
pll->pmc = pmc;
|
||||
|
||||
pll->freq_table = freq_table;
|
||||
pll->params = pll_params;
|
||||
pll->fixed_rate = fixed_rate;
|
||||
pll->flags = pll_flags;
|
||||
pll->lock = lock;
|
||||
|
||||
if (!pll_params->div_nmp)
|
||||
|
@ -1293,17 +1390,15 @@ static struct clk *_tegra_clk_register_pll(struct tegra_clk_pll *pll,
|
|||
|
||||
struct clk *tegra_clk_register_pll(const char *name, const char *parent_name,
|
||||
void __iomem *clk_base, void __iomem *pmc,
|
||||
unsigned long flags, unsigned long fixed_rate,
|
||||
struct tegra_clk_pll_params *pll_params, u32 pll_flags,
|
||||
struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock)
|
||||
unsigned long flags, struct tegra_clk_pll_params *pll_params,
|
||||
spinlock_t *lock)
|
||||
{
|
||||
struct tegra_clk_pll *pll;
|
||||
struct clk *clk;
|
||||
|
||||
pll_flags |= TEGRA_PLL_BYPASS;
|
||||
pll_flags |= TEGRA_PLL_HAS_LOCK_ENABLE;
|
||||
pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags,
|
||||
freq_table, lock);
|
||||
pll_params->flags |= TEGRA_PLL_BYPASS;
|
||||
pll_params->flags |= TEGRA_PLL_HAS_LOCK_ENABLE;
|
||||
pll = _tegra_init_pll(clk_base, pmc, pll_params, lock);
|
||||
if (IS_ERR(pll))
|
||||
return ERR_CAST(pll);
|
||||
|
||||
|
@ -1317,17 +1412,15 @@ struct clk *tegra_clk_register_pll(const char *name, const char *parent_name,
|
|||
|
||||
struct clk *tegra_clk_register_plle(const char *name, const char *parent_name,
|
||||
void __iomem *clk_base, void __iomem *pmc,
|
||||
unsigned long flags, unsigned long fixed_rate,
|
||||
struct tegra_clk_pll_params *pll_params, u32 pll_flags,
|
||||
struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock)
|
||||
unsigned long flags, struct tegra_clk_pll_params *pll_params,
|
||||
spinlock_t *lock)
|
||||
{
|
||||
struct tegra_clk_pll *pll;
|
||||
struct clk *clk;
|
||||
|
||||
pll_flags |= TEGRA_PLL_LOCK_MISC | TEGRA_PLL_BYPASS;
|
||||
pll_flags |= TEGRA_PLL_HAS_LOCK_ENABLE;
|
||||
pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags,
|
||||
freq_table, lock);
|
||||
pll_params->flags |= TEGRA_PLL_LOCK_MISC | TEGRA_PLL_BYPASS;
|
||||
pll_params->flags |= TEGRA_PLL_HAS_LOCK_ENABLE;
|
||||
pll = _tegra_init_pll(clk_base, pmc, pll_params, lock);
|
||||
if (IS_ERR(pll))
|
||||
return ERR_CAST(pll);
|
||||
|
||||
|
@ -1339,8 +1432,8 @@ struct clk *tegra_clk_register_plle(const char *name, const char *parent_name,
|
|||
return clk;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ARCH_TEGRA_114_SOC
|
||||
const struct clk_ops tegra_clk_pllxc_ops = {
|
||||
#if defined(CONFIG_ARCH_TEGRA_114_SOC) || defined(CONFIG_ARCH_TEGRA_124_SOC)
|
||||
static const struct clk_ops tegra_clk_pllxc_ops = {
|
||||
.is_enabled = clk_pll_is_enabled,
|
||||
.enable = clk_pll_iddq_enable,
|
||||
.disable = clk_pll_iddq_disable,
|
||||
|
@ -1349,7 +1442,7 @@ const struct clk_ops tegra_clk_pllxc_ops = {
|
|||
.set_rate = clk_pllxc_set_rate,
|
||||
};
|
||||
|
||||
const struct clk_ops tegra_clk_pllm_ops = {
|
||||
static const struct clk_ops tegra_clk_pllm_ops = {
|
||||
.is_enabled = clk_pll_is_enabled,
|
||||
.enable = clk_pll_iddq_enable,
|
||||
.disable = clk_pll_iddq_disable,
|
||||
|
@ -1358,7 +1451,7 @@ const struct clk_ops tegra_clk_pllm_ops = {
|
|||
.set_rate = clk_pllm_set_rate,
|
||||
};
|
||||
|
||||
const struct clk_ops tegra_clk_pllc_ops = {
|
||||
static const struct clk_ops tegra_clk_pllc_ops = {
|
||||
.is_enabled = clk_pll_is_enabled,
|
||||
.enable = clk_pllc_enable,
|
||||
.disable = clk_pllc_disable,
|
||||
|
@ -1367,7 +1460,7 @@ const struct clk_ops tegra_clk_pllc_ops = {
|
|||
.set_rate = clk_pllc_set_rate,
|
||||
};
|
||||
|
||||
const struct clk_ops tegra_clk_pllre_ops = {
|
||||
static const struct clk_ops tegra_clk_pllre_ops = {
|
||||
.is_enabled = clk_pll_is_enabled,
|
||||
.enable = clk_pll_iddq_enable,
|
||||
.disable = clk_pll_iddq_disable,
|
||||
|
@ -1376,7 +1469,7 @@ const struct clk_ops tegra_clk_pllre_ops = {
|
|||
.set_rate = clk_pllre_set_rate,
|
||||
};
|
||||
|
||||
const struct clk_ops tegra_clk_plle_tegra114_ops = {
|
||||
static const struct clk_ops tegra_clk_plle_tegra114_ops = {
|
||||
.is_enabled = clk_pll_is_enabled,
|
||||
.enable = clk_plle_tegra114_enable,
|
||||
.disable = clk_plle_tegra114_disable,
|
||||
|
@ -1386,21 +1479,46 @@ const struct clk_ops tegra_clk_plle_tegra114_ops = {
|
|||
|
||||
struct clk *tegra_clk_register_pllxc(const char *name, const char *parent_name,
|
||||
void __iomem *clk_base, void __iomem *pmc,
|
||||
unsigned long flags, unsigned long fixed_rate,
|
||||
unsigned long flags,
|
||||
struct tegra_clk_pll_params *pll_params,
|
||||
u32 pll_flags,
|
||||
struct tegra_clk_pll_freq_table *freq_table,
|
||||
spinlock_t *lock)
|
||||
{
|
||||
struct tegra_clk_pll *pll;
|
||||
struct clk *clk;
|
||||
struct clk *clk, *parent;
|
||||
unsigned long parent_rate;
|
||||
int err;
|
||||
u32 val, val_iddq;
|
||||
|
||||
parent = __clk_lookup(parent_name);
|
||||
if (!parent) {
|
||||
WARN(1, "parent clk %s of %s must be registered first\n",
|
||||
name, parent_name);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
if (!pll_params->pdiv_tohw)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
pll_flags |= TEGRA_PLL_HAS_LOCK_ENABLE;
|
||||
pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags,
|
||||
freq_table, lock);
|
||||
parent_rate = __clk_get_rate(parent);
|
||||
|
||||
pll_params->vco_min = _clip_vco_min(pll_params->vco_min, parent_rate);
|
||||
|
||||
err = _setup_dynamic_ramp(pll_params, clk_base, parent_rate);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
val = readl_relaxed(clk_base + pll_params->base_reg);
|
||||
val_iddq = readl_relaxed(clk_base + pll_params->iddq_reg);
|
||||
|
||||
if (val & PLL_BASE_ENABLE)
|
||||
WARN_ON(val_iddq & BIT(pll_params->iddq_bit_idx));
|
||||
else {
|
||||
val_iddq |= BIT(pll_params->iddq_bit_idx);
|
||||
writel_relaxed(val_iddq, clk_base + pll_params->iddq_reg);
|
||||
}
|
||||
|
||||
pll_params->flags |= TEGRA_PLL_HAS_LOCK_ENABLE;
|
||||
pll = _tegra_init_pll(clk_base, pmc, pll_params, lock);
|
||||
if (IS_ERR(pll))
|
||||
return ERR_CAST(pll);
|
||||
|
||||
|
@ -1414,19 +1532,19 @@ struct clk *tegra_clk_register_pllxc(const char *name, const char *parent_name,
|
|||
|
||||
struct clk *tegra_clk_register_pllre(const char *name, const char *parent_name,
|
||||
void __iomem *clk_base, void __iomem *pmc,
|
||||
unsigned long flags, unsigned long fixed_rate,
|
||||
unsigned long flags,
|
||||
struct tegra_clk_pll_params *pll_params,
|
||||
u32 pll_flags,
|
||||
struct tegra_clk_pll_freq_table *freq_table,
|
||||
spinlock_t *lock, unsigned long parent_rate)
|
||||
{
|
||||
u32 val;
|
||||
struct tegra_clk_pll *pll;
|
||||
struct clk *clk;
|
||||
|
||||
pll_flags |= TEGRA_PLL_HAS_LOCK_ENABLE | TEGRA_PLL_LOCK_MISC;
|
||||
pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags,
|
||||
freq_table, lock);
|
||||
pll_params->flags |= TEGRA_PLL_HAS_LOCK_ENABLE | TEGRA_PLL_LOCK_MISC;
|
||||
|
||||
pll_params->vco_min = _clip_vco_min(pll_params->vco_min, parent_rate);
|
||||
|
||||
pll = _tegra_init_pll(clk_base, pmc, pll_params, lock);
|
||||
if (IS_ERR(pll))
|
||||
return ERR_CAST(pll);
|
||||
|
||||
|
@ -1461,23 +1579,32 @@ struct clk *tegra_clk_register_pllre(const char *name, const char *parent_name,
|
|||
|
||||
struct clk *tegra_clk_register_pllm(const char *name, const char *parent_name,
|
||||
void __iomem *clk_base, void __iomem *pmc,
|
||||
unsigned long flags, unsigned long fixed_rate,
|
||||
unsigned long flags,
|
||||
struct tegra_clk_pll_params *pll_params,
|
||||
u32 pll_flags,
|
||||
struct tegra_clk_pll_freq_table *freq_table,
|
||||
spinlock_t *lock)
|
||||
{
|
||||
struct tegra_clk_pll *pll;
|
||||
struct clk *clk;
|
||||
struct clk *clk, *parent;
|
||||
unsigned long parent_rate;
|
||||
|
||||
if (!pll_params->pdiv_tohw)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
pll_flags |= TEGRA_PLL_BYPASS;
|
||||
pll_flags |= TEGRA_PLL_HAS_LOCK_ENABLE;
|
||||
pll_flags |= TEGRA_PLLM;
|
||||
pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags,
|
||||
freq_table, lock);
|
||||
parent = __clk_lookup(parent_name);
|
||||
if (!parent) {
|
||||
WARN(1, "parent clk %s of %s must be registered first\n",
|
||||
name, parent_name);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
parent_rate = __clk_get_rate(parent);
|
||||
|
||||
pll_params->vco_min = _clip_vco_min(pll_params->vco_min, parent_rate);
|
||||
|
||||
pll_params->flags |= TEGRA_PLL_BYPASS;
|
||||
pll_params->flags |= TEGRA_PLL_HAS_LOCK_ENABLE;
|
||||
pll_params->flags |= TEGRA_PLLM;
|
||||
pll = _tegra_init_pll(clk_base, pmc, pll_params, lock);
|
||||
if (IS_ERR(pll))
|
||||
return ERR_CAST(pll);
|
||||
|
||||
|
@ -1491,10 +1618,8 @@ struct clk *tegra_clk_register_pllm(const char *name, const char *parent_name,
|
|||
|
||||
struct clk *tegra_clk_register_pllc(const char *name, const char *parent_name,
|
||||
void __iomem *clk_base, void __iomem *pmc,
|
||||
unsigned long flags, unsigned long fixed_rate,
|
||||
unsigned long flags,
|
||||
struct tegra_clk_pll_params *pll_params,
|
||||
u32 pll_flags,
|
||||
struct tegra_clk_pll_freq_table *freq_table,
|
||||
spinlock_t *lock)
|
||||
{
|
||||
struct clk *parent, *clk;
|
||||
|
@ -1507,20 +1632,21 @@ struct clk *tegra_clk_register_pllc(const char *name, const char *parent_name,
|
|||
return ERR_PTR(-EINVAL);
|
||||
|
||||
parent = __clk_lookup(parent_name);
|
||||
if (IS_ERR(parent)) {
|
||||
if (!parent) {
|
||||
WARN(1, "parent clk %s of %s must be registered first\n",
|
||||
name, parent_name);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
pll_flags |= TEGRA_PLL_BYPASS;
|
||||
pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags,
|
||||
freq_table, lock);
|
||||
parent_rate = __clk_get_rate(parent);
|
||||
|
||||
pll_params->vco_min = _clip_vco_min(pll_params->vco_min, parent_rate);
|
||||
|
||||
pll_params->flags |= TEGRA_PLL_BYPASS;
|
||||
pll = _tegra_init_pll(clk_base, pmc, pll_params, lock);
|
||||
if (IS_ERR(pll))
|
||||
return ERR_CAST(pll);
|
||||
|
||||
parent_rate = __clk_get_rate(parent);
|
||||
|
||||
/*
|
||||
* Most of PLLC register fields are shadowed, and can not be read
|
||||
* directly from PLL h/w. Hence, actual PLLC boot state is unknown.
|
||||
|
@ -1567,17 +1693,15 @@ struct clk *tegra_clk_register_pllc(const char *name, const char *parent_name,
|
|||
struct clk *tegra_clk_register_plle_tegra114(const char *name,
|
||||
const char *parent_name,
|
||||
void __iomem *clk_base, unsigned long flags,
|
||||
unsigned long fixed_rate,
|
||||
struct tegra_clk_pll_params *pll_params,
|
||||
struct tegra_clk_pll_freq_table *freq_table,
|
||||
spinlock_t *lock)
|
||||
{
|
||||
struct tegra_clk_pll *pll;
|
||||
struct clk *clk;
|
||||
u32 val, val_aux;
|
||||
|
||||
pll = _tegra_init_pll(clk_base, NULL, fixed_rate, pll_params,
|
||||
TEGRA_PLL_HAS_LOCK_ENABLE, freq_table, lock);
|
||||
pll_params->flags |= TEGRA_PLL_HAS_LOCK_ENABLE;
|
||||
pll = _tegra_init_pll(clk_base, NULL, pll_params, lock);
|
||||
if (IS_ERR(pll))
|
||||
return ERR_CAST(pll);
|
||||
|
||||
|
@ -1587,11 +1711,13 @@ struct clk *tegra_clk_register_plle_tegra114(const char *name,
|
|||
val_aux = pll_readl(pll_params->aux_reg, pll);
|
||||
|
||||
if (val & PLL_BASE_ENABLE) {
|
||||
if (!(val_aux & PLLE_AUX_PLLRE_SEL))
|
||||
if ((val_aux & PLLE_AUX_PLLRE_SEL) ||
|
||||
(val_aux & PLLE_AUX_PLLP_SEL))
|
||||
WARN(1, "pll_e enabled with unsupported parent %s\n",
|
||||
(val & PLLE_AUX_PLLP_SEL) ? "pllp_out0" : "pll_ref");
|
||||
(val_aux & PLLE_AUX_PLLP_SEL) ? "pllp_out0" :
|
||||
"pll_re_vco");
|
||||
} else {
|
||||
val_aux |= PLLE_AUX_PLLRE_SEL;
|
||||
val_aux &= ~(PLLE_AUX_PLLRE_SEL | PLLE_AUX_PLLP_SEL);
|
||||
pll_writel(val, pll_params->aux_reg, pll);
|
||||
}
|
||||
|
||||
|
@ -1603,3 +1729,92 @@ struct clk *tegra_clk_register_plle_tegra114(const char *name,
|
|||
return clk;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARCH_TEGRA_124_SOC
|
||||
static const struct clk_ops tegra_clk_pllss_ops = {
|
||||
.is_enabled = clk_pll_is_enabled,
|
||||
.enable = clk_pll_iddq_enable,
|
||||
.disable = clk_pll_iddq_disable,
|
||||
.recalc_rate = clk_pll_recalc_rate,
|
||||
.round_rate = clk_pll_ramp_round_rate,
|
||||
.set_rate = clk_pllxc_set_rate,
|
||||
};
|
||||
|
||||
struct clk *tegra_clk_register_pllss(const char *name, const char *parent_name,
|
||||
void __iomem *clk_base, unsigned long flags,
|
||||
struct tegra_clk_pll_params *pll_params,
|
||||
spinlock_t *lock)
|
||||
{
|
||||
struct tegra_clk_pll *pll;
|
||||
struct clk *clk, *parent;
|
||||
struct tegra_clk_pll_freq_table cfg;
|
||||
unsigned long parent_rate;
|
||||
u32 val;
|
||||
int i;
|
||||
|
||||
if (!pll_params->div_nmp)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
parent = __clk_lookup(parent_name);
|
||||
if (!parent) {
|
||||
WARN(1, "parent clk %s of %s must be registered first\n",
|
||||
name, parent_name);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
pll_params->flags = TEGRA_PLL_HAS_LOCK_ENABLE | TEGRA_PLL_USE_LOCK;
|
||||
pll = _tegra_init_pll(clk_base, NULL, pll_params, lock);
|
||||
if (IS_ERR(pll))
|
||||
return ERR_CAST(pll);
|
||||
|
||||
val = pll_readl_base(pll);
|
||||
val &= ~PLLSS_REF_SRC_SEL_MASK;
|
||||
pll_writel_base(val, pll);
|
||||
|
||||
parent_rate = __clk_get_rate(parent);
|
||||
|
||||
pll_params->vco_min = _clip_vco_min(pll_params->vco_min, parent_rate);
|
||||
|
||||
/* initialize PLL to minimum rate */
|
||||
|
||||
cfg.m = _pll_fixed_mdiv(pll_params, parent_rate);
|
||||
cfg.n = cfg.m * pll_params->vco_min / parent_rate;
|
||||
|
||||
for (i = 0; pll_params->pdiv_tohw[i].pdiv; i++)
|
||||
;
|
||||
if (!i) {
|
||||
kfree(pll);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
cfg.p = pll_params->pdiv_tohw[i-1].hw_val;
|
||||
|
||||
_update_pll_mnp(pll, &cfg);
|
||||
|
||||
pll_writel_misc(PLLSS_MISC_DEFAULT, pll);
|
||||
pll_writel(PLLSS_CFG_DEFAULT, pll_params->ext_misc_reg[0], pll);
|
||||
pll_writel(PLLSS_CTRL1_DEFAULT, pll_params->ext_misc_reg[1], pll);
|
||||
pll_writel(PLLSS_CTRL1_DEFAULT, pll_params->ext_misc_reg[2], pll);
|
||||
|
||||
val = pll_readl_base(pll);
|
||||
if (val & PLL_BASE_ENABLE) {
|
||||
if (val & BIT(pll_params->iddq_bit_idx)) {
|
||||
WARN(1, "%s is on but IDDQ set\n", name);
|
||||
kfree(pll);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
} else
|
||||
val |= BIT(pll_params->iddq_bit_idx);
|
||||
|
||||
val &= ~PLLSS_LOCK_OVERRIDE;
|
||||
pll_writel_base(val, pll);
|
||||
|
||||
clk = _tegra_clk_register_pll(pll, name, parent_name, flags,
|
||||
&tegra_clk_pllss_ops);
|
||||
|
||||
if (IS_ERR(clk))
|
||||
kfree(pll);
|
||||
|
||||
return clk;
|
||||
}
|
||||
#endif
|
||||
|
|
215
drivers/clk/tegra/clk-tegra-audio.c
Normal file
215
drivers/clk/tegra/clk-tegra-audio.c
Normal file
|
@ -0,0 +1,215 @@
|
|||
/*
|
||||
* Copyright (c) 2012, 2013, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/clk/tegra.h>
|
||||
|
||||
#include "clk.h"
|
||||
#include "clk-id.h"
|
||||
|
||||
#define AUDIO_SYNC_CLK_I2S0 0x4a0
|
||||
#define AUDIO_SYNC_CLK_I2S1 0x4a4
|
||||
#define AUDIO_SYNC_CLK_I2S2 0x4a8
|
||||
#define AUDIO_SYNC_CLK_I2S3 0x4ac
|
||||
#define AUDIO_SYNC_CLK_I2S4 0x4b0
|
||||
#define AUDIO_SYNC_CLK_SPDIF 0x4b4
|
||||
|
||||
#define AUDIO_SYNC_DOUBLER 0x49c
|
||||
|
||||
#define PLLA_OUT 0xb4
|
||||
|
||||
struct tegra_sync_source_initdata {
|
||||
char *name;
|
||||
unsigned long rate;
|
||||
unsigned long max_rate;
|
||||
int clk_id;
|
||||
};
|
||||
|
||||
#define SYNC(_name) \
|
||||
{\
|
||||
.name = #_name,\
|
||||
.rate = 24000000,\
|
||||
.max_rate = 24000000,\
|
||||
.clk_id = tegra_clk_ ## _name,\
|
||||
}
|
||||
|
||||
struct tegra_audio_clk_initdata {
|
||||
char *gate_name;
|
||||
char *mux_name;
|
||||
u32 offset;
|
||||
int gate_clk_id;
|
||||
int mux_clk_id;
|
||||
};
|
||||
|
||||
#define AUDIO(_name, _offset) \
|
||||
{\
|
||||
.gate_name = #_name,\
|
||||
.mux_name = #_name"_mux",\
|
||||
.offset = _offset,\
|
||||
.gate_clk_id = tegra_clk_ ## _name,\
|
||||
.mux_clk_id = tegra_clk_ ## _name ## _mux,\
|
||||
}
|
||||
|
||||
struct tegra_audio2x_clk_initdata {
|
||||
char *parent;
|
||||
char *gate_name;
|
||||
char *name_2x;
|
||||
char *div_name;
|
||||
int clk_id;
|
||||
int clk_num;
|
||||
u8 div_offset;
|
||||
};
|
||||
|
||||
#define AUDIO2X(_name, _num, _offset) \
|
||||
{\
|
||||
.parent = #_name,\
|
||||
.gate_name = #_name"_2x",\
|
||||
.name_2x = #_name"_doubler",\
|
||||
.div_name = #_name"_div",\
|
||||
.clk_id = tegra_clk_ ## _name ## _2x,\
|
||||
.clk_num = _num,\
|
||||
.div_offset = _offset,\
|
||||
}
|
||||
|
||||
static DEFINE_SPINLOCK(clk_doubler_lock);
|
||||
|
||||
static const char *mux_audio_sync_clk[] = { "spdif_in_sync", "i2s0_sync",
|
||||
"i2s1_sync", "i2s2_sync", "i2s3_sync", "i2s4_sync", "vimclk_sync",
|
||||
};
|
||||
|
||||
static struct tegra_sync_source_initdata sync_source_clks[] __initdata = {
|
||||
SYNC(spdif_in_sync),
|
||||
SYNC(i2s0_sync),
|
||||
SYNC(i2s1_sync),
|
||||
SYNC(i2s2_sync),
|
||||
SYNC(i2s3_sync),
|
||||
SYNC(i2s4_sync),
|
||||
SYNC(vimclk_sync),
|
||||
};
|
||||
|
||||
static struct tegra_audio_clk_initdata audio_clks[] = {
|
||||
AUDIO(audio0, AUDIO_SYNC_CLK_I2S0),
|
||||
AUDIO(audio1, AUDIO_SYNC_CLK_I2S1),
|
||||
AUDIO(audio2, AUDIO_SYNC_CLK_I2S2),
|
||||
AUDIO(audio3, AUDIO_SYNC_CLK_I2S3),
|
||||
AUDIO(audio4, AUDIO_SYNC_CLK_I2S4),
|
||||
AUDIO(spdif, AUDIO_SYNC_CLK_SPDIF),
|
||||
};
|
||||
|
||||
static struct tegra_audio2x_clk_initdata audio2x_clks[] = {
|
||||
AUDIO2X(audio0, 113, 24),
|
||||
AUDIO2X(audio1, 114, 25),
|
||||
AUDIO2X(audio2, 115, 26),
|
||||
AUDIO2X(audio3, 116, 27),
|
||||
AUDIO2X(audio4, 117, 28),
|
||||
AUDIO2X(spdif, 118, 29),
|
||||
};
|
||||
|
||||
void __init tegra_audio_clk_init(void __iomem *clk_base,
|
||||
void __iomem *pmc_base, struct tegra_clk *tegra_clks,
|
||||
struct tegra_clk_pll_params *pll_a_params)
|
||||
{
|
||||
struct clk *clk;
|
||||
struct clk **dt_clk;
|
||||
int i;
|
||||
|
||||
/* PLLA */
|
||||
dt_clk = tegra_lookup_dt_id(tegra_clk_pll_a, tegra_clks);
|
||||
if (dt_clk) {
|
||||
clk = tegra_clk_register_pll("pll_a", "pll_p_out1", clk_base,
|
||||
pmc_base, 0, pll_a_params, NULL);
|
||||
*dt_clk = clk;
|
||||
}
|
||||
|
||||
/* PLLA_OUT0 */
|
||||
dt_clk = tegra_lookup_dt_id(tegra_clk_pll_a_out0, tegra_clks);
|
||||
if (dt_clk) {
|
||||
clk = tegra_clk_register_divider("pll_a_out0_div", "pll_a",
|
||||
clk_base + PLLA_OUT, 0, TEGRA_DIVIDER_ROUND_UP,
|
||||
8, 8, 1, NULL);
|
||||
clk = tegra_clk_register_pll_out("pll_a_out0", "pll_a_out0_div",
|
||||
clk_base + PLLA_OUT, 1, 0, CLK_IGNORE_UNUSED |
|
||||
CLK_SET_RATE_PARENT, 0, NULL);
|
||||
*dt_clk = clk;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(sync_source_clks); i++) {
|
||||
struct tegra_sync_source_initdata *data;
|
||||
|
||||
data = &sync_source_clks[i];
|
||||
|
||||
dt_clk = tegra_lookup_dt_id(data->clk_id, tegra_clks);
|
||||
if (!dt_clk)
|
||||
continue;
|
||||
|
||||
clk = tegra_clk_register_sync_source(data->name,
|
||||
data->rate, data->max_rate);
|
||||
*dt_clk = clk;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(audio_clks); i++) {
|
||||
struct tegra_audio_clk_initdata *data;
|
||||
|
||||
data = &audio_clks[i];
|
||||
dt_clk = tegra_lookup_dt_id(data->mux_clk_id, tegra_clks);
|
||||
|
||||
if (!dt_clk)
|
||||
continue;
|
||||
clk = clk_register_mux(NULL, data->mux_name, mux_audio_sync_clk,
|
||||
ARRAY_SIZE(mux_audio_sync_clk),
|
||||
CLK_SET_RATE_NO_REPARENT,
|
||||
clk_base + data->offset, 0, 3, 0,
|
||||
NULL);
|
||||
*dt_clk = clk;
|
||||
|
||||
dt_clk = tegra_lookup_dt_id(data->gate_clk_id, tegra_clks);
|
||||
if (!dt_clk)
|
||||
continue;
|
||||
|
||||
clk = clk_register_gate(NULL, data->gate_name, data->mux_name,
|
||||
0, clk_base + data->offset, 4,
|
||||
CLK_GATE_SET_TO_DISABLE, NULL);
|
||||
*dt_clk = clk;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(audio2x_clks); i++) {
|
||||
struct tegra_audio2x_clk_initdata *data;
|
||||
|
||||
data = &audio2x_clks[i];
|
||||
dt_clk = tegra_lookup_dt_id(data->clk_id, tegra_clks);
|
||||
if (!dt_clk)
|
||||
continue;
|
||||
|
||||
clk = clk_register_fixed_factor(NULL, data->name_2x,
|
||||
data->parent, CLK_SET_RATE_PARENT, 2, 1);
|
||||
clk = tegra_clk_register_divider(data->div_name,
|
||||
data->name_2x, clk_base + AUDIO_SYNC_DOUBLER,
|
||||
0, 0, data->div_offset, 1, 0,
|
||||
&clk_doubler_lock);
|
||||
clk = tegra_clk_register_periph_gate(data->gate_name,
|
||||
data->div_name, TEGRA_PERIPH_NO_RESET,
|
||||
clk_base, CLK_SET_RATE_PARENT, data->clk_num,
|
||||
periph_clk_enb_refcnt);
|
||||
*dt_clk = clk;
|
||||
}
|
||||
}
|
||||
|
111
drivers/clk/tegra/clk-tegra-fixed.c
Normal file
111
drivers/clk/tegra/clk-tegra-fixed.c
Normal file
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* Copyright (c) 2012, 2013, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/clk/tegra.h>
|
||||
|
||||
#include "clk.h"
|
||||
#include "clk-id.h"
|
||||
|
||||
#define OSC_CTRL 0x50
|
||||
#define OSC_CTRL_OSC_FREQ_SHIFT 28
|
||||
#define OSC_CTRL_PLL_REF_DIV_SHIFT 26
|
||||
|
||||
int __init tegra_osc_clk_init(void __iomem *clk_base,
|
||||
struct tegra_clk *tegra_clks,
|
||||
unsigned long *input_freqs, int num,
|
||||
unsigned long *osc_freq,
|
||||
unsigned long *pll_ref_freq)
|
||||
{
|
||||
struct clk *clk;
|
||||
struct clk **dt_clk;
|
||||
u32 val, pll_ref_div;
|
||||
unsigned osc_idx;
|
||||
|
||||
val = readl_relaxed(clk_base + OSC_CTRL);
|
||||
osc_idx = val >> OSC_CTRL_OSC_FREQ_SHIFT;
|
||||
|
||||
if (osc_idx < num)
|
||||
*osc_freq = input_freqs[osc_idx];
|
||||
else
|
||||
*osc_freq = 0;
|
||||
|
||||
if (!*osc_freq) {
|
||||
WARN_ON(1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dt_clk = tegra_lookup_dt_id(tegra_clk_clk_m, tegra_clks);
|
||||
if (!dt_clk)
|
||||
return 0;
|
||||
|
||||
clk = clk_register_fixed_rate(NULL, "clk_m", NULL, CLK_IS_ROOT,
|
||||
*osc_freq);
|
||||
*dt_clk = clk;
|
||||
|
||||
/* pll_ref */
|
||||
val = (val >> OSC_CTRL_PLL_REF_DIV_SHIFT) & 3;
|
||||
pll_ref_div = 1 << val;
|
||||
dt_clk = tegra_lookup_dt_id(tegra_clk_pll_ref, tegra_clks);
|
||||
if (!dt_clk)
|
||||
return 0;
|
||||
|
||||
clk = clk_register_fixed_factor(NULL, "pll_ref", "clk_m",
|
||||
0, 1, pll_ref_div);
|
||||
*dt_clk = clk;
|
||||
|
||||
if (pll_ref_freq)
|
||||
*pll_ref_freq = *osc_freq / pll_ref_div;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __init tegra_fixed_clk_init(struct tegra_clk *tegra_clks)
|
||||
{
|
||||
struct clk *clk;
|
||||
struct clk **dt_clk;
|
||||
|
||||
/* clk_32k */
|
||||
dt_clk = tegra_lookup_dt_id(tegra_clk_clk_32k, tegra_clks);
|
||||
if (dt_clk) {
|
||||
clk = clk_register_fixed_rate(NULL, "clk_32k", NULL,
|
||||
CLK_IS_ROOT, 32768);
|
||||
*dt_clk = clk;
|
||||
}
|
||||
|
||||
/* clk_m_div2 */
|
||||
dt_clk = tegra_lookup_dt_id(tegra_clk_clk_m_div2, tegra_clks);
|
||||
if (dt_clk) {
|
||||
clk = clk_register_fixed_factor(NULL, "clk_m_div2", "clk_m",
|
||||
CLK_SET_RATE_PARENT, 1, 2);
|
||||
*dt_clk = clk;
|
||||
}
|
||||
|
||||
/* clk_m_div4 */
|
||||
dt_clk = tegra_lookup_dt_id(tegra_clk_clk_m_div4, tegra_clks);
|
||||
if (dt_clk) {
|
||||
clk = clk_register_fixed_factor(NULL, "clk_m_div4", "clk_m",
|
||||
CLK_SET_RATE_PARENT, 1, 4);
|
||||
*dt_clk = clk;
|
||||
}
|
||||
}
|
||||
|
674
drivers/clk/tegra/clk-tegra-periph.c
Normal file
674
drivers/clk/tegra/clk-tegra-periph.c
Normal file
|
@ -0,0 +1,674 @@
|
|||
/*
|
||||
* Copyright (c) 2012, 2013, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/clk/tegra.h>
|
||||
|
||||
#include "clk.h"
|
||||
#include "clk-id.h"
|
||||
|
||||
#define CLK_SOURCE_I2S0 0x1d8
|
||||
#define CLK_SOURCE_I2S1 0x100
|
||||
#define CLK_SOURCE_I2S2 0x104
|
||||
#define CLK_SOURCE_NDFLASH 0x160
|
||||
#define CLK_SOURCE_I2S3 0x3bc
|
||||
#define CLK_SOURCE_I2S4 0x3c0
|
||||
#define CLK_SOURCE_SPDIF_OUT 0x108
|
||||
#define CLK_SOURCE_SPDIF_IN 0x10c
|
||||
#define CLK_SOURCE_PWM 0x110
|
||||
#define CLK_SOURCE_ADX 0x638
|
||||
#define CLK_SOURCE_ADX1 0x670
|
||||
#define CLK_SOURCE_AMX 0x63c
|
||||
#define CLK_SOURCE_AMX1 0x674
|
||||
#define CLK_SOURCE_HDA 0x428
|
||||
#define CLK_SOURCE_HDA2CODEC_2X 0x3e4
|
||||
#define CLK_SOURCE_SBC1 0x134
|
||||
#define CLK_SOURCE_SBC2 0x118
|
||||
#define CLK_SOURCE_SBC3 0x11c
|
||||
#define CLK_SOURCE_SBC4 0x1b4
|
||||
#define CLK_SOURCE_SBC5 0x3c8
|
||||
#define CLK_SOURCE_SBC6 0x3cc
|
||||
#define CLK_SOURCE_SATA_OOB 0x420
|
||||
#define CLK_SOURCE_SATA 0x424
|
||||
#define CLK_SOURCE_NDSPEED 0x3f8
|
||||
#define CLK_SOURCE_VFIR 0x168
|
||||
#define CLK_SOURCE_SDMMC1 0x150
|
||||
#define CLK_SOURCE_SDMMC2 0x154
|
||||
#define CLK_SOURCE_SDMMC3 0x1bc
|
||||
#define CLK_SOURCE_SDMMC4 0x164
|
||||
#define CLK_SOURCE_CVE 0x140
|
||||
#define CLK_SOURCE_TVO 0x188
|
||||
#define CLK_SOURCE_TVDAC 0x194
|
||||
#define CLK_SOURCE_VDE 0x1c8
|
||||
#define CLK_SOURCE_CSITE 0x1d4
|
||||
#define CLK_SOURCE_LA 0x1f8
|
||||
#define CLK_SOURCE_TRACE 0x634
|
||||
#define CLK_SOURCE_OWR 0x1cc
|
||||
#define CLK_SOURCE_NOR 0x1d0
|
||||
#define CLK_SOURCE_MIPI 0x174
|
||||
#define CLK_SOURCE_I2C1 0x124
|
||||
#define CLK_SOURCE_I2C2 0x198
|
||||
#define CLK_SOURCE_I2C3 0x1b8
|
||||
#define CLK_SOURCE_I2C4 0x3c4
|
||||
#define CLK_SOURCE_I2C5 0x128
|
||||
#define CLK_SOURCE_I2C6 0x65c
|
||||
#define CLK_SOURCE_UARTA 0x178
|
||||
#define CLK_SOURCE_UARTB 0x17c
|
||||
#define CLK_SOURCE_UARTC 0x1a0
|
||||
#define CLK_SOURCE_UARTD 0x1c0
|
||||
#define CLK_SOURCE_UARTE 0x1c4
|
||||
#define CLK_SOURCE_3D 0x158
|
||||
#define CLK_SOURCE_2D 0x15c
|
||||
#define CLK_SOURCE_MPE 0x170
|
||||
#define CLK_SOURCE_UARTE 0x1c4
|
||||
#define CLK_SOURCE_VI_SENSOR 0x1a8
|
||||
#define CLK_SOURCE_VI 0x148
|
||||
#define CLK_SOURCE_EPP 0x16c
|
||||
#define CLK_SOURCE_MSENC 0x1f0
|
||||
#define CLK_SOURCE_TSEC 0x1f4
|
||||
#define CLK_SOURCE_HOST1X 0x180
|
||||
#define CLK_SOURCE_HDMI 0x18c
|
||||
#define CLK_SOURCE_DISP1 0x138
|
||||
#define CLK_SOURCE_DISP2 0x13c
|
||||
#define CLK_SOURCE_CILAB 0x614
|
||||
#define CLK_SOURCE_CILCD 0x618
|
||||
#define CLK_SOURCE_CILE 0x61c
|
||||
#define CLK_SOURCE_DSIALP 0x620
|
||||
#define CLK_SOURCE_DSIBLP 0x624
|
||||
#define CLK_SOURCE_TSENSOR 0x3b8
|
||||
#define CLK_SOURCE_D_AUDIO 0x3d0
|
||||
#define CLK_SOURCE_DAM0 0x3d8
|
||||
#define CLK_SOURCE_DAM1 0x3dc
|
||||
#define CLK_SOURCE_DAM2 0x3e0
|
||||
#define CLK_SOURCE_ACTMON 0x3e8
|
||||
#define CLK_SOURCE_EXTERN1 0x3ec
|
||||
#define CLK_SOURCE_EXTERN2 0x3f0
|
||||
#define CLK_SOURCE_EXTERN3 0x3f4
|
||||
#define CLK_SOURCE_I2CSLOW 0x3fc
|
||||
#define CLK_SOURCE_SE 0x42c
|
||||
#define CLK_SOURCE_MSELECT 0x3b4
|
||||
#define CLK_SOURCE_DFLL_REF 0x62c
|
||||
#define CLK_SOURCE_DFLL_SOC 0x630
|
||||
#define CLK_SOURCE_SOC_THERM 0x644
|
||||
#define CLK_SOURCE_XUSB_HOST_SRC 0x600
|
||||
#define CLK_SOURCE_XUSB_FALCON_SRC 0x604
|
||||
#define CLK_SOURCE_XUSB_FS_SRC 0x608
|
||||
#define CLK_SOURCE_XUSB_SS_SRC 0x610
|
||||
#define CLK_SOURCE_XUSB_DEV_SRC 0x60c
|
||||
#define CLK_SOURCE_ISP 0x144
|
||||
#define CLK_SOURCE_SOR0 0x414
|
||||
#define CLK_SOURCE_DPAUX 0x418
|
||||
#define CLK_SOURCE_SATA_OOB 0x420
|
||||
#define CLK_SOURCE_SATA 0x424
|
||||
#define CLK_SOURCE_ENTROPY 0x628
|
||||
#define CLK_SOURCE_VI_SENSOR2 0x658
|
||||
#define CLK_SOURCE_HDMI_AUDIO 0x668
|
||||
#define CLK_SOURCE_VIC03 0x678
|
||||
#define CLK_SOURCE_CLK72MHZ 0x66c
|
||||
|
||||
#define MASK(x) (BIT(x) - 1)
|
||||
|
||||
#define MUX(_name, _parents, _offset, \
|
||||
_clk_num, _gate_flags, _clk_id) \
|
||||
TEGRA_INIT_DATA_TABLE(_name, NULL, NULL, _parents, _offset,\
|
||||
30, MASK(2), 0, 0, 8, 1, TEGRA_DIVIDER_ROUND_UP, \
|
||||
_clk_num, _gate_flags, _clk_id, _parents##_idx, 0,\
|
||||
NULL)
|
||||
|
||||
#define MUX_FLAGS(_name, _parents, _offset,\
|
||||
_clk_num, _gate_flags, _clk_id, flags)\
|
||||
TEGRA_INIT_DATA_TABLE(_name, NULL, NULL, _parents, _offset,\
|
||||
30, MASK(2), 0, 0, 8, 1, TEGRA_DIVIDER_ROUND_UP,\
|
||||
_clk_num, _gate_flags, _clk_id, _parents##_idx, flags,\
|
||||
NULL)
|
||||
|
||||
#define MUX8(_name, _parents, _offset, \
|
||||
_clk_num, _gate_flags, _clk_id) \
|
||||
TEGRA_INIT_DATA_TABLE(_name, NULL, NULL, _parents, _offset,\
|
||||
29, MASK(3), 0, 0, 8, 1, TEGRA_DIVIDER_ROUND_UP,\
|
||||
_clk_num, _gate_flags, _clk_id, _parents##_idx, 0,\
|
||||
NULL)
|
||||
|
||||
#define MUX8_NOGATE_LOCK(_name, _parents, _offset, _clk_id, _lock) \
|
||||
TEGRA_INIT_DATA_TABLE(_name, NULL, NULL, _parents, _offset, \
|
||||
29, MASK(3), 0, 0, 8, 1, TEGRA_DIVIDER_ROUND_UP,\
|
||||
0, TEGRA_PERIPH_NO_GATE, _clk_id,\
|
||||
_parents##_idx, 0, _lock)
|
||||
|
||||
#define INT(_name, _parents, _offset, \
|
||||
_clk_num, _gate_flags, _clk_id) \
|
||||
TEGRA_INIT_DATA_TABLE(_name, NULL, NULL, _parents, _offset,\
|
||||
30, MASK(2), 0, 0, 8, 1, TEGRA_DIVIDER_INT| \
|
||||
TEGRA_DIVIDER_ROUND_UP, _clk_num, _gate_flags,\
|
||||
_clk_id, _parents##_idx, 0, NULL)
|
||||
|
||||
#define INT_FLAGS(_name, _parents, _offset,\
|
||||
_clk_num, _gate_flags, _clk_id, flags)\
|
||||
TEGRA_INIT_DATA_TABLE(_name, NULL, NULL, _parents, _offset,\
|
||||
30, MASK(2), 0, 0, 8, 1, TEGRA_DIVIDER_INT| \
|
||||
TEGRA_DIVIDER_ROUND_UP, _clk_num, _gate_flags,\
|
||||
_clk_id, _parents##_idx, flags, NULL)
|
||||
|
||||
#define INT8(_name, _parents, _offset,\
|
||||
_clk_num, _gate_flags, _clk_id) \
|
||||
TEGRA_INIT_DATA_TABLE(_name, NULL, NULL, _parents, _offset,\
|
||||
29, MASK(3), 0, 0, 8, 1, TEGRA_DIVIDER_INT| \
|
||||
TEGRA_DIVIDER_ROUND_UP, _clk_num, _gate_flags,\
|
||||
_clk_id, _parents##_idx, 0, NULL)
|
||||
|
||||
#define UART(_name, _parents, _offset,\
|
||||
_clk_num, _clk_id) \
|
||||
TEGRA_INIT_DATA_TABLE(_name, NULL, NULL, _parents, _offset,\
|
||||
30, MASK(2), 0, 0, 16, 1, TEGRA_DIVIDER_UART| \
|
||||
TEGRA_DIVIDER_ROUND_UP, _clk_num, 0, _clk_id,\
|
||||
_parents##_idx, 0, NULL)
|
||||
|
||||
#define I2C(_name, _parents, _offset,\
|
||||
_clk_num, _clk_id) \
|
||||
TEGRA_INIT_DATA_TABLE(_name, NULL, NULL, _parents, _offset,\
|
||||
30, MASK(2), 0, 0, 16, 0, TEGRA_DIVIDER_ROUND_UP,\
|
||||
_clk_num, 0, _clk_id, _parents##_idx, 0, NULL)
|
||||
|
||||
#define XUSB(_name, _parents, _offset, \
|
||||
_clk_num, _gate_flags, _clk_id) \
|
||||
TEGRA_INIT_DATA_TABLE(_name, NULL, NULL, _parents, _offset, \
|
||||
29, MASK(3), 0, 0, 8, 1, TEGRA_DIVIDER_INT| \
|
||||
TEGRA_DIVIDER_ROUND_UP, _clk_num, _gate_flags,\
|
||||
_clk_id, _parents##_idx, 0, NULL)
|
||||
|
||||
#define AUDIO(_name, _offset, _clk_num,\
|
||||
_gate_flags, _clk_id) \
|
||||
TEGRA_INIT_DATA_TABLE(_name, NULL, NULL, mux_d_audio_clk, \
|
||||
_offset, 16, 0xE01F, 0, 0, 8, 1, \
|
||||
TEGRA_DIVIDER_ROUND_UP, _clk_num, _gate_flags, \
|
||||
_clk_id, mux_d_audio_clk_idx, 0, NULL)
|
||||
|
||||
#define NODIV(_name, _parents, _offset, \
|
||||
_mux_shift, _mux_mask, _clk_num, \
|
||||
_gate_flags, _clk_id, _lock) \
|
||||
TEGRA_INIT_DATA_TABLE(_name, NULL, NULL, _parents, _offset,\
|
||||
_mux_shift, _mux_mask, 0, 0, 0, 0, 0,\
|
||||
_clk_num, (_gate_flags) | TEGRA_PERIPH_NO_DIV,\
|
||||
_clk_id, _parents##_idx, 0, _lock)
|
||||
|
||||
#define GATE(_name, _parent_name, \
|
||||
_clk_num, _gate_flags, _clk_id, _flags) \
|
||||
{ \
|
||||
.name = _name, \
|
||||
.clk_id = _clk_id, \
|
||||
.p.parent_name = _parent_name, \
|
||||
.periph = TEGRA_CLK_PERIPH(0, 0, 0, 0, 0, 0, 0, \
|
||||
_clk_num, _gate_flags, 0, NULL), \
|
||||
.flags = _flags \
|
||||
}
|
||||
|
||||
#define PLLP_BASE 0xa0
|
||||
#define PLLP_MISC 0xac
|
||||
#define PLLP_OUTA 0xa4
|
||||
#define PLLP_OUTB 0xa8
|
||||
#define PLLP_OUTC 0x67c
|
||||
|
||||
#define PLL_BASE_LOCK BIT(27)
|
||||
#define PLL_MISC_LOCK_ENABLE 18
|
||||
|
||||
static DEFINE_SPINLOCK(PLLP_OUTA_lock);
|
||||
static DEFINE_SPINLOCK(PLLP_OUTB_lock);
|
||||
static DEFINE_SPINLOCK(PLLP_OUTC_lock);
|
||||
static DEFINE_SPINLOCK(sor0_lock);
|
||||
|
||||
#define MUX_I2S_SPDIF(_id) \
|
||||
static const char *mux_pllaout0_##_id##_2x_pllp_clkm[] = { "pll_a_out0", \
|
||||
#_id, "pll_p",\
|
||||
"clk_m"};
|
||||
MUX_I2S_SPDIF(audio0)
|
||||
MUX_I2S_SPDIF(audio1)
|
||||
MUX_I2S_SPDIF(audio2)
|
||||
MUX_I2S_SPDIF(audio3)
|
||||
MUX_I2S_SPDIF(audio4)
|
||||
MUX_I2S_SPDIF(audio)
|
||||
|
||||
#define mux_pllaout0_audio0_2x_pllp_clkm_idx NULL
|
||||
#define mux_pllaout0_audio1_2x_pllp_clkm_idx NULL
|
||||
#define mux_pllaout0_audio2_2x_pllp_clkm_idx NULL
|
||||
#define mux_pllaout0_audio3_2x_pllp_clkm_idx NULL
|
||||
#define mux_pllaout0_audio4_2x_pllp_clkm_idx NULL
|
||||
#define mux_pllaout0_audio_2x_pllp_clkm_idx NULL
|
||||
|
||||
static const char *mux_pllp_pllc_pllm_clkm[] = {
|
||||
"pll_p", "pll_c", "pll_m", "clk_m"
|
||||
};
|
||||
#define mux_pllp_pllc_pllm_clkm_idx NULL
|
||||
|
||||
static const char *mux_pllp_pllc_pllm[] = { "pll_p", "pll_c", "pll_m" };
|
||||
#define mux_pllp_pllc_pllm_idx NULL
|
||||
|
||||
static const char *mux_pllp_pllc_clk32_clkm[] = {
|
||||
"pll_p", "pll_c", "clk_32k", "clk_m"
|
||||
};
|
||||
#define mux_pllp_pllc_clk32_clkm_idx NULL
|
||||
|
||||
static const char *mux_plla_pllc_pllp_clkm[] = {
|
||||
"pll_a_out0", "pll_c", "pll_p", "clk_m"
|
||||
};
|
||||
#define mux_plla_pllc_pllp_clkm_idx mux_pllp_pllc_pllm_clkm_idx
|
||||
|
||||
static const char *mux_pllp_pllc2_c_c3_pllm_clkm[] = {
|
||||
"pll_p", "pll_c2", "pll_c", "pll_c3", "pll_m", "clk_m"
|
||||
};
|
||||
static u32 mux_pllp_pllc2_c_c3_pllm_clkm_idx[] = {
|
||||
[0] = 0, [1] = 1, [2] = 2, [3] = 3, [4] = 4, [5] = 6,
|
||||
};
|
||||
|
||||
static const char *mux_pllp_clkm[] = {
|
||||
"pll_p", "clk_m"
|
||||
};
|
||||
static u32 mux_pllp_clkm_idx[] = {
|
||||
[0] = 0, [1] = 3,
|
||||
};
|
||||
|
||||
static const char *mux_pllm_pllc2_c_c3_pllp_plla[] = {
|
||||
"pll_m", "pll_c2", "pll_c", "pll_c3", "pll_p", "pll_a_out0"
|
||||
};
|
||||
#define mux_pllm_pllc2_c_c3_pllp_plla_idx mux_pllp_pllc2_c_c3_pllm_clkm_idx
|
||||
|
||||
static const char *mux_pllp_pllm_plld_plla_pllc_plld2_clkm[] = {
|
||||
"pll_p", "pll_m", "pll_d_out0", "pll_a_out0", "pll_c",
|
||||
"pll_d2_out0", "clk_m"
|
||||
};
|
||||
#define mux_pllp_pllm_plld_plla_pllc_plld2_clkm_idx NULL
|
||||
|
||||
static const char *mux_pllm_pllc_pllp_plla[] = {
|
||||
"pll_m", "pll_c", "pll_p", "pll_a_out0"
|
||||
};
|
||||
#define mux_pllm_pllc_pllp_plla_idx mux_pllp_pllc_pllm_clkm_idx
|
||||
|
||||
static const char *mux_pllp_pllc_clkm[] = {
|
||||
"pll_p", "pll_c", "pll_m"
|
||||
};
|
||||
static u32 mux_pllp_pllc_clkm_idx[] = {
|
||||
[0] = 0, [1] = 1, [2] = 3,
|
||||
};
|
||||
|
||||
static const char *mux_pllp_pllc_clkm_clk32[] = {
|
||||
"pll_p", "pll_c", "clk_m", "clk_32k"
|
||||
};
|
||||
#define mux_pllp_pllc_clkm_clk32_idx NULL
|
||||
|
||||
static const char *mux_plla_clk32_pllp_clkm_plle[] = {
|
||||
"pll_a_out0", "clk_32k", "pll_p", "clk_m", "pll_e_out0"
|
||||
};
|
||||
#define mux_plla_clk32_pllp_clkm_plle_idx NULL
|
||||
|
||||
static const char *mux_clkm_pllp_pllc_pllre[] = {
|
||||
"clk_m", "pll_p", "pll_c", "pll_re_out"
|
||||
};
|
||||
static u32 mux_clkm_pllp_pllc_pllre_idx[] = {
|
||||
[0] = 0, [1] = 1, [2] = 3, [3] = 5,
|
||||
};
|
||||
|
||||
static const char *mux_clkm_48M_pllp_480M[] = {
|
||||
"clk_m", "pll_u_48M", "pll_p", "pll_u_480M"
|
||||
};
|
||||
#define mux_clkm_48M_pllp_480M_idx NULL
|
||||
|
||||
static const char *mux_clkm_pllre_clk32_480M_pllc_ref[] = {
|
||||
"clk_m", "pll_re_out", "clk_32k", "pll_u_480M", "pll_c", "pll_ref"
|
||||
};
|
||||
static u32 mux_clkm_pllre_clk32_480M_pllc_ref_idx[] = {
|
||||
[0] = 0, [1] = 1, [2] = 3, [3] = 3, [4] = 4, [5] = 7,
|
||||
};
|
||||
|
||||
static const char *mux_d_audio_clk[] = {
|
||||
"pll_a_out0", "pll_p", "clk_m", "spdif_in_sync", "i2s0_sync",
|
||||
"i2s1_sync", "i2s2_sync", "i2s3_sync", "i2s4_sync", "vimclk_sync",
|
||||
};
|
||||
static u32 mux_d_audio_clk_idx[] = {
|
||||
[0] = 0, [1] = 0x8000, [2] = 0xc000, [3] = 0xE000, [4] = 0xE001,
|
||||
[5] = 0xE002, [6] = 0xE003, [7] = 0xE004, [8] = 0xE005, [9] = 0xE007,
|
||||
};
|
||||
|
||||
static const char *mux_pllp_plld_pllc_clkm[] = {
|
||||
"pll_p", "pll_d_out0", "pll_c", "clk_m"
|
||||
};
|
||||
#define mux_pllp_plld_pllc_clkm_idx NULL
|
||||
static const char *mux_pllm_pllc_pllp_plla_clkm_pllc4[] = {
|
||||
"pll_m", "pll_c", "pll_p", "pll_a_out0", "clk_m", "pll_c4",
|
||||
};
|
||||
static u32 mux_pllm_pllc_pllp_plla_clkm_pllc4_idx[] = {
|
||||
[0] = 0, [1] = 1, [2] = 3, [3] = 3, [4] = 6, [5] = 7,
|
||||
};
|
||||
|
||||
static const char *mux_pllp_clkm1[] = {
|
||||
"pll_p", "clk_m",
|
||||
};
|
||||
#define mux_pllp_clkm1_idx NULL
|
||||
|
||||
static const char *mux_pllp3_pllc_clkm[] = {
|
||||
"pll_p_out3", "pll_c", "pll_c2", "clk_m",
|
||||
};
|
||||
#define mux_pllp3_pllc_clkm_idx NULL
|
||||
|
||||
static const char *mux_pllm_pllc_pllp_plla_pllc2_c3_clkm[] = {
|
||||
"pll_m", "pll_c", "pll_p", "pll_a", "pll_c2", "pll_c3", "clk_m"
|
||||
};
|
||||
static u32 mux_pllm_pllc_pllp_plla_pllc2_c3_clkm_idx[] = {
|
||||
[0] = 0, [1] = 1, [2] = 2, [3] = 3, [4] = 4, [5] = 6,
|
||||
};
|
||||
|
||||
static const char *mux_pllm_pllc2_c_c3_pllp_plla_pllc4[] = {
|
||||
"pll_m", "pll_c2", "pll_c", "pll_c3", "pll_p", "pll_a_out0", "pll_c4",
|
||||
};
|
||||
static u32 mux_pllm_pllc2_c_c3_pllp_plla_pllc4_idx[] = {
|
||||
[0] = 0, [1] = 1, [2] = 2, [3] = 3, [4] = 4, [5] = 6, [6] = 7,
|
||||
};
|
||||
|
||||
static const char *mux_clkm_plldp_sor0lvds[] = {
|
||||
"clk_m", "pll_dp", "sor0_lvds",
|
||||
};
|
||||
#define mux_clkm_plldp_sor0lvds_idx NULL
|
||||
|
||||
static struct tegra_periph_init_data periph_clks[] = {
|
||||
AUDIO("d_audio", CLK_SOURCE_D_AUDIO, 106, TEGRA_PERIPH_ON_APB, tegra_clk_d_audio),
|
||||
AUDIO("dam0", CLK_SOURCE_DAM0, 108, TEGRA_PERIPH_ON_APB, tegra_clk_dam0),
|
||||
AUDIO("dam1", CLK_SOURCE_DAM1, 109, TEGRA_PERIPH_ON_APB, tegra_clk_dam1),
|
||||
AUDIO("dam2", CLK_SOURCE_DAM2, 110, TEGRA_PERIPH_ON_APB, tegra_clk_dam2),
|
||||
I2C("i2c1", mux_pllp_clkm, CLK_SOURCE_I2C1, 12, tegra_clk_i2c1),
|
||||
I2C("i2c2", mux_pllp_clkm, CLK_SOURCE_I2C2, 54, tegra_clk_i2c2),
|
||||
I2C("i2c3", mux_pllp_clkm, CLK_SOURCE_I2C3, 67, tegra_clk_i2c3),
|
||||
I2C("i2c4", mux_pllp_clkm, CLK_SOURCE_I2C4, 103, tegra_clk_i2c4),
|
||||
I2C("i2c5", mux_pllp_clkm, CLK_SOURCE_I2C5, 47, tegra_clk_i2c5),
|
||||
INT("vde", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_VDE, 61, 0, tegra_clk_vde),
|
||||
INT("vi", mux_pllm_pllc_pllp_plla, CLK_SOURCE_VI, 20, 0, tegra_clk_vi),
|
||||
INT("epp", mux_pllm_pllc_pllp_plla, CLK_SOURCE_EPP, 19, 0, tegra_clk_epp),
|
||||
INT("host1x", mux_pllm_pllc_pllp_plla, CLK_SOURCE_HOST1X, 28, 0, tegra_clk_host1x),
|
||||
INT("mpe", mux_pllm_pllc_pllp_plla, CLK_SOURCE_MPE, 60, 0, tegra_clk_mpe),
|
||||
INT("2d", mux_pllm_pllc_pllp_plla, CLK_SOURCE_2D, 21, 0, tegra_clk_gr2d),
|
||||
INT("3d", mux_pllm_pllc_pllp_plla, CLK_SOURCE_3D, 24, 0, tegra_clk_gr3d),
|
||||
INT8("vde", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_VDE, 61, 0, tegra_clk_vde_8),
|
||||
INT8("vi", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_VI, 20, 0, tegra_clk_vi_8),
|
||||
INT8("vi", mux_pllm_pllc2_c_c3_pllp_plla_pllc4, CLK_SOURCE_VI, 20, 0, tegra_clk_vi_9),
|
||||
INT8("epp", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_EPP, 19, 0, tegra_clk_epp_8),
|
||||
INT8("msenc", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_MSENC, 91, TEGRA_PERIPH_WAR_1005168, tegra_clk_msenc),
|
||||
INT8("tsec", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_TSEC, 83, 0, tegra_clk_tsec),
|
||||
INT8("host1x", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_HOST1X, 28, 0, tegra_clk_host1x_8),
|
||||
INT8("se", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_SE, 127, TEGRA_PERIPH_ON_APB, tegra_clk_se),
|
||||
INT8("2d", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_2D, 21, 0, tegra_clk_gr2d_8),
|
||||
INT8("3d", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_3D, 24, 0, tegra_clk_gr3d_8),
|
||||
INT8("vic03", mux_pllm_pllc_pllp_plla_pllc2_c3_clkm, CLK_SOURCE_VIC03, 178, 0, tegra_clk_vic03),
|
||||
INT_FLAGS("mselect", mux_pllp_clkm, CLK_SOURCE_MSELECT, 99, 0, tegra_clk_mselect, CLK_IGNORE_UNUSED),
|
||||
MUX("i2s0", mux_pllaout0_audio0_2x_pllp_clkm, CLK_SOURCE_I2S0, 30, TEGRA_PERIPH_ON_APB, tegra_clk_i2s0),
|
||||
MUX("i2s1", mux_pllaout0_audio1_2x_pllp_clkm, CLK_SOURCE_I2S1, 11, TEGRA_PERIPH_ON_APB, tegra_clk_i2s1),
|
||||
MUX("i2s2", mux_pllaout0_audio2_2x_pllp_clkm, CLK_SOURCE_I2S2, 18, TEGRA_PERIPH_ON_APB, tegra_clk_i2s2),
|
||||
MUX("i2s3", mux_pllaout0_audio3_2x_pllp_clkm, CLK_SOURCE_I2S3, 101, TEGRA_PERIPH_ON_APB, tegra_clk_i2s3),
|
||||
MUX("i2s4", mux_pllaout0_audio4_2x_pllp_clkm, CLK_SOURCE_I2S4, 102, TEGRA_PERIPH_ON_APB, tegra_clk_i2s4),
|
||||
MUX("spdif_out", mux_pllaout0_audio_2x_pllp_clkm, CLK_SOURCE_SPDIF_OUT, 10, TEGRA_PERIPH_ON_APB, tegra_clk_spdif_out),
|
||||
MUX("spdif_in", mux_pllp_pllc_pllm, CLK_SOURCE_SPDIF_IN, 10, TEGRA_PERIPH_ON_APB, tegra_clk_spdif_in),
|
||||
MUX("pwm", mux_pllp_pllc_clk32_clkm, CLK_SOURCE_PWM, 17, TEGRA_PERIPH_ON_APB, tegra_clk_pwm),
|
||||
MUX("adx", mux_plla_pllc_pllp_clkm, CLK_SOURCE_ADX, 154, TEGRA_PERIPH_ON_APB, tegra_clk_adx),
|
||||
MUX("amx", mux_plla_pllc_pllp_clkm, CLK_SOURCE_AMX, 153, TEGRA_PERIPH_ON_APB, tegra_clk_amx),
|
||||
MUX("hda", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_HDA, 125, TEGRA_PERIPH_ON_APB, tegra_clk_hda),
|
||||
MUX("hda2codec_2x", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_HDA2CODEC_2X, 111, TEGRA_PERIPH_ON_APB, tegra_clk_hda2codec_2x),
|
||||
MUX("vfir", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_VFIR, 7, TEGRA_PERIPH_ON_APB, tegra_clk_vfir),
|
||||
MUX("sdmmc1", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SDMMC1, 14, 0, tegra_clk_sdmmc1),
|
||||
MUX("sdmmc2", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SDMMC2, 9, 0, tegra_clk_sdmmc2),
|
||||
MUX("sdmmc3", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SDMMC3, 69, 0, tegra_clk_sdmmc3),
|
||||
MUX("sdmmc4", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SDMMC4, 15, 0, tegra_clk_sdmmc4),
|
||||
MUX("la", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_LA, 76, TEGRA_PERIPH_ON_APB, tegra_clk_la),
|
||||
MUX("trace", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_TRACE, 77, TEGRA_PERIPH_ON_APB, tegra_clk_trace),
|
||||
MUX("owr", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_OWR, 71, TEGRA_PERIPH_ON_APB, tegra_clk_owr),
|
||||
MUX("nor", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_NOR, 42, 0, tegra_clk_nor),
|
||||
MUX("mipi", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_MIPI, 50, TEGRA_PERIPH_ON_APB, tegra_clk_mipi),
|
||||
MUX("vi_sensor", mux_pllm_pllc_pllp_plla, CLK_SOURCE_VI_SENSOR, 20, TEGRA_PERIPH_NO_RESET, tegra_clk_vi_sensor),
|
||||
MUX("cilab", mux_pllp_pllc_clkm, CLK_SOURCE_CILAB, 144, 0, tegra_clk_cilab),
|
||||
MUX("cilcd", mux_pllp_pllc_clkm, CLK_SOURCE_CILCD, 145, 0, tegra_clk_cilcd),
|
||||
MUX("cile", mux_pllp_pllc_clkm, CLK_SOURCE_CILE, 146, 0, tegra_clk_cile),
|
||||
MUX("dsialp", mux_pllp_pllc_clkm, CLK_SOURCE_DSIALP, 147, 0, tegra_clk_dsialp),
|
||||
MUX("dsiblp", mux_pllp_pllc_clkm, CLK_SOURCE_DSIBLP, 148, 0, tegra_clk_dsiblp),
|
||||
MUX("tsensor", mux_pllp_pllc_clkm_clk32, CLK_SOURCE_TSENSOR, 100, TEGRA_PERIPH_ON_APB, tegra_clk_tsensor),
|
||||
MUX("actmon", mux_pllp_pllc_clk32_clkm, CLK_SOURCE_ACTMON, 119, 0, tegra_clk_actmon),
|
||||
MUX("dfll_ref", mux_pllp_clkm, CLK_SOURCE_DFLL_REF, 155, TEGRA_PERIPH_ON_APB, tegra_clk_dfll_ref),
|
||||
MUX("dfll_soc", mux_pllp_clkm, CLK_SOURCE_DFLL_SOC, 155, TEGRA_PERIPH_ON_APB, tegra_clk_dfll_soc),
|
||||
MUX("i2cslow", mux_pllp_pllc_clk32_clkm, CLK_SOURCE_I2CSLOW, 81, TEGRA_PERIPH_ON_APB, tegra_clk_i2cslow),
|
||||
MUX("sbc1", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC1, 41, TEGRA_PERIPH_ON_APB, tegra_clk_sbc1),
|
||||
MUX("sbc2", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC2, 44, TEGRA_PERIPH_ON_APB, tegra_clk_sbc2),
|
||||
MUX("sbc3", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC3, 46, TEGRA_PERIPH_ON_APB, tegra_clk_sbc3),
|
||||
MUX("sbc4", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC4, 68, TEGRA_PERIPH_ON_APB, tegra_clk_sbc4),
|
||||
MUX("sbc5", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC5, 104, TEGRA_PERIPH_ON_APB, tegra_clk_sbc5),
|
||||
MUX("sbc6", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC6, 105, TEGRA_PERIPH_ON_APB, tegra_clk_sbc6),
|
||||
MUX("cve", mux_pllp_plld_pllc_clkm, CLK_SOURCE_CVE, 49, 0, tegra_clk_cve),
|
||||
MUX("tvo", mux_pllp_plld_pllc_clkm, CLK_SOURCE_TVO, 49, 0, tegra_clk_tvo),
|
||||
MUX("tvdac", mux_pllp_plld_pllc_clkm, CLK_SOURCE_TVDAC, 53, 0, tegra_clk_tvdac),
|
||||
MUX("ndflash", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_NDFLASH, 13, TEGRA_PERIPH_ON_APB, tegra_clk_ndflash),
|
||||
MUX("ndspeed", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_NDSPEED, 80, TEGRA_PERIPH_ON_APB, tegra_clk_ndspeed),
|
||||
MUX("sata_oob", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SATA_OOB, 123, TEGRA_PERIPH_ON_APB, tegra_clk_sata_oob),
|
||||
MUX("sata", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SATA, 124, TEGRA_PERIPH_ON_APB, tegra_clk_sata),
|
||||
MUX("adx1", mux_plla_pllc_pllp_clkm, CLK_SOURCE_ADX1, 180, TEGRA_PERIPH_ON_APB, tegra_clk_adx1),
|
||||
MUX("amx1", mux_plla_pllc_pllp_clkm, CLK_SOURCE_AMX1, 185, TEGRA_PERIPH_ON_APB, tegra_clk_amx1),
|
||||
MUX("vi_sensor2", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_VI_SENSOR2, 20, TEGRA_PERIPH_NO_RESET, tegra_clk_vi_sensor2),
|
||||
MUX8("sbc1", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_SBC1, 41, TEGRA_PERIPH_ON_APB, tegra_clk_sbc1_8),
|
||||
MUX8("sbc2", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_SBC2, 44, TEGRA_PERIPH_ON_APB, tegra_clk_sbc2_8),
|
||||
MUX8("sbc3", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_SBC3, 46, TEGRA_PERIPH_ON_APB, tegra_clk_sbc3_8),
|
||||
MUX8("sbc4", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_SBC4, 68, TEGRA_PERIPH_ON_APB, tegra_clk_sbc4_8),
|
||||
MUX8("sbc5", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_SBC5, 104, TEGRA_PERIPH_ON_APB, tegra_clk_sbc5_8),
|
||||
MUX8("sbc6", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_SBC6, 105, TEGRA_PERIPH_ON_APB, tegra_clk_sbc6_8),
|
||||
MUX8("ndflash", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_NDFLASH, 13, TEGRA_PERIPH_ON_APB, tegra_clk_ndflash_8),
|
||||
MUX8("ndspeed", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_NDSPEED, 80, TEGRA_PERIPH_ON_APB, tegra_clk_ndspeed_8),
|
||||
MUX8("hdmi", mux_pllp_pllm_plld_plla_pllc_plld2_clkm, CLK_SOURCE_HDMI, 51, 0, tegra_clk_hdmi),
|
||||
MUX8("extern1", mux_plla_clk32_pllp_clkm_plle, CLK_SOURCE_EXTERN1, 120, 0, tegra_clk_extern1),
|
||||
MUX8("extern2", mux_plla_clk32_pllp_clkm_plle, CLK_SOURCE_EXTERN2, 121, 0, tegra_clk_extern2),
|
||||
MUX8("extern3", mux_plla_clk32_pllp_clkm_plle, CLK_SOURCE_EXTERN3, 122, 0, tegra_clk_extern3),
|
||||
MUX8("soc_therm", mux_pllm_pllc_pllp_plla, CLK_SOURCE_SOC_THERM, 78, TEGRA_PERIPH_ON_APB, tegra_clk_soc_therm),
|
||||
MUX8("vi_sensor", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_VI_SENSOR, 20, TEGRA_PERIPH_NO_RESET, tegra_clk_vi_sensor_8),
|
||||
MUX8("isp", mux_pllm_pllc_pllp_plla_clkm_pllc4, CLK_SOURCE_ISP, 23, TEGRA_PERIPH_ON_APB, tegra_clk_isp_8),
|
||||
MUX8("entropy", mux_pllp_clkm1, CLK_SOURCE_ENTROPY, 149, 0, tegra_clk_entropy),
|
||||
MUX8("hdmi_audio", mux_pllp3_pllc_clkm, CLK_SOURCE_HDMI_AUDIO, 176, TEGRA_PERIPH_NO_RESET, tegra_clk_hdmi_audio),
|
||||
MUX8("clk72mhz", mux_pllp3_pllc_clkm, CLK_SOURCE_CLK72MHZ, 177, TEGRA_PERIPH_NO_RESET, tegra_clk_clk72Mhz),
|
||||
MUX8_NOGATE_LOCK("sor0_lvds", mux_pllp_pllm_plld_plla_pllc_plld2_clkm, CLK_SOURCE_SOR0, tegra_clk_sor0_lvds, &sor0_lock),
|
||||
MUX_FLAGS("csite", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_CSITE, 73, TEGRA_PERIPH_ON_APB, tegra_clk_csite, CLK_IGNORE_UNUSED),
|
||||
NODIV("disp1", mux_pllp_pllm_plld_plla_pllc_plld2_clkm, CLK_SOURCE_DISP1, 29, 7, 27, 0, tegra_clk_disp1, NULL),
|
||||
NODIV("disp2", mux_pllp_pllm_plld_plla_pllc_plld2_clkm, CLK_SOURCE_DISP2, 29, 7, 26, 0, tegra_clk_disp2, NULL),
|
||||
NODIV("sor0", mux_clkm_plldp_sor0lvds, CLK_SOURCE_SOR0, 14, 3, 182, 0, tegra_clk_sor0, &sor0_lock),
|
||||
UART("uarta", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTA, 6, tegra_clk_uarta),
|
||||
UART("uartb", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTB, 7, tegra_clk_uartb),
|
||||
UART("uartc", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTC, 55, tegra_clk_uartc),
|
||||
UART("uartd", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTD, 65, tegra_clk_uartd),
|
||||
UART("uarte", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTE, 65, tegra_clk_uarte),
|
||||
XUSB("xusb_host_src", mux_clkm_pllp_pllc_pllre, CLK_SOURCE_XUSB_HOST_SRC, 143, TEGRA_PERIPH_ON_APB | TEGRA_PERIPH_NO_RESET, tegra_clk_xusb_host_src),
|
||||
XUSB("xusb_falcon_src", mux_clkm_pllp_pllc_pllre, CLK_SOURCE_XUSB_FALCON_SRC, 143, TEGRA_PERIPH_NO_RESET, tegra_clk_xusb_falcon_src),
|
||||
XUSB("xusb_fs_src", mux_clkm_48M_pllp_480M, CLK_SOURCE_XUSB_FS_SRC, 143, TEGRA_PERIPH_NO_RESET, tegra_clk_xusb_fs_src),
|
||||
XUSB("xusb_ss_src", mux_clkm_pllre_clk32_480M_pllc_ref, CLK_SOURCE_XUSB_SS_SRC, 143, TEGRA_PERIPH_NO_RESET, tegra_clk_xusb_ss_src),
|
||||
XUSB("xusb_dev_src", mux_clkm_pllp_pllc_pllre, CLK_SOURCE_XUSB_DEV_SRC, 95, TEGRA_PERIPH_ON_APB | TEGRA_PERIPH_NO_RESET, tegra_clk_xusb_dev_src),
|
||||
};
|
||||
|
||||
static struct tegra_periph_init_data gate_clks[] = {
|
||||
GATE("rtc", "clk_32k", 4, TEGRA_PERIPH_ON_APB | TEGRA_PERIPH_NO_RESET, tegra_clk_rtc, 0),
|
||||
GATE("timer", "clk_m", 5, 0, tegra_clk_timer, 0),
|
||||
GATE("isp", "clk_m", 23, 0, tegra_clk_isp, 0),
|
||||
GATE("vcp", "clk_m", 29, 0, tegra_clk_vcp, 0),
|
||||
GATE("apbdma", "clk_m", 34, 0, tegra_clk_apbdma, 0),
|
||||
GATE("kbc", "clk_32k", 36, TEGRA_PERIPH_ON_APB | TEGRA_PERIPH_NO_RESET, tegra_clk_kbc, 0),
|
||||
GATE("fuse", "clk_m", 39, TEGRA_PERIPH_ON_APB, tegra_clk_fuse, 0),
|
||||
GATE("fuse_burn", "clk_m", 39, TEGRA_PERIPH_ON_APB, tegra_clk_fuse_burn, 0),
|
||||
GATE("kfuse", "clk_m", 40, TEGRA_PERIPH_ON_APB, tegra_clk_kfuse, 0),
|
||||
GATE("apbif", "clk_m", 107, TEGRA_PERIPH_ON_APB, tegra_clk_apbif, 0),
|
||||
GATE("hda2hdmi", "clk_m", 128, TEGRA_PERIPH_ON_APB, tegra_clk_hda2hdmi, 0),
|
||||
GATE("bsea", "clk_m", 62, 0, tegra_clk_bsea, 0),
|
||||
GATE("bsev", "clk_m", 63, 0, tegra_clk_bsev, 0),
|
||||
GATE("mipi-cal", "clk_m", 56, 0, tegra_clk_mipi_cal, 0),
|
||||
GATE("usbd", "clk_m", 22, 0, tegra_clk_usbd, 0),
|
||||
GATE("usb2", "clk_m", 58, 0, tegra_clk_usb2, 0),
|
||||
GATE("usb3", "clk_m", 59, 0, tegra_clk_usb3, 0),
|
||||
GATE("csi", "pll_p_out3", 52, 0, tegra_clk_csi, 0),
|
||||
GATE("afi", "clk_m", 72, 0, tegra_clk_afi, 0),
|
||||
GATE("csus", "clk_m", 92, TEGRA_PERIPH_NO_RESET, tegra_clk_csus, 0),
|
||||
GATE("dds", "clk_m", 150, TEGRA_PERIPH_ON_APB, tegra_clk_dds, 0),
|
||||
GATE("dp2", "clk_m", 152, TEGRA_PERIPH_ON_APB, tegra_clk_dp2, 0),
|
||||
GATE("dtv", "clk_m", 79, TEGRA_PERIPH_ON_APB, tegra_clk_dtv, 0),
|
||||
GATE("xusb_host", "xusb_host_src", 89, 0, tegra_clk_xusb_host, 0),
|
||||
GATE("xusb_ss", "xusb_ss_src", 156, 0, tegra_clk_xusb_ss, 0),
|
||||
GATE("xusb_dev", "xusb_dev_src", 95, 0, tegra_clk_xusb_dev, 0),
|
||||
GATE("dsia", "dsia_mux", 48, 0, tegra_clk_dsia, 0),
|
||||
GATE("dsib", "dsib_mux", 82, 0, tegra_clk_dsib, 0),
|
||||
GATE("emc", "emc_mux", 57, 0, tegra_clk_emc, CLK_IGNORE_UNUSED),
|
||||
GATE("sata_cold", "clk_m", 129, TEGRA_PERIPH_ON_APB, tegra_clk_sata_cold, 0),
|
||||
GATE("ispb", "clk_m", 3, 0, tegra_clk_ispb, 0),
|
||||
GATE("vim2_clk", "clk_m", 11, 0, tegra_clk_vim2_clk, 0),
|
||||
GATE("pcie", "clk_m", 70, 0, tegra_clk_pcie, 0),
|
||||
GATE("dpaux", "clk_m", 181, 0, tegra_clk_dpaux, 0),
|
||||
GATE("gpu", "pll_ref", 184, 0, tegra_clk_gpu, 0),
|
||||
};
|
||||
|
||||
struct pll_out_data {
|
||||
char *div_name;
|
||||
char *pll_out_name;
|
||||
u32 offset;
|
||||
int clk_id;
|
||||
u8 div_shift;
|
||||
u8 div_flags;
|
||||
u8 rst_shift;
|
||||
spinlock_t *lock;
|
||||
};
|
||||
|
||||
#define PLL_OUT(_num, _offset, _div_shift, _div_flags, _rst_shift, _id) \
|
||||
{\
|
||||
.div_name = "pll_p_out" #_num "_div",\
|
||||
.pll_out_name = "pll_p_out" #_num,\
|
||||
.offset = _offset,\
|
||||
.div_shift = _div_shift,\
|
||||
.div_flags = _div_flags | TEGRA_DIVIDER_FIXED |\
|
||||
TEGRA_DIVIDER_ROUND_UP,\
|
||||
.rst_shift = _rst_shift,\
|
||||
.clk_id = tegra_clk_ ## _id,\
|
||||
.lock = &_offset ##_lock,\
|
||||
}
|
||||
|
||||
static struct pll_out_data pllp_out_clks[] = {
|
||||
PLL_OUT(1, PLLP_OUTA, 8, 0, 0, pll_p_out1),
|
||||
PLL_OUT(2, PLLP_OUTA, 24, 0, 16, pll_p_out2),
|
||||
PLL_OUT(2, PLLP_OUTA, 24, TEGRA_DIVIDER_INT, 16, pll_p_out2_int),
|
||||
PLL_OUT(3, PLLP_OUTB, 8, 0, 0, pll_p_out3),
|
||||
PLL_OUT(4, PLLP_OUTB, 24, 0, 16, pll_p_out4),
|
||||
PLL_OUT(5, PLLP_OUTC, 24, 0, 16, pll_p_out5),
|
||||
};
|
||||
|
||||
static void __init periph_clk_init(void __iomem *clk_base,
|
||||
struct tegra_clk *tegra_clks)
|
||||
{
|
||||
int i;
|
||||
struct clk *clk;
|
||||
struct clk **dt_clk;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(periph_clks); i++) {
|
||||
struct tegra_clk_periph_regs *bank;
|
||||
struct tegra_periph_init_data *data;
|
||||
|
||||
data = periph_clks + i;
|
||||
|
||||
dt_clk = tegra_lookup_dt_id(data->clk_id, tegra_clks);
|
||||
if (!dt_clk)
|
||||
continue;
|
||||
|
||||
bank = get_reg_bank(data->periph.gate.clk_num);
|
||||
if (!bank)
|
||||
continue;
|
||||
|
||||
data->periph.gate.regs = bank;
|
||||
clk = tegra_clk_register_periph(data->name,
|
||||
data->p.parent_names, data->num_parents,
|
||||
&data->periph, clk_base, data->offset,
|
||||
data->flags);
|
||||
*dt_clk = clk;
|
||||
}
|
||||
}
|
||||
|
||||
static void __init gate_clk_init(void __iomem *clk_base,
|
||||
struct tegra_clk *tegra_clks)
|
||||
{
|
||||
int i;
|
||||
struct clk *clk;
|
||||
struct clk **dt_clk;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(gate_clks); i++) {
|
||||
struct tegra_periph_init_data *data;
|
||||
|
||||
data = gate_clks + i;
|
||||
|
||||
dt_clk = tegra_lookup_dt_id(data->clk_id, tegra_clks);
|
||||
if (!dt_clk)
|
||||
continue;
|
||||
|
||||
clk = tegra_clk_register_periph_gate(data->name,
|
||||
data->p.parent_name, data->periph.gate.flags,
|
||||
clk_base, data->flags,
|
||||
data->periph.gate.clk_num,
|
||||
periph_clk_enb_refcnt);
|
||||
*dt_clk = clk;
|
||||
}
|
||||
}
|
||||
|
||||
static void __init init_pllp(void __iomem *clk_base, void __iomem *pmc_base,
|
||||
struct tegra_clk *tegra_clks,
|
||||
struct tegra_clk_pll_params *pll_params)
|
||||
{
|
||||
struct clk *clk;
|
||||
struct clk **dt_clk;
|
||||
int i;
|
||||
|
||||
dt_clk = tegra_lookup_dt_id(tegra_clk_pll_p, tegra_clks);
|
||||
if (dt_clk) {
|
||||
/* PLLP */
|
||||
clk = tegra_clk_register_pll("pll_p", "pll_ref", clk_base,
|
||||
pmc_base, 0, pll_params, NULL);
|
||||
clk_register_clkdev(clk, "pll_p", NULL);
|
||||
*dt_clk = clk;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pllp_out_clks); i++) {
|
||||
struct pll_out_data *data;
|
||||
|
||||
data = pllp_out_clks + i;
|
||||
|
||||
dt_clk = tegra_lookup_dt_id(data->clk_id, tegra_clks);
|
||||
if (!dt_clk)
|
||||
continue;
|
||||
|
||||
clk = tegra_clk_register_divider(data->div_name, "pll_p",
|
||||
clk_base + data->offset, 0, data->div_flags,
|
||||
data->div_shift, 8, 1, data->lock);
|
||||
clk = tegra_clk_register_pll_out(data->pll_out_name,
|
||||
data->div_name, clk_base + data->offset,
|
||||
data->rst_shift + 1, data->rst_shift,
|
||||
CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0,
|
||||
data->lock);
|
||||
*dt_clk = clk;
|
||||
}
|
||||
}
|
||||
|
||||
void __init tegra_periph_clk_init(void __iomem *clk_base,
|
||||
void __iomem *pmc_base, struct tegra_clk *tegra_clks,
|
||||
struct tegra_clk_pll_params *pll_params)
|
||||
{
|
||||
init_pllp(clk_base, pmc_base, tegra_clks, pll_params);
|
||||
periph_clk_init(clk_base, tegra_clks);
|
||||
gate_clk_init(clk_base, tegra_clks);
|
||||
}
|
132
drivers/clk/tegra/clk-tegra-pmc.c
Normal file
132
drivers/clk/tegra/clk-tegra-pmc.c
Normal file
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* Copyright (c) 2012, 2013, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/clk/tegra.h>
|
||||
|
||||
#include "clk.h"
|
||||
#include "clk-id.h"
|
||||
|
||||
#define PMC_CLK_OUT_CNTRL 0x1a8
|
||||
#define PMC_DPD_PADS_ORIDE 0x1c
|
||||
#define PMC_DPD_PADS_ORIDE_BLINK_ENB 20
|
||||
#define PMC_CTRL 0
|
||||
#define PMC_CTRL_BLINK_ENB 7
|
||||
#define PMC_BLINK_TIMER 0x40
|
||||
|
||||
struct pmc_clk_init_data {
|
||||
char *mux_name;
|
||||
char *gate_name;
|
||||
const char **parents;
|
||||
int num_parents;
|
||||
int mux_id;
|
||||
int gate_id;
|
||||
char *dev_name;
|
||||
u8 mux_shift;
|
||||
u8 gate_shift;
|
||||
};
|
||||
|
||||
#define PMC_CLK(_num, _mux_shift, _gate_shift)\
|
||||
{\
|
||||
.mux_name = "clk_out_" #_num "_mux",\
|
||||
.gate_name = "clk_out_" #_num,\
|
||||
.parents = clk_out ##_num ##_parents,\
|
||||
.num_parents = ARRAY_SIZE(clk_out ##_num ##_parents),\
|
||||
.mux_id = tegra_clk_clk_out_ ##_num ##_mux,\
|
||||
.gate_id = tegra_clk_clk_out_ ##_num,\
|
||||
.dev_name = "extern" #_num,\
|
||||
.mux_shift = _mux_shift,\
|
||||
.gate_shift = _gate_shift,\
|
||||
}
|
||||
|
||||
static DEFINE_SPINLOCK(clk_out_lock);
|
||||
|
||||
static const char *clk_out1_parents[] = { "clk_m", "clk_m_div2",
|
||||
"clk_m_div4", "extern1",
|
||||
};
|
||||
|
||||
static const char *clk_out2_parents[] = { "clk_m", "clk_m_div2",
|
||||
"clk_m_div4", "extern2",
|
||||
};
|
||||
|
||||
static const char *clk_out3_parents[] = { "clk_m", "clk_m_div2",
|
||||
"clk_m_div4", "extern3",
|
||||
};
|
||||
|
||||
static struct pmc_clk_init_data pmc_clks[] = {
|
||||
PMC_CLK(1, 6, 2),
|
||||
PMC_CLK(2, 14, 10),
|
||||
PMC_CLK(3, 22, 18),
|
||||
};
|
||||
|
||||
void __init tegra_pmc_clk_init(void __iomem *pmc_base,
|
||||
struct tegra_clk *tegra_clks)
|
||||
{
|
||||
struct clk *clk;
|
||||
struct clk **dt_clk;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pmc_clks); i++) {
|
||||
struct pmc_clk_init_data *data;
|
||||
|
||||
data = pmc_clks + i;
|
||||
|
||||
dt_clk = tegra_lookup_dt_id(data->mux_id, tegra_clks);
|
||||
if (!dt_clk)
|
||||
continue;
|
||||
|
||||
clk = clk_register_mux(NULL, data->mux_name, data->parents,
|
||||
data->num_parents, CLK_SET_RATE_NO_REPARENT,
|
||||
pmc_base + PMC_CLK_OUT_CNTRL, data->mux_shift,
|
||||
3, 0, &clk_out_lock);
|
||||
*dt_clk = clk;
|
||||
|
||||
|
||||
dt_clk = tegra_lookup_dt_id(data->gate_id, tegra_clks);
|
||||
if (!dt_clk)
|
||||
continue;
|
||||
|
||||
clk = clk_register_gate(NULL, data->gate_name, data->mux_name,
|
||||
0, pmc_base + PMC_CLK_OUT_CNTRL,
|
||||
data->gate_shift, 0, &clk_out_lock);
|
||||
*dt_clk = clk;
|
||||
clk_register_clkdev(clk, data->dev_name, data->gate_name);
|
||||
}
|
||||
|
||||
/* blink */
|
||||
writel_relaxed(0, pmc_base + PMC_BLINK_TIMER);
|
||||
clk = clk_register_gate(NULL, "blink_override", "clk_32k", 0,
|
||||
pmc_base + PMC_DPD_PADS_ORIDE,
|
||||
PMC_DPD_PADS_ORIDE_BLINK_ENB, 0, NULL);
|
||||
|
||||
dt_clk = tegra_lookup_dt_id(tegra_clk_blink, tegra_clks);
|
||||
if (!dt_clk)
|
||||
return;
|
||||
|
||||
clk = clk_register_gate(NULL, "blink", "blink_override", 0,
|
||||
pmc_base + PMC_CTRL,
|
||||
PMC_CTRL_BLINK_ENB, 0, NULL);
|
||||
clk_register_clkdev(clk, "blink", NULL);
|
||||
*dt_clk = clk;
|
||||
}
|
||||
|
149
drivers/clk/tegra/clk-tegra-super-gen4.c
Normal file
149
drivers/clk/tegra/clk-tegra-super-gen4.c
Normal file
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* Copyright (c) 2012, 2013, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/clk/tegra.h>
|
||||
|
||||
#include "clk.h"
|
||||
#include "clk-id.h"
|
||||
|
||||
#define PLLX_BASE 0xe0
|
||||
#define PLLX_MISC 0xe4
|
||||
#define PLLX_MISC2 0x514
|
||||
#define PLLX_MISC3 0x518
|
||||
|
||||
#define CCLKG_BURST_POLICY 0x368
|
||||
#define CCLKLP_BURST_POLICY 0x370
|
||||
#define SCLK_BURST_POLICY 0x028
|
||||
#define SYSTEM_CLK_RATE 0x030
|
||||
|
||||
static DEFINE_SPINLOCK(sysrate_lock);
|
||||
|
||||
static const char *sclk_parents[] = { "clk_m", "pll_c_out1", "pll_p_out4",
|
||||
"pll_p", "pll_p_out2", "unused",
|
||||
"clk_32k", "pll_m_out1" };
|
||||
|
||||
static const char *cclk_g_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m",
|
||||
"pll_p", "pll_p_out4", "unused",
|
||||
"unused", "pll_x" };
|
||||
|
||||
static const char *cclk_lp_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m",
|
||||
"pll_p", "pll_p_out4", "unused",
|
||||
"unused", "pll_x", "pll_x_out0" };
|
||||
|
||||
static void __init tegra_sclk_init(void __iomem *clk_base,
|
||||
struct tegra_clk *tegra_clks)
|
||||
{
|
||||
struct clk *clk;
|
||||
struct clk **dt_clk;
|
||||
|
||||
/* SCLK */
|
||||
dt_clk = tegra_lookup_dt_id(tegra_clk_sclk, tegra_clks);
|
||||
if (dt_clk) {
|
||||
clk = tegra_clk_register_super_mux("sclk", sclk_parents,
|
||||
ARRAY_SIZE(sclk_parents),
|
||||
CLK_SET_RATE_PARENT,
|
||||
clk_base + SCLK_BURST_POLICY,
|
||||
0, 4, 0, 0, NULL);
|
||||
*dt_clk = clk;
|
||||
}
|
||||
|
||||
/* HCLK */
|
||||
dt_clk = tegra_lookup_dt_id(tegra_clk_hclk, tegra_clks);
|
||||
if (dt_clk) {
|
||||
clk = clk_register_divider(NULL, "hclk_div", "sclk", 0,
|
||||
clk_base + SYSTEM_CLK_RATE, 4, 2, 0,
|
||||
&sysrate_lock);
|
||||
clk = clk_register_gate(NULL, "hclk", "hclk_div",
|
||||
CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
|
||||
clk_base + SYSTEM_CLK_RATE,
|
||||
7, CLK_GATE_SET_TO_DISABLE, &sysrate_lock);
|
||||
*dt_clk = clk;
|
||||
}
|
||||
|
||||
/* PCLK */
|
||||
dt_clk = tegra_lookup_dt_id(tegra_clk_pclk, tegra_clks);
|
||||
if (!dt_clk)
|
||||
return;
|
||||
|
||||
clk = clk_register_divider(NULL, "pclk_div", "hclk", 0,
|
||||
clk_base + SYSTEM_CLK_RATE, 0, 2, 0,
|
||||
&sysrate_lock);
|
||||
clk = clk_register_gate(NULL, "pclk", "pclk_div", CLK_SET_RATE_PARENT |
|
||||
CLK_IGNORE_UNUSED, clk_base + SYSTEM_CLK_RATE,
|
||||
3, CLK_GATE_SET_TO_DISABLE, &sysrate_lock);
|
||||
*dt_clk = clk;
|
||||
}
|
||||
|
||||
void __init tegra_super_clk_gen4_init(void __iomem *clk_base,
|
||||
void __iomem *pmc_base,
|
||||
struct tegra_clk *tegra_clks,
|
||||
struct tegra_clk_pll_params *params)
|
||||
{
|
||||
struct clk *clk;
|
||||
struct clk **dt_clk;
|
||||
|
||||
/* CCLKG */
|
||||
dt_clk = tegra_lookup_dt_id(tegra_clk_cclk_g, tegra_clks);
|
||||
if (dt_clk) {
|
||||
clk = tegra_clk_register_super_mux("cclk_g", cclk_g_parents,
|
||||
ARRAY_SIZE(cclk_g_parents),
|
||||
CLK_SET_RATE_PARENT,
|
||||
clk_base + CCLKG_BURST_POLICY,
|
||||
0, 4, 0, 0, NULL);
|
||||
*dt_clk = clk;
|
||||
}
|
||||
|
||||
/* CCLKLP */
|
||||
dt_clk = tegra_lookup_dt_id(tegra_clk_cclk_lp, tegra_clks);
|
||||
if (dt_clk) {
|
||||
clk = tegra_clk_register_super_mux("cclk_lp", cclk_lp_parents,
|
||||
ARRAY_SIZE(cclk_lp_parents),
|
||||
CLK_SET_RATE_PARENT,
|
||||
clk_base + CCLKLP_BURST_POLICY,
|
||||
0, 4, 8, 9, NULL);
|
||||
*dt_clk = clk;
|
||||
}
|
||||
|
||||
tegra_sclk_init(clk_base, tegra_clks);
|
||||
|
||||
#if defined(CONFIG_ARCH_TEGRA_114_SOC) || defined(CONFIG_ARCH_TEGRA_124_SOC)
|
||||
/* PLLX */
|
||||
dt_clk = tegra_lookup_dt_id(tegra_clk_pll_x, tegra_clks);
|
||||
if (!dt_clk)
|
||||
return;
|
||||
|
||||
clk = tegra_clk_register_pllxc("pll_x", "pll_ref", clk_base,
|
||||
pmc_base, CLK_IGNORE_UNUSED, params, NULL);
|
||||
*dt_clk = clk;
|
||||
|
||||
/* PLLX_OUT0 */
|
||||
|
||||
dt_clk = tegra_lookup_dt_id(tegra_clk_pll_x_out0, tegra_clks);
|
||||
if (!dt_clk)
|
||||
return;
|
||||
clk = clk_register_fixed_factor(NULL, "pll_x_out0", "pll_x",
|
||||
CLK_SET_RATE_PARENT, 1, 2);
|
||||
*dt_clk = clk;
|
||||
#endif
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load diff
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue