The clock framework changes for 3.15 look similar to past pull requests.
Mostly clock driver updates, more Device Tree support in the form of common functions useful across platforms and a handful of features and fixes to the framework core. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.14 (GNU/Linux) iQIcBAABAgAGBQJTPKLWAAoJEDqPOy9afJhJTJUP/32NJ6+g2/Ren3LNW2QFUAzj XAJ1PiuciuMFBI1ttErBwgpgtETj1qLQKakipNxoVQk0hN4Ymi6Dz23+7Vif0241 8uDgvMg70eeZlyUk2cc0huJzta2kCWQB7jOZT0oDTlzXA8lq3OiSJrc5ey/leVwW SM3NySvbN+t/bOaHW5z7oFtsqANCS/t3P0+cL9I+EgUtCJ4boqqI/a01dgZt4qp3 C68ar1Iy5ko6cFNzsjhmHBw1rz3ChQQhCdKDQsIgTbsgMXlI7AHD8CKizB9dxLpI dmM4HFprHlwKdNSsCwMltXT4ROhV6to1Jlo64dekvYbJzGsqR4OoRTUzUC549kOW OijFk7QDWMkCBvKA6pmCMpa3GuxRCnU8P8EtmiTra7tz6wwSFESKKEywG6r17/eO 9TU+apzknHYN//Mfx1ODfHGpXxqgZaJCAR8YGZ/sKFAQZSbJqxl7czqr26BmXDgJ FQxlxgYHGn2PnKr8aI8F35PZWZf2dOKDYImwdslmQXc122I8+qnHsruxLKdGxzQR VH33ezMP/IhTjcTLwDSmK9JleX5SxxmULRM5kFM+cDh3KJDpw0h/GZXo8XKFSyN4 8qxh5V+QmROzZ8cFFFa/QVXfNHxkAgVSofP/YovkYYMpVt0o7SBMpEXDrfePrmBD OdoXQ0ETAaitehRph1Aj =zk74 -----END PGP SIGNATURE----- Merge tag 'clk-for-linus-3.15' of git://git.linaro.org/people/mike.turquette/linux Pull clock framework changes from Mike Turquette: "The clock framework changes for 3.15 look similar to past pull requests. Mostly clock driver updates, more Device Tree support in the form of common functions useful across platforms and a handful of features and fixes to the framework core" * tag 'clk-for-linus-3.15' of git://git.linaro.org/people/mike.turquette/linux: (86 commits) clk: shmobile: fix setting paretn clock rate clk: shmobile: rcar-gen2: fix lb/sd0/sd1/sdh clock parent to pll1 clk: Fix minor errors in of_clk_init() function comments clk: reverse default clk provider initialization order in of_clk_init() clk: sirf: update copyright years to 2014 clk: mmp: try to use closer one when do round rate clk: mmp: fix the wrong calculation formula clk: mmp: fix wrong mask when calculate denominator clk: st: Adds quadfs clock binding clk: st: Adds clockgen-vcc and clockgen-mux clock binding clk: st: Adds clockgen clock binding clk: st: Adds divmux and prediv clock binding clk: st: Support for A9 MUX clocks clk: st: Support for ClockGenA9/DDR/GPU clk: st: Support for QUADFS inside ClockGenB/C/D/E/F clk: st: Support for VCC-mux and MUX clocks clk: st: Support for PLLs inside ClockGenA(s) clk: st: Support for DIVMUX and PreDiv Clocks clk: support hardware-specific debugfs entries clk: s2mps11: Use of_get_child_by_name ...
This commit is contained in:
commit
19bc2eec3c
74 changed files with 5881 additions and 732 deletions
|
@ -255,3 +255,37 @@ are sorted out.
|
|||
|
||||
To bypass this disabling, include "clk_ignore_unused" in the bootargs to the
|
||||
kernel.
|
||||
|
||||
Part 7 - Locking
|
||||
|
||||
The common clock framework uses two global locks, the prepare lock and the
|
||||
enable lock.
|
||||
|
||||
The enable lock is a spinlock and is held across calls to the .enable,
|
||||
.disable and .is_enabled operations. Those operations are thus not allowed to
|
||||
sleep, and calls to the clk_enable(), clk_disable() and clk_is_enabled() API
|
||||
functions are allowed in atomic context.
|
||||
|
||||
The prepare lock is a mutex and is held across calls to all other operations.
|
||||
All those operations are allowed to sleep, and calls to the corresponding API
|
||||
functions are not allowed in atomic context.
|
||||
|
||||
This effectively divides operations in two groups from a locking perspective.
|
||||
|
||||
Drivers don't need to manually protect resources shared between the operations
|
||||
of one group, regardless of whether those resources are shared by multiple
|
||||
clocks or not. However, access to resources that are shared between operations
|
||||
of the two groups needs to be protected by the drivers. An example of such a
|
||||
resource would be a register that controls both the clock rate and the clock
|
||||
enable/disable state.
|
||||
|
||||
The clock framework is reentrant, in that a driver is allowed to call clock
|
||||
framework functions from within its implementation of clock operations. This
|
||||
can for instance cause a .set_rate operation of one clock being called from
|
||||
within the .set_rate operation of another clock. This case must be considered
|
||||
in the driver implementations, but the code flow is usually controlled by the
|
||||
driver in that case.
|
||||
|
||||
Note that locking must also be considered when code outside of the common
|
||||
clock framework needs to access resources used by the clock operations. This
|
||||
is considered out of scope of this document.
|
||||
|
|
|
@ -30,3 +30,17 @@ Example:
|
|||
resume-offset = <0x308>;
|
||||
reboot-offset = <0x4>;
|
||||
};
|
||||
|
||||
PCTRL: Peripheral misc control register
|
||||
|
||||
Required Properties:
|
||||
- compatible: "hisilicon,pctrl"
|
||||
- reg: Address and size of pctrl.
|
||||
|
||||
Example:
|
||||
|
||||
/* for Hi3620 */
|
||||
pctrl: pctrl@fca09000 {
|
||||
compatible = "hisilicon,pctrl";
|
||||
reg = <0xfca09000 0x1000>;
|
||||
};
|
||||
|
|
|
@ -23,3 +23,8 @@ Optional properties:
|
|||
and the bit index.
|
||||
- div-reg : For "socfpga-gate-clk", div-reg contains the divider register, bit shift,
|
||||
and width.
|
||||
- clk-phase : For the sdmmc_clk, contains the value of the clock phase that controls
|
||||
the SDMMC CIU clock. The first value is the clk_sample(smpsel), and the second
|
||||
value is the cclk_in_drv(drvsel). The clk-phase is used to enable the correct
|
||||
hold/delay times that is needed for the SD/MMC CIU clock. The values of both
|
||||
can be 0-315 degrees, in 45 degree increments.
|
||||
|
|
|
@ -5,7 +5,7 @@ This binding uses the common clock binding[1].
|
|||
[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
|
||||
Required properties:
|
||||
- compatible : shall be "adi,axi-clkgen".
|
||||
- compatible : shall be "adi,axi-clkgen-1.00.a" or "adi,axi-clkgen-2.00.a".
|
||||
- #clock-cells : from common clock binding; Should always be set to 0.
|
||||
- reg : Address and length of the axi-clkgen register set.
|
||||
- clocks : Phandle and clock specifier for the parent clock.
|
||||
|
|
|
@ -44,6 +44,23 @@ For example:
|
|||
clocks by index. The names should reflect the clock output signal
|
||||
names for the device.
|
||||
|
||||
clock-indices: If the identifyng number for the clocks in the node
|
||||
is not linear from zero, then the this mapping allows
|
||||
the mapping of identifiers into the clock-output-names
|
||||
array.
|
||||
|
||||
For example, if we have two clocks <&oscillator 1> and <&oscillator 3>:
|
||||
|
||||
oscillator {
|
||||
compatible = "myclocktype";
|
||||
#clock-cells = <1>;
|
||||
clock-indices = <1>, <3>;
|
||||
clock-output-names = "clka", "clkb";
|
||||
}
|
||||
|
||||
This ensures we do not have any empty nodes in clock-output-names
|
||||
|
||||
|
||||
==Clock consumers==
|
||||
|
||||
Required properties:
|
||||
|
|
|
@ -7,6 +7,7 @@ Required Properties:
|
|||
|
||||
- compatible: should be one of the following.
|
||||
- "hisilicon,hi3620-clock" - controller compatible with Hi3620 SoC.
|
||||
- "hisilicon,hi3620-mmc-clock" - controller specific for Hi3620 mmc.
|
||||
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
Device Tree Clock bindings for arch-moxart
|
||||
|
||||
This binding uses the common clock binding[1].
|
||||
|
||||
[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
|
||||
MOXA ART SoCs allow to determine PLL output and APB frequencies
|
||||
by reading registers holding multiplier and divisor information.
|
||||
|
||||
|
||||
PLL:
|
||||
|
||||
Required properties:
|
||||
- compatible : Must be "moxa,moxart-pll-clock"
|
||||
- #clock-cells : Should be 0
|
||||
- reg : Should contain registers location and length
|
||||
- clocks : Should contain phandle + clock-specifier for the parent clock
|
||||
|
||||
Optional properties:
|
||||
- clock-output-names : Should contain clock name
|
||||
|
||||
|
||||
APB:
|
||||
|
||||
Required properties:
|
||||
- compatible : Must be "moxa,moxart-apb-clock"
|
||||
- #clock-cells : Should be 0
|
||||
- reg : Should contain registers location and length
|
||||
- clocks : Should contain phandle + clock-specifier for the parent clock
|
||||
|
||||
Optional properties:
|
||||
- clock-output-names : Should contain clock name
|
||||
|
||||
|
||||
For example:
|
||||
|
||||
clk_pll: clk_pll@98100000 {
|
||||
compatible = "moxa,moxart-pll-clock";
|
||||
#clock-cells = <0>;
|
||||
reg = <0x98100000 0x34>;
|
||||
};
|
||||
|
||||
clk_apb: clk_apb@98100000 {
|
||||
compatible = "moxa,moxart-apb-clock";
|
||||
#clock-cells = <0>;
|
||||
reg = <0x98100000 0x34>;
|
||||
clocks = <&clk_pll>;
|
||||
};
|
|
@ -11,6 +11,18 @@ The following is a list of provided IDs and clock names on Armada 370/XP:
|
|||
3 = hclk (DRAM control clock)
|
||||
4 = dramclk (DDR clock)
|
||||
|
||||
The following is a list of provided IDs and clock names on Armada 375:
|
||||
0 = tclk (Internal Bus clock)
|
||||
1 = cpuclk (CPU clock)
|
||||
2 = l2clk (L2 Cache clock)
|
||||
3 = ddrclk (DDR clock)
|
||||
|
||||
The following is a list of provided IDs and clock names on Armada 380/385:
|
||||
0 = tclk (Internal Bus clock)
|
||||
1 = cpuclk (CPU clock)
|
||||
2 = l2clk (L2 Cache clock)
|
||||
3 = ddrclk (DDR clock)
|
||||
|
||||
The following is a list of provided IDs and clock names on Kirkwood and Dove:
|
||||
0 = tclk (Internal Bus clock)
|
||||
1 = cpuclk (CPU0 clock)
|
||||
|
@ -20,6 +32,8 @@ The following is a list of provided IDs and clock names on Kirkwood and Dove:
|
|||
Required properties:
|
||||
- compatible : shall be one of the following:
|
||||
"marvell,armada-370-core-clock" - For Armada 370 SoC core clocks
|
||||
"marvell,armada-375-core-clock" - For Armada 375 SoC core clocks
|
||||
"marvell,armada-380-core-clock" - For Armada 380/385 SoC core clocks
|
||||
"marvell,armada-xp-core-clock" - For Armada XP SoC core clocks
|
||||
"marvell,dove-core-clock" - for Dove SoC core clocks
|
||||
"marvell,kirkwood-core-clock" - for Kirkwood SoC (except mv88f6180)
|
||||
|
|
|
@ -4,7 +4,10 @@ The following is a list of provided IDs and clock names on Armada 370/XP:
|
|||
0 = nand (NAND clock)
|
||||
|
||||
Required properties:
|
||||
- compatible : must be "marvell,armada-370-corediv-clock"
|
||||
- compatible : must be "marvell,armada-370-corediv-clock",
|
||||
"marvell,armada-375-corediv-clock",
|
||||
"marvell,armada-380-corediv-clock",
|
||||
|
||||
- reg : must be the register address of Core Divider control register
|
||||
- #clock-cells : from common clock binding; shall be set to 1
|
||||
- clocks : must be set to the parent's phandle
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
* Gated Clock bindings for Marvell EBU SoCs
|
||||
|
||||
Marvell Armada 370/XP, Dove and Kirkwood allow some peripheral clocks to be
|
||||
gated to save some power. The clock consumer should specify the desired clock
|
||||
by having the clock ID in its "clocks" phandle cell. The clock ID is directly
|
||||
mapped to the corresponding clock gating control bit in HW to ease manual clock
|
||||
Marvell Armada 370/375/380/385/XP, Dove and Kirkwood allow some
|
||||
peripheral clocks to be gated to save some power. The clock consumer
|
||||
should specify the desired clock by having the clock ID in its
|
||||
"clocks" phandle cell. The clock ID is directly mapped to the
|
||||
corresponding clock gating control bit in HW to ease manual clock
|
||||
lookup in datasheet.
|
||||
|
||||
The following is a list of provided IDs for Armada 370:
|
||||
|
@ -22,6 +23,60 @@ ID Clock Peripheral
|
|||
28 ddr DDR Cntrl
|
||||
30 sata1 SATA Host 0
|
||||
|
||||
The following is a list of provided IDs for Armada 375:
|
||||
ID Clock Peripheral
|
||||
-----------------------------------
|
||||
2 mu Management Unit
|
||||
3 pp Packet Processor
|
||||
4 ptp PTP
|
||||
5 pex0 PCIe 0 Clock out
|
||||
6 pex1 PCIe 1 Clock out
|
||||
8 audio Audio Cntrl
|
||||
11 nd_clk Nand Flash Cntrl
|
||||
14 sata0_link SATA 0 Link
|
||||
15 sata0_core SATA 0 Core
|
||||
16 usb3 USB3 Host
|
||||
17 sdio SDHCI Host
|
||||
18 usb USB Host
|
||||
19 gop Gigabit Ethernet MAC
|
||||
20 sata1_link SATA 1 Link
|
||||
21 sata1_core SATA 1 Core
|
||||
22 xor0 XOR DMA 0
|
||||
23 xor1 XOR DMA 0
|
||||
24 copro Coprocessor
|
||||
25 tdm Time Division Mplx
|
||||
28 crypto0_enc Cryptographic Unit Port 0 Encryption
|
||||
29 crypto0_core Cryptographic Unit Port 0 Core
|
||||
30 crypto1_enc Cryptographic Unit Port 1 Encryption
|
||||
31 crypto1_core Cryptographic Unit Port 1 Core
|
||||
|
||||
The following is a list of provided IDs for Armada 380/385:
|
||||
ID Clock Peripheral
|
||||
-----------------------------------
|
||||
0 audio Audio
|
||||
2 ge2 Gigabit Ethernet 2
|
||||
3 ge1 Gigabit Ethernet 1
|
||||
4 ge0 Gigabit Ethernet 0
|
||||
5 pex1 PCIe 1
|
||||
6 pex2 PCIe 2
|
||||
7 pex3 PCIe 3
|
||||
8 pex0 PCIe 0
|
||||
9 usb3h0 USB3 Host 0
|
||||
10 usb3h1 USB3 Host 1
|
||||
11 usb3d USB3 Device
|
||||
13 bm Buffer Management
|
||||
14 crypto0z Cryptographic 0 Z
|
||||
15 sata0 SATA 0
|
||||
16 crypto1z Cryptographic 1 Z
|
||||
17 sdio SDIO
|
||||
18 usb2 USB 2
|
||||
21 crypto1 Cryptographic 1
|
||||
22 xor0 XOR 0
|
||||
23 crypto0 Cryptographic 0
|
||||
25 tdm Time Division Multiplexing
|
||||
28 xor1 XOR 1
|
||||
30 sata1 SATA 1
|
||||
|
||||
The following is a list of provided IDs for Armada XP:
|
||||
ID Clock Peripheral
|
||||
-----------------------------------
|
||||
|
@ -95,6 +150,8 @@ ID Clock Peripheral
|
|||
Required properties:
|
||||
- compatible : shall be one of the following:
|
||||
"marvell,armada-370-gating-clock" - for Armada 370 SoC clock gating
|
||||
"marvell,armada-375-gating-clock" - for Armada 375 SoC clock gating
|
||||
"marvell,armada-380-gating-clock" - for Armada 380/385 SoC clock gating
|
||||
"marvell,armada-xp-gating-clock" - for Armada XP SoC clock gating
|
||||
"marvell,dove-gating-clock" - for Dove SoC clock gating
|
||||
"marvell,kirkwood-gating-clock" - for Kirkwood SoC clock gating
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
* Renesas RZ Clock Pulse Generator (CPG)
|
||||
|
||||
The CPG generates core clocks for the RZ SoCs. It includes the PLL, variable
|
||||
CPU and GPU clocks, and several fixed ratio dividers.
|
||||
|
||||
Required Properties:
|
||||
|
||||
- compatible: Must be one of
|
||||
- "renesas,r7s72100-cpg-clocks" for the r7s72100 CPG
|
||||
- "renesas,rz-cpg-clocks" for the generic RZ CPG
|
||||
- reg: Base address and length of the memory resource used by the CPG
|
||||
- clocks: References to possible parent clocks. Order must match clock modes
|
||||
in the datasheet. For the r7s72100, this is extal, usb_x1.
|
||||
- #clock-cells: Must be 1
|
||||
- clock-output-names: The names of the clocks. Supported clocks are "pll",
|
||||
"i", and "g"
|
||||
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
cpg_clocks: cpg_clocks@fcfe0000 {
|
||||
#clock-cells = <1>;
|
||||
compatible = "renesas,r7s72100-cpg-clocks",
|
||||
"renesas,rz-cpg-clocks";
|
||||
reg = <0xfcfe0000 0x18>;
|
||||
clocks = <&extal_clk>, <&usb_x1_clk>;
|
||||
clock-output-names = "pll", "i", "g";
|
||||
};
|
|
@ -0,0 +1,49 @@
|
|||
Binding for a ST divider and multiplexer clock driver.
|
||||
|
||||
This binding uses the common clock binding[1].
|
||||
Base address is located to the parent node. See clock binding[2]
|
||||
|
||||
[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
[2] Documentation/devicetree/bindings/clock/st/st,clkgen.txt
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : shall be:
|
||||
"st,clkgena-divmux-c65-hs", "st,clkgena-divmux"
|
||||
"st,clkgena-divmux-c65-ls", "st,clkgena-divmux"
|
||||
"st,clkgena-divmux-c32-odf0", "st,clkgena-divmux"
|
||||
"st,clkgena-divmux-c32-odf1", "st,clkgena-divmux"
|
||||
"st,clkgena-divmux-c32-odf2", "st,clkgena-divmux"
|
||||
"st,clkgena-divmux-c32-odf3", "st,clkgena-divmux"
|
||||
|
||||
- #clock-cells : From common clock binding; shall be set to 1.
|
||||
|
||||
- clocks : From common clock binding
|
||||
|
||||
- clock-output-names : From common clock binding.
|
||||
|
||||
Example:
|
||||
|
||||
clockgenA@fd345000 {
|
||||
reg = <0xfd345000 0xb50>;
|
||||
|
||||
CLK_M_A1_DIV1: CLK_M_A1_DIV1 {
|
||||
#clock-cells = <1>;
|
||||
compatible = "st,clkgena-divmux-c32-odf1",
|
||||
"st,clkgena-divmux";
|
||||
|
||||
clocks = <&CLK_M_A1_OSC_PREDIV>,
|
||||
<&CLK_M_A1_PLL0 1>, /* PLL0 PHI1 */
|
||||
<&CLK_M_A1_PLL1 1>; /* PLL1 PHI1 */
|
||||
|
||||
clock-output-names = "CLK_M_RX_ICN_TS",
|
||||
"CLK_M_RX_ICN_VDP_0",
|
||||
"", /* Unused */
|
||||
"CLK_M_PRV_T1_BUS",
|
||||
"CLK_M_ICN_REG_12",
|
||||
"CLK_M_ICN_REG_10",
|
||||
"", /* Unused */
|
||||
"CLK_M_ICN_ST231";
|
||||
};
|
||||
};
|
||||
|
36
Documentation/devicetree/bindings/clock/st/st,clkgen-mux.txt
Normal file
36
Documentation/devicetree/bindings/clock/st/st,clkgen-mux.txt
Normal file
|
@ -0,0 +1,36 @@
|
|||
Binding for a ST multiplexed clock driver.
|
||||
|
||||
This binding supports only simple indexed multiplexers, it does not
|
||||
support table based parent index to hardware value translations.
|
||||
|
||||
This binding uses the common clock binding[1].
|
||||
|
||||
[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : shall be:
|
||||
"st,stih416-clkgenc-vcc-hd", "st,clkgen-mux"
|
||||
"st,stih416-clkgenf-vcc-fvdp", "st,clkgen-mux"
|
||||
"st,stih416-clkgenf-vcc-hva", "st,clkgen-mux"
|
||||
"st,stih416-clkgenf-vcc-hd", "st,clkgen-mux"
|
||||
"st,stih416-clkgenf-vcc-sd", "st,clkgen-mux"
|
||||
"st,stih415-clkgen-a9-mux", "st,clkgen-mux"
|
||||
"st,stih416-clkgen-a9-mux", "st,clkgen-mux"
|
||||
|
||||
|
||||
- #clock-cells : from common clock binding; shall be set to 0.
|
||||
|
||||
- reg : A Base address and length of the register set.
|
||||
|
||||
- clocks : from common clock binding
|
||||
|
||||
Example:
|
||||
|
||||
CLK_M_HVA: CLK_M_HVA {
|
||||
#clock-cells = <0>;
|
||||
compatible = "st,stih416-clkgenf-vcc-hva", "st,clkgen-mux";
|
||||
reg = <0xfd690868 4>;
|
||||
|
||||
clocks = <&CLOCKGEN_F 1>, <&CLK_M_A1_DIV0 3>;
|
||||
};
|
48
Documentation/devicetree/bindings/clock/st/st,clkgen-pll.txt
Normal file
48
Documentation/devicetree/bindings/clock/st/st,clkgen-pll.txt
Normal file
|
@ -0,0 +1,48 @@
|
|||
Binding for a ST pll clock driver.
|
||||
|
||||
This binding uses the common clock binding[1].
|
||||
Base address is located to the parent node. See clock binding[2]
|
||||
|
||||
[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
[2] Documentation/devicetree/bindings/clock/st/st,clkgen.txt
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : shall be:
|
||||
"st,clkgena-prediv-c65", "st,clkgena-prediv"
|
||||
"st,clkgena-prediv-c32", "st,clkgena-prediv"
|
||||
|
||||
"st,clkgena-plls-c65"
|
||||
"st,plls-c32-a1x-0", "st,clkgen-plls-c32"
|
||||
"st,plls-c32-a1x-1", "st,clkgen-plls-c32"
|
||||
"st,stih415-plls-c32-a9", "st,clkgen-plls-c32"
|
||||
"st,stih415-plls-c32-ddr", "st,clkgen-plls-c32"
|
||||
"st,stih416-plls-c32-a9", "st,clkgen-plls-c32"
|
||||
"st,stih416-plls-c32-ddr", "st,clkgen-plls-c32"
|
||||
|
||||
"st,stih415-gpu-pll-c32", "st,clkgengpu-pll-c32"
|
||||
"st,stih416-gpu-pll-c32", "st,clkgengpu-pll-c32"
|
||||
|
||||
|
||||
- #clock-cells : From common clock binding; shall be set to 1.
|
||||
|
||||
- clocks : From common clock binding
|
||||
|
||||
- clock-output-names : From common clock binding.
|
||||
|
||||
Example:
|
||||
|
||||
clockgenA@fee62000 {
|
||||
reg = <0xfee62000 0xb48>;
|
||||
|
||||
CLK_S_A0_PLL: CLK_S_A0_PLL {
|
||||
#clock-cells = <1>;
|
||||
compatible = "st,clkgena-plls-c65";
|
||||
|
||||
clocks = <&CLK_SYSIN>;
|
||||
|
||||
clock-output-names = "CLK_S_A0_PLL0_HS",
|
||||
"CLK_S_A0_PLL0_LS",
|
||||
"CLK_S_A0_PLL1";
|
||||
};
|
||||
};
|
|
@ -0,0 +1,36 @@
|
|||
Binding for a ST pre-divider clock driver.
|
||||
|
||||
This binding uses the common clock binding[1].
|
||||
Base address is located to the parent node. See clock binding[2]
|
||||
|
||||
[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
[2] Documentation/devicetree/bindings/clock/st/st,clkgen.txt
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : shall be:
|
||||
"st,clkgena-prediv-c65", "st,clkgena-prediv"
|
||||
"st,clkgena-prediv-c32", "st,clkgena-prediv"
|
||||
|
||||
- #clock-cells : From common clock binding; shall be set to 0.
|
||||
|
||||
- clocks : From common clock binding
|
||||
|
||||
- clock-output-names : From common clock binding.
|
||||
|
||||
Example:
|
||||
|
||||
clockgenA@fd345000 {
|
||||
reg = <0xfd345000 0xb50>;
|
||||
|
||||
CLK_M_A2_OSC_PREDIV: CLK_M_A2_OSC_PREDIV {
|
||||
#clock-cells = <0>;
|
||||
compatible = "st,clkgena-prediv-c32",
|
||||
"st,clkgena-prediv";
|
||||
|
||||
clocks = <&CLK_SYSIN>;
|
||||
|
||||
clock-output-names = "CLK_M_A2_OSC_PREDIV";
|
||||
};
|
||||
};
|
||||
|
53
Documentation/devicetree/bindings/clock/st/st,clkgen-vcc.txt
Normal file
53
Documentation/devicetree/bindings/clock/st/st,clkgen-vcc.txt
Normal file
|
@ -0,0 +1,53 @@
|
|||
Binding for a type of STMicroelectronics clock crossbar (VCC).
|
||||
|
||||
The crossbar can take up to 4 input clocks and control up to 16
|
||||
output clocks. Not all inputs or outputs have to be in use in a
|
||||
particular instantiation. Each output can be individually enabled,
|
||||
select any of the input clocks and apply a divide (by 1,2,4 or 8) to
|
||||
that selected clock.
|
||||
|
||||
This binding uses the common clock binding[1].
|
||||
|
||||
[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : shall be:
|
||||
"st,stih416-clkgenc", "st,vcc"
|
||||
"st,stih416-clkgenf", "st,vcc"
|
||||
|
||||
- #clock-cells : from common clock binding; shall be set to 1.
|
||||
|
||||
- reg : A Base address and length of the register set.
|
||||
|
||||
- clocks : from common clock binding
|
||||
|
||||
- clock-output-names : From common clock binding. The block has 16
|
||||
clock outputs but not all of them in a specific instance
|
||||
have to be used in the SoC. If a clock name is left as
|
||||
an empty string then no clock will be created for the
|
||||
output associated with that string index. If fewer than
|
||||
16 strings are provided then no clocks will be created
|
||||
for the remaining outputs.
|
||||
|
||||
Example:
|
||||
|
||||
CLOCKGEN_C_VCC: CLOCKGEN_C_VCC {
|
||||
#clock-cells = <1>;
|
||||
compatible = "st,stih416-clkgenc", "st,clkgen-vcc";
|
||||
reg = <0xfe8308ac 12>;
|
||||
|
||||
clocks = <&CLK_S_VCC_HD>, <&CLOCKGEN_C 1>,
|
||||
<&CLK_S_TMDS_FROMPHY>, <&CLOCKGEN_C 2>;
|
||||
|
||||
clock-output-names =
|
||||
"CLK_S_PIX_HDMI", "CLK_S_PIX_DVO",
|
||||
"CLK_S_OUT_DVO", "CLK_S_PIX_HD",
|
||||
"CLK_S_HDDAC", "CLK_S_DENC",
|
||||
"CLK_S_SDDAC", "CLK_S_PIX_MAIN",
|
||||
"CLK_S_PIX_AUX", "CLK_S_STFE_FRC_0",
|
||||
"CLK_S_REF_MCRU", "CLK_S_SLAVE_MCRU",
|
||||
"CLK_S_TMDS_HDMI", "CLK_S_HDMI_REJECT_PLL",
|
||||
"CLK_S_THSENS";
|
||||
};
|
||||
|
83
Documentation/devicetree/bindings/clock/st/st,clkgen.txt
Normal file
83
Documentation/devicetree/bindings/clock/st/st,clkgen.txt
Normal file
|
@ -0,0 +1,83 @@
|
|||
Binding for a Clockgen hardware block found on
|
||||
certain STMicroelectronics consumer electronics SoC devices.
|
||||
|
||||
A Clockgen node can contain pll, diviser or multiplexer nodes.
|
||||
|
||||
We will find only the base address of the Clockgen, this base
|
||||
address is common of all subnode.
|
||||
|
||||
clockgen_node {
|
||||
reg = <>;
|
||||
|
||||
pll_node {
|
||||
...
|
||||
};
|
||||
|
||||
prediv_node {
|
||||
...
|
||||
};
|
||||
|
||||
divmux_node {
|
||||
...
|
||||
};
|
||||
|
||||
quadfs_node {
|
||||
...
|
||||
};
|
||||
...
|
||||
};
|
||||
|
||||
This binding uses the common clock binding[1].
|
||||
Each subnode should use the binding discribe in [2]..[4]
|
||||
|
||||
[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
[2] Documentation/devicetree/bindings/clock/st,quadfs.txt
|
||||
[3] Documentation/devicetree/bindings/clock/st,quadfs.txt
|
||||
[4] Documentation/devicetree/bindings/clock/st,quadfs.txt
|
||||
|
||||
Required properties:
|
||||
- reg : A Base address and length of the register set.
|
||||
|
||||
Example:
|
||||
|
||||
clockgenA@fee62000 {
|
||||
|
||||
reg = <0xfee62000 0xb48>;
|
||||
|
||||
CLK_S_A0_PLL: CLK_S_A0_PLL {
|
||||
#clock-cells = <1>;
|
||||
compatible = "st,clkgena-plls-c65";
|
||||
|
||||
clocks = <&CLK_SYSIN>;
|
||||
|
||||
clock-output-names = "CLK_S_A0_PLL0_HS",
|
||||
"CLK_S_A0_PLL0_LS",
|
||||
"CLK_S_A0_PLL1";
|
||||
};
|
||||
|
||||
CLK_S_A0_OSC_PREDIV: CLK_S_A0_OSC_PREDIV {
|
||||
#clock-cells = <0>;
|
||||
compatible = "st,clkgena-prediv-c65",
|
||||
"st,clkgena-prediv";
|
||||
|
||||
clocks = <&CLK_SYSIN>;
|
||||
|
||||
clock-output-names = "CLK_S_A0_OSC_PREDIV";
|
||||
};
|
||||
|
||||
CLK_S_A0_HS: CLK_S_A0_HS {
|
||||
#clock-cells = <1>;
|
||||
compatible = "st,clkgena-divmux-c65-hs",
|
||||
"st,clkgena-divmux";
|
||||
|
||||
clocks = <&CLK_S_A0_OSC_PREDIV>,
|
||||
<&CLK_S_A0_PLL 0>, /* PLL0 HS */
|
||||
<&CLK_S_A0_PLL 2>; /* PLL1 */
|
||||
|
||||
clock-output-names = "CLK_S_FDMA_0",
|
||||
"CLK_S_FDMA_1",
|
||||
""; /* CLK_S_JIT_SENSE */
|
||||
/* Fourth output unused */
|
||||
};
|
||||
};
|
||||
|
45
Documentation/devicetree/bindings/clock/st/st,quadfs.txt
Normal file
45
Documentation/devicetree/bindings/clock/st/st,quadfs.txt
Normal file
|
@ -0,0 +1,45 @@
|
|||
Binding for a type of quad channel digital frequency synthesizer found on
|
||||
certain STMicroelectronics consumer electronics SoC devices.
|
||||
|
||||
This version contains a programmable PLL which can generate up to 216, 432
|
||||
or 660MHz (from a 30MHz oscillator input) as the input to the digital
|
||||
synthesizers.
|
||||
|
||||
This binding uses the common clock binding[1].
|
||||
|
||||
[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
|
||||
Required properties:
|
||||
- compatible : shall be:
|
||||
"st,stih416-quadfs216", "st,quadfs"
|
||||
"st,stih416-quadfs432", "st,quadfs"
|
||||
"st,stih416-quadfs660-E", "st,quadfs"
|
||||
"st,stih416-quadfs660-F", "st,quadfs"
|
||||
|
||||
- #clock-cells : from common clock binding; shall be set to 1.
|
||||
|
||||
- reg : A Base address and length of the register set.
|
||||
|
||||
- clocks : from common clock binding
|
||||
|
||||
- clock-output-names : From common clock binding. The block has 4
|
||||
clock outputs but not all of them in a specific instance
|
||||
have to be used in the SoC. If a clock name is left as
|
||||
an empty string then no clock will be created for the
|
||||
output associated with that string index. If fewer than
|
||||
4 strings are provided then no clocks will be created
|
||||
for the remaining outputs.
|
||||
|
||||
Example:
|
||||
|
||||
CLOCKGEN_E: CLOCKGEN_E {
|
||||
#clock-cells = <1>;
|
||||
compatible = "st,stih416-quadfs660-E", "st,quadfs";
|
||||
reg = <0xfd3208bc 0xB0>;
|
||||
|
||||
clocks = <&CLK_SYSIN>;
|
||||
clock-output-names = "CLK_M_PIX_MDTP_0",
|
||||
"CLK_M_PIX_MDTP_1",
|
||||
"CLK_M_PIX_MDTP_2",
|
||||
"CLK_M_MPELPC";
|
||||
};
|
|
@ -6,37 +6,41 @@ 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 and PLL4
|
||||
"allwinner,sun4i-a10-osc-clk" - for a gatable oscillator
|
||||
"allwinner,sun4i-a10-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
|
||||
"allwinner,sun4i-ahb-clk" - for the AHB clock
|
||||
"allwinner,sun4i-ahb-gates-clk" - for the AHB gates on A10
|
||||
"allwinner,sun4i-a10-pll5-clk" - for the PLL5 clock
|
||||
"allwinner,sun4i-a10-pll6-clk" - for the PLL6 clock
|
||||
"allwinner,sun6i-a31-pll6-clk" - for the PLL6 clock on A31
|
||||
"allwinner,sun4i-a10-cpu-clk" - for the CPU multiplexer clock
|
||||
"allwinner,sun4i-a10-axi-clk" - for the AXI clock
|
||||
"allwinner,sun4i-a10-axi-gates-clk" - for the AXI gates
|
||||
"allwinner,sun4i-a10-ahb-clk" - for the AHB clock
|
||||
"allwinner,sun4i-a10-ahb-gates-clk" - for the AHB gates on A10
|
||||
"allwinner,sun5i-a13-ahb-gates-clk" - for the AHB gates on A13
|
||||
"allwinner,sun5i-a10s-ahb-gates-clk" - for the AHB gates on A10s
|
||||
"allwinner,sun7i-a20-ahb-gates-clk" - for the AHB gates on A20
|
||||
"allwinner,sun6i-a31-ahb1-mux-clk" - for the AHB1 multiplexer on A31
|
||||
"allwinner,sun6i-a31-ahb1-gates-clk" - for the AHB1 gates on A31
|
||||
"allwinner,sun4i-apb0-clk" - for the APB0 clock
|
||||
"allwinner,sun4i-apb0-gates-clk" - for the APB0 gates on A10
|
||||
"allwinner,sun4i-a10-apb0-clk" - for the APB0 clock
|
||||
"allwinner,sun4i-a10-apb0-gates-clk" - for the APB0 gates on A10
|
||||
"allwinner,sun5i-a13-apb0-gates-clk" - for the APB0 gates on A13
|
||||
"allwinner,sun5i-a10s-apb0-gates-clk" - for the APB0 gates on A10s
|
||||
"allwinner,sun7i-a20-apb0-gates-clk" - for the APB0 gates on A20
|
||||
"allwinner,sun4i-apb1-clk" - for the APB1 clock
|
||||
"allwinner,sun4i-apb1-mux-clk" - for the APB1 clock muxing
|
||||
"allwinner,sun4i-apb1-gates-clk" - for the APB1 gates on A10
|
||||
"allwinner,sun4i-a10-apb1-clk" - for the APB1 clock
|
||||
"allwinner,sun4i-a10-apb1-mux-clk" - for the APB1 clock muxing
|
||||
"allwinner,sun4i-a10-apb1-gates-clk" - for the APB1 gates on A10
|
||||
"allwinner,sun5i-a13-apb1-gates-clk" - for the APB1 gates on A13
|
||||
"allwinner,sun5i-a10s-apb1-gates-clk" - for the APB1 gates on A10s
|
||||
"allwinner,sun6i-a31-apb1-gates-clk" - for the APB1 gates on A31
|
||||
"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,sun4i-a10-mod0-clk" - for the module 0 family of clocks
|
||||
"allwinner,sun7i-a20-out-clk" - for the external output clocks
|
||||
"allwinner,sun7i-a20-gmac-clk" - for the GMAC clock module on A20/A31
|
||||
"allwinner,sun4i-a10-usb-clk" - for usb gates + resets on A10 / A20
|
||||
"allwinner,sun5i-a13-usb-clk" - for usb gates + resets on A13
|
||||
|
||||
Required properties for all clocks:
|
||||
- reg : shall be the control register address for the clock.
|
||||
|
@ -44,10 +48,17 @@ Required properties for all clocks:
|
|||
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
|
||||
"allwinner,*-gates-clk", "allwinner,sun4i-pll5-clk" and
|
||||
"allwinner,sun4i-pll6-clk" where it shall be set to 1
|
||||
- clock-output-names : shall be the corresponding names of the outputs.
|
||||
If the clock module only has one output, the name shall be the
|
||||
module name.
|
||||
|
||||
Additionally, "allwinner,*-gates-clk" clocks require:
|
||||
- clock-output-names : the corresponding gate names that the clock controls
|
||||
And "allwinner,*-usb-clk" clocks also require:
|
||||
- reset-cells : shall be set to 1
|
||||
|
||||
For "allwinner,sun7i-a20-gmac-clk", the parent clocks shall be fixed rate
|
||||
dummy clocks at 25 MHz and 125 MHz, respectively. See example.
|
||||
|
||||
Clock consumers should specify the desired clocks they use with a
|
||||
"clocks" phandle cell. Consumers that are using a gated clock should
|
||||
|
@ -56,23 +67,68 @@ offset of the bit controlling this particular gate in the register.
|
|||
|
||||
For example:
|
||||
|
||||
osc24M: osc24M@01c20050 {
|
||||
osc24M: clk@01c20050 {
|
||||
#clock-cells = <0>;
|
||||
compatible = "allwinner,sun4i-osc-clk";
|
||||
compatible = "allwinner,sun4i-a10-osc-clk";
|
||||
reg = <0x01c20050 0x4>;
|
||||
clocks = <&osc24M_fixed>;
|
||||
clock-output-names = "osc24M";
|
||||
};
|
||||
|
||||
pll1: pll1@01c20000 {
|
||||
pll1: clk@01c20000 {
|
||||
#clock-cells = <0>;
|
||||
compatible = "allwinner,sun4i-pll1-clk";
|
||||
compatible = "allwinner,sun4i-a10-pll1-clk";
|
||||
reg = <0x01c20000 0x4>;
|
||||
clocks = <&osc24M>;
|
||||
clock-output-names = "pll1";
|
||||
};
|
||||
|
||||
pll5: clk@01c20020 {
|
||||
#clock-cells = <1>;
|
||||
compatible = "allwinner,sun4i-pll5-clk";
|
||||
reg = <0x01c20020 0x4>;
|
||||
clocks = <&osc24M>;
|
||||
clock-output-names = "pll5_ddr", "pll5_other";
|
||||
};
|
||||
|
||||
cpu: cpu@01c20054 {
|
||||
#clock-cells = <0>;
|
||||
compatible = "allwinner,sun4i-cpu-clk";
|
||||
compatible = "allwinner,sun4i-a10-cpu-clk";
|
||||
reg = <0x01c20054 0x4>;
|
||||
clocks = <&osc32k>, <&osc24M>, <&pll1>;
|
||||
clock-output-names = "cpu";
|
||||
};
|
||||
|
||||
mmc0_clk: clk@01c20088 {
|
||||
#clock-cells = <0>;
|
||||
compatible = "allwinner,sun4i-mod0-clk";
|
||||
reg = <0x01c20088 0x4>;
|
||||
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
|
||||
clock-output-names = "mmc0";
|
||||
};
|
||||
|
||||
mii_phy_tx_clk: clk@2 {
|
||||
#clock-cells = <0>;
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <25000000>;
|
||||
clock-output-names = "mii_phy_tx";
|
||||
};
|
||||
|
||||
gmac_int_tx_clk: clk@3 {
|
||||
#clock-cells = <0>;
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <125000000>;
|
||||
clock-output-names = "gmac_int_tx";
|
||||
};
|
||||
|
||||
gmac_clk: clk@01c20164 {
|
||||
#clock-cells = <0>;
|
||||
compatible = "allwinner,sun7i-a20-gmac-clk";
|
||||
reg = <0x01c20164 0x4>;
|
||||
/*
|
||||
* The first clock must be fixed at 25MHz;
|
||||
* the second clock must be fixed at 125MHz
|
||||
*/
|
||||
clocks = <&mii_phy_tx_clk>, <&gmac_int_tx_clk>;
|
||||
clock-output-names = "gmac";
|
||||
};
|
||||
|
|
|
@ -2318,7 +2318,7 @@ F: include/uapi/linux/coda*.h
|
|||
|
||||
COMMON CLK FRAMEWORK
|
||||
M: Mike Turquette <mturquette@linaro.org>
|
||||
L: linux-arm-kernel@lists.infradead.org (same as CLK API & CLKDEV)
|
||||
L: linux-kernel@vger.kernel.org
|
||||
T: git git://git.linaro.org/people/mturquette/linux.git
|
||||
S: Maintained
|
||||
F: drivers/clk/
|
||||
|
|
|
@ -424,6 +424,7 @@
|
|||
compatible = "altr,socfpga-gate-clk";
|
||||
clocks = <&f2s_periph_ref_clk>, <&main_nand_sdmmc_clk>, <&per_nand_mmc_clk>;
|
||||
clk-gate = <0xa0 8>;
|
||||
clk-phase = <0 135>;
|
||||
};
|
||||
|
||||
nand_x_clk: nand_x_clk {
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
void __iomem *socfpga_scu_base_addr = ((void __iomem *)(SOCFPGA_SCU_VIRT_BASE));
|
||||
void __iomem *sys_manager_base_addr;
|
||||
void __iomem *rst_manager_base_addr;
|
||||
void __iomem *clk_mgr_base_addr;
|
||||
unsigned long cpu1start_addr;
|
||||
|
||||
static struct map_desc scu_io_desc __initdata = {
|
||||
|
@ -78,9 +77,6 @@ void __init socfpga_sysmgr_init(void)
|
|||
|
||||
np = of_find_compatible_node(NULL, NULL, "altr,rst-mgr");
|
||||
rst_manager_base_addr = of_iomap(np, 0);
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "altr,clk-mgr");
|
||||
clk_mgr_base_addr = of_iomap(np, 0);
|
||||
}
|
||||
|
||||
static void __init socfpga_init_irq(void)
|
||||
|
@ -106,7 +102,6 @@ static void __init socfpga_cyclone5_init(void)
|
|||
{
|
||||
l2x0_of_init(0, ~0UL);
|
||||
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
|
||||
socfpga_init_clocks();
|
||||
}
|
||||
|
||||
static const char *altera_dt_match[] = {
|
||||
|
|
|
@ -65,10 +65,12 @@ config COMMON_CLK_SI570
|
|||
clock generators.
|
||||
|
||||
config COMMON_CLK_S2MPS11
|
||||
tristate "Clock driver for S2MPS11 MFD"
|
||||
tristate "Clock driver for S2MPS11/S5M8767 MFD"
|
||||
depends on MFD_SEC_CORE
|
||||
---help---
|
||||
This driver supports S2MPS11 crystal oscillator clock.
|
||||
This driver supports S2MPS11/S5M8767 crystal oscillator clock. These
|
||||
multi-function devices have 3 fixed-rate oscillators, clocked at
|
||||
32KHz each.
|
||||
|
||||
config CLK_TWL6040
|
||||
tristate "External McPDM functional clock from twl6040"
|
||||
|
|
|
@ -17,6 +17,7 @@ obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o
|
|||
obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o
|
||||
obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o
|
||||
obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o
|
||||
obj-$(CONFIG_ARCH_MOXART) += clk-moxart.o
|
||||
obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o
|
||||
obj-$(CONFIG_ARCH_NSPIRE) += clk-nspire.o
|
||||
obj-$(CONFIG_CLK_PPC_CORENET) += clk-ppc-corenet.o
|
||||
|
@ -31,6 +32,7 @@ obj-$(CONFIG_COMMON_CLK_XGENE) += clk-xgene.o
|
|||
obj-$(CONFIG_COMMON_CLK_AT91) += at91/
|
||||
obj-$(CONFIG_ARCH_BCM_MOBILE) += bcm/
|
||||
obj-$(CONFIG_ARCH_HI3xxx) += hisilicon/
|
||||
obj-$(CONFIG_ARCH_HIP04) += hisilicon/
|
||||
obj-$(CONFIG_COMMON_CLK_KEYSTONE) += keystone/
|
||||
ifeq ($(CONFIG_COMMON_CLK), y)
|
||||
obj-$(CONFIG_ARCH_MMP) += mmp/
|
||||
|
@ -44,6 +46,7 @@ obj-$(CONFIG_ARCH_SHMOBILE_MULTI) += shmobile/
|
|||
obj-$(CONFIG_ARCH_SIRF) += sirf/
|
||||
obj-$(CONFIG_ARCH_SOCFPGA) += socfpga/
|
||||
obj-$(CONFIG_PLAT_SPEAR) += spear/
|
||||
obj-$(CONFIG_ARCH_STI) += st/
|
||||
obj-$(CONFIG_ARCH_SUNXI) += sunxi/
|
||||
obj-$(CONFIG_ARCH_TEGRA) += tegra/
|
||||
obj-$(CONFIG_ARCH_OMAP2PLUS) += ti/
|
||||
|
|
|
@ -13,12 +13,9 @@
|
|||
#include <linux/clk/at91_pmc.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
|
||||
#include "pmc.h"
|
||||
|
||||
|
@ -38,104 +35,59 @@ struct clk_programmable_layout {
|
|||
struct clk_programmable {
|
||||
struct clk_hw hw;
|
||||
struct at91_pmc *pmc;
|
||||
unsigned int irq;
|
||||
wait_queue_head_t wait;
|
||||
u8 id;
|
||||
u8 css;
|
||||
u8 pres;
|
||||
u8 slckmck;
|
||||
const struct clk_programmable_layout *layout;
|
||||
};
|
||||
|
||||
#define to_clk_programmable(hw) container_of(hw, struct clk_programmable, hw)
|
||||
|
||||
|
||||
static irqreturn_t clk_programmable_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct clk_programmable *prog = (struct clk_programmable *)dev_id;
|
||||
|
||||
wake_up(&prog->wait);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int clk_programmable_prepare(struct clk_hw *hw)
|
||||
{
|
||||
u32 tmp;
|
||||
struct clk_programmable *prog = to_clk_programmable(hw);
|
||||
struct at91_pmc *pmc = prog->pmc;
|
||||
const struct clk_programmable_layout *layout = prog->layout;
|
||||
u8 id = prog->id;
|
||||
u32 mask = PROG_STATUS_MASK(id);
|
||||
|
||||
tmp = prog->css | (prog->pres << layout->pres_shift);
|
||||
if (layout->have_slck_mck && prog->slckmck)
|
||||
tmp |= AT91_PMC_CSSMCK_MCK;
|
||||
|
||||
pmc_write(pmc, AT91_PMC_PCKR(id), tmp);
|
||||
|
||||
while (!(pmc_read(pmc, AT91_PMC_SR) & mask))
|
||||
wait_event(prog->wait, pmc_read(pmc, AT91_PMC_SR) & mask);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int clk_programmable_is_ready(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_programmable *prog = to_clk_programmable(hw);
|
||||
struct at91_pmc *pmc = prog->pmc;
|
||||
|
||||
return !!(pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_PCKR(prog->id));
|
||||
}
|
||||
|
||||
static unsigned long clk_programmable_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
u32 tmp;
|
||||
u32 pres;
|
||||
struct clk_programmable *prog = to_clk_programmable(hw);
|
||||
struct at91_pmc *pmc = prog->pmc;
|
||||
const struct clk_programmable_layout *layout = prog->layout;
|
||||
|
||||
tmp = pmc_read(pmc, AT91_PMC_PCKR(prog->id));
|
||||
prog->pres = (tmp >> layout->pres_shift) & PROG_PRES_MASK;
|
||||
|
||||
return parent_rate >> prog->pres;
|
||||
pres = (pmc_read(pmc, AT91_PMC_PCKR(prog->id)) >> layout->pres_shift) &
|
||||
PROG_PRES_MASK;
|
||||
return parent_rate >> pres;
|
||||
}
|
||||
|
||||
static long clk_programmable_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
static long clk_programmable_determine_rate(struct clk_hw *hw,
|
||||
unsigned long rate,
|
||||
unsigned long *best_parent_rate,
|
||||
struct clk **best_parent_clk)
|
||||
{
|
||||
unsigned long best_rate = *parent_rate;
|
||||
unsigned long best_diff;
|
||||
unsigned long new_diff;
|
||||
unsigned long cur_rate;
|
||||
int shift = shift;
|
||||
struct clk *parent = NULL;
|
||||
long best_rate = -EINVAL;
|
||||
unsigned long parent_rate;
|
||||
unsigned long tmp_rate;
|
||||
int shift;
|
||||
int i;
|
||||
|
||||
if (rate > *parent_rate)
|
||||
return *parent_rate;
|
||||
else
|
||||
best_diff = *parent_rate - rate;
|
||||
for (i = 0; i < __clk_get_num_parents(hw->clk); i++) {
|
||||
parent = clk_get_parent_by_index(hw->clk, i);
|
||||
if (!parent)
|
||||
continue;
|
||||
|
||||
if (!best_diff)
|
||||
return best_rate;
|
||||
|
||||
for (shift = 1; shift < PROG_PRES_MASK; shift++) {
|
||||
cur_rate = *parent_rate >> shift;
|
||||
|
||||
if (cur_rate > rate)
|
||||
new_diff = cur_rate - rate;
|
||||
else
|
||||
new_diff = rate - cur_rate;
|
||||
|
||||
if (!new_diff)
|
||||
return cur_rate;
|
||||
|
||||
if (new_diff < best_diff) {
|
||||
best_diff = new_diff;
|
||||
best_rate = cur_rate;
|
||||
parent_rate = __clk_get_rate(parent);
|
||||
for (shift = 0; shift < PROG_PRES_MASK; shift++) {
|
||||
tmp_rate = parent_rate >> shift;
|
||||
if (tmp_rate <= rate)
|
||||
break;
|
||||
}
|
||||
|
||||
if (rate > cur_rate)
|
||||
if (tmp_rate > rate)
|
||||
continue;
|
||||
|
||||
if (best_rate < 0 || (rate - tmp_rate) < (rate - best_rate)) {
|
||||
best_rate = tmp_rate;
|
||||
*best_parent_rate = parent_rate;
|
||||
*best_parent_clk = parent;
|
||||
}
|
||||
|
||||
if (!best_rate)
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -146,17 +98,22 @@ static int clk_programmable_set_parent(struct clk_hw *hw, u8 index)
|
|||
{
|
||||
struct clk_programmable *prog = to_clk_programmable(hw);
|
||||
const struct clk_programmable_layout *layout = prog->layout;
|
||||
struct at91_pmc *pmc = prog->pmc;
|
||||
u32 tmp = pmc_read(pmc, AT91_PMC_PCKR(prog->id)) & ~layout->css_mask;
|
||||
|
||||
if (layout->have_slck_mck)
|
||||
tmp &= AT91_PMC_CSSMCK_MCK;
|
||||
|
||||
if (index > layout->css_mask) {
|
||||
if (index > PROG_MAX_RM9200_CSS && layout->have_slck_mck) {
|
||||
prog->css = 0;
|
||||
prog->slckmck = 1;
|
||||
tmp |= AT91_PMC_CSSMCK_MCK;
|
||||
return 0;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
prog->css = index;
|
||||
pmc_write(pmc, AT91_PMC_PCKR(prog->id), tmp | index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -169,13 +126,9 @@ static u8 clk_programmable_get_parent(struct clk_hw *hw)
|
|||
const struct clk_programmable_layout *layout = prog->layout;
|
||||
|
||||
tmp = pmc_read(pmc, AT91_PMC_PCKR(prog->id));
|
||||
prog->css = tmp & layout->css_mask;
|
||||
ret = prog->css;
|
||||
if (layout->have_slck_mck) {
|
||||
prog->slckmck = !!(tmp & AT91_PMC_CSSMCK_MCK);
|
||||
if (prog->slckmck && !ret)
|
||||
ret = PROG_MAX_RM9200_CSS + 1;
|
||||
}
|
||||
ret = tmp & layout->css_mask;
|
||||
if (layout->have_slck_mck && (tmp & AT91_PMC_CSSMCK_MCK) && !ret)
|
||||
ret = PROG_MAX_RM9200_CSS + 1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -184,67 +137,47 @@ static int clk_programmable_set_rate(struct clk_hw *hw, unsigned long rate,
|
|||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_programmable *prog = to_clk_programmable(hw);
|
||||
unsigned long best_rate = parent_rate;
|
||||
unsigned long best_diff;
|
||||
unsigned long new_diff;
|
||||
unsigned long cur_rate;
|
||||
struct at91_pmc *pmc = prog->pmc;
|
||||
const struct clk_programmable_layout *layout = prog->layout;
|
||||
unsigned long div = parent_rate / rate;
|
||||
int shift = 0;
|
||||
u32 tmp = pmc_read(pmc, AT91_PMC_PCKR(prog->id)) &
|
||||
~(PROG_PRES_MASK << layout->pres_shift);
|
||||
|
||||
if (rate > parent_rate)
|
||||
return parent_rate;
|
||||
else
|
||||
best_diff = parent_rate - rate;
|
||||
if (!div)
|
||||
return -EINVAL;
|
||||
|
||||
if (!best_diff) {
|
||||
prog->pres = shift;
|
||||
return 0;
|
||||
}
|
||||
shift = fls(div) - 1;
|
||||
|
||||
for (shift = 1; shift < PROG_PRES_MASK; shift++) {
|
||||
cur_rate = parent_rate >> shift;
|
||||
if (div != (1<<shift))
|
||||
return -EINVAL;
|
||||
|
||||
if (cur_rate > rate)
|
||||
new_diff = cur_rate - rate;
|
||||
else
|
||||
new_diff = rate - cur_rate;
|
||||
if (shift >= PROG_PRES_MASK)
|
||||
return -EINVAL;
|
||||
|
||||
if (!new_diff)
|
||||
break;
|
||||
pmc_write(pmc, AT91_PMC_PCKR(prog->id),
|
||||
tmp | (shift << layout->pres_shift));
|
||||
|
||||
if (new_diff < best_diff) {
|
||||
best_diff = new_diff;
|
||||
best_rate = cur_rate;
|
||||
}
|
||||
|
||||
if (rate > cur_rate)
|
||||
break;
|
||||
}
|
||||
|
||||
prog->pres = shift;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops programmable_ops = {
|
||||
.prepare = clk_programmable_prepare,
|
||||
.is_prepared = clk_programmable_is_ready,
|
||||
.recalc_rate = clk_programmable_recalc_rate,
|
||||
.round_rate = clk_programmable_round_rate,
|
||||
.determine_rate = clk_programmable_determine_rate,
|
||||
.get_parent = clk_programmable_get_parent,
|
||||
.set_parent = clk_programmable_set_parent,
|
||||
.set_rate = clk_programmable_set_rate,
|
||||
};
|
||||
|
||||
static struct clk * __init
|
||||
at91_clk_register_programmable(struct at91_pmc *pmc, unsigned int irq,
|
||||
at91_clk_register_programmable(struct at91_pmc *pmc,
|
||||
const char *name, const char **parent_names,
|
||||
u8 num_parents, u8 id,
|
||||
const struct clk_programmable_layout *layout)
|
||||
{
|
||||
int ret;
|
||||
struct clk_programmable *prog;
|
||||
struct clk *clk = NULL;
|
||||
struct clk_init_data init;
|
||||
char irq_name[11];
|
||||
|
||||
if (id > PROG_ID_MAX)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
@ -263,14 +196,6 @@ at91_clk_register_programmable(struct at91_pmc *pmc, unsigned int irq,
|
|||
prog->layout = layout;
|
||||
prog->hw.init = &init;
|
||||
prog->pmc = pmc;
|
||||
prog->irq = irq;
|
||||
init_waitqueue_head(&prog->wait);
|
||||
irq_set_status_flags(prog->irq, IRQ_NOAUTOEN);
|
||||
snprintf(irq_name, sizeof(irq_name), "clk-prog%d", id);
|
||||
ret = request_irq(prog->irq, clk_programmable_irq_handler,
|
||||
IRQF_TRIGGER_HIGH, irq_name, prog);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
clk = clk_register(NULL, &prog->hw);
|
||||
if (IS_ERR(clk))
|
||||
|
@ -304,7 +229,6 @@ of_at91_clk_prog_setup(struct device_node *np, struct at91_pmc *pmc,
|
|||
int num;
|
||||
u32 id;
|
||||
int i;
|
||||
unsigned int irq;
|
||||
struct clk *clk;
|
||||
int num_parents;
|
||||
const char *parent_names[PROG_SOURCE_MAX];
|
||||
|
@ -332,11 +256,7 @@ of_at91_clk_prog_setup(struct device_node *np, struct at91_pmc *pmc,
|
|||
if (of_property_read_string(np, "clock-output-names", &name))
|
||||
name = progclknp->name;
|
||||
|
||||
irq = irq_of_parse_and_map(progclknp, 0);
|
||||
if (!irq)
|
||||
continue;
|
||||
|
||||
clk = at91_clk_register_programmable(pmc, irq, name,
|
||||
clk = at91_clk_register_programmable(pmc, name,
|
||||
parent_names, num_parents,
|
||||
id, layout);
|
||||
if (IS_ERR(clk))
|
||||
|
|
|
@ -14,6 +14,11 @@
|
|||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#include "pmc.h"
|
||||
|
||||
|
@ -25,19 +30,48 @@
|
|||
struct clk_system {
|
||||
struct clk_hw hw;
|
||||
struct at91_pmc *pmc;
|
||||
unsigned int irq;
|
||||
wait_queue_head_t wait;
|
||||
u8 id;
|
||||
};
|
||||
|
||||
static int clk_system_enable(struct clk_hw *hw)
|
||||
static inline int is_pck(int id)
|
||||
{
|
||||
return (id >= 8) && (id <= 15);
|
||||
}
|
||||
static irqreturn_t clk_system_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct clk_system *sys = (struct clk_system *)dev_id;
|
||||
|
||||
wake_up(&sys->wait);
|
||||
disable_irq_nosync(sys->irq);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int clk_system_prepare(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_system *sys = to_clk_system(hw);
|
||||
struct at91_pmc *pmc = sys->pmc;
|
||||
u32 mask = 1 << sys->id;
|
||||
|
||||
pmc_write(pmc, AT91_PMC_SCER, 1 << sys->id);
|
||||
pmc_write(pmc, AT91_PMC_SCER, mask);
|
||||
|
||||
if (!is_pck(sys->id))
|
||||
return 0;
|
||||
|
||||
while (!(pmc_read(pmc, AT91_PMC_SR) & mask)) {
|
||||
if (sys->irq) {
|
||||
enable_irq(sys->irq);
|
||||
wait_event(sys->wait,
|
||||
pmc_read(pmc, AT91_PMC_SR) & mask);
|
||||
} else
|
||||
cpu_relax();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void clk_system_disable(struct clk_hw *hw)
|
||||
static void clk_system_unprepare(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_system *sys = to_clk_system(hw);
|
||||
struct at91_pmc *pmc = sys->pmc;
|
||||
|
@ -45,27 +79,34 @@ static void clk_system_disable(struct clk_hw *hw)
|
|||
pmc_write(pmc, AT91_PMC_SCDR, 1 << sys->id);
|
||||
}
|
||||
|
||||
static int clk_system_is_enabled(struct clk_hw *hw)
|
||||
static int clk_system_is_prepared(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_system *sys = to_clk_system(hw);
|
||||
struct at91_pmc *pmc = sys->pmc;
|
||||
|
||||
return !!(pmc_read(pmc, AT91_PMC_SCSR) & (1 << sys->id));
|
||||
if (!(pmc_read(pmc, AT91_PMC_SCSR) & (1 << sys->id)))
|
||||
return 0;
|
||||
|
||||
if (!is_pck(sys->id))
|
||||
return 1;
|
||||
|
||||
return !!(pmc_read(pmc, AT91_PMC_SR) & (1 << sys->id));
|
||||
}
|
||||
|
||||
static const struct clk_ops system_ops = {
|
||||
.enable = clk_system_enable,
|
||||
.disable = clk_system_disable,
|
||||
.is_enabled = clk_system_is_enabled,
|
||||
.prepare = clk_system_prepare,
|
||||
.unprepare = clk_system_unprepare,
|
||||
.is_prepared = clk_system_is_prepared,
|
||||
};
|
||||
|
||||
static struct clk * __init
|
||||
at91_clk_register_system(struct at91_pmc *pmc, const char *name,
|
||||
const char *parent_name, u8 id)
|
||||
const char *parent_name, u8 id, int irq)
|
||||
{
|
||||
struct clk_system *sys;
|
||||
struct clk *clk = NULL;
|
||||
struct clk_init_data init;
|
||||
int ret;
|
||||
|
||||
if (!parent_name || id > SYSTEM_MAX_ID)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
@ -84,11 +125,20 @@ at91_clk_register_system(struct at91_pmc *pmc, const char *name,
|
|||
* (see drivers/memory) which would request and enable the ddrck clock.
|
||||
* When this is done we will be able to remove CLK_IGNORE_UNUSED flag.
|
||||
*/
|
||||
init.flags = CLK_IGNORE_UNUSED;
|
||||
init.flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED;
|
||||
|
||||
sys->id = id;
|
||||
sys->hw.init = &init;
|
||||
sys->pmc = pmc;
|
||||
sys->irq = irq;
|
||||
if (irq) {
|
||||
init_waitqueue_head(&sys->wait);
|
||||
irq_set_status_flags(sys->irq, IRQ_NOAUTOEN);
|
||||
ret = request_irq(sys->irq, clk_system_irq_handler,
|
||||
IRQF_TRIGGER_HIGH, name, sys);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
clk = clk_register(NULL, &sys->hw);
|
||||
if (IS_ERR(clk))
|
||||
|
@ -101,6 +151,7 @@ static void __init
|
|||
of_at91_clk_sys_setup(struct device_node *np, struct at91_pmc *pmc)
|
||||
{
|
||||
int num;
|
||||
int irq = 0;
|
||||
u32 id;
|
||||
struct clk *clk;
|
||||
const char *name;
|
||||
|
@ -118,9 +169,12 @@ of_at91_clk_sys_setup(struct device_node *np, struct at91_pmc *pmc)
|
|||
if (of_property_read_string(np, "clock-output-names", &name))
|
||||
name = sysclknp->name;
|
||||
|
||||
if (is_pck(id))
|
||||
irq = irq_of_parse_and_map(sysclknp, 0);
|
||||
|
||||
parent_name = of_clk_get_parent_name(sysclknp, 0);
|
||||
|
||||
clk = at91_clk_register_system(pmc, name, parent_name, id);
|
||||
clk = at91_clk_register_system(pmc, name, parent_name, id, irq);
|
||||
if (IS_ERR(clk))
|
||||
continue;
|
||||
|
||||
|
|
|
@ -17,23 +17,75 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
#define AXI_CLKGEN_REG_UPDATE_ENABLE 0x04
|
||||
#define AXI_CLKGEN_REG_CLK_OUT1 0x08
|
||||
#define AXI_CLKGEN_REG_CLK_OUT2 0x0c
|
||||
#define AXI_CLKGEN_REG_CLK_DIV 0x10
|
||||
#define AXI_CLKGEN_REG_CLK_FB1 0x14
|
||||
#define AXI_CLKGEN_REG_CLK_FB2 0x18
|
||||
#define AXI_CLKGEN_REG_LOCK1 0x1c
|
||||
#define AXI_CLKGEN_REG_LOCK2 0x20
|
||||
#define AXI_CLKGEN_REG_LOCK3 0x24
|
||||
#define AXI_CLKGEN_REG_FILTER1 0x28
|
||||
#define AXI_CLKGEN_REG_FILTER2 0x2c
|
||||
#define AXI_CLKGEN_V1_REG_UPDATE_ENABLE 0x04
|
||||
#define AXI_CLKGEN_V1_REG_CLK_OUT1 0x08
|
||||
#define AXI_CLKGEN_V1_REG_CLK_OUT2 0x0c
|
||||
#define AXI_CLKGEN_V1_REG_CLK_DIV 0x10
|
||||
#define AXI_CLKGEN_V1_REG_CLK_FB1 0x14
|
||||
#define AXI_CLKGEN_V1_REG_CLK_FB2 0x18
|
||||
#define AXI_CLKGEN_V1_REG_LOCK1 0x1c
|
||||
#define AXI_CLKGEN_V1_REG_LOCK2 0x20
|
||||
#define AXI_CLKGEN_V1_REG_LOCK3 0x24
|
||||
#define AXI_CLKGEN_V1_REG_FILTER1 0x28
|
||||
#define AXI_CLKGEN_V1_REG_FILTER2 0x2c
|
||||
|
||||
#define AXI_CLKGEN_V2_REG_RESET 0x40
|
||||
#define AXI_CLKGEN_V2_REG_DRP_CNTRL 0x70
|
||||
#define AXI_CLKGEN_V2_REG_DRP_STATUS 0x74
|
||||
|
||||
#define AXI_CLKGEN_V2_RESET_MMCM_ENABLE BIT(1)
|
||||
#define AXI_CLKGEN_V2_RESET_ENABLE BIT(0)
|
||||
|
||||
#define AXI_CLKGEN_V2_DRP_CNTRL_SEL BIT(29)
|
||||
#define AXI_CLKGEN_V2_DRP_CNTRL_READ BIT(28)
|
||||
|
||||
#define AXI_CLKGEN_V2_DRP_STATUS_BUSY BIT(16)
|
||||
|
||||
#define MMCM_REG_CLKOUT0_1 0x08
|
||||
#define MMCM_REG_CLKOUT0_2 0x09
|
||||
#define MMCM_REG_CLK_FB1 0x14
|
||||
#define MMCM_REG_CLK_FB2 0x15
|
||||
#define MMCM_REG_CLK_DIV 0x16
|
||||
#define MMCM_REG_LOCK1 0x18
|
||||
#define MMCM_REG_LOCK2 0x19
|
||||
#define MMCM_REG_LOCK3 0x1a
|
||||
#define MMCM_REG_FILTER1 0x4e
|
||||
#define MMCM_REG_FILTER2 0x4f
|
||||
|
||||
struct axi_clkgen;
|
||||
|
||||
struct axi_clkgen_mmcm_ops {
|
||||
void (*enable)(struct axi_clkgen *axi_clkgen, bool enable);
|
||||
int (*write)(struct axi_clkgen *axi_clkgen, unsigned int reg,
|
||||
unsigned int val, unsigned int mask);
|
||||
int (*read)(struct axi_clkgen *axi_clkgen, unsigned int reg,
|
||||
unsigned int *val);
|
||||
};
|
||||
|
||||
struct axi_clkgen {
|
||||
void __iomem *base;
|
||||
const struct axi_clkgen_mmcm_ops *mmcm_ops;
|
||||
struct clk_hw clk_hw;
|
||||
};
|
||||
|
||||
static void axi_clkgen_mmcm_enable(struct axi_clkgen *axi_clkgen,
|
||||
bool enable)
|
||||
{
|
||||
axi_clkgen->mmcm_ops->enable(axi_clkgen, enable);
|
||||
}
|
||||
|
||||
static int axi_clkgen_mmcm_write(struct axi_clkgen *axi_clkgen,
|
||||
unsigned int reg, unsigned int val, unsigned int mask)
|
||||
{
|
||||
return axi_clkgen->mmcm_ops->write(axi_clkgen, reg, val, mask);
|
||||
}
|
||||
|
||||
static int axi_clkgen_mmcm_read(struct axi_clkgen *axi_clkgen,
|
||||
unsigned int reg, unsigned int *val)
|
||||
{
|
||||
return axi_clkgen->mmcm_ops->read(axi_clkgen, reg, val);
|
||||
}
|
||||
|
||||
static uint32_t axi_clkgen_lookup_filter(unsigned int m)
|
||||
{
|
||||
switch (m) {
|
||||
|
@ -156,6 +208,148 @@ static void axi_clkgen_read(struct axi_clkgen *axi_clkgen,
|
|||
*val = readl(axi_clkgen->base + reg);
|
||||
}
|
||||
|
||||
static unsigned int axi_clkgen_v1_map_mmcm_reg(unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case MMCM_REG_CLKOUT0_1:
|
||||
return AXI_CLKGEN_V1_REG_CLK_OUT1;
|
||||
case MMCM_REG_CLKOUT0_2:
|
||||
return AXI_CLKGEN_V1_REG_CLK_OUT2;
|
||||
case MMCM_REG_CLK_FB1:
|
||||
return AXI_CLKGEN_V1_REG_CLK_FB1;
|
||||
case MMCM_REG_CLK_FB2:
|
||||
return AXI_CLKGEN_V1_REG_CLK_FB2;
|
||||
case MMCM_REG_CLK_DIV:
|
||||
return AXI_CLKGEN_V1_REG_CLK_DIV;
|
||||
case MMCM_REG_LOCK1:
|
||||
return AXI_CLKGEN_V1_REG_LOCK1;
|
||||
case MMCM_REG_LOCK2:
|
||||
return AXI_CLKGEN_V1_REG_LOCK2;
|
||||
case MMCM_REG_LOCK3:
|
||||
return AXI_CLKGEN_V1_REG_LOCK3;
|
||||
case MMCM_REG_FILTER1:
|
||||
return AXI_CLKGEN_V1_REG_FILTER1;
|
||||
case MMCM_REG_FILTER2:
|
||||
return AXI_CLKGEN_V1_REG_FILTER2;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int axi_clkgen_v1_mmcm_write(struct axi_clkgen *axi_clkgen,
|
||||
unsigned int reg, unsigned int val, unsigned int mask)
|
||||
{
|
||||
reg = axi_clkgen_v1_map_mmcm_reg(reg);
|
||||
if (reg == 0)
|
||||
return -EINVAL;
|
||||
|
||||
axi_clkgen_write(axi_clkgen, reg, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int axi_clkgen_v1_mmcm_read(struct axi_clkgen *axi_clkgen,
|
||||
unsigned int reg, unsigned int *val)
|
||||
{
|
||||
reg = axi_clkgen_v1_map_mmcm_reg(reg);
|
||||
if (reg == 0)
|
||||
return -EINVAL;
|
||||
|
||||
axi_clkgen_read(axi_clkgen, reg, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void axi_clkgen_v1_mmcm_enable(struct axi_clkgen *axi_clkgen,
|
||||
bool enable)
|
||||
{
|
||||
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V1_REG_UPDATE_ENABLE, enable);
|
||||
}
|
||||
|
||||
static const struct axi_clkgen_mmcm_ops axi_clkgen_v1_mmcm_ops = {
|
||||
.write = axi_clkgen_v1_mmcm_write,
|
||||
.read = axi_clkgen_v1_mmcm_read,
|
||||
.enable = axi_clkgen_v1_mmcm_enable,
|
||||
};
|
||||
|
||||
static int axi_clkgen_wait_non_busy(struct axi_clkgen *axi_clkgen)
|
||||
{
|
||||
unsigned int timeout = 10000;
|
||||
unsigned int val;
|
||||
|
||||
do {
|
||||
axi_clkgen_read(axi_clkgen, AXI_CLKGEN_V2_REG_DRP_STATUS, &val);
|
||||
} while ((val & AXI_CLKGEN_V2_DRP_STATUS_BUSY) && --timeout);
|
||||
|
||||
if (val & AXI_CLKGEN_V2_DRP_STATUS_BUSY)
|
||||
return -EIO;
|
||||
|
||||
return val & 0xffff;
|
||||
}
|
||||
|
||||
static int axi_clkgen_v2_mmcm_read(struct axi_clkgen *axi_clkgen,
|
||||
unsigned int reg, unsigned int *val)
|
||||
{
|
||||
unsigned int reg_val;
|
||||
int ret;
|
||||
|
||||
ret = axi_clkgen_wait_non_busy(axi_clkgen);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
reg_val = AXI_CLKGEN_V2_DRP_CNTRL_SEL | AXI_CLKGEN_V2_DRP_CNTRL_READ;
|
||||
reg_val |= (reg << 16);
|
||||
|
||||
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V2_REG_DRP_CNTRL, reg_val);
|
||||
|
||||
ret = axi_clkgen_wait_non_busy(axi_clkgen);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int axi_clkgen_v2_mmcm_write(struct axi_clkgen *axi_clkgen,
|
||||
unsigned int reg, unsigned int val, unsigned int mask)
|
||||
{
|
||||
unsigned int reg_val = 0;
|
||||
int ret;
|
||||
|
||||
ret = axi_clkgen_wait_non_busy(axi_clkgen);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (mask != 0xffff) {
|
||||
axi_clkgen_v2_mmcm_read(axi_clkgen, reg, ®_val);
|
||||
reg_val &= ~mask;
|
||||
}
|
||||
|
||||
reg_val |= AXI_CLKGEN_V2_DRP_CNTRL_SEL | (reg << 16) | (val & mask);
|
||||
|
||||
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V2_REG_DRP_CNTRL, reg_val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void axi_clkgen_v2_mmcm_enable(struct axi_clkgen *axi_clkgen,
|
||||
bool enable)
|
||||
{
|
||||
unsigned int val = AXI_CLKGEN_V2_RESET_ENABLE;
|
||||
|
||||
if (enable)
|
||||
val |= AXI_CLKGEN_V2_RESET_MMCM_ENABLE;
|
||||
|
||||
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V2_REG_RESET, val);
|
||||
}
|
||||
|
||||
static const struct axi_clkgen_mmcm_ops axi_clkgen_v2_mmcm_ops = {
|
||||
.write = axi_clkgen_v2_mmcm_write,
|
||||
.read = axi_clkgen_v2_mmcm_read,
|
||||
.enable = axi_clkgen_v2_mmcm_enable,
|
||||
};
|
||||
|
||||
static struct axi_clkgen *clk_hw_to_axi_clkgen(struct clk_hw *clk_hw)
|
||||
{
|
||||
return container_of(clk_hw, struct axi_clkgen, clk_hw);
|
||||
|
@ -184,33 +378,29 @@ static int axi_clkgen_set_rate(struct clk_hw *clk_hw,
|
|||
filter = axi_clkgen_lookup_filter(m - 1);
|
||||
lock = axi_clkgen_lookup_lock(m - 1);
|
||||
|
||||
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_UPDATE_ENABLE, 0);
|
||||
|
||||
axi_clkgen_calc_clk_params(dout, &low, &high, &edge, &nocount);
|
||||
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT1,
|
||||
(high << 6) | low);
|
||||
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT2,
|
||||
(edge << 7) | (nocount << 6));
|
||||
axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLKOUT0_1,
|
||||
(high << 6) | low, 0xefff);
|
||||
axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLKOUT0_2,
|
||||
(edge << 7) | (nocount << 6), 0x03ff);
|
||||
|
||||
axi_clkgen_calc_clk_params(d, &low, &high, &edge, &nocount);
|
||||
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_DIV,
|
||||
(edge << 13) | (nocount << 12) | (high << 6) | low);
|
||||
axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_DIV,
|
||||
(edge << 13) | (nocount << 12) | (high << 6) | low, 0x3fff);
|
||||
|
||||
axi_clkgen_calc_clk_params(m, &low, &high, &edge, &nocount);
|
||||
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_FB1,
|
||||
(high << 6) | low);
|
||||
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_FB2,
|
||||
(edge << 7) | (nocount << 6));
|
||||
axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_FB1,
|
||||
(high << 6) | low, 0xefff);
|
||||
axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_FB2,
|
||||
(edge << 7) | (nocount << 6), 0x03ff);
|
||||
|
||||
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK1, lock & 0x3ff);
|
||||
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK2,
|
||||
(((lock >> 16) & 0x1f) << 10) | 0x1);
|
||||
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK3,
|
||||
(((lock >> 24) & 0x1f) << 10) | 0x3e9);
|
||||
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_FILTER1, filter >> 16);
|
||||
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_FILTER2, filter);
|
||||
|
||||
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_UPDATE_ENABLE, 1);
|
||||
axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK1, lock & 0x3ff, 0x3ff);
|
||||
axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK2,
|
||||
(((lock >> 16) & 0x1f) << 10) | 0x1, 0x7fff);
|
||||
axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK3,
|
||||
(((lock >> 24) & 0x1f) << 10) | 0x3e9, 0x7fff);
|
||||
axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_FILTER1, filter >> 16, 0x9900);
|
||||
axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_FILTER2, filter, 0x9900);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -236,11 +426,11 @@ static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw,
|
|||
unsigned int reg;
|
||||
unsigned long long tmp;
|
||||
|
||||
axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT1, ®);
|
||||
axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLKOUT0_1, ®);
|
||||
dout = (reg & 0x3f) + ((reg >> 6) & 0x3f);
|
||||
axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_DIV, ®);
|
||||
axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_DIV, ®);
|
||||
d = (reg & 0x3f) + ((reg >> 6) & 0x3f);
|
||||
axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_FB1, ®);
|
||||
axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_FB1, ®);
|
||||
m = (reg & 0x3f) + ((reg >> 6) & 0x3f);
|
||||
|
||||
if (d == 0 || dout == 0)
|
||||
|
@ -255,14 +445,45 @@ static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw,
|
|||
return tmp;
|
||||
}
|
||||
|
||||
static int axi_clkgen_enable(struct clk_hw *clk_hw)
|
||||
{
|
||||
struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
|
||||
|
||||
axi_clkgen_mmcm_enable(axi_clkgen, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void axi_clkgen_disable(struct clk_hw *clk_hw)
|
||||
{
|
||||
struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
|
||||
|
||||
axi_clkgen_mmcm_enable(axi_clkgen, false);
|
||||
}
|
||||
|
||||
static const struct clk_ops axi_clkgen_ops = {
|
||||
.recalc_rate = axi_clkgen_recalc_rate,
|
||||
.round_rate = axi_clkgen_round_rate,
|
||||
.set_rate = axi_clkgen_set_rate,
|
||||
.enable = axi_clkgen_enable,
|
||||
.disable = axi_clkgen_disable,
|
||||
};
|
||||
|
||||
static const struct of_device_id axi_clkgen_ids[] = {
|
||||
{
|
||||
.compatible = "adi,axi-clkgen-1.00.a",
|
||||
.data = &axi_clkgen_v1_mmcm_ops
|
||||
}, {
|
||||
.compatible = "adi,axi-clkgen-2.00.a",
|
||||
.data = &axi_clkgen_v2_mmcm_ops,
|
||||
},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, axi_clkgen_ids);
|
||||
|
||||
static int axi_clkgen_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *id;
|
||||
struct axi_clkgen *axi_clkgen;
|
||||
struct clk_init_data init;
|
||||
const char *parent_name;
|
||||
|
@ -270,10 +491,19 @@ static int axi_clkgen_probe(struct platform_device *pdev)
|
|||
struct resource *mem;
|
||||
struct clk *clk;
|
||||
|
||||
if (!pdev->dev.of_node)
|
||||
return -ENODEV;
|
||||
|
||||
id = of_match_node(axi_clkgen_ids, pdev->dev.of_node);
|
||||
if (!id)
|
||||
return -ENODEV;
|
||||
|
||||
axi_clkgen = devm_kzalloc(&pdev->dev, sizeof(*axi_clkgen), GFP_KERNEL);
|
||||
if (!axi_clkgen)
|
||||
return -ENOMEM;
|
||||
|
||||
axi_clkgen->mmcm_ops = id->data;
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
axi_clkgen->base = devm_ioremap_resource(&pdev->dev, mem);
|
||||
if (IS_ERR(axi_clkgen->base))
|
||||
|
@ -289,10 +519,12 @@ static int axi_clkgen_probe(struct platform_device *pdev)
|
|||
|
||||
init.name = clk_name;
|
||||
init.ops = &axi_clkgen_ops;
|
||||
init.flags = 0;
|
||||
init.flags = CLK_SET_RATE_GATE;
|
||||
init.parent_names = &parent_name;
|
||||
init.num_parents = 1;
|
||||
|
||||
axi_clkgen_mmcm_enable(axi_clkgen, false);
|
||||
|
||||
axi_clkgen->clk_hw.init = &init;
|
||||
clk = devm_clk_register(&pdev->dev, &axi_clkgen->clk_hw);
|
||||
if (IS_ERR(clk))
|
||||
|
@ -309,12 +541,6 @@ static int axi_clkgen_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id axi_clkgen_ids[] = {
|
||||
{ .compatible = "adi,axi-clkgen-1.00.a" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, axi_clkgen_ids);
|
||||
|
||||
static struct platform_driver axi_clkgen_driver = {
|
||||
.driver = {
|
||||
.name = "adi-axi-clkgen",
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
* 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 / divisor
|
||||
* rate - rate is adjustable. clk->rate = DIV_ROUND_UP(parent->rate / divisor)
|
||||
* parent - fixed parent. No clk_set_parent support
|
||||
*/
|
||||
|
||||
|
@ -115,7 +115,7 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
|
|||
return parent_rate;
|
||||
}
|
||||
|
||||
return parent_rate / div;
|
||||
return DIV_ROUND_UP(parent_rate, div);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -185,7 +185,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
|
|||
}
|
||||
parent_rate = __clk_round_rate(__clk_get_parent(hw->clk),
|
||||
MULT_ROUND_UP(rate, i));
|
||||
now = parent_rate / i;
|
||||
now = DIV_ROUND_UP(parent_rate, i);
|
||||
if (now <= rate && now > best) {
|
||||
bestdiv = i;
|
||||
best = now;
|
||||
|
@ -207,7 +207,7 @@ static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
|
|||
int div;
|
||||
div = clk_divider_bestdiv(hw, rate, prate);
|
||||
|
||||
return *prate / div;
|
||||
return DIV_ROUND_UP(*prate, div);
|
||||
}
|
||||
|
||||
static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
|
@ -218,7 +218,7 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
|
|||
unsigned long flags = 0;
|
||||
u32 val;
|
||||
|
||||
div = parent_rate / rate;
|
||||
div = DIV_ROUND_UP(parent_rate, rate);
|
||||
value = _get_val(divider, div);
|
||||
|
||||
if (value > div_mask(divider))
|
||||
|
|
97
drivers/clk/clk-moxart.c
Normal file
97
drivers/clk/clk-moxart.c
Normal file
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* MOXA ART SoCs clock driver.
|
||||
*
|
||||
* Copyright (C) 2013 Jonas Jensen
|
||||
*
|
||||
* Jonas Jensen <jonas.jensen@gmail.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/clk-provider.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/clkdev.h>
|
||||
|
||||
void __init moxart_of_pll_clk_init(struct device_node *node)
|
||||
{
|
||||
static void __iomem *base;
|
||||
struct clk *clk, *ref_clk;
|
||||
unsigned int mul;
|
||||
const char *name = node->name;
|
||||
const char *parent_name;
|
||||
|
||||
of_property_read_string(node, "clock-output-names", &name);
|
||||
parent_name = of_clk_get_parent_name(node, 0);
|
||||
|
||||
base = of_iomap(node, 0);
|
||||
if (!base) {
|
||||
pr_err("%s: of_iomap failed\n", node->full_name);
|
||||
return;
|
||||
}
|
||||
|
||||
mul = readl(base + 0x30) >> 3 & 0x3f;
|
||||
iounmap(base);
|
||||
|
||||
ref_clk = of_clk_get(node, 0);
|
||||
if (IS_ERR(ref_clk)) {
|
||||
pr_err("%s: of_clk_get failed\n", node->full_name);
|
||||
return;
|
||||
}
|
||||
|
||||
clk = clk_register_fixed_factor(NULL, name, parent_name, 0, mul, 1);
|
||||
if (IS_ERR(clk)) {
|
||||
pr_err("%s: failed to register clock\n", node->full_name);
|
||||
return;
|
||||
}
|
||||
|
||||
clk_register_clkdev(clk, NULL, name);
|
||||
of_clk_add_provider(node, of_clk_src_simple_get, clk);
|
||||
}
|
||||
CLK_OF_DECLARE(moxart_pll_clock, "moxa,moxart-pll-clock",
|
||||
moxart_of_pll_clk_init);
|
||||
|
||||
void __init moxart_of_apb_clk_init(struct device_node *node)
|
||||
{
|
||||
static void __iomem *base;
|
||||
struct clk *clk, *pll_clk;
|
||||
unsigned int div, val;
|
||||
unsigned int div_idx[] = { 2, 3, 4, 6, 8};
|
||||
const char *name = node->name;
|
||||
const char *parent_name;
|
||||
|
||||
of_property_read_string(node, "clock-output-names", &name);
|
||||
parent_name = of_clk_get_parent_name(node, 0);
|
||||
|
||||
base = of_iomap(node, 0);
|
||||
if (!base) {
|
||||
pr_err("%s: of_iomap failed\n", node->full_name);
|
||||
return;
|
||||
}
|
||||
|
||||
val = readl(base + 0xc) >> 4 & 0x7;
|
||||
iounmap(base);
|
||||
|
||||
if (val > 4)
|
||||
val = 0;
|
||||
div = div_idx[val] * 2;
|
||||
|
||||
pll_clk = of_clk_get(node, 0);
|
||||
if (IS_ERR(pll_clk)) {
|
||||
pr_err("%s: of_clk_get failed\n", node->full_name);
|
||||
return;
|
||||
}
|
||||
|
||||
clk = clk_register_fixed_factor(NULL, name, parent_name, 0, 1, div);
|
||||
if (IS_ERR(clk)) {
|
||||
pr_err("%s: failed to register clock\n", node->full_name);
|
||||
return;
|
||||
}
|
||||
|
||||
clk_register_clkdev(clk, NULL, name);
|
||||
of_clk_add_provider(node, of_clk_src_simple_get, clk);
|
||||
}
|
||||
CLK_OF_DECLARE(moxart_apb_clock, "moxa,moxart-apb-clock",
|
||||
moxart_of_apb_clk_init);
|
|
@ -27,7 +27,6 @@ struct cmux_clk {
|
|||
#define CLKSEL_ADJUST BIT(0)
|
||||
#define to_cmux_clk(p) container_of(p, struct cmux_clk, hw)
|
||||
|
||||
static void __iomem *base;
|
||||
static unsigned int clocks_per_pll;
|
||||
|
||||
static int cmux_set_parent(struct clk_hw *hw, u8 idx)
|
||||
|
@ -100,7 +99,11 @@ static void __init core_mux_init(struct device_node *np)
|
|||
pr_err("%s: could not allocate cmux_clk\n", __func__);
|
||||
goto err_name;
|
||||
}
|
||||
cmux_clk->reg = base + offset;
|
||||
cmux_clk->reg = of_iomap(np, 0);
|
||||
if (!cmux_clk->reg) {
|
||||
pr_err("%s: could not map register\n", __func__);
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
node = of_find_compatible_node(NULL, NULL, "fsl,p4080-clockgen");
|
||||
if (node && (offset >= 0x80))
|
||||
|
@ -143,38 +146,39 @@ static void __init core_mux_init(struct device_node *np)
|
|||
|
||||
static void __init core_pll_init(struct device_node *np)
|
||||
{
|
||||
u32 offset, mult;
|
||||
u32 mult;
|
||||
int i, rc, count;
|
||||
const char *clk_name, *parent_name;
|
||||
struct clk_onecell_data *onecell_data;
|
||||
struct clk **subclks;
|
||||
void __iomem *base;
|
||||
|
||||
rc = of_property_read_u32(np, "reg", &offset);
|
||||
if (rc) {
|
||||
pr_err("%s: could not get reg property\n", np->name);
|
||||
base = of_iomap(np, 0);
|
||||
if (!base) {
|
||||
pr_err("clk-ppc: iomap error\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* get the multiple of PLL */
|
||||
mult = ioread32be(base + offset);
|
||||
mult = ioread32be(base);
|
||||
|
||||
/* check if this PLL is disabled */
|
||||
if (mult & PLL_KILL) {
|
||||
pr_debug("PLL:%s is disabled\n", np->name);
|
||||
return;
|
||||
goto err_map;
|
||||
}
|
||||
mult = (mult >> 1) & 0x3f;
|
||||
|
||||
parent_name = of_clk_get_parent_name(np, 0);
|
||||
if (!parent_name) {
|
||||
pr_err("PLL: %s must have a parent\n", np->name);
|
||||
return;
|
||||
goto err_map;
|
||||
}
|
||||
|
||||
count = of_property_count_strings(np, "clock-output-names");
|
||||
if (count < 0 || count > 4) {
|
||||
pr_err("%s: clock is not supported\n", np->name);
|
||||
return;
|
||||
goto err_map;
|
||||
}
|
||||
|
||||
/* output clock number per PLL */
|
||||
|
@ -183,7 +187,7 @@ static void __init core_pll_init(struct device_node *np)
|
|||
subclks = kzalloc(sizeof(struct clk *) * count, GFP_KERNEL);
|
||||
if (!subclks) {
|
||||
pr_err("%s: could not allocate subclks\n", __func__);
|
||||
return;
|
||||
goto err_map;
|
||||
}
|
||||
|
||||
onecell_data = kzalloc(sizeof(struct clk_onecell_data), GFP_KERNEL);
|
||||
|
@ -230,30 +234,52 @@ static void __init core_pll_init(struct device_node *np)
|
|||
goto err_cell;
|
||||
}
|
||||
|
||||
iounmap(base);
|
||||
return;
|
||||
err_cell:
|
||||
kfree(onecell_data);
|
||||
err_clks:
|
||||
kfree(subclks);
|
||||
err_map:
|
||||
iounmap(base);
|
||||
}
|
||||
|
||||
static void __init sysclk_init(struct device_node *node)
|
||||
{
|
||||
struct clk *clk;
|
||||
const char *clk_name = node->name;
|
||||
struct device_node *np = of_get_parent(node);
|
||||
u32 rate;
|
||||
|
||||
if (!np) {
|
||||
pr_err("ppc-clk: could not get parent node\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (of_property_read_u32(np, "clock-frequency", &rate)) {
|
||||
of_node_put(node);
|
||||
return;
|
||||
}
|
||||
|
||||
of_property_read_string(np, "clock-output-names", &clk_name);
|
||||
|
||||
clk = clk_register_fixed_rate(NULL, clk_name, NULL, CLK_IS_ROOT, rate);
|
||||
if (!IS_ERR(clk))
|
||||
of_clk_add_provider(np, of_clk_src_simple_get, clk);
|
||||
}
|
||||
|
||||
static const struct of_device_id clk_match[] __initconst = {
|
||||
{ .compatible = "fixed-clock", .data = of_fixed_clk_setup, },
|
||||
{ .compatible = "fsl,core-pll-clock", .data = core_pll_init, },
|
||||
{ .compatible = "fsl,core-mux-clock", .data = core_mux_init, },
|
||||
{ .compatible = "fsl,qoriq-sysclk-1.0", .data = sysclk_init, },
|
||||
{ .compatible = "fsl,qoriq-sysclk-2.0", .data = sysclk_init, },
|
||||
{ .compatible = "fsl,qoriq-core-pll-1.0", .data = core_pll_init, },
|
||||
{ .compatible = "fsl,qoriq-core-pll-2.0", .data = core_pll_init, },
|
||||
{ .compatible = "fsl,qoriq-core-mux-1.0", .data = core_mux_init, },
|
||||
{ .compatible = "fsl,qoriq-core-mux-2.0", .data = core_mux_init, },
|
||||
{}
|
||||
};
|
||||
|
||||
static int __init ppc_corenet_clk_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np;
|
||||
|
||||
np = pdev->dev.of_node;
|
||||
base = of_iomap(np, 0);
|
||||
if (!base) {
|
||||
dev_err(&pdev->dev, "iomap error\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
of_clk_init(clk_match);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <linux/clk-provider.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mfd/samsung/s2mps11.h>
|
||||
#include <linux/mfd/samsung/s5m8767.h>
|
||||
#include <linux/mfd/samsung/core.h>
|
||||
|
||||
#define s2mps11_name(a) (a->hw.init->name)
|
||||
|
@ -48,6 +49,7 @@ struct s2mps11_clk {
|
|||
struct clk_lookup *lookup;
|
||||
u32 mask;
|
||||
bool enabled;
|
||||
unsigned int reg;
|
||||
};
|
||||
|
||||
static struct s2mps11_clk *to_s2mps11_clk(struct clk_hw *hw)
|
||||
|
@ -61,7 +63,7 @@ static int s2mps11_clk_prepare(struct clk_hw *hw)
|
|||
int ret;
|
||||
|
||||
ret = regmap_update_bits(s2mps11->iodev->regmap_pmic,
|
||||
S2MPS11_REG_RTC_CTRL,
|
||||
s2mps11->reg,
|
||||
s2mps11->mask, s2mps11->mask);
|
||||
if (!ret)
|
||||
s2mps11->enabled = true;
|
||||
|
@ -74,7 +76,7 @@ static void s2mps11_clk_unprepare(struct clk_hw *hw)
|
|||
struct s2mps11_clk *s2mps11 = to_s2mps11_clk(hw);
|
||||
int ret;
|
||||
|
||||
ret = regmap_update_bits(s2mps11->iodev->regmap_pmic, S2MPS11_REG_RTC_CTRL,
|
||||
ret = regmap_update_bits(s2mps11->iodev->regmap_pmic, s2mps11->reg,
|
||||
s2mps11->mask, ~s2mps11->mask);
|
||||
|
||||
if (!ret)
|
||||
|
@ -130,9 +132,9 @@ static struct device_node *s2mps11_clk_parse_dt(struct platform_device *pdev)
|
|||
int i;
|
||||
|
||||
if (!iodev->dev->of_node)
|
||||
return NULL;
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
clk_np = of_find_node_by_name(iodev->dev->of_node, "clocks");
|
||||
clk_np = of_get_child_by_name(iodev->dev->of_node, "clocks");
|
||||
if (!clk_np) {
|
||||
dev_err(&pdev->dev, "could not find clock sub-node\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
@ -155,6 +157,7 @@ static int s2mps11_clk_probe(struct platform_device *pdev)
|
|||
struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent);
|
||||
struct s2mps11_clk *s2mps11_clks, *s2mps11_clk;
|
||||
struct device_node *clk_np = NULL;
|
||||
unsigned int s2mps11_reg;
|
||||
int i, ret = 0;
|
||||
u32 val;
|
||||
|
||||
|
@ -169,13 +172,26 @@ static int s2mps11_clk_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(clk_np))
|
||||
return PTR_ERR(clk_np);
|
||||
|
||||
switch(platform_get_device_id(pdev)->driver_data) {
|
||||
case S2MPS11X:
|
||||
s2mps11_reg = S2MPS11_REG_RTC_CTRL;
|
||||
break;
|
||||
case S5M8767X:
|
||||
s2mps11_reg = S5M8767_REG_CTRL1;
|
||||
break;
|
||||
default:
|
||||
dev_err(&pdev->dev, "Invalid device type\n");
|
||||
return -EINVAL;
|
||||
};
|
||||
|
||||
for (i = 0; i < S2MPS11_CLKS_NUM; i++, s2mps11_clk++) {
|
||||
s2mps11_clk->iodev = iodev;
|
||||
s2mps11_clk->hw.init = &s2mps11_clks_init[i];
|
||||
s2mps11_clk->mask = 1 << i;
|
||||
s2mps11_clk->reg = s2mps11_reg;
|
||||
|
||||
ret = regmap_read(s2mps11_clk->iodev->regmap_pmic,
|
||||
S2MPS11_REG_RTC_CTRL, &val);
|
||||
s2mps11_clk->reg, &val);
|
||||
if (ret < 0)
|
||||
goto err_reg;
|
||||
|
||||
|
@ -241,7 +257,8 @@ static int s2mps11_clk_remove(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
static const struct platform_device_id s2mps11_clk_id[] = {
|
||||
{ "s2mps11-clk", 0},
|
||||
{ "s2mps11-clk", S2MPS11X},
|
||||
{ "s5m8767-clk", S5M8767X},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, s2mps11_clk_id);
|
||||
|
|
|
@ -277,6 +277,10 @@ static int clk_debug_create_one(struct clk *clk, struct dentry *pdentry)
|
|||
if (!d)
|
||||
goto err_out;
|
||||
|
||||
if (clk->ops->debug_init)
|
||||
if (clk->ops->debug_init(clk->hw, clk->dentry))
|
||||
goto err_out;
|
||||
|
||||
ret = 0;
|
||||
goto out;
|
||||
|
||||
|
@ -1339,8 +1343,11 @@ static int __clk_speculate_rates(struct clk *clk, unsigned long parent_rate)
|
|||
if (clk->notifier_count)
|
||||
ret = __clk_notify(clk, PRE_RATE_CHANGE, clk->rate, new_rate);
|
||||
|
||||
if (ret & NOTIFY_STOP_MASK)
|
||||
if (ret & NOTIFY_STOP_MASK) {
|
||||
pr_debug("%s: clk notifier callback for clock %s aborted with error %d\n",
|
||||
__func__, clk->name, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
hlist_for_each_entry(child, &clk->children, child_node) {
|
||||
ret = __clk_speculate_rates(child, new_rate);
|
||||
|
@ -1588,7 +1595,7 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
|
|||
/* notify that we are about to change rates */
|
||||
fail_clk = clk_propagate_rate_change(top, PRE_RATE_CHANGE);
|
||||
if (fail_clk) {
|
||||
pr_warn("%s: failed to set %s rate\n", __func__,
|
||||
pr_debug("%s: failed to set %s rate\n", __func__,
|
||||
fail_clk->name);
|
||||
clk_propagate_rate_change(top, ABORT_RATE_CHANGE);
|
||||
ret = -EBUSY;
|
||||
|
@ -2260,20 +2267,11 @@ void __clk_put(struct clk *clk)
|
|||
* re-enter into the clk framework by calling any top-level clk APIs;
|
||||
* this will cause a nested prepare_lock mutex.
|
||||
*
|
||||
* Pre-change notifier callbacks will be passed the current, pre-change
|
||||
* rate of the clk via struct clk_notifier_data.old_rate. The new,
|
||||
* post-change rate of the clk is passed via struct
|
||||
* In all notification cases cases (pre, post and abort rate change) the
|
||||
* original clock rate is passed to the callback via struct
|
||||
* clk_notifier_data.old_rate and the new frequency is passed via struct
|
||||
* clk_notifier_data.new_rate.
|
||||
*
|
||||
* Post-change notifiers will pass the now-current, post-change rate of
|
||||
* the clk in both struct clk_notifier_data.old_rate and struct
|
||||
* clk_notifier_data.new_rate.
|
||||
*
|
||||
* Abort-change notifiers are effectively the opposite of pre-change
|
||||
* notifiers: the original pre-change clk rate is passed in via struct
|
||||
* clk_notifier_data.new_rate and the failed post-change rate is passed
|
||||
* in via struct clk_notifier_data.old_rate.
|
||||
*
|
||||
* clk_notifier_register() must be called from non-atomic context.
|
||||
* Returns -EINVAL if called with null arguments, -ENOMEM upon
|
||||
* allocation failure; otherwise, passes along the return value of
|
||||
|
@ -2473,7 +2471,7 @@ EXPORT_SYMBOL_GPL(of_clk_del_provider);
|
|||
struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec)
|
||||
{
|
||||
struct of_clk_provider *provider;
|
||||
struct clk *clk = ERR_PTR(-ENOENT);
|
||||
struct clk *clk = ERR_PTR(-EPROBE_DEFER);
|
||||
|
||||
/* Check if we have such a provider in our array */
|
||||
list_for_each_entry(provider, &of_clk_providers, link) {
|
||||
|
@ -2506,8 +2504,12 @@ EXPORT_SYMBOL_GPL(of_clk_get_parent_count);
|
|||
const char *of_clk_get_parent_name(struct device_node *np, int index)
|
||||
{
|
||||
struct of_phandle_args clkspec;
|
||||
struct property *prop;
|
||||
const char *clk_name;
|
||||
const __be32 *vp;
|
||||
u32 pv;
|
||||
int rc;
|
||||
int count;
|
||||
|
||||
if (index < 0)
|
||||
return NULL;
|
||||
|
@ -2517,8 +2519,22 @@ const char *of_clk_get_parent_name(struct device_node *np, int index)
|
|||
if (rc)
|
||||
return NULL;
|
||||
|
||||
index = clkspec.args_count ? clkspec.args[0] : 0;
|
||||
count = 0;
|
||||
|
||||
/* if there is an indices property, use it to transfer the index
|
||||
* specified into an array offset for the clock-output-names property.
|
||||
*/
|
||||
of_property_for_each_u32(clkspec.np, "clock-indices", prop, vp, pv) {
|
||||
if (index == pv) {
|
||||
index = count;
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
if (of_property_read_string_index(clkspec.np, "clock-output-names",
|
||||
clkspec.args_count ? clkspec.args[0] : 0,
|
||||
index,
|
||||
&clk_name) < 0)
|
||||
clk_name = clkspec.np->name;
|
||||
|
||||
|
@ -2527,24 +2543,99 @@ const char *of_clk_get_parent_name(struct device_node *np, int index)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(of_clk_get_parent_name);
|
||||
|
||||
struct clock_provider {
|
||||
of_clk_init_cb_t clk_init_cb;
|
||||
struct device_node *np;
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
static LIST_HEAD(clk_provider_list);
|
||||
|
||||
/*
|
||||
* This function looks for a parent clock. If there is one, then it
|
||||
* checks that the provider for this parent clock was initialized, in
|
||||
* this case the parent clock will be ready.
|
||||
*/
|
||||
static int parent_ready(struct device_node *np)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
while (true) {
|
||||
struct clk *clk = of_clk_get(np, i);
|
||||
|
||||
/* this parent is ready we can check the next one */
|
||||
if (!IS_ERR(clk)) {
|
||||
clk_put(clk);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* at least one parent is not ready, we exit now */
|
||||
if (PTR_ERR(clk) == -EPROBE_DEFER)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Here we make assumption that the device tree is
|
||||
* written correctly. So an error means that there is
|
||||
* no more parent. As we didn't exit yet, then the
|
||||
* previous parent are ready. If there is no clock
|
||||
* parent, no need to wait for them, then we can
|
||||
* consider their absence as being ready
|
||||
*/
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* of_clk_init() - Scan and init clock providers from the DT
|
||||
* @matches: array of compatible values and init functions for providers.
|
||||
*
|
||||
* This function scans the device tree for matching clock providers and
|
||||
* calls their initialization functions
|
||||
* This function scans the device tree for matching clock providers
|
||||
* and calls their initialization functions. It also does it by trying
|
||||
* to follow the dependencies.
|
||||
*/
|
||||
void __init of_clk_init(const struct of_device_id *matches)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
struct device_node *np;
|
||||
struct clock_provider *clk_provider, *next;
|
||||
bool is_init_done;
|
||||
bool force = false;
|
||||
|
||||
if (!matches)
|
||||
matches = &__clk_of_table;
|
||||
|
||||
/* First prepare the list of the clocks providers */
|
||||
for_each_matching_node_and_match(np, matches, &match) {
|
||||
of_clk_init_cb_t clk_init_cb = match->data;
|
||||
clk_init_cb(np);
|
||||
struct clock_provider *parent =
|
||||
kzalloc(sizeof(struct clock_provider), GFP_KERNEL);
|
||||
|
||||
parent->clk_init_cb = match->data;
|
||||
parent->np = np;
|
||||
list_add_tail(&parent->node, &clk_provider_list);
|
||||
}
|
||||
|
||||
while (!list_empty(&clk_provider_list)) {
|
||||
is_init_done = false;
|
||||
list_for_each_entry_safe(clk_provider, next,
|
||||
&clk_provider_list, node) {
|
||||
if (force || parent_ready(clk_provider->np)) {
|
||||
clk_provider->clk_init_cb(clk_provider->np);
|
||||
list_del(&clk_provider->node);
|
||||
kfree(clk_provider);
|
||||
is_init_done = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We didn't manage to initialize any of the
|
||||
* remaining providers during the last loop, so now we
|
||||
* initialize all the remaining ones unconditionally
|
||||
* in case the clock parent was not mandatory
|
||||
*/
|
||||
if (!is_init_done)
|
||||
force = true;
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -167,6 +167,8 @@ struct clk *clk_get(struct device *dev, const char *con_id)
|
|||
clk = of_clk_get_by_name(dev->of_node, con_id);
|
||||
if (!IS_ERR(clk))
|
||||
return clk;
|
||||
if (PTR_ERR(clk) == -EPROBE_DEFER)
|
||||
return clk;
|
||||
}
|
||||
|
||||
return clk_get_sys(dev_id, con_id);
|
||||
|
|
|
@ -2,4 +2,7 @@
|
|||
# Hisilicon Clock specific Makefile
|
||||
#
|
||||
|
||||
obj-y += clk.o clkgate-separated.o clk-hi3620.o
|
||||
obj-y += clk.o clkgate-separated.o
|
||||
|
||||
obj-$(CONFIG_ARCH_HI3xxx) += clk-hi3620.o
|
||||
obj-$(CONFIG_ARCH_HIP04) += clk-hip04.o
|
||||
|
|
|
@ -210,33 +210,297 @@ static struct hisi_gate_clock hi3620_seperated_gate_clks[] __initdata = {
|
|||
|
||||
static void __init hi3620_clk_init(struct device_node *np)
|
||||
{
|
||||
void __iomem *base;
|
||||
struct hisi_clock_data *clk_data;
|
||||
|
||||
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");
|
||||
clk_data = hisi_clk_init(np, HI3620_NR_CLKS);
|
||||
if (!clk_data)
|
||||
return;
|
||||
}
|
||||
|
||||
hisi_clk_init(np, HI3620_NR_CLKS);
|
||||
|
||||
hisi_clk_register_fixed_rate(hi3620_fixed_rate_clks,
|
||||
ARRAY_SIZE(hi3620_fixed_rate_clks),
|
||||
base);
|
||||
clk_data);
|
||||
hisi_clk_register_fixed_factor(hi3620_fixed_factor_clks,
|
||||
ARRAY_SIZE(hi3620_fixed_factor_clks),
|
||||
base);
|
||||
clk_data);
|
||||
hisi_clk_register_mux(hi3620_mux_clks, ARRAY_SIZE(hi3620_mux_clks),
|
||||
base);
|
||||
clk_data);
|
||||
hisi_clk_register_divider(hi3620_div_clks, ARRAY_SIZE(hi3620_div_clks),
|
||||
base);
|
||||
clk_data);
|
||||
hisi_clk_register_gate_sep(hi3620_seperated_gate_clks,
|
||||
ARRAY_SIZE(hi3620_seperated_gate_clks),
|
||||
base);
|
||||
clk_data);
|
||||
}
|
||||
CLK_OF_DECLARE(hi3620_clk, "hisilicon,hi3620-clock", hi3620_clk_init);
|
||||
|
||||
struct hisi_mmc_clock {
|
||||
unsigned int id;
|
||||
const char *name;
|
||||
const char *parent_name;
|
||||
unsigned long flags;
|
||||
u32 clken_reg;
|
||||
u32 clken_bit;
|
||||
u32 div_reg;
|
||||
u32 div_off;
|
||||
u32 div_bits;
|
||||
u32 drv_reg;
|
||||
u32 drv_off;
|
||||
u32 drv_bits;
|
||||
u32 sam_reg;
|
||||
u32 sam_off;
|
||||
u32 sam_bits;
|
||||
};
|
||||
|
||||
struct clk_mmc {
|
||||
struct clk_hw hw;
|
||||
u32 id;
|
||||
void __iomem *clken_reg;
|
||||
u32 clken_bit;
|
||||
void __iomem *div_reg;
|
||||
u32 div_off;
|
||||
u32 div_bits;
|
||||
void __iomem *drv_reg;
|
||||
u32 drv_off;
|
||||
u32 drv_bits;
|
||||
void __iomem *sam_reg;
|
||||
u32 sam_off;
|
||||
u32 sam_bits;
|
||||
};
|
||||
|
||||
#define to_mmc(_hw) container_of(_hw, struct clk_mmc, hw)
|
||||
|
||||
static struct hisi_mmc_clock hi3620_mmc_clks[] __initdata = {
|
||||
{ HI3620_SD_CIUCLK, "sd_bclk1", "sd_clk", CLK_SET_RATE_PARENT, 0x1f8, 0, 0x1f8, 1, 3, 0x1f8, 4, 4, 0x1f8, 8, 4},
|
||||
{ HI3620_MMC_CIUCLK1, "mmc_bclk1", "mmc_clk1", CLK_SET_RATE_PARENT, 0x1f8, 12, 0x1f8, 13, 3, 0x1f8, 16, 4, 0x1f8, 20, 4},
|
||||
{ HI3620_MMC_CIUCLK2, "mmc_bclk2", "mmc_clk2", CLK_SET_RATE_PARENT, 0x1f8, 24, 0x1f8, 25, 3, 0x1f8, 28, 4, 0x1fc, 0, 4},
|
||||
{ HI3620_MMC_CIUCLK3, "mmc_bclk3", "mmc_clk3", CLK_SET_RATE_PARENT, 0x1fc, 4, 0x1fc, 5, 3, 0x1fc, 8, 4, 0x1fc, 12, 4},
|
||||
};
|
||||
|
||||
static unsigned long mmc_clk_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
switch (parent_rate) {
|
||||
case 26000000:
|
||||
return 13000000;
|
||||
case 180000000:
|
||||
return 25000000;
|
||||
case 360000000:
|
||||
return 50000000;
|
||||
case 720000000:
|
||||
return 100000000;
|
||||
case 1440000000:
|
||||
return 180000000;
|
||||
default:
|
||||
return parent_rate;
|
||||
}
|
||||
}
|
||||
|
||||
static long mmc_clk_determine_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *best_parent_rate,
|
||||
struct clk **best_parent_p)
|
||||
{
|
||||
struct clk_mmc *mclk = to_mmc(hw);
|
||||
unsigned long best = 0;
|
||||
|
||||
if ((rate <= 13000000) && (mclk->id == HI3620_MMC_CIUCLK1)) {
|
||||
rate = 13000000;
|
||||
best = 26000000;
|
||||
} else if (rate <= 26000000) {
|
||||
rate = 25000000;
|
||||
best = 180000000;
|
||||
} else if (rate <= 52000000) {
|
||||
rate = 50000000;
|
||||
best = 360000000;
|
||||
} else if (rate <= 100000000) {
|
||||
rate = 100000000;
|
||||
best = 720000000;
|
||||
} else {
|
||||
/* max is 180M */
|
||||
rate = 180000000;
|
||||
best = 1440000000;
|
||||
}
|
||||
*best_parent_rate = best;
|
||||
return rate;
|
||||
}
|
||||
|
||||
static u32 mmc_clk_delay(u32 val, u32 para, u32 off, u32 len)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (para % 2)
|
||||
val |= 1 << (off + i);
|
||||
else
|
||||
val &= ~(1 << (off + i));
|
||||
para = para >> 1;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static int mmc_clk_set_timing(struct clk_hw *hw, unsigned long rate)
|
||||
{
|
||||
struct clk_mmc *mclk = to_mmc(hw);
|
||||
unsigned long flags;
|
||||
u32 sam, drv, div, val;
|
||||
static DEFINE_SPINLOCK(mmc_clk_lock);
|
||||
|
||||
switch (rate) {
|
||||
case 13000000:
|
||||
sam = 3;
|
||||
drv = 1;
|
||||
div = 1;
|
||||
break;
|
||||
case 25000000:
|
||||
sam = 13;
|
||||
drv = 6;
|
||||
div = 6;
|
||||
break;
|
||||
case 50000000:
|
||||
sam = 3;
|
||||
drv = 6;
|
||||
div = 6;
|
||||
break;
|
||||
case 100000000:
|
||||
sam = 6;
|
||||
drv = 4;
|
||||
div = 6;
|
||||
break;
|
||||
case 180000000:
|
||||
sam = 6;
|
||||
drv = 4;
|
||||
div = 7;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&mmc_clk_lock, flags);
|
||||
|
||||
val = readl_relaxed(mclk->clken_reg);
|
||||
val &= ~(1 << mclk->clken_bit);
|
||||
writel_relaxed(val, mclk->clken_reg);
|
||||
|
||||
val = readl_relaxed(mclk->sam_reg);
|
||||
val = mmc_clk_delay(val, sam, mclk->sam_off, mclk->sam_bits);
|
||||
writel_relaxed(val, mclk->sam_reg);
|
||||
|
||||
val = readl_relaxed(mclk->drv_reg);
|
||||
val = mmc_clk_delay(val, drv, mclk->drv_off, mclk->drv_bits);
|
||||
writel_relaxed(val, mclk->drv_reg);
|
||||
|
||||
val = readl_relaxed(mclk->div_reg);
|
||||
val = mmc_clk_delay(val, div, mclk->div_off, mclk->div_bits);
|
||||
writel_relaxed(val, mclk->div_reg);
|
||||
|
||||
val = readl_relaxed(mclk->clken_reg);
|
||||
val |= 1 << mclk->clken_bit;
|
||||
writel_relaxed(val, mclk->clken_reg);
|
||||
|
||||
spin_unlock_irqrestore(&mmc_clk_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mmc_clk_prepare(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_mmc *mclk = to_mmc(hw);
|
||||
unsigned long rate;
|
||||
|
||||
if (mclk->id == HI3620_MMC_CIUCLK1)
|
||||
rate = 13000000;
|
||||
else
|
||||
rate = 25000000;
|
||||
|
||||
return mmc_clk_set_timing(hw, rate);
|
||||
}
|
||||
|
||||
static int mmc_clk_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return mmc_clk_set_timing(hw, rate);
|
||||
}
|
||||
|
||||
static struct clk_ops clk_mmc_ops = {
|
||||
.prepare = mmc_clk_prepare,
|
||||
.determine_rate = mmc_clk_determine_rate,
|
||||
.set_rate = mmc_clk_set_rate,
|
||||
.recalc_rate = mmc_clk_recalc_rate,
|
||||
};
|
||||
|
||||
static struct clk *hisi_register_clk_mmc(struct hisi_mmc_clock *mmc_clk,
|
||||
void __iomem *base, struct device_node *np)
|
||||
{
|
||||
struct clk_mmc *mclk;
|
||||
struct clk *clk;
|
||||
struct clk_init_data init;
|
||||
|
||||
mclk = kzalloc(sizeof(*mclk), GFP_KERNEL);
|
||||
if (!mclk) {
|
||||
pr_err("%s: fail to allocate mmc clk\n", __func__);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
init.name = mmc_clk->name;
|
||||
init.ops = &clk_mmc_ops;
|
||||
init.flags = mmc_clk->flags | CLK_IS_BASIC;
|
||||
init.parent_names = (mmc_clk->parent_name ? &mmc_clk->parent_name : NULL);
|
||||
init.num_parents = (mmc_clk->parent_name ? 1 : 0);
|
||||
mclk->hw.init = &init;
|
||||
|
||||
mclk->id = mmc_clk->id;
|
||||
mclk->clken_reg = base + mmc_clk->clken_reg;
|
||||
mclk->clken_bit = mmc_clk->clken_bit;
|
||||
mclk->div_reg = base + mmc_clk->div_reg;
|
||||
mclk->div_off = mmc_clk->div_off;
|
||||
mclk->div_bits = mmc_clk->div_bits;
|
||||
mclk->drv_reg = base + mmc_clk->drv_reg;
|
||||
mclk->drv_off = mmc_clk->drv_off;
|
||||
mclk->drv_bits = mmc_clk->drv_bits;
|
||||
mclk->sam_reg = base + mmc_clk->sam_reg;
|
||||
mclk->sam_off = mmc_clk->sam_off;
|
||||
mclk->sam_bits = mmc_clk->sam_bits;
|
||||
|
||||
clk = clk_register(NULL, &mclk->hw);
|
||||
if (WARN_ON(IS_ERR(clk)))
|
||||
kfree(mclk);
|
||||
return clk;
|
||||
}
|
||||
|
||||
static void __init hi3620_mmc_clk_init(struct device_node *node)
|
||||
{
|
||||
void __iomem *base;
|
||||
int i, num = ARRAY_SIZE(hi3620_mmc_clks);
|
||||
struct clk_onecell_data *clk_data;
|
||||
|
||||
if (!node) {
|
||||
pr_err("failed to find pctrl node in DTS\n");
|
||||
return;
|
||||
}
|
||||
|
||||
base = of_iomap(node, 0);
|
||||
if (!base) {
|
||||
pr_err("failed to map pctrl\n");
|
||||
return;
|
||||
}
|
||||
|
||||
clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
|
||||
if (WARN_ON(!clk_data))
|
||||
return;
|
||||
|
||||
clk_data->clks = kzalloc(sizeof(struct clk *) * num, GFP_KERNEL);
|
||||
if (!clk_data->clks) {
|
||||
pr_err("%s: fail to allocate mmc clk\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
struct hisi_mmc_clock *mmc_clk = &hi3620_mmc_clks[i];
|
||||
clk_data->clks[mmc_clk->id] =
|
||||
hisi_register_clk_mmc(mmc_clk, base, node);
|
||||
}
|
||||
|
||||
clk_data->clk_num = num;
|
||||
of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
|
||||
}
|
||||
|
||||
CLK_OF_DECLARE(hi3620_mmc_clk, "hisilicon,hi3620-mmc-clock", hi3620_mmc_clk_init);
|
||||
|
|
58
drivers/clk/hisilicon/clk-hip04.c
Normal file
58
drivers/clk/hisilicon/clk-hip04.c
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Hisilicon HiP04 clock driver
|
||||
*
|
||||
* Copyright (c) 2013-2014 Hisilicon Limited.
|
||||
* Copyright (c) 2013-2014 Linaro Limited.
|
||||
*
|
||||
* Author: Haojian Zhuang <haojian.zhuang@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/hip04-clock.h>
|
||||
|
||||
#include "clk.h"
|
||||
|
||||
/* fixed rate clocks */
|
||||
static struct hisi_fixed_rate_clock hip04_fixed_rate_clks[] __initdata = {
|
||||
{ HIP04_OSC50M, "osc50m", NULL, CLK_IS_ROOT, 50000000, },
|
||||
{ HIP04_CLK_50M, "clk50m", NULL, CLK_IS_ROOT, 50000000, },
|
||||
{ HIP04_CLK_168M, "clk168m", NULL, CLK_IS_ROOT, 168750000, },
|
||||
};
|
||||
|
||||
static void __init hip04_clk_init(struct device_node *np)
|
||||
{
|
||||
struct hisi_clock_data *clk_data;
|
||||
|
||||
clk_data = hisi_clk_init(np, HIP04_NR_CLKS);
|
||||
if (!clk_data)
|
||||
return;
|
||||
|
||||
hisi_clk_register_fixed_rate(hip04_fixed_rate_clks,
|
||||
ARRAY_SIZE(hip04_fixed_rate_clks),
|
||||
clk_data);
|
||||
}
|
||||
CLK_OF_DECLARE(hip04_clk, "hisilicon,hip04-clock", hip04_clk_init);
|
|
@ -37,23 +37,49 @@
|
|||
#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)
|
||||
struct hisi_clock_data __init *hisi_clk_init(struct device_node *np,
|
||||
int nr_clks)
|
||||
{
|
||||
struct hisi_clock_data *clk_data;
|
||||
struct clk **clk_table;
|
||||
void __iomem *base;
|
||||
|
||||
if (np) {
|
||||
base = of_iomap(np, 0);
|
||||
if (!base) {
|
||||
pr_err("failed to map Hisilicon clock registers\n");
|
||||
goto err;
|
||||
}
|
||||
} else {
|
||||
pr_err("failed to find Hisilicon clock node in DTS\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
|
||||
if (!clk_data) {
|
||||
pr_err("%s: could not allocate clock data\n", __func__);
|
||||
goto err;
|
||||
}
|
||||
clk_data->base = base;
|
||||
|
||||
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;
|
||||
goto err_data;
|
||||
}
|
||||
clk_data.clks = clk_table;
|
||||
clk_data.clk_num = nr_clks;
|
||||
of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
|
||||
clk_data->clk_data.clks = clk_table;
|
||||
clk_data->clk_data.clk_num = nr_clks;
|
||||
of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data->clk_data);
|
||||
return clk_data;
|
||||
err_data:
|
||||
kfree(clk_data);
|
||||
err:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void __init hisi_clk_register_fixed_rate(struct hisi_fixed_rate_clock *clks,
|
||||
int nums, void __iomem *base)
|
||||
int nums, struct hisi_clock_data *data)
|
||||
{
|
||||
struct clk *clk;
|
||||
int i;
|
||||
|
@ -68,11 +94,13 @@ void __init hisi_clk_register_fixed_rate(struct hisi_fixed_rate_clock *clks,
|
|||
__func__, clks[i].name);
|
||||
continue;
|
||||
}
|
||||
data->clk_data.clks[clks[i].id] = clk;
|
||||
}
|
||||
}
|
||||
|
||||
void __init hisi_clk_register_fixed_factor(struct hisi_fixed_factor_clock *clks,
|
||||
int nums, void __iomem *base)
|
||||
int nums,
|
||||
struct hisi_clock_data *data)
|
||||
{
|
||||
struct clk *clk;
|
||||
int i;
|
||||
|
@ -87,13 +115,15 @@ void __init hisi_clk_register_fixed_factor(struct hisi_fixed_factor_clock *clks,
|
|||
__func__, clks[i].name);
|
||||
continue;
|
||||
}
|
||||
data->clk_data.clks[clks[i].id] = clk;
|
||||
}
|
||||
}
|
||||
|
||||
void __init hisi_clk_register_mux(struct hisi_mux_clock *clks,
|
||||
int nums, void __iomem *base)
|
||||
int nums, struct hisi_clock_data *data)
|
||||
{
|
||||
struct clk *clk;
|
||||
void __iomem *base = data->base;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nums; i++) {
|
||||
|
@ -111,14 +141,15 @@ void __init hisi_clk_register_mux(struct hisi_mux_clock *clks,
|
|||
if (clks[i].alias)
|
||||
clk_register_clkdev(clk, clks[i].alias, NULL);
|
||||
|
||||
clk_table[clks[i].id] = clk;
|
||||
data->clk_data.clks[clks[i].id] = clk;
|
||||
}
|
||||
}
|
||||
|
||||
void __init hisi_clk_register_divider(struct hisi_divider_clock *clks,
|
||||
int nums, void __iomem *base)
|
||||
int nums, struct hisi_clock_data *data)
|
||||
{
|
||||
struct clk *clk;
|
||||
void __iomem *base = data->base;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nums; i++) {
|
||||
|
@ -139,14 +170,15 @@ void __init hisi_clk_register_divider(struct hisi_divider_clock *clks,
|
|||
if (clks[i].alias)
|
||||
clk_register_clkdev(clk, clks[i].alias, NULL);
|
||||
|
||||
clk_table[clks[i].id] = clk;
|
||||
data->clk_data.clks[clks[i].id] = clk;
|
||||
}
|
||||
}
|
||||
|
||||
void __init hisi_clk_register_gate_sep(struct hisi_gate_clock *clks,
|
||||
int nums, void __iomem *base)
|
||||
int nums, struct hisi_clock_data *data)
|
||||
{
|
||||
struct clk *clk;
|
||||
void __iomem *base = data->base;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nums; i++) {
|
||||
|
@ -166,6 +198,6 @@ void __init hisi_clk_register_gate_sep(struct hisi_gate_clock *clks,
|
|||
if (clks[i].alias)
|
||||
clk_register_clkdev(clk, clks[i].alias, NULL);
|
||||
|
||||
clk_table[clks[i].id] = clk;
|
||||
data->clk_data.clks[clks[i].id] = clk;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,11 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
struct hisi_clock_data {
|
||||
struct clk_onecell_data clk_data;
|
||||
void __iomem *base;
|
||||
};
|
||||
|
||||
struct hisi_fixed_rate_clock {
|
||||
unsigned int id;
|
||||
char *name;
|
||||
|
@ -89,15 +94,15 @@ struct clk *hisi_register_clkgate_sep(struct device *, const char *,
|
|||
void __iomem *, u8,
|
||||
u8, spinlock_t *);
|
||||
|
||||
void __init hisi_clk_init(struct device_node *, int);
|
||||
struct hisi_clock_data __init *hisi_clk_init(struct device_node *, int);
|
||||
void __init hisi_clk_register_fixed_rate(struct hisi_fixed_rate_clock *,
|
||||
int, void __iomem *);
|
||||
int, struct hisi_clock_data *);
|
||||
void __init hisi_clk_register_fixed_factor(struct hisi_fixed_factor_clock *,
|
||||
int, void __iomem *);
|
||||
int, struct hisi_clock_data *);
|
||||
void __init hisi_clk_register_mux(struct hisi_mux_clock *, int,
|
||||
void __iomem *);
|
||||
struct hisi_clock_data *);
|
||||
void __init hisi_clk_register_divider(struct hisi_divider_clock *,
|
||||
int, void __iomem *);
|
||||
int, struct hisi_clock_data *);
|
||||
void __init hisi_clk_register_gate_sep(struct hisi_gate_clock *,
|
||||
int, void __iomem *);
|
||||
int, struct hisi_clock_data *);
|
||||
#endif /* __HISI_CLK_H */
|
||||
|
|
|
@ -40,15 +40,19 @@ static long clk_factor_round_rate(struct clk_hw *hw, unsigned long drate,
|
|||
|
||||
for (i = 0; i < factor->ftbl_cnt; i++) {
|
||||
prev_rate = rate;
|
||||
rate = (((*prate / 10000) * factor->ftbl[i].num) /
|
||||
(factor->ftbl[i].den * factor->masks->factor)) * 10000;
|
||||
rate = (((*prate / 10000) * factor->ftbl[i].den) /
|
||||
(factor->ftbl[i].num * factor->masks->factor)) * 10000;
|
||||
if (rate > drate)
|
||||
break;
|
||||
}
|
||||
if (i == 0)
|
||||
if ((i == 0) || (i == factor->ftbl_cnt)) {
|
||||
return rate;
|
||||
else
|
||||
return prev_rate;
|
||||
} else {
|
||||
if ((drate - prev_rate) > (rate - drate))
|
||||
return rate;
|
||||
else
|
||||
return prev_rate;
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned long clk_factor_recalc_rate(struct clk_hw *hw,
|
||||
|
@ -64,7 +68,7 @@ static unsigned long clk_factor_recalc_rate(struct clk_hw *hw,
|
|||
num = (val >> masks->num_shift) & masks->num_mask;
|
||||
|
||||
/* calculate denominator */
|
||||
den = (val >> masks->den_shift) & masks->num_mask;
|
||||
den = (val >> masks->den_shift) & masks->den_mask;
|
||||
|
||||
if (!den)
|
||||
return 0;
|
||||
|
@ -85,8 +89,8 @@ static int clk_factor_set_rate(struct clk_hw *hw, unsigned long drate,
|
|||
|
||||
for (i = 0; i < factor->ftbl_cnt; i++) {
|
||||
prev_rate = rate;
|
||||
rate = (((prate / 10000) * factor->ftbl[i].num) /
|
||||
(factor->ftbl[i].den * factor->masks->factor)) * 10000;
|
||||
rate = (((prate / 10000) * factor->ftbl[i].den) /
|
||||
(factor->ftbl[i].num * factor->masks->factor)) * 10000;
|
||||
if (rate > drate)
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -13,6 +13,14 @@ config ARMADA_370_CLK
|
|||
select MVEBU_CLK_CPU
|
||||
select MVEBU_CLK_COREDIV
|
||||
|
||||
config ARMADA_375_CLK
|
||||
bool
|
||||
select MVEBU_CLK_COMMON
|
||||
|
||||
config ARMADA_38X_CLK
|
||||
bool
|
||||
select MVEBU_CLK_COMMON
|
||||
|
||||
config ARMADA_XP_CLK
|
||||
bool
|
||||
select MVEBU_CLK_COMMON
|
||||
|
|
|
@ -3,6 +3,8 @@ 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_375_CLK) += armada-375.o
|
||||
obj-$(CONFIG_ARMADA_38X_CLK) += armada-38x.o
|
||||
obj-$(CONFIG_ARMADA_XP_CLK) += armada-xp.o
|
||||
obj-$(CONFIG_DOVE_CLK) += dove.o
|
||||
obj-$(CONFIG_KIRKWOOD_CLK) += kirkwood.o
|
||||
|
|
184
drivers/clk/mvebu/armada-375.c
Normal file
184
drivers/clk/mvebu/armada-375.c
Normal file
|
@ -0,0 +1,184 @@
|
|||
/*
|
||||
* Marvell Armada 375 SoC clocks
|
||||
*
|
||||
* Copyright (C) 2014 Marvell
|
||||
*
|
||||
* Gregory CLEMENT <gregory.clement@free-electrons.com>
|
||||
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
|
||||
* Andrew Lunn <andrew@lunn.ch>
|
||||
*
|
||||
* 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/io.h>
|
||||
#include <linux/of.h>
|
||||
#include "common.h"
|
||||
|
||||
/*
|
||||
* Core Clocks
|
||||
*/
|
||||
|
||||
/*
|
||||
* For the Armada 375 SoCs, the CPU, DDR and L2 clocks frequencies are
|
||||
* all modified at the same time, and not separately as for the Armada
|
||||
* 370 or the Armada XP SoCs.
|
||||
*
|
||||
* SAR0[21:17] : CPU frequency DDR frequency L2 frequency
|
||||
* 6 = 400 MHz 400 MHz 200 MHz
|
||||
* 15 = 600 MHz 600 MHz 300 MHz
|
||||
* 21 = 800 MHz 534 MHz 400 MHz
|
||||
* 25 = 1000 MHz 500 MHz 500 MHz
|
||||
* others reserved.
|
||||
*
|
||||
* SAR0[22] : TCLK frequency
|
||||
* 0 = 166 MHz
|
||||
* 1 = 200 MHz
|
||||
*/
|
||||
|
||||
#define SAR1_A375_TCLK_FREQ_OPT 22
|
||||
#define SAR1_A375_TCLK_FREQ_OPT_MASK 0x1
|
||||
#define SAR1_A375_CPU_DDR_L2_FREQ_OPT 17
|
||||
#define SAR1_A375_CPU_DDR_L2_FREQ_OPT_MASK 0x1F
|
||||
|
||||
static const u32 armada_375_tclk_frequencies[] __initconst = {
|
||||
166000000,
|
||||
200000000,
|
||||
};
|
||||
|
||||
static u32 __init armada_375_get_tclk_freq(void __iomem *sar)
|
||||
{
|
||||
u8 tclk_freq_select;
|
||||
|
||||
tclk_freq_select = ((readl(sar) >> SAR1_A375_TCLK_FREQ_OPT) &
|
||||
SAR1_A375_TCLK_FREQ_OPT_MASK);
|
||||
return armada_375_tclk_frequencies[tclk_freq_select];
|
||||
}
|
||||
|
||||
|
||||
static const u32 armada_375_cpu_frequencies[] __initconst = {
|
||||
0, 0, 0, 0, 0, 0,
|
||||
400000000,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
600000000,
|
||||
0, 0, 0, 0, 0,
|
||||
800000000,
|
||||
0, 0, 0,
|
||||
1000000000,
|
||||
};
|
||||
|
||||
static u32 __init armada_375_get_cpu_freq(void __iomem *sar)
|
||||
{
|
||||
u8 cpu_freq_select;
|
||||
|
||||
cpu_freq_select = ((readl(sar) >> SAR1_A375_CPU_DDR_L2_FREQ_OPT) &
|
||||
SAR1_A375_CPU_DDR_L2_FREQ_OPT_MASK);
|
||||
if (cpu_freq_select >= ARRAY_SIZE(armada_375_cpu_frequencies)) {
|
||||
pr_err("Selected CPU frequency (%d) unsupported\n",
|
||||
cpu_freq_select);
|
||||
return 0;
|
||||
} else
|
||||
return armada_375_cpu_frequencies[cpu_freq_select];
|
||||
}
|
||||
|
||||
enum { A375_CPU_TO_DDR, A375_CPU_TO_L2 };
|
||||
|
||||
static const struct coreclk_ratio armada_375_coreclk_ratios[] __initconst = {
|
||||
{ .id = A375_CPU_TO_L2, .name = "l2clk" },
|
||||
{ .id = A375_CPU_TO_DDR, .name = "ddrclk" },
|
||||
};
|
||||
|
||||
static const int armada_375_cpu_l2_ratios[32][2] __initconst = {
|
||||
{0, 1}, {0, 1}, {0, 1}, {0, 1},
|
||||
{0, 1}, {0, 1}, {1, 2}, {0, 1},
|
||||
{0, 1}, {0, 1}, {0, 1}, {0, 1},
|
||||
{0, 1}, {0, 1}, {0, 1}, {1, 2},
|
||||
{0, 1}, {0, 1}, {0, 1}, {0, 1},
|
||||
{0, 1}, {1, 2}, {0, 1}, {0, 1},
|
||||
{0, 1}, {1, 2}, {0, 1}, {0, 1},
|
||||
{0, 1}, {0, 1}, {0, 1}, {0, 1},
|
||||
};
|
||||
|
||||
static const int armada_375_cpu_ddr_ratios[32][2] __initconst = {
|
||||
{0, 1}, {0, 1}, {0, 1}, {0, 1},
|
||||
{0, 1}, {0, 1}, {1, 1}, {0, 1},
|
||||
{0, 1}, {0, 1}, {0, 1}, {0, 1},
|
||||
{0, 1}, {0, 1}, {0, 1}, {2, 3},
|
||||
{0, 1}, {0, 1}, {0, 1}, {0, 1},
|
||||
{0, 1}, {2, 3}, {0, 1}, {0, 1},
|
||||
{0, 1}, {1, 2}, {0, 1}, {0, 1},
|
||||
{0, 1}, {0, 1}, {0, 1}, {0, 1},
|
||||
};
|
||||
|
||||
static void __init armada_375_get_clk_ratio(
|
||||
void __iomem *sar, int id, int *mult, int *div)
|
||||
{
|
||||
u32 opt = ((readl(sar) >> SAR1_A375_CPU_DDR_L2_FREQ_OPT) &
|
||||
SAR1_A375_CPU_DDR_L2_FREQ_OPT_MASK);
|
||||
|
||||
switch (id) {
|
||||
case A375_CPU_TO_L2:
|
||||
*mult = armada_375_cpu_l2_ratios[opt][0];
|
||||
*div = armada_375_cpu_l2_ratios[opt][1];
|
||||
break;
|
||||
case A375_CPU_TO_DDR:
|
||||
*mult = armada_375_cpu_ddr_ratios[opt][0];
|
||||
*div = armada_375_cpu_ddr_ratios[opt][1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct coreclk_soc_desc armada_375_coreclks = {
|
||||
.get_tclk_freq = armada_375_get_tclk_freq,
|
||||
.get_cpu_freq = armada_375_get_cpu_freq,
|
||||
.get_clk_ratio = armada_375_get_clk_ratio,
|
||||
.ratios = armada_375_coreclk_ratios,
|
||||
.num_ratios = ARRAY_SIZE(armada_375_coreclk_ratios),
|
||||
};
|
||||
|
||||
static void __init armada_375_coreclk_init(struct device_node *np)
|
||||
{
|
||||
mvebu_coreclk_setup(np, &armada_375_coreclks);
|
||||
}
|
||||
CLK_OF_DECLARE(armada_375_core_clk, "marvell,armada-375-core-clock",
|
||||
armada_375_coreclk_init);
|
||||
|
||||
/*
|
||||
* Clock Gating Control
|
||||
*/
|
||||
static const struct clk_gating_soc_desc armada_375_gating_desc[] __initconst = {
|
||||
{ "mu", NULL, 2 },
|
||||
{ "pp", NULL, 3 },
|
||||
{ "ptp", NULL, 4 },
|
||||
{ "pex0", NULL, 5 },
|
||||
{ "pex1", NULL, 6 },
|
||||
{ "audio", NULL, 8 },
|
||||
{ "nd_clk", "nand", 11 },
|
||||
{ "sata0_link", "sata0_core", 14 },
|
||||
{ "sata0_core", NULL, 15 },
|
||||
{ "usb3", NULL, 16 },
|
||||
{ "sdio", NULL, 17 },
|
||||
{ "usb", NULL, 18 },
|
||||
{ "gop", NULL, 19 },
|
||||
{ "sata1_link", "sata1_core", 20 },
|
||||
{ "sata1_core", NULL, 21 },
|
||||
{ "xor0", NULL, 22 },
|
||||
{ "xor1", NULL, 23 },
|
||||
{ "copro", NULL, 24 },
|
||||
{ "tdm", NULL, 25 },
|
||||
{ "crypto0_enc", NULL, 28 },
|
||||
{ "crypto0_core", NULL, 29 },
|
||||
{ "crypto1_enc", NULL, 30 },
|
||||
{ "crypto1_core", NULL, 31 },
|
||||
{ }
|
||||
};
|
||||
|
||||
static void __init armada_375_clk_gating_init(struct device_node *np)
|
||||
{
|
||||
mvebu_clk_gating_setup(np, armada_375_gating_desc);
|
||||
}
|
||||
CLK_OF_DECLARE(armada_375_clk_gating, "marvell,armada-375-gating-clock",
|
||||
armada_375_clk_gating_init);
|
167
drivers/clk/mvebu/armada-38x.c
Normal file
167
drivers/clk/mvebu/armada-38x.c
Normal file
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
* Marvell Armada 380/385 SoC clocks
|
||||
*
|
||||
* Copyright (C) 2014 Marvell
|
||||
*
|
||||
* Gregory CLEMENT <gregory.clement@free-electrons.com>
|
||||
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
|
||||
* Andrew Lunn <andrew@lunn.ch>
|
||||
*
|
||||
* 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/io.h>
|
||||
#include <linux/of.h>
|
||||
#include "common.h"
|
||||
|
||||
/*
|
||||
* SAR[14:10] : Ratios between PCLK0, NBCLK, HCLK and DRAM clocks
|
||||
*
|
||||
* SAR[15] : TCLK frequency
|
||||
* 0 = 250 MHz
|
||||
* 1 = 200 MHz
|
||||
*/
|
||||
|
||||
#define SAR_A380_TCLK_FREQ_OPT 15
|
||||
#define SAR_A380_TCLK_FREQ_OPT_MASK 0x1
|
||||
#define SAR_A380_CPU_DDR_L2_FREQ_OPT 10
|
||||
#define SAR_A380_CPU_DDR_L2_FREQ_OPT_MASK 0x1F
|
||||
|
||||
static const u32 armada_38x_tclk_frequencies[] __initconst = {
|
||||
250000000,
|
||||
200000000,
|
||||
};
|
||||
|
||||
static u32 __init armada_38x_get_tclk_freq(void __iomem *sar)
|
||||
{
|
||||
u8 tclk_freq_select;
|
||||
|
||||
tclk_freq_select = ((readl(sar) >> SAR_A380_TCLK_FREQ_OPT) &
|
||||
SAR_A380_TCLK_FREQ_OPT_MASK);
|
||||
return armada_38x_tclk_frequencies[tclk_freq_select];
|
||||
}
|
||||
|
||||
static const u32 armada_38x_cpu_frequencies[] __initconst = {
|
||||
0, 0, 0, 0,
|
||||
1066 * 1000 * 1000, 0, 0, 0,
|
||||
1332 * 1000 * 1000, 0, 0, 0,
|
||||
1600 * 1000 * 1000,
|
||||
};
|
||||
|
||||
static u32 __init armada_38x_get_cpu_freq(void __iomem *sar)
|
||||
{
|
||||
u8 cpu_freq_select;
|
||||
|
||||
cpu_freq_select = ((readl(sar) >> SAR_A380_CPU_DDR_L2_FREQ_OPT) &
|
||||
SAR_A380_CPU_DDR_L2_FREQ_OPT_MASK);
|
||||
if (cpu_freq_select >= ARRAY_SIZE(armada_38x_cpu_frequencies)) {
|
||||
pr_err("Selected CPU frequency (%d) unsupported\n",
|
||||
cpu_freq_select);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return armada_38x_cpu_frequencies[cpu_freq_select];
|
||||
}
|
||||
|
||||
enum { A380_CPU_TO_DDR, A380_CPU_TO_L2 };
|
||||
|
||||
static const struct coreclk_ratio armada_38x_coreclk_ratios[] __initconst = {
|
||||
{ .id = A380_CPU_TO_L2, .name = "l2clk" },
|
||||
{ .id = A380_CPU_TO_DDR, .name = "ddrclk" },
|
||||
};
|
||||
|
||||
static const int armada_38x_cpu_l2_ratios[32][2] __initconst = {
|
||||
{0, 1}, {0, 1}, {0, 1}, {0, 1},
|
||||
{1, 2}, {0, 1}, {0, 1}, {0, 1},
|
||||
{1, 2}, {0, 1}, {0, 1}, {0, 1},
|
||||
{1, 2}, {0, 1}, {0, 1}, {0, 1},
|
||||
{0, 1}, {0, 1}, {0, 1}, {0, 1},
|
||||
{0, 1}, {0, 1}, {0, 1}, {0, 1},
|
||||
{0, 1}, {0, 1}, {0, 1}, {0, 1},
|
||||
{0, 1}, {0, 1}, {0, 1}, {0, 1},
|
||||
};
|
||||
|
||||
static const int armada_38x_cpu_ddr_ratios[32][2] __initconst = {
|
||||
{0, 1}, {0, 1}, {0, 1}, {0, 1},
|
||||
{1, 2}, {0, 1}, {0, 1}, {0, 1},
|
||||
{1, 2}, {0, 1}, {0, 1}, {0, 1},
|
||||
{1, 2}, {0, 1}, {0, 1}, {0, 1},
|
||||
{0, 1}, {0, 1}, {0, 1}, {0, 1},
|
||||
{0, 1}, {0, 1}, {0, 1}, {0, 1},
|
||||
{0, 1}, {0, 1}, {0, 1}, {0, 1},
|
||||
{0, 1}, {0, 1}, {0, 1}, {0, 1},
|
||||
};
|
||||
|
||||
static void __init armada_38x_get_clk_ratio(
|
||||
void __iomem *sar, int id, int *mult, int *div)
|
||||
{
|
||||
u32 opt = ((readl(sar) >> SAR_A380_CPU_DDR_L2_FREQ_OPT) &
|
||||
SAR_A380_CPU_DDR_L2_FREQ_OPT_MASK);
|
||||
|
||||
switch (id) {
|
||||
case A380_CPU_TO_L2:
|
||||
*mult = armada_38x_cpu_l2_ratios[opt][0];
|
||||
*div = armada_38x_cpu_l2_ratios[opt][1];
|
||||
break;
|
||||
case A380_CPU_TO_DDR:
|
||||
*mult = armada_38x_cpu_ddr_ratios[opt][0];
|
||||
*div = armada_38x_cpu_ddr_ratios[opt][1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct coreclk_soc_desc armada_38x_coreclks = {
|
||||
.get_tclk_freq = armada_38x_get_tclk_freq,
|
||||
.get_cpu_freq = armada_38x_get_cpu_freq,
|
||||
.get_clk_ratio = armada_38x_get_clk_ratio,
|
||||
.ratios = armada_38x_coreclk_ratios,
|
||||
.num_ratios = ARRAY_SIZE(armada_38x_coreclk_ratios),
|
||||
};
|
||||
|
||||
static void __init armada_38x_coreclk_init(struct device_node *np)
|
||||
{
|
||||
mvebu_coreclk_setup(np, &armada_38x_coreclks);
|
||||
}
|
||||
CLK_OF_DECLARE(armada_38x_core_clk, "marvell,armada-380-core-clock",
|
||||
armada_38x_coreclk_init);
|
||||
|
||||
/*
|
||||
* Clock Gating Control
|
||||
*/
|
||||
static const struct clk_gating_soc_desc armada_38x_gating_desc[] __initconst = {
|
||||
{ "audio", NULL, 0 },
|
||||
{ "ge2", NULL, 2 },
|
||||
{ "ge1", NULL, 3 },
|
||||
{ "ge0", NULL, 4 },
|
||||
{ "pex1", NULL, 5 },
|
||||
{ "pex2", NULL, 6 },
|
||||
{ "pex3", NULL, 7 },
|
||||
{ "pex0", NULL, 8 },
|
||||
{ "usb3h0", NULL, 9 },
|
||||
{ "usb3h1", NULL, 10 },
|
||||
{ "usb3d", NULL, 11 },
|
||||
{ "bm", NULL, 13 },
|
||||
{ "crypto0z", NULL, 14 },
|
||||
{ "sata0", NULL, 15 },
|
||||
{ "crypto1z", NULL, 16 },
|
||||
{ "sdio", NULL, 17 },
|
||||
{ "usb2", NULL, 18 },
|
||||
{ "crypto1", NULL, 21 },
|
||||
{ "xor0", NULL, 22 },
|
||||
{ "crypto0", NULL, 23 },
|
||||
{ "tdm", NULL, 25 },
|
||||
{ "xor1", NULL, 28 },
|
||||
{ "sata1", NULL, 30 },
|
||||
{ }
|
||||
};
|
||||
|
||||
static void __init armada_38x_clk_gating_init(struct device_node *np)
|
||||
{
|
||||
mvebu_clk_gating_setup(np, armada_38x_gating_desc);
|
||||
}
|
||||
CLK_OF_DECLARE(armada_38x_clk_gating, "marvell,armada-380-gating-clock",
|
||||
armada_38x_clk_gating_init);
|
|
@ -18,26 +18,56 @@
|
|||
#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
|
||||
|
||||
/*
|
||||
* This structure describes the hardware details (bit offset and mask)
|
||||
* to configure one particular core divider clock. Those hardware
|
||||
* details may differ from one SoC to another. This structure is
|
||||
* therefore typically instantiated statically to describe the
|
||||
* hardware details.
|
||||
*/
|
||||
struct clk_corediv_desc {
|
||||
unsigned int mask;
|
||||
unsigned int offset;
|
||||
unsigned int fieldbit;
|
||||
};
|
||||
|
||||
/*
|
||||
* This structure describes the hardware details to configure the core
|
||||
* divider clocks on a given SoC. Amongst others, it points to the
|
||||
* array of core divider clock descriptors for this SoC, as well as
|
||||
* the corresponding operations to manipulate them.
|
||||
*/
|
||||
struct clk_corediv_soc_desc {
|
||||
const struct clk_corediv_desc *descs;
|
||||
unsigned int ndescs;
|
||||
const struct clk_ops ops;
|
||||
u32 ratio_reload;
|
||||
u32 enable_bit_offset;
|
||||
u32 ratio_offset;
|
||||
};
|
||||
|
||||
/*
|
||||
* This structure represents one core divider clock for the clock
|
||||
* framework, and is dynamically allocated for each core divider clock
|
||||
* existing in the current SoC.
|
||||
*/
|
||||
struct clk_corediv {
|
||||
struct clk_hw hw;
|
||||
void __iomem *reg;
|
||||
struct clk_corediv_desc desc;
|
||||
const struct clk_corediv_desc *desc;
|
||||
const struct clk_corediv_soc_desc *soc_desc;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
static struct clk_onecell_data clk_data;
|
||||
|
||||
static const struct clk_corediv_desc mvebu_corediv_desc[] __initconst = {
|
||||
/*
|
||||
* Description of the core divider clocks available. For now, we
|
||||
* support only NAND, and it is available at the same register
|
||||
* locations regardless of the SoC.
|
||||
*/
|
||||
static const struct clk_corediv_desc mvebu_corediv_desc[] = {
|
||||
{ .mask = 0x3f, .offset = 8, .fieldbit = 1 }, /* NAND clock */
|
||||
};
|
||||
|
||||
|
@ -46,8 +76,9 @@ static const struct clk_corediv_desc mvebu_corediv_desc[] __initconst = {
|
|||
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;
|
||||
const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc;
|
||||
const struct clk_corediv_desc *desc = corediv->desc;
|
||||
u32 enable_mask = BIT(desc->fieldbit) << soc_desc->enable_bit_offset;
|
||||
|
||||
return !!(readl(corediv->reg) & enable_mask);
|
||||
}
|
||||
|
@ -55,14 +86,15 @@ static int clk_corediv_is_enabled(struct clk_hw *hwclk)
|
|||
static int clk_corediv_enable(struct clk_hw *hwclk)
|
||||
{
|
||||
struct clk_corediv *corediv = to_corediv_clk(hwclk);
|
||||
struct clk_corediv_desc *desc = &corediv->desc;
|
||||
const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc;
|
||||
const 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);
|
||||
reg |= (BIT(desc->fieldbit) << soc_desc->enable_bit_offset);
|
||||
writel(reg, corediv->reg);
|
||||
|
||||
spin_unlock_irqrestore(&corediv->lock, flags);
|
||||
|
@ -73,14 +105,15 @@ static int clk_corediv_enable(struct clk_hw *hwclk)
|
|||
static void clk_corediv_disable(struct clk_hw *hwclk)
|
||||
{
|
||||
struct clk_corediv *corediv = to_corediv_clk(hwclk);
|
||||
struct clk_corediv_desc *desc = &corediv->desc;
|
||||
const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc;
|
||||
const 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);
|
||||
reg &= ~(BIT(desc->fieldbit) << soc_desc->enable_bit_offset);
|
||||
writel(reg, corediv->reg);
|
||||
|
||||
spin_unlock_irqrestore(&corediv->lock, flags);
|
||||
|
@ -90,10 +123,11 @@ 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;
|
||||
const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc;
|
||||
const struct clk_corediv_desc *desc = corediv->desc;
|
||||
u32 reg, div;
|
||||
|
||||
reg = readl(corediv->reg + CORE_CLK_DIV_RATIO_OFFSET);
|
||||
reg = readl(corediv->reg + soc_desc->ratio_offset);
|
||||
div = (reg >> desc->offset) & desc->mask;
|
||||
return parent_rate / div;
|
||||
}
|
||||
|
@ -117,7 +151,8 @@ 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;
|
||||
const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc;
|
||||
const struct clk_corediv_desc *desc = corediv->desc;
|
||||
unsigned long flags = 0;
|
||||
u32 reg, div;
|
||||
|
||||
|
@ -126,17 +161,17 @@ static int clk_corediv_set_rate(struct clk_hw *hwclk, unsigned long 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 = readl(corediv->reg + soc_desc->ratio_offset);
|
||||
reg &= ~(desc->mask << desc->offset);
|
||||
reg |= (div & desc->mask) << desc->offset;
|
||||
writel(reg, corediv->reg + CORE_CLK_DIV_RATIO_OFFSET);
|
||||
writel(reg, corediv->reg + soc_desc->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;
|
||||
reg = readl(corediv->reg) | soc_desc->ratio_reload;
|
||||
writel(reg, corediv->reg);
|
||||
|
||||
/*
|
||||
|
@ -144,7 +179,7 @@ static int clk_corediv_set_rate(struct clk_hw *hwclk, unsigned long rate,
|
|||
* ratios request and the reload request.
|
||||
*/
|
||||
udelay(1000);
|
||||
reg &= ~(CORE_CLK_DIV_RATIO_MASK | CORE_CLK_DIV_RATIO_RELOAD);
|
||||
reg &= ~(CORE_CLK_DIV_RATIO_MASK | soc_desc->ratio_reload);
|
||||
writel(reg, corediv->reg);
|
||||
udelay(1000);
|
||||
|
||||
|
@ -153,16 +188,53 @@ static int clk_corediv_set_rate(struct clk_hw *hwclk, unsigned long rate,
|
|||
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 const struct clk_corediv_soc_desc armada370_corediv_soc = {
|
||||
.descs = mvebu_corediv_desc,
|
||||
.ndescs = ARRAY_SIZE(mvebu_corediv_desc),
|
||||
.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,
|
||||
},
|
||||
.ratio_reload = BIT(8),
|
||||
.enable_bit_offset = 24,
|
||||
.ratio_offset = 0x8,
|
||||
};
|
||||
|
||||
static void __init mvebu_corediv_clk_init(struct device_node *node)
|
||||
static const struct clk_corediv_soc_desc armada380_corediv_soc = {
|
||||
.descs = mvebu_corediv_desc,
|
||||
.ndescs = ARRAY_SIZE(mvebu_corediv_desc),
|
||||
.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,
|
||||
},
|
||||
.ratio_reload = BIT(8),
|
||||
.enable_bit_offset = 16,
|
||||
.ratio_offset = 0x4,
|
||||
};
|
||||
|
||||
static const struct clk_corediv_soc_desc armada375_corediv_soc = {
|
||||
.descs = mvebu_corediv_desc,
|
||||
.ndescs = ARRAY_SIZE(mvebu_corediv_desc),
|
||||
.ops = {
|
||||
.recalc_rate = clk_corediv_recalc_rate,
|
||||
.round_rate = clk_corediv_round_rate,
|
||||
.set_rate = clk_corediv_set_rate,
|
||||
},
|
||||
.ratio_reload = BIT(8),
|
||||
.ratio_offset = 0x4,
|
||||
};
|
||||
|
||||
static void __init
|
||||
mvebu_corediv_clk_init(struct device_node *node,
|
||||
const struct clk_corediv_soc_desc *soc_desc)
|
||||
{
|
||||
struct clk_init_data init;
|
||||
struct clk_corediv *corediv;
|
||||
|
@ -178,7 +250,7 @@ static void __init mvebu_corediv_clk_init(struct device_node *node)
|
|||
|
||||
parent_name = of_clk_get_parent_name(node, 0);
|
||||
|
||||
clk_data.clk_num = ARRAY_SIZE(mvebu_corediv_desc);
|
||||
clk_data.clk_num = soc_desc->ndescs;
|
||||
|
||||
/* clks holds the clock array */
|
||||
clks = kcalloc(clk_data.clk_num, sizeof(struct clk *),
|
||||
|
@ -199,10 +271,11 @@ static void __init mvebu_corediv_clk_init(struct device_node *node)
|
|||
init.num_parents = 1;
|
||||
init.parent_names = &parent_name;
|
||||
init.name = clk_name;
|
||||
init.ops = &corediv_ops;
|
||||
init.ops = &soc_desc->ops;
|
||||
init.flags = 0;
|
||||
|
||||
corediv[i].desc = mvebu_corediv_desc[i];
|
||||
corediv[i].soc_desc = soc_desc;
|
||||
corediv[i].desc = soc_desc->descs + i;
|
||||
corediv[i].reg = base;
|
||||
corediv[i].hw.init = &init;
|
||||
|
||||
|
@ -219,5 +292,24 @@ static void __init mvebu_corediv_clk_init(struct device_node *node)
|
|||
err_unmap:
|
||||
iounmap(base);
|
||||
}
|
||||
CLK_OF_DECLARE(mvebu_corediv_clk, "marvell,armada-370-corediv-clock",
|
||||
mvebu_corediv_clk_init);
|
||||
|
||||
static void __init armada370_corediv_clk_init(struct device_node *node)
|
||||
{
|
||||
return mvebu_corediv_clk_init(node, &armada370_corediv_soc);
|
||||
}
|
||||
CLK_OF_DECLARE(armada370_corediv_clk, "marvell,armada-370-corediv-clock",
|
||||
armada370_corediv_clk_init);
|
||||
|
||||
static void __init armada375_corediv_clk_init(struct device_node *node)
|
||||
{
|
||||
return mvebu_corediv_clk_init(node, &armada375_corediv_soc);
|
||||
}
|
||||
CLK_OF_DECLARE(armada375_corediv_clk, "marvell,armada-375-corediv-clock",
|
||||
armada375_corediv_clk_init);
|
||||
|
||||
static void __init armada380_corediv_clk_init(struct device_node *node)
|
||||
{
|
||||
return mvebu_corediv_clk_init(node, &armada380_corediv_soc);
|
||||
}
|
||||
CLK_OF_DECLARE(armada380_corediv_clk, "marvell,armada-380-corediv-clock",
|
||||
armada380_corediv_clk_init);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
obj-$(CONFIG_ARCH_EMEV2) += clk-emev2.o
|
||||
obj-$(CONFIG_ARCH_R7S72100) += clk-rz.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
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
#define CPG_DIV6_DIV_MASK 0x3f
|
||||
|
||||
/**
|
||||
* struct div6_clock - MSTP gating clock
|
||||
* struct div6_clock - CPG 6 bit divider clock
|
||||
* @hw: handle between common and hardware-specific interfaces
|
||||
* @reg: IO-remapped register
|
||||
* @div: divisor value (1-64)
|
||||
|
|
|
@ -137,7 +137,7 @@ cpg_mstp_clock_register(const char *name, const char *parent_name,
|
|||
|
||||
init.name = name;
|
||||
init.ops = &cpg_mstp_clock_ops;
|
||||
init.flags = CLK_IS_BASIC;
|
||||
init.flags = CLK_IS_BASIC | CLK_SET_RATE_PARENT;
|
||||
init.parent_names = &parent_name;
|
||||
init.num_parents = 1;
|
||||
|
||||
|
|
|
@ -242,22 +242,22 @@ rcar_gen2_cpg_register_clock(struct device_node *np, struct rcar_gen2_cpg *cpg,
|
|||
parent_name = "main";
|
||||
mult = config->pll3_mult;
|
||||
} else if (!strcmp(name, "lb")) {
|
||||
parent_name = "pll1_div2";
|
||||
parent_name = "pll1";
|
||||
div = cpg_mode & BIT(18) ? 36 : 24;
|
||||
} else if (!strcmp(name, "qspi")) {
|
||||
parent_name = "pll1_div2";
|
||||
div = (cpg_mode & (BIT(3) | BIT(2) | BIT(1))) == BIT(2)
|
||||
? 8 : 10;
|
||||
} else if (!strcmp(name, "sdh")) {
|
||||
parent_name = "pll1_div2";
|
||||
parent_name = "pll1";
|
||||
table = cpg_sdh_div_table;
|
||||
shift = 8;
|
||||
} else if (!strcmp(name, "sd0")) {
|
||||
parent_name = "pll1_div2";
|
||||
parent_name = "pll1";
|
||||
table = cpg_sd01_div_table;
|
||||
shift = 4;
|
||||
} else if (!strcmp(name, "sd1")) {
|
||||
parent_name = "pll1_div2";
|
||||
parent_name = "pll1";
|
||||
table = cpg_sd01_div_table;
|
||||
shift = 0;
|
||||
} else if (!strcmp(name, "z")) {
|
||||
|
|
103
drivers/clk/shmobile/clk-rz.c
Normal file
103
drivers/clk/shmobile/clk-rz.c
Normal file
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* rz Core CPG Clocks
|
||||
*
|
||||
* Copyright (C) 2013 Ideas On Board SPRL
|
||||
* Copyright (C) 2014 Wolfram Sang, Sang Engineering <wsa@sang-engineering.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/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
struct rz_cpg {
|
||||
struct clk_onecell_data data;
|
||||
void __iomem *reg;
|
||||
};
|
||||
|
||||
#define CPG_FRQCR 0x10
|
||||
#define CPG_FRQCR2 0x14
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Initialization
|
||||
*/
|
||||
|
||||
static struct clk * __init
|
||||
rz_cpg_register_clock(struct device_node *np, struct rz_cpg *cpg, const char *name)
|
||||
{
|
||||
u32 val;
|
||||
unsigned mult;
|
||||
static const unsigned frqcr_tab[4] = { 3, 2, 0, 1 };
|
||||
|
||||
if (strcmp(name, "pll") == 0) {
|
||||
/* FIXME: cpg_mode should be read from GPIO. But no GPIO support yet */
|
||||
unsigned cpg_mode = 0; /* hardcoded to EXTAL for now */
|
||||
const char *parent_name = of_clk_get_parent_name(np, cpg_mode);
|
||||
|
||||
mult = cpg_mode ? (32 / 4) : 30;
|
||||
|
||||
return clk_register_fixed_factor(NULL, name, parent_name, 0, mult, 1);
|
||||
}
|
||||
|
||||
/* If mapping regs failed, skip non-pll clocks. System will boot anyhow */
|
||||
if (!cpg->reg)
|
||||
return ERR_PTR(-ENXIO);
|
||||
|
||||
/* FIXME:"i" and "g" are variable clocks with non-integer dividers (e.g. 2/3)
|
||||
* and the constraint that always g <= i. To get the rz platform started,
|
||||
* let them run at fixed current speed and implement the details later.
|
||||
*/
|
||||
if (strcmp(name, "i") == 0)
|
||||
val = (clk_readl(cpg->reg + CPG_FRQCR) >> 8) & 3;
|
||||
else if (strcmp(name, "g") == 0)
|
||||
val = clk_readl(cpg->reg + CPG_FRQCR2) & 3;
|
||||
else
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
mult = frqcr_tab[val];
|
||||
return clk_register_fixed_factor(NULL, name, "pll", 0, mult, 3);
|
||||
}
|
||||
|
||||
static void __init rz_cpg_clocks_init(struct device_node *np)
|
||||
{
|
||||
struct rz_cpg *cpg;
|
||||
struct clk **clks;
|
||||
unsigned i;
|
||||
int num_clks;
|
||||
|
||||
num_clks = of_property_count_strings(np, "clock-output-names");
|
||||
if (WARN(num_clks <= 0, "can't count CPG clocks\n"))
|
||||
return;
|
||||
|
||||
cpg = kzalloc(sizeof(*cpg), GFP_KERNEL);
|
||||
clks = kzalloc(num_clks * sizeof(*clks), GFP_KERNEL);
|
||||
BUG_ON(!cpg || !clks);
|
||||
|
||||
cpg->data.clks = clks;
|
||||
cpg->data.clk_num = num_clks;
|
||||
|
||||
cpg->reg = of_iomap(np, 0);
|
||||
|
||||
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 = rz_cpg_register_clock(np, cpg, name);
|
||||
if (IS_ERR(clk))
|
||||
pr_err("%s: failed to register %s %s clock (%ld)\n",
|
||||
__func__, np->name, name, PTR_ERR(clk));
|
||||
else
|
||||
cpg->data.clks[i] = clk;
|
||||
}
|
||||
|
||||
of_clk_add_provider(np, of_clk_src_onecell_get, &cpg->data);
|
||||
}
|
||||
CLK_OF_DECLARE(rz_cpg_clks, "renesas,rz-cpg-clocks", rz_cpg_clocks_init);
|
|
@ -1,7 +1,8 @@
|
|||
/*
|
||||
* Clock tree for CSR SiRFatlasVI
|
||||
*
|
||||
* Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
|
||||
* Copyright (c) 2011 - 2014 Cambridge Silicon Radio Limited, a CSR plc group
|
||||
* company.
|
||||
*
|
||||
* Licensed under GPLv2 or later.
|
||||
*/
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
/*
|
||||
* common clks module for all SiRF SoCs
|
||||
*
|
||||
* Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
|
||||
* Copyright (c) 2011 - 2014 Cambridge Silicon Radio Limited, a CSR plc group
|
||||
* company.
|
||||
*
|
||||
* Licensed under GPLv2 or later.
|
||||
*/
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
/*
|
||||
* Clock tree for CSR SiRFprimaII
|
||||
*
|
||||
* Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
|
||||
* Copyright (c) 2011 - 2014 Cambridge Silicon Radio Limited, a CSR plc group
|
||||
* company.
|
||||
*
|
||||
* Licensed under GPLv2 or later.
|
||||
*/
|
||||
|
|
|
@ -1 +1,4 @@
|
|||
obj-y += clk.o
|
||||
obj-y += clk-gate.o
|
||||
obj-y += clk-pll.o
|
||||
obj-y += clk-periph.o
|
||||
|
|
263
drivers/clk/socfpga/clk-gate.c
Normal file
263
drivers/clk/socfpga/clk-gate.c
Normal file
|
@ -0,0 +1,263 @@
|
|||
/*
|
||||
* Copyright 2011-2012 Calxeda, Inc.
|
||||
* Copyright (C) 2012-2013 Altera Corporation <www.altera.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.
|
||||
*
|
||||
* Based from clk-highbank.c
|
||||
*
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include "clk.h"
|
||||
|
||||
#define SOCFPGA_L4_MP_CLK "l4_mp_clk"
|
||||
#define SOCFPGA_L4_SP_CLK "l4_sp_clk"
|
||||
#define SOCFPGA_NAND_CLK "nand_clk"
|
||||
#define SOCFPGA_NAND_X_CLK "nand_x_clk"
|
||||
#define SOCFPGA_MMC_CLK "sdmmc_clk"
|
||||
#define SOCFPGA_GPIO_DB_CLK_OFFSET 0xA8
|
||||
|
||||
#define div_mask(width) ((1 << (width)) - 1)
|
||||
#define streq(a, b) (strcmp((a), (b)) == 0)
|
||||
|
||||
#define to_socfpga_gate_clk(p) container_of(p, struct socfpga_gate_clk, hw.hw)
|
||||
|
||||
/* SDMMC Group for System Manager defines */
|
||||
#define SYSMGR_SDMMCGRP_CTRL_OFFSET 0x108
|
||||
#define SYSMGR_SDMMC_CTRL_SET(smplsel, drvsel) \
|
||||
((((smplsel) & 0x7) << 3) | (((drvsel) & 0x7) << 0))
|
||||
|
||||
static u8 socfpga_clk_get_parent(struct clk_hw *hwclk)
|
||||
{
|
||||
u32 l4_src;
|
||||
u32 perpll_src;
|
||||
|
||||
if (streq(hwclk->init->name, SOCFPGA_L4_MP_CLK)) {
|
||||
l4_src = readl(clk_mgr_base_addr + CLKMGR_L4SRC);
|
||||
return l4_src &= 0x1;
|
||||
}
|
||||
if (streq(hwclk->init->name, SOCFPGA_L4_SP_CLK)) {
|
||||
l4_src = readl(clk_mgr_base_addr + CLKMGR_L4SRC);
|
||||
return !!(l4_src & 2);
|
||||
}
|
||||
|
||||
perpll_src = readl(clk_mgr_base_addr + CLKMGR_PERPLL_SRC);
|
||||
if (streq(hwclk->init->name, SOCFPGA_MMC_CLK))
|
||||
return perpll_src &= 0x3;
|
||||
if (streq(hwclk->init->name, SOCFPGA_NAND_CLK) ||
|
||||
streq(hwclk->init->name, SOCFPGA_NAND_X_CLK))
|
||||
return (perpll_src >> 2) & 3;
|
||||
|
||||
/* QSPI clock */
|
||||
return (perpll_src >> 4) & 3;
|
||||
|
||||
}
|
||||
|
||||
static int socfpga_clk_set_parent(struct clk_hw *hwclk, u8 parent)
|
||||
{
|
||||
u32 src_reg;
|
||||
|
||||
if (streq(hwclk->init->name, SOCFPGA_L4_MP_CLK)) {
|
||||
src_reg = readl(clk_mgr_base_addr + CLKMGR_L4SRC);
|
||||
src_reg &= ~0x1;
|
||||
src_reg |= parent;
|
||||
writel(src_reg, clk_mgr_base_addr + CLKMGR_L4SRC);
|
||||
} else if (streq(hwclk->init->name, SOCFPGA_L4_SP_CLK)) {
|
||||
src_reg = readl(clk_mgr_base_addr + CLKMGR_L4SRC);
|
||||
src_reg &= ~0x2;
|
||||
src_reg |= (parent << 1);
|
||||
writel(src_reg, clk_mgr_base_addr + CLKMGR_L4SRC);
|
||||
} else {
|
||||
src_reg = readl(clk_mgr_base_addr + CLKMGR_PERPLL_SRC);
|
||||
if (streq(hwclk->init->name, SOCFPGA_MMC_CLK)) {
|
||||
src_reg &= ~0x3;
|
||||
src_reg |= parent;
|
||||
} else if (streq(hwclk->init->name, SOCFPGA_NAND_CLK) ||
|
||||
streq(hwclk->init->name, SOCFPGA_NAND_X_CLK)) {
|
||||
src_reg &= ~0xC;
|
||||
src_reg |= (parent << 2);
|
||||
} else {/* QSPI clock */
|
||||
src_reg &= ~0x30;
|
||||
src_reg |= (parent << 4);
|
||||
}
|
||||
writel(src_reg, clk_mgr_base_addr + CLKMGR_PERPLL_SRC);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long socfpga_clk_recalc_rate(struct clk_hw *hwclk,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk);
|
||||
u32 div = 1, val;
|
||||
|
||||
if (socfpgaclk->fixed_div)
|
||||
div = socfpgaclk->fixed_div;
|
||||
else if (socfpgaclk->div_reg) {
|
||||
val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift;
|
||||
val &= div_mask(socfpgaclk->width);
|
||||
/* Check for GPIO_DB_CLK by its offset */
|
||||
if ((int) socfpgaclk->div_reg & SOCFPGA_GPIO_DB_CLK_OFFSET)
|
||||
div = val + 1;
|
||||
else
|
||||
div = (1 << val);
|
||||
}
|
||||
|
||||
return parent_rate / div;
|
||||
}
|
||||
|
||||
static int socfpga_clk_prepare(struct clk_hw *hwclk)
|
||||
{
|
||||
struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk);
|
||||
struct regmap *sys_mgr_base_addr;
|
||||
int i;
|
||||
u32 hs_timing;
|
||||
u32 clk_phase[2];
|
||||
|
||||
if (socfpgaclk->clk_phase[0] || socfpgaclk->clk_phase[1]) {
|
||||
sys_mgr_base_addr = syscon_regmap_lookup_by_compatible("altr,sys-mgr");
|
||||
if (IS_ERR(sys_mgr_base_addr)) {
|
||||
pr_err("%s: failed to find altr,sys-mgr regmap!\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
switch (socfpgaclk->clk_phase[i]) {
|
||||
case 0:
|
||||
clk_phase[i] = 0;
|
||||
break;
|
||||
case 45:
|
||||
clk_phase[i] = 1;
|
||||
break;
|
||||
case 90:
|
||||
clk_phase[i] = 2;
|
||||
break;
|
||||
case 135:
|
||||
clk_phase[i] = 3;
|
||||
break;
|
||||
case 180:
|
||||
clk_phase[i] = 4;
|
||||
break;
|
||||
case 225:
|
||||
clk_phase[i] = 5;
|
||||
break;
|
||||
case 270:
|
||||
clk_phase[i] = 6;
|
||||
break;
|
||||
case 315:
|
||||
clk_phase[i] = 7;
|
||||
break;
|
||||
default:
|
||||
clk_phase[i] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
hs_timing = SYSMGR_SDMMC_CTRL_SET(clk_phase[0], clk_phase[1]);
|
||||
regmap_write(sys_mgr_base_addr, SYSMGR_SDMMCGRP_CTRL_OFFSET,
|
||||
hs_timing);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct clk_ops gateclk_ops = {
|
||||
.prepare = socfpga_clk_prepare,
|
||||
.recalc_rate = socfpga_clk_recalc_rate,
|
||||
.get_parent = socfpga_clk_get_parent,
|
||||
.set_parent = socfpga_clk_set_parent,
|
||||
};
|
||||
|
||||
static void __init __socfpga_gate_init(struct device_node *node,
|
||||
const struct clk_ops *ops)
|
||||
{
|
||||
u32 clk_gate[2];
|
||||
u32 div_reg[3];
|
||||
u32 clk_phase[2];
|
||||
u32 fixed_div;
|
||||
struct clk *clk;
|
||||
struct socfpga_gate_clk *socfpga_clk;
|
||||
const char *clk_name = node->name;
|
||||
const char *parent_name[SOCFPGA_MAX_PARENTS];
|
||||
struct clk_init_data init;
|
||||
int rc;
|
||||
int i = 0;
|
||||
|
||||
socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL);
|
||||
if (WARN_ON(!socfpga_clk))
|
||||
return;
|
||||
|
||||
rc = of_property_read_u32_array(node, "clk-gate", clk_gate, 2);
|
||||
if (rc)
|
||||
clk_gate[0] = 0;
|
||||
|
||||
if (clk_gate[0]) {
|
||||
socfpga_clk->hw.reg = clk_mgr_base_addr + clk_gate[0];
|
||||
socfpga_clk->hw.bit_idx = clk_gate[1];
|
||||
|
||||
gateclk_ops.enable = clk_gate_ops.enable;
|
||||
gateclk_ops.disable = clk_gate_ops.disable;
|
||||
}
|
||||
|
||||
rc = of_property_read_u32(node, "fixed-divider", &fixed_div);
|
||||
if (rc)
|
||||
socfpga_clk->fixed_div = 0;
|
||||
else
|
||||
socfpga_clk->fixed_div = fixed_div;
|
||||
|
||||
rc = of_property_read_u32_array(node, "div-reg", div_reg, 3);
|
||||
if (!rc) {
|
||||
socfpga_clk->div_reg = clk_mgr_base_addr + div_reg[0];
|
||||
socfpga_clk->shift = div_reg[1];
|
||||
socfpga_clk->width = div_reg[2];
|
||||
} else {
|
||||
socfpga_clk->div_reg = 0;
|
||||
}
|
||||
|
||||
rc = of_property_read_u32_array(node, "clk-phase", clk_phase, 2);
|
||||
if (!rc) {
|
||||
socfpga_clk->clk_phase[0] = clk_phase[0];
|
||||
socfpga_clk->clk_phase[1] = clk_phase[1];
|
||||
}
|
||||
|
||||
of_property_read_string(node, "clock-output-names", &clk_name);
|
||||
|
||||
init.name = clk_name;
|
||||
init.ops = ops;
|
||||
init.flags = 0;
|
||||
while (i < SOCFPGA_MAX_PARENTS && (parent_name[i] =
|
||||
of_clk_get_parent_name(node, i)) != NULL)
|
||||
i++;
|
||||
|
||||
init.parent_names = parent_name;
|
||||
init.num_parents = i;
|
||||
socfpga_clk->hw.hw.init = &init;
|
||||
|
||||
clk = clk_register(NULL, &socfpga_clk->hw.hw);
|
||||
if (WARN_ON(IS_ERR(clk))) {
|
||||
kfree(socfpga_clk);
|
||||
return;
|
||||
}
|
||||
rc = of_clk_add_provider(node, of_clk_src_simple_get, clk);
|
||||
if (WARN_ON(rc))
|
||||
return;
|
||||
}
|
||||
|
||||
void __init socfpga_gate_init(struct device_node *node)
|
||||
{
|
||||
__socfpga_gate_init(node, &gateclk_ops);
|
||||
}
|
94
drivers/clk/socfpga/clk-periph.c
Normal file
94
drivers/clk/socfpga/clk-periph.c
Normal file
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* Copyright 2011-2012 Calxeda, Inc.
|
||||
* Copyright (C) 2012-2013 Altera Corporation <www.altera.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.
|
||||
*
|
||||
* Based from clk-highbank.c
|
||||
*
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include "clk.h"
|
||||
|
||||
#define to_socfpga_periph_clk(p) container_of(p, struct socfpga_periph_clk, hw.hw)
|
||||
|
||||
static unsigned long clk_periclk_recalc_rate(struct clk_hw *hwclk,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct socfpga_periph_clk *socfpgaclk = to_socfpga_periph_clk(hwclk);
|
||||
u32 div;
|
||||
|
||||
if (socfpgaclk->fixed_div)
|
||||
div = socfpgaclk->fixed_div;
|
||||
else
|
||||
div = ((readl(socfpgaclk->hw.reg) & 0x1ff) + 1);
|
||||
|
||||
return parent_rate / div;
|
||||
}
|
||||
|
||||
static const struct clk_ops periclk_ops = {
|
||||
.recalc_rate = clk_periclk_recalc_rate,
|
||||
};
|
||||
|
||||
static __init void __socfpga_periph_init(struct device_node *node,
|
||||
const struct clk_ops *ops)
|
||||
{
|
||||
u32 reg;
|
||||
struct clk *clk;
|
||||
struct socfpga_periph_clk *periph_clk;
|
||||
const char *clk_name = node->name;
|
||||
const char *parent_name;
|
||||
struct clk_init_data init;
|
||||
int rc;
|
||||
u32 fixed_div;
|
||||
|
||||
of_property_read_u32(node, "reg", ®);
|
||||
|
||||
periph_clk = kzalloc(sizeof(*periph_clk), GFP_KERNEL);
|
||||
if (WARN_ON(!periph_clk))
|
||||
return;
|
||||
|
||||
periph_clk->hw.reg = clk_mgr_base_addr + reg;
|
||||
|
||||
rc = of_property_read_u32(node, "fixed-divider", &fixed_div);
|
||||
if (rc)
|
||||
periph_clk->fixed_div = 0;
|
||||
else
|
||||
periph_clk->fixed_div = fixed_div;
|
||||
|
||||
of_property_read_string(node, "clock-output-names", &clk_name);
|
||||
|
||||
init.name = clk_name;
|
||||
init.ops = ops;
|
||||
init.flags = 0;
|
||||
parent_name = of_clk_get_parent_name(node, 0);
|
||||
init.parent_names = &parent_name;
|
||||
init.num_parents = 1;
|
||||
|
||||
periph_clk->hw.hw.init = &init;
|
||||
|
||||
clk = clk_register(NULL, &periph_clk->hw.hw);
|
||||
if (WARN_ON(IS_ERR(clk))) {
|
||||
kfree(periph_clk);
|
||||
return;
|
||||
}
|
||||
rc = of_clk_add_provider(node, of_clk_src_simple_get, clk);
|
||||
}
|
||||
|
||||
void __init socfpga_periph_init(struct device_node *node)
|
||||
{
|
||||
__socfpga_periph_init(node, &periclk_ops);
|
||||
}
|
131
drivers/clk/socfpga/clk-pll.c
Normal file
131
drivers/clk/socfpga/clk-pll.c
Normal file
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* Copyright 2011-2012 Calxeda, Inc.
|
||||
* Copyright (C) 2012-2013 Altera Corporation <www.altera.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.
|
||||
*
|
||||
* Based from clk-highbank.c
|
||||
*
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include "clk.h"
|
||||
|
||||
/* Clock bypass bits */
|
||||
#define MAINPLL_BYPASS (1<<0)
|
||||
#define SDRAMPLL_BYPASS (1<<1)
|
||||
#define SDRAMPLL_SRC_BYPASS (1<<2)
|
||||
#define PERPLL_BYPASS (1<<3)
|
||||
#define PERPLL_SRC_BYPASS (1<<4)
|
||||
|
||||
#define SOCFPGA_PLL_BG_PWRDWN 0
|
||||
#define SOCFPGA_PLL_EXT_ENA 1
|
||||
#define SOCFPGA_PLL_PWR_DOWN 2
|
||||
#define SOCFPGA_PLL_DIVF_MASK 0x0000FFF8
|
||||
#define SOCFPGA_PLL_DIVF_SHIFT 3
|
||||
#define SOCFPGA_PLL_DIVQ_MASK 0x003F0000
|
||||
#define SOCFPGA_PLL_DIVQ_SHIFT 16
|
||||
|
||||
#define CLK_MGR_PLL_CLK_SRC_SHIFT 22
|
||||
#define CLK_MGR_PLL_CLK_SRC_MASK 0x3
|
||||
|
||||
#define to_socfpga_clk(p) container_of(p, struct socfpga_pll, hw.hw)
|
||||
|
||||
static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);
|
||||
unsigned long divf, divq, reg;
|
||||
unsigned long long vco_freq;
|
||||
unsigned long bypass;
|
||||
|
||||
reg = readl(socfpgaclk->hw.reg);
|
||||
bypass = readl(clk_mgr_base_addr + CLKMGR_BYPASS);
|
||||
if (bypass & MAINPLL_BYPASS)
|
||||
return parent_rate;
|
||||
|
||||
divf = (reg & SOCFPGA_PLL_DIVF_MASK) >> SOCFPGA_PLL_DIVF_SHIFT;
|
||||
divq = (reg & SOCFPGA_PLL_DIVQ_MASK) >> SOCFPGA_PLL_DIVQ_SHIFT;
|
||||
vco_freq = (unsigned long long)parent_rate * (divf + 1);
|
||||
do_div(vco_freq, (1 + divq));
|
||||
return (unsigned long)vco_freq;
|
||||
}
|
||||
|
||||
static u8 clk_pll_get_parent(struct clk_hw *hwclk)
|
||||
{
|
||||
u32 pll_src;
|
||||
struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);
|
||||
|
||||
pll_src = readl(socfpgaclk->hw.reg);
|
||||
return (pll_src >> CLK_MGR_PLL_CLK_SRC_SHIFT) &
|
||||
CLK_MGR_PLL_CLK_SRC_MASK;
|
||||
}
|
||||
|
||||
static struct clk_ops clk_pll_ops = {
|
||||
.recalc_rate = clk_pll_recalc_rate,
|
||||
.get_parent = clk_pll_get_parent,
|
||||
};
|
||||
|
||||
static __init struct clk *__socfpga_pll_init(struct device_node *node,
|
||||
const struct clk_ops *ops)
|
||||
{
|
||||
u32 reg;
|
||||
struct clk *clk;
|
||||
struct socfpga_pll *pll_clk;
|
||||
const char *clk_name = node->name;
|
||||
const char *parent_name[SOCFPGA_MAX_PARENTS];
|
||||
struct clk_init_data init;
|
||||
int rc;
|
||||
int i = 0;
|
||||
|
||||
of_property_read_u32(node, "reg", ®);
|
||||
|
||||
pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL);
|
||||
if (WARN_ON(!pll_clk))
|
||||
return NULL;
|
||||
|
||||
pll_clk->hw.reg = clk_mgr_base_addr + reg;
|
||||
|
||||
of_property_read_string(node, "clock-output-names", &clk_name);
|
||||
|
||||
init.name = clk_name;
|
||||
init.ops = ops;
|
||||
init.flags = 0;
|
||||
|
||||
while (i < SOCFPGA_MAX_PARENTS && (parent_name[i] =
|
||||
of_clk_get_parent_name(node, i)) != NULL)
|
||||
i++;
|
||||
|
||||
init.num_parents = i;
|
||||
init.parent_names = parent_name;
|
||||
pll_clk->hw.hw.init = &init;
|
||||
|
||||
pll_clk->hw.bit_idx = SOCFPGA_PLL_EXT_ENA;
|
||||
clk_pll_ops.enable = clk_gate_ops.enable;
|
||||
clk_pll_ops.disable = clk_gate_ops.disable;
|
||||
|
||||
clk = clk_register(NULL, &pll_clk->hw.hw);
|
||||
if (WARN_ON(IS_ERR(clk))) {
|
||||
kfree(pll_clk);
|
||||
return NULL;
|
||||
}
|
||||
rc = of_clk_add_provider(node, of_clk_src_simple_get, clk);
|
||||
return clk;
|
||||
}
|
||||
|
||||
void __init socfpga_pll_init(struct device_node *node)
|
||||
{
|
||||
__socfpga_pll_init(node, &clk_pll_ops);
|
||||
}
|
|
@ -22,325 +22,23 @@
|
|||
#include <linux/clk-provider.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
|
||||
/* Clock Manager offsets */
|
||||
#define CLKMGR_CTRL 0x0
|
||||
#define CLKMGR_BYPASS 0x4
|
||||
#define CLKMGR_L4SRC 0x70
|
||||
#define CLKMGR_PERPLL_SRC 0xAC
|
||||
#include "clk.h"
|
||||
|
||||
/* Clock bypass bits */
|
||||
#define MAINPLL_BYPASS (1<<0)
|
||||
#define SDRAMPLL_BYPASS (1<<1)
|
||||
#define SDRAMPLL_SRC_BYPASS (1<<2)
|
||||
#define PERPLL_BYPASS (1<<3)
|
||||
#define PERPLL_SRC_BYPASS (1<<4)
|
||||
void __iomem *clk_mgr_base_addr;
|
||||
|
||||
#define SOCFPGA_PLL_BG_PWRDWN 0
|
||||
#define SOCFPGA_PLL_EXT_ENA 1
|
||||
#define SOCFPGA_PLL_PWR_DOWN 2
|
||||
#define SOCFPGA_PLL_DIVF_MASK 0x0000FFF8
|
||||
#define SOCFPGA_PLL_DIVF_SHIFT 3
|
||||
#define SOCFPGA_PLL_DIVQ_MASK 0x003F0000
|
||||
#define SOCFPGA_PLL_DIVQ_SHIFT 16
|
||||
#define SOCFGPA_MAX_PARENTS 3
|
||||
|
||||
#define SOCFPGA_L4_MP_CLK "l4_mp_clk"
|
||||
#define SOCFPGA_L4_SP_CLK "l4_sp_clk"
|
||||
#define SOCFPGA_NAND_CLK "nand_clk"
|
||||
#define SOCFPGA_NAND_X_CLK "nand_x_clk"
|
||||
#define SOCFPGA_MMC_CLK "sdmmc_clk"
|
||||
#define SOCFPGA_DB_CLK "gpio_db_clk"
|
||||
|
||||
#define div_mask(width) ((1 << (width)) - 1)
|
||||
#define streq(a, b) (strcmp((a), (b)) == 0)
|
||||
|
||||
extern void __iomem *clk_mgr_base_addr;
|
||||
|
||||
struct socfpga_clk {
|
||||
struct clk_gate hw;
|
||||
char *parent_name;
|
||||
char *clk_name;
|
||||
u32 fixed_div;
|
||||
void __iomem *div_reg;
|
||||
u32 width; /* only valid if div_reg != 0 */
|
||||
u32 shift; /* only valid if div_reg != 0 */
|
||||
};
|
||||
#define to_socfpga_clk(p) container_of(p, struct socfpga_clk, hw.hw)
|
||||
|
||||
static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct socfpga_clk *socfpgaclk = to_socfpga_clk(hwclk);
|
||||
unsigned long divf, divq, vco_freq, reg;
|
||||
unsigned long bypass;
|
||||
|
||||
reg = readl(socfpgaclk->hw.reg);
|
||||
bypass = readl(clk_mgr_base_addr + CLKMGR_BYPASS);
|
||||
if (bypass & MAINPLL_BYPASS)
|
||||
return parent_rate;
|
||||
|
||||
divf = (reg & SOCFPGA_PLL_DIVF_MASK) >> SOCFPGA_PLL_DIVF_SHIFT;
|
||||
divq = (reg & SOCFPGA_PLL_DIVQ_MASK) >> SOCFPGA_PLL_DIVQ_SHIFT;
|
||||
vco_freq = parent_rate * (divf + 1);
|
||||
return vco_freq / (1 + divq);
|
||||
}
|
||||
|
||||
|
||||
static struct clk_ops clk_pll_ops = {
|
||||
.recalc_rate = clk_pll_recalc_rate,
|
||||
static const struct of_device_id socfpga_child_clocks[] __initconst = {
|
||||
{ .compatible = "altr,socfpga-pll-clock", socfpga_pll_init, },
|
||||
{ .compatible = "altr,socfpga-perip-clk", socfpga_periph_init, },
|
||||
{ .compatible = "altr,socfpga-gate-clk", socfpga_gate_init, },
|
||||
{},
|
||||
};
|
||||
|
||||
static unsigned long clk_periclk_recalc_rate(struct clk_hw *hwclk,
|
||||
unsigned long parent_rate)
|
||||
static void __init socfpga_clkmgr_init(struct device_node *node)
|
||||
{
|
||||
struct socfpga_clk *socfpgaclk = to_socfpga_clk(hwclk);
|
||||
u32 div;
|
||||
|
||||
if (socfpgaclk->fixed_div)
|
||||
div = socfpgaclk->fixed_div;
|
||||
else
|
||||
div = ((readl(socfpgaclk->hw.reg) & 0x1ff) + 1);
|
||||
|
||||
return parent_rate / div;
|
||||
clk_mgr_base_addr = of_iomap(node, 0);
|
||||
of_clk_init(socfpga_child_clocks);
|
||||
}
|
||||
CLK_OF_DECLARE(socfpga_mgr, "altr,clk-mgr", socfpga_clkmgr_init);
|
||||
|
||||
static const struct clk_ops periclk_ops = {
|
||||
.recalc_rate = clk_periclk_recalc_rate,
|
||||
};
|
||||
|
||||
static __init struct clk *socfpga_clk_init(struct device_node *node,
|
||||
const struct clk_ops *ops)
|
||||
{
|
||||
u32 reg;
|
||||
struct clk *clk;
|
||||
struct socfpga_clk *socfpga_clk;
|
||||
const char *clk_name = node->name;
|
||||
const char *parent_name;
|
||||
struct clk_init_data init;
|
||||
int rc;
|
||||
u32 fixed_div;
|
||||
|
||||
of_property_read_u32(node, "reg", ®);
|
||||
|
||||
socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL);
|
||||
if (WARN_ON(!socfpga_clk))
|
||||
return NULL;
|
||||
|
||||
socfpga_clk->hw.reg = clk_mgr_base_addr + reg;
|
||||
|
||||
rc = of_property_read_u32(node, "fixed-divider", &fixed_div);
|
||||
if (rc)
|
||||
socfpga_clk->fixed_div = 0;
|
||||
else
|
||||
socfpga_clk->fixed_div = fixed_div;
|
||||
|
||||
of_property_read_string(node, "clock-output-names", &clk_name);
|
||||
|
||||
init.name = clk_name;
|
||||
init.ops = ops;
|
||||
init.flags = 0;
|
||||
parent_name = of_clk_get_parent_name(node, 0);
|
||||
init.parent_names = &parent_name;
|
||||
init.num_parents = 1;
|
||||
|
||||
socfpga_clk->hw.hw.init = &init;
|
||||
|
||||
if (streq(clk_name, "main_pll") ||
|
||||
streq(clk_name, "periph_pll") ||
|
||||
streq(clk_name, "sdram_pll")) {
|
||||
socfpga_clk->hw.bit_idx = SOCFPGA_PLL_EXT_ENA;
|
||||
clk_pll_ops.enable = clk_gate_ops.enable;
|
||||
clk_pll_ops.disable = clk_gate_ops.disable;
|
||||
}
|
||||
|
||||
clk = clk_register(NULL, &socfpga_clk->hw.hw);
|
||||
if (WARN_ON(IS_ERR(clk))) {
|
||||
kfree(socfpga_clk);
|
||||
return NULL;
|
||||
}
|
||||
rc = of_clk_add_provider(node, of_clk_src_simple_get, clk);
|
||||
return clk;
|
||||
}
|
||||
|
||||
static u8 socfpga_clk_get_parent(struct clk_hw *hwclk)
|
||||
{
|
||||
u32 l4_src;
|
||||
u32 perpll_src;
|
||||
|
||||
if (streq(hwclk->init->name, SOCFPGA_L4_MP_CLK)) {
|
||||
l4_src = readl(clk_mgr_base_addr + CLKMGR_L4SRC);
|
||||
return l4_src &= 0x1;
|
||||
}
|
||||
if (streq(hwclk->init->name, SOCFPGA_L4_SP_CLK)) {
|
||||
l4_src = readl(clk_mgr_base_addr + CLKMGR_L4SRC);
|
||||
return !!(l4_src & 2);
|
||||
}
|
||||
|
||||
perpll_src = readl(clk_mgr_base_addr + CLKMGR_PERPLL_SRC);
|
||||
if (streq(hwclk->init->name, SOCFPGA_MMC_CLK))
|
||||
return perpll_src &= 0x3;
|
||||
if (streq(hwclk->init->name, SOCFPGA_NAND_CLK) ||
|
||||
streq(hwclk->init->name, SOCFPGA_NAND_X_CLK))
|
||||
return (perpll_src >> 2) & 3;
|
||||
|
||||
/* QSPI clock */
|
||||
return (perpll_src >> 4) & 3;
|
||||
|
||||
}
|
||||
|
||||
static int socfpga_clk_set_parent(struct clk_hw *hwclk, u8 parent)
|
||||
{
|
||||
u32 src_reg;
|
||||
|
||||
if (streq(hwclk->init->name, SOCFPGA_L4_MP_CLK)) {
|
||||
src_reg = readl(clk_mgr_base_addr + CLKMGR_L4SRC);
|
||||
src_reg &= ~0x1;
|
||||
src_reg |= parent;
|
||||
writel(src_reg, clk_mgr_base_addr + CLKMGR_L4SRC);
|
||||
} else if (streq(hwclk->init->name, SOCFPGA_L4_SP_CLK)) {
|
||||
src_reg = readl(clk_mgr_base_addr + CLKMGR_L4SRC);
|
||||
src_reg &= ~0x2;
|
||||
src_reg |= (parent << 1);
|
||||
writel(src_reg, clk_mgr_base_addr + CLKMGR_L4SRC);
|
||||
} else {
|
||||
src_reg = readl(clk_mgr_base_addr + CLKMGR_PERPLL_SRC);
|
||||
if (streq(hwclk->init->name, SOCFPGA_MMC_CLK)) {
|
||||
src_reg &= ~0x3;
|
||||
src_reg |= parent;
|
||||
} else if (streq(hwclk->init->name, SOCFPGA_NAND_CLK) ||
|
||||
streq(hwclk->init->name, SOCFPGA_NAND_X_CLK)) {
|
||||
src_reg &= ~0xC;
|
||||
src_reg |= (parent << 2);
|
||||
} else {/* QSPI clock */
|
||||
src_reg &= ~0x30;
|
||||
src_reg |= (parent << 4);
|
||||
}
|
||||
writel(src_reg, clk_mgr_base_addr + CLKMGR_PERPLL_SRC);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long socfpga_clk_recalc_rate(struct clk_hw *hwclk,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct socfpga_clk *socfpgaclk = to_socfpga_clk(hwclk);
|
||||
u32 div = 1, val;
|
||||
|
||||
if (socfpgaclk->fixed_div)
|
||||
div = socfpgaclk->fixed_div;
|
||||
else if (socfpgaclk->div_reg) {
|
||||
val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift;
|
||||
val &= div_mask(socfpgaclk->width);
|
||||
if (streq(hwclk->init->name, SOCFPGA_DB_CLK))
|
||||
div = val + 1;
|
||||
else
|
||||
div = (1 << val);
|
||||
}
|
||||
|
||||
return parent_rate / div;
|
||||
}
|
||||
|
||||
static struct clk_ops gateclk_ops = {
|
||||
.recalc_rate = socfpga_clk_recalc_rate,
|
||||
.get_parent = socfpga_clk_get_parent,
|
||||
.set_parent = socfpga_clk_set_parent,
|
||||
};
|
||||
|
||||
static void __init socfpga_gate_clk_init(struct device_node *node,
|
||||
const struct clk_ops *ops)
|
||||
{
|
||||
u32 clk_gate[2];
|
||||
u32 div_reg[3];
|
||||
u32 fixed_div;
|
||||
struct clk *clk;
|
||||
struct socfpga_clk *socfpga_clk;
|
||||
const char *clk_name = node->name;
|
||||
const char *parent_name[SOCFGPA_MAX_PARENTS];
|
||||
struct clk_init_data init;
|
||||
int rc;
|
||||
int i = 0;
|
||||
|
||||
socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL);
|
||||
if (WARN_ON(!socfpga_clk))
|
||||
return;
|
||||
|
||||
rc = of_property_read_u32_array(node, "clk-gate", clk_gate, 2);
|
||||
if (rc)
|
||||
clk_gate[0] = 0;
|
||||
|
||||
if (clk_gate[0]) {
|
||||
socfpga_clk->hw.reg = clk_mgr_base_addr + clk_gate[0];
|
||||
socfpga_clk->hw.bit_idx = clk_gate[1];
|
||||
|
||||
gateclk_ops.enable = clk_gate_ops.enable;
|
||||
gateclk_ops.disable = clk_gate_ops.disable;
|
||||
}
|
||||
|
||||
rc = of_property_read_u32(node, "fixed-divider", &fixed_div);
|
||||
if (rc)
|
||||
socfpga_clk->fixed_div = 0;
|
||||
else
|
||||
socfpga_clk->fixed_div = fixed_div;
|
||||
|
||||
rc = of_property_read_u32_array(node, "div-reg", div_reg, 3);
|
||||
if (!rc) {
|
||||
socfpga_clk->div_reg = clk_mgr_base_addr + div_reg[0];
|
||||
socfpga_clk->shift = div_reg[1];
|
||||
socfpga_clk->width = div_reg[2];
|
||||
} else {
|
||||
socfpga_clk->div_reg = NULL;
|
||||
}
|
||||
|
||||
of_property_read_string(node, "clock-output-names", &clk_name);
|
||||
|
||||
init.name = clk_name;
|
||||
init.ops = ops;
|
||||
init.flags = 0;
|
||||
while (i < SOCFGPA_MAX_PARENTS && (parent_name[i] =
|
||||
of_clk_get_parent_name(node, i)) != NULL)
|
||||
i++;
|
||||
|
||||
init.parent_names = parent_name;
|
||||
init.num_parents = i;
|
||||
socfpga_clk->hw.hw.init = &init;
|
||||
|
||||
clk = clk_register(NULL, &socfpga_clk->hw.hw);
|
||||
if (WARN_ON(IS_ERR(clk))) {
|
||||
kfree(socfpga_clk);
|
||||
return;
|
||||
}
|
||||
rc = of_clk_add_provider(node, of_clk_src_simple_get, clk);
|
||||
if (WARN_ON(rc))
|
||||
return;
|
||||
}
|
||||
|
||||
static void __init socfpga_pll_init(struct device_node *node)
|
||||
{
|
||||
socfpga_clk_init(node, &clk_pll_ops);
|
||||
}
|
||||
CLK_OF_DECLARE(socfpga_pll, "altr,socfpga-pll-clock", socfpga_pll_init);
|
||||
|
||||
static void __init socfpga_periph_init(struct device_node *node)
|
||||
{
|
||||
socfpga_clk_init(node, &periclk_ops);
|
||||
}
|
||||
CLK_OF_DECLARE(socfpga_periph, "altr,socfpga-perip-clk", socfpga_periph_init);
|
||||
|
||||
static void __init socfpga_gate_init(struct device_node *node)
|
||||
{
|
||||
socfpga_gate_clk_init(node, &gateclk_ops);
|
||||
}
|
||||
CLK_OF_DECLARE(socfpga_gate, "altr,socfpga-gate-clk", socfpga_gate_init);
|
||||
|
||||
void __init socfpga_init_clocks(void)
|
||||
{
|
||||
struct clk *clk;
|
||||
int ret;
|
||||
|
||||
clk = clk_register_fixed_factor(NULL, "smp_twd", "mpuclk", 0, 1, 4);
|
||||
ret = clk_register_clkdev(clk, NULL, "smp_twd");
|
||||
if (ret)
|
||||
pr_err("smp_twd alias not registered\n");
|
||||
}
|
||||
|
|
57
drivers/clk/socfpga/clk.h
Normal file
57
drivers/clk/socfpga/clk.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (c) 2013, Steffen Trumtrar <s.trumtrar@pengutronix.de>
|
||||
*
|
||||
* based on drivers/clk/tegra/clk.h
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __SOCFPGA_CLK_H
|
||||
#define __SOCFPGA_CLK_H
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
|
||||
/* Clock Manager offsets */
|
||||
#define CLKMGR_CTRL 0x0
|
||||
#define CLKMGR_BYPASS 0x4
|
||||
#define CLKMGR_L4SRC 0x70
|
||||
#define CLKMGR_PERPLL_SRC 0xAC
|
||||
|
||||
#define SOCFPGA_MAX_PARENTS 3
|
||||
|
||||
extern void __iomem *clk_mgr_base_addr;
|
||||
|
||||
void __init socfpga_pll_init(struct device_node *node);
|
||||
void __init socfpga_periph_init(struct device_node *node);
|
||||
void __init socfpga_gate_init(struct device_node *node);
|
||||
|
||||
struct socfpga_pll {
|
||||
struct clk_gate hw;
|
||||
};
|
||||
|
||||
struct socfpga_gate_clk {
|
||||
struct clk_gate hw;
|
||||
char *parent_name;
|
||||
u32 fixed_div;
|
||||
void __iomem *div_reg;
|
||||
u32 width; /* only valid if div_reg != 0 */
|
||||
u32 shift; /* only valid if div_reg != 0 */
|
||||
u32 clk_phase[2];
|
||||
};
|
||||
|
||||
struct socfpga_periph_clk {
|
||||
struct clk_gate hw;
|
||||
char *parent_name;
|
||||
u32 fixed_div;
|
||||
};
|
||||
|
||||
#endif /* SOCFPGA_CLK_H */
|
1
drivers/clk/st/Makefile
Normal file
1
drivers/clk/st/Makefile
Normal file
|
@ -0,0 +1 @@
|
|||
obj-y += clkgen-mux.o clkgen-pll.o clkgen-fsyn.o
|
1039
drivers/clk/st/clkgen-fsyn.c
Normal file
1039
drivers/clk/st/clkgen-fsyn.c
Normal file
File diff suppressed because it is too large
Load diff
820
drivers/clk/st/clkgen-mux.c
Normal file
820
drivers/clk/st/clkgen-mux.c
Normal file
|
@ -0,0 +1,820 @@
|
|||
/*
|
||||
* clkgen-mux.c: ST GEN-MUX Clock driver
|
||||
*
|
||||
* Copyright (C) 2014 STMicroelectronics (R&D) Limited
|
||||
*
|
||||
* Authors: Stephen Gallimore <stephen.gallimore@st.com>
|
||||
* Pankaj Dev <pankaj.dev@st.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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/clk-provider.h>
|
||||
|
||||
static DEFINE_SPINLOCK(clkgena_divmux_lock);
|
||||
static DEFINE_SPINLOCK(clkgenf_lock);
|
||||
|
||||
static const char ** __init clkgen_mux_get_parents(struct device_node *np,
|
||||
int *num_parents)
|
||||
{
|
||||
const char **parents;
|
||||
int nparents, i;
|
||||
|
||||
nparents = of_count_phandle_with_args(np, "clocks", "#clock-cells");
|
||||
if (WARN_ON(nparents <= 0))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
parents = kzalloc(nparents * sizeof(const char *), GFP_KERNEL);
|
||||
if (!parents)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
for (i = 0; i < nparents; i++)
|
||||
parents[i] = of_clk_get_parent_name(np, i);
|
||||
|
||||
*num_parents = nparents;
|
||||
return parents;
|
||||
}
|
||||
|
||||
/**
|
||||
* DOC: Clock mux with a programmable divider on each of its three inputs.
|
||||
* The mux has an input setting which effectively gates its output.
|
||||
*
|
||||
* Traits of this clock:
|
||||
* prepare - clk_(un)prepare only ensures parent is (un)prepared
|
||||
* enable - clk_enable and clk_disable are functional & control gating
|
||||
* rate - set rate is supported
|
||||
* parent - set/get parent
|
||||
*/
|
||||
|
||||
#define NUM_INPUTS 3
|
||||
|
||||
struct clkgena_divmux {
|
||||
struct clk_hw hw;
|
||||
/* Subclassed mux and divider structures */
|
||||
struct clk_mux mux;
|
||||
struct clk_divider div[NUM_INPUTS];
|
||||
/* Enable/running feedback register bits for each input */
|
||||
void __iomem *feedback_reg[NUM_INPUTS];
|
||||
int feedback_bit_idx;
|
||||
|
||||
u8 muxsel;
|
||||
};
|
||||
|
||||
#define to_clkgena_divmux(_hw) container_of(_hw, struct clkgena_divmux, hw)
|
||||
|
||||
struct clkgena_divmux_data {
|
||||
int num_outputs;
|
||||
int mux_offset;
|
||||
int mux_offset2;
|
||||
int mux_start_bit;
|
||||
int div_offsets[NUM_INPUTS];
|
||||
int fb_offsets[NUM_INPUTS];
|
||||
int fb_start_bit_idx;
|
||||
};
|
||||
|
||||
#define CKGAX_CLKOPSRC_SWITCH_OFF 0x3
|
||||
|
||||
static int clkgena_divmux_is_running(struct clkgena_divmux *mux)
|
||||
{
|
||||
u32 regval = readl(mux->feedback_reg[mux->muxsel]);
|
||||
u32 running = regval & BIT(mux->feedback_bit_idx);
|
||||
return !!running;
|
||||
}
|
||||
|
||||
static int clkgena_divmux_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
|
||||
struct clk_hw *mux_hw = &genamux->mux.hw;
|
||||
unsigned long timeout;
|
||||
int ret = 0;
|
||||
|
||||
mux_hw->clk = hw->clk;
|
||||
|
||||
ret = clk_mux_ops.set_parent(mux_hw, genamux->muxsel);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
timeout = jiffies + msecs_to_jiffies(10);
|
||||
|
||||
while (!clkgena_divmux_is_running(genamux)) {
|
||||
if (time_after(jiffies, timeout))
|
||||
return -ETIMEDOUT;
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void clkgena_divmux_disable(struct clk_hw *hw)
|
||||
{
|
||||
struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
|
||||
struct clk_hw *mux_hw = &genamux->mux.hw;
|
||||
|
||||
mux_hw->clk = hw->clk;
|
||||
|
||||
clk_mux_ops.set_parent(mux_hw, CKGAX_CLKOPSRC_SWITCH_OFF);
|
||||
}
|
||||
|
||||
static int clkgena_divmux_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
|
||||
struct clk_hw *mux_hw = &genamux->mux.hw;
|
||||
|
||||
mux_hw->clk = hw->clk;
|
||||
|
||||
return (s8)clk_mux_ops.get_parent(mux_hw) > 0;
|
||||
}
|
||||
|
||||
u8 clkgena_divmux_get_parent(struct clk_hw *hw)
|
||||
{
|
||||
struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
|
||||
struct clk_hw *mux_hw = &genamux->mux.hw;
|
||||
|
||||
mux_hw->clk = hw->clk;
|
||||
|
||||
genamux->muxsel = clk_mux_ops.get_parent(mux_hw);
|
||||
if ((s8)genamux->muxsel < 0) {
|
||||
pr_debug("%s: %s: Invalid parent, setting to default.\n",
|
||||
__func__, __clk_get_name(hw->clk));
|
||||
genamux->muxsel = 0;
|
||||
}
|
||||
|
||||
return genamux->muxsel;
|
||||
}
|
||||
|
||||
static int clkgena_divmux_set_parent(struct clk_hw *hw, u8 index)
|
||||
{
|
||||
struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
|
||||
|
||||
if (index >= CKGAX_CLKOPSRC_SWITCH_OFF)
|
||||
return -EINVAL;
|
||||
|
||||
genamux->muxsel = index;
|
||||
|
||||
/*
|
||||
* If the mux is already enabled, call enable directly to set the
|
||||
* new mux position and wait for it to start running again. Otherwise
|
||||
* do nothing.
|
||||
*/
|
||||
if (clkgena_divmux_is_enabled(hw))
|
||||
clkgena_divmux_enable(hw);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned long clkgena_divmux_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
|
||||
struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw;
|
||||
|
||||
div_hw->clk = hw->clk;
|
||||
|
||||
return clk_divider_ops.recalc_rate(div_hw, parent_rate);
|
||||
}
|
||||
|
||||
static int clkgena_divmux_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
|
||||
struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw;
|
||||
|
||||
div_hw->clk = hw->clk;
|
||||
|
||||
return clk_divider_ops.set_rate(div_hw, rate, parent_rate);
|
||||
}
|
||||
|
||||
static long clkgena_divmux_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
|
||||
struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw;
|
||||
|
||||
div_hw->clk = hw->clk;
|
||||
|
||||
return clk_divider_ops.round_rate(div_hw, rate, prate);
|
||||
}
|
||||
|
||||
static const struct clk_ops clkgena_divmux_ops = {
|
||||
.enable = clkgena_divmux_enable,
|
||||
.disable = clkgena_divmux_disable,
|
||||
.is_enabled = clkgena_divmux_is_enabled,
|
||||
.get_parent = clkgena_divmux_get_parent,
|
||||
.set_parent = clkgena_divmux_set_parent,
|
||||
.round_rate = clkgena_divmux_round_rate,
|
||||
.recalc_rate = clkgena_divmux_recalc_rate,
|
||||
.set_rate = clkgena_divmux_set_rate,
|
||||
};
|
||||
|
||||
/**
|
||||
* clk_register_genamux - register a genamux clock with the clock framework
|
||||
*/
|
||||
struct clk *clk_register_genamux(const char *name,
|
||||
const char **parent_names, u8 num_parents,
|
||||
void __iomem *reg,
|
||||
const struct clkgena_divmux_data *muxdata,
|
||||
u32 idx)
|
||||
{
|
||||
/*
|
||||
* Fixed constants across all ClockgenA variants
|
||||
*/
|
||||
const int mux_width = 2;
|
||||
const int divider_width = 5;
|
||||
struct clkgena_divmux *genamux;
|
||||
struct clk *clk;
|
||||
struct clk_init_data init;
|
||||
int i;
|
||||
|
||||
genamux = kzalloc(sizeof(*genamux), GFP_KERNEL);
|
||||
if (!genamux)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = name;
|
||||
init.ops = &clkgena_divmux_ops;
|
||||
init.flags = CLK_IS_BASIC;
|
||||
init.parent_names = parent_names;
|
||||
init.num_parents = num_parents;
|
||||
|
||||
genamux->mux.lock = &clkgena_divmux_lock;
|
||||
genamux->mux.mask = BIT(mux_width) - 1;
|
||||
genamux->mux.shift = muxdata->mux_start_bit + (idx * mux_width);
|
||||
if (genamux->mux.shift > 31) {
|
||||
/*
|
||||
* We have spilled into the second mux register so
|
||||
* adjust the register address and the bit shift accordingly
|
||||
*/
|
||||
genamux->mux.reg = reg + muxdata->mux_offset2;
|
||||
genamux->mux.shift -= 32;
|
||||
} else {
|
||||
genamux->mux.reg = reg + muxdata->mux_offset;
|
||||
}
|
||||
|
||||
for (i = 0; i < NUM_INPUTS; i++) {
|
||||
/*
|
||||
* Divider config for each input
|
||||
*/
|
||||
void __iomem *divbase = reg + muxdata->div_offsets[i];
|
||||
genamux->div[i].width = divider_width;
|
||||
genamux->div[i].reg = divbase + (idx * sizeof(u32));
|
||||
|
||||
/*
|
||||
* Mux enabled/running feedback register for each input.
|
||||
*/
|
||||
genamux->feedback_reg[i] = reg + muxdata->fb_offsets[i];
|
||||
}
|
||||
|
||||
genamux->feedback_bit_idx = muxdata->fb_start_bit_idx + idx;
|
||||
genamux->hw.init = &init;
|
||||
|
||||
clk = clk_register(NULL, &genamux->hw);
|
||||
if (IS_ERR(clk)) {
|
||||
kfree(genamux);
|
||||
goto err;
|
||||
}
|
||||
|
||||
pr_debug("%s: parent %s rate %lu\n",
|
||||
__clk_get_name(clk),
|
||||
__clk_get_name(clk_get_parent(clk)),
|
||||
clk_get_rate(clk));
|
||||
err:
|
||||
return clk;
|
||||
}
|
||||
|
||||
static struct clkgena_divmux_data st_divmux_c65hs = {
|
||||
.num_outputs = 4,
|
||||
.mux_offset = 0x14,
|
||||
.mux_start_bit = 0,
|
||||
.div_offsets = { 0x800, 0x900, 0xb00 },
|
||||
.fb_offsets = { 0x18, 0x1c, 0x20 },
|
||||
.fb_start_bit_idx = 0,
|
||||
};
|
||||
|
||||
static struct clkgena_divmux_data st_divmux_c65ls = {
|
||||
.num_outputs = 14,
|
||||
.mux_offset = 0x14,
|
||||
.mux_offset2 = 0x24,
|
||||
.mux_start_bit = 8,
|
||||
.div_offsets = { 0x810, 0xa10, 0xb10 },
|
||||
.fb_offsets = { 0x18, 0x1c, 0x20 },
|
||||
.fb_start_bit_idx = 4,
|
||||
};
|
||||
|
||||
static struct clkgena_divmux_data st_divmux_c32odf0 = {
|
||||
.num_outputs = 8,
|
||||
.mux_offset = 0x1c,
|
||||
.mux_start_bit = 0,
|
||||
.div_offsets = { 0x800, 0x900, 0xa60 },
|
||||
.fb_offsets = { 0x2c, 0x24, 0x28 },
|
||||
.fb_start_bit_idx = 0,
|
||||
};
|
||||
|
||||
static struct clkgena_divmux_data st_divmux_c32odf1 = {
|
||||
.num_outputs = 8,
|
||||
.mux_offset = 0x1c,
|
||||
.mux_start_bit = 16,
|
||||
.div_offsets = { 0x820, 0x980, 0xa80 },
|
||||
.fb_offsets = { 0x2c, 0x24, 0x28 },
|
||||
.fb_start_bit_idx = 8,
|
||||
};
|
||||
|
||||
static struct clkgena_divmux_data st_divmux_c32odf2 = {
|
||||
.num_outputs = 8,
|
||||
.mux_offset = 0x20,
|
||||
.mux_start_bit = 0,
|
||||
.div_offsets = { 0x840, 0xa20, 0xb10 },
|
||||
.fb_offsets = { 0x2c, 0x24, 0x28 },
|
||||
.fb_start_bit_idx = 16,
|
||||
};
|
||||
|
||||
static struct clkgena_divmux_data st_divmux_c32odf3 = {
|
||||
.num_outputs = 8,
|
||||
.mux_offset = 0x20,
|
||||
.mux_start_bit = 16,
|
||||
.div_offsets = { 0x860, 0xa40, 0xb30 },
|
||||
.fb_offsets = { 0x2c, 0x24, 0x28 },
|
||||
.fb_start_bit_idx = 24,
|
||||
};
|
||||
|
||||
static struct of_device_id clkgena_divmux_of_match[] = {
|
||||
{
|
||||
.compatible = "st,clkgena-divmux-c65-hs",
|
||||
.data = &st_divmux_c65hs,
|
||||
},
|
||||
{
|
||||
.compatible = "st,clkgena-divmux-c65-ls",
|
||||
.data = &st_divmux_c65ls,
|
||||
},
|
||||
{
|
||||
.compatible = "st,clkgena-divmux-c32-odf0",
|
||||
.data = &st_divmux_c32odf0,
|
||||
},
|
||||
{
|
||||
.compatible = "st,clkgena-divmux-c32-odf1",
|
||||
.data = &st_divmux_c32odf1,
|
||||
},
|
||||
{
|
||||
.compatible = "st,clkgena-divmux-c32-odf2",
|
||||
.data = &st_divmux_c32odf2,
|
||||
},
|
||||
{
|
||||
.compatible = "st,clkgena-divmux-c32-odf3",
|
||||
.data = &st_divmux_c32odf3,
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
static void __iomem * __init clkgen_get_register_base(
|
||||
struct device_node *np)
|
||||
{
|
||||
struct device_node *pnode;
|
||||
void __iomem *reg = NULL;
|
||||
|
||||
pnode = of_get_parent(np);
|
||||
if (!pnode)
|
||||
return NULL;
|
||||
|
||||
reg = of_iomap(pnode, 0);
|
||||
|
||||
of_node_put(pnode);
|
||||
return reg;
|
||||
}
|
||||
|
||||
void __init st_of_clkgena_divmux_setup(struct device_node *np)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
const struct clkgena_divmux_data *data;
|
||||
struct clk_onecell_data *clk_data;
|
||||
void __iomem *reg;
|
||||
const char **parents;
|
||||
int num_parents = 0, i;
|
||||
|
||||
match = of_match_node(clkgena_divmux_of_match, np);
|
||||
if (WARN_ON(!match))
|
||||
return;
|
||||
|
||||
data = (struct clkgena_divmux_data *)match->data;
|
||||
|
||||
reg = clkgen_get_register_base(np);
|
||||
if (!reg)
|
||||
return;
|
||||
|
||||
parents = clkgen_mux_get_parents(np, &num_parents);
|
||||
if (IS_ERR(parents))
|
||||
return;
|
||||
|
||||
clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
|
||||
if (!clk_data)
|
||||
goto err;
|
||||
|
||||
clk_data->clk_num = data->num_outputs;
|
||||
clk_data->clks = kzalloc(clk_data->clk_num * sizeof(struct clk *),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!clk_data->clks)
|
||||
goto err;
|
||||
|
||||
for (i = 0; i < clk_data->clk_num; i++) {
|
||||
struct clk *clk;
|
||||
const char *clk_name;
|
||||
|
||||
if (of_property_read_string_index(np, "clock-output-names",
|
||||
i, &clk_name))
|
||||
break;
|
||||
|
||||
/*
|
||||
* If we read an empty clock name then the output is unused
|
||||
*/
|
||||
if (*clk_name == '\0')
|
||||
continue;
|
||||
|
||||
clk = clk_register_genamux(clk_name, parents, num_parents,
|
||||
reg, data, i);
|
||||
|
||||
if (IS_ERR(clk))
|
||||
goto err;
|
||||
|
||||
clk_data->clks[i] = clk;
|
||||
}
|
||||
|
||||
kfree(parents);
|
||||
|
||||
of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
|
||||
return;
|
||||
err:
|
||||
if (clk_data)
|
||||
kfree(clk_data->clks);
|
||||
|
||||
kfree(clk_data);
|
||||
kfree(parents);
|
||||
}
|
||||
CLK_OF_DECLARE(clkgenadivmux, "st,clkgena-divmux", st_of_clkgena_divmux_setup);
|
||||
|
||||
struct clkgena_prediv_data {
|
||||
u32 offset;
|
||||
u8 shift;
|
||||
struct clk_div_table *table;
|
||||
};
|
||||
|
||||
static struct clk_div_table prediv_table16[] = {
|
||||
{ .val = 0, .div = 1 },
|
||||
{ .val = 1, .div = 16 },
|
||||
{ .div = 0 },
|
||||
};
|
||||
|
||||
static struct clkgena_prediv_data prediv_c65_data = {
|
||||
.offset = 0x4c,
|
||||
.shift = 31,
|
||||
.table = prediv_table16,
|
||||
};
|
||||
|
||||
static struct clkgena_prediv_data prediv_c32_data = {
|
||||
.offset = 0x50,
|
||||
.shift = 1,
|
||||
.table = prediv_table16,
|
||||
};
|
||||
|
||||
static struct of_device_id clkgena_prediv_of_match[] = {
|
||||
{ .compatible = "st,clkgena-prediv-c65", .data = &prediv_c65_data },
|
||||
{ .compatible = "st,clkgena-prediv-c32", .data = &prediv_c32_data },
|
||||
{}
|
||||
};
|
||||
|
||||
void __init st_of_clkgena_prediv_setup(struct device_node *np)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
void __iomem *reg;
|
||||
const char *parent_name, *clk_name;
|
||||
struct clk *clk;
|
||||
struct clkgena_prediv_data *data;
|
||||
|
||||
match = of_match_node(clkgena_prediv_of_match, np);
|
||||
if (!match) {
|
||||
pr_err("%s: No matching data\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
data = (struct clkgena_prediv_data *)match->data;
|
||||
|
||||
reg = clkgen_get_register_base(np);
|
||||
if (!reg)
|
||||
return;
|
||||
|
||||
parent_name = of_clk_get_parent_name(np, 0);
|
||||
if (!parent_name)
|
||||
return;
|
||||
|
||||
if (of_property_read_string_index(np, "clock-output-names",
|
||||
0, &clk_name))
|
||||
return;
|
||||
|
||||
clk = clk_register_divider_table(NULL, clk_name, parent_name, 0,
|
||||
reg + data->offset, data->shift, 1,
|
||||
0, data->table, NULL);
|
||||
if (IS_ERR(clk))
|
||||
return;
|
||||
|
||||
of_clk_add_provider(np, of_clk_src_simple_get, clk);
|
||||
pr_debug("%s: parent %s rate %u\n",
|
||||
__clk_get_name(clk),
|
||||
__clk_get_name(clk_get_parent(clk)),
|
||||
(unsigned int)clk_get_rate(clk));
|
||||
|
||||
return;
|
||||
}
|
||||
CLK_OF_DECLARE(clkgenaprediv, "st,clkgena-prediv", st_of_clkgena_prediv_setup);
|
||||
|
||||
struct clkgen_mux_data {
|
||||
u32 offset;
|
||||
u8 shift;
|
||||
u8 width;
|
||||
spinlock_t *lock;
|
||||
unsigned long clk_flags;
|
||||
u8 mux_flags;
|
||||
};
|
||||
|
||||
static struct clkgen_mux_data clkgen_mux_c_vcc_hd_416 = {
|
||||
.offset = 0,
|
||||
.shift = 0,
|
||||
.width = 1,
|
||||
};
|
||||
|
||||
static struct clkgen_mux_data clkgen_mux_f_vcc_fvdp_416 = {
|
||||
.offset = 0,
|
||||
.shift = 0,
|
||||
.width = 1,
|
||||
};
|
||||
|
||||
static struct clkgen_mux_data clkgen_mux_f_vcc_hva_416 = {
|
||||
.offset = 0,
|
||||
.shift = 0,
|
||||
.width = 1,
|
||||
};
|
||||
|
||||
static struct clkgen_mux_data clkgen_mux_f_vcc_hd_416 = {
|
||||
.offset = 0,
|
||||
.shift = 16,
|
||||
.width = 1,
|
||||
.lock = &clkgenf_lock,
|
||||
};
|
||||
|
||||
static struct clkgen_mux_data clkgen_mux_c_vcc_sd_416 = {
|
||||
.offset = 0,
|
||||
.shift = 17,
|
||||
.width = 1,
|
||||
.lock = &clkgenf_lock,
|
||||
};
|
||||
|
||||
static struct clkgen_mux_data stih415_a9_mux_data = {
|
||||
.offset = 0,
|
||||
.shift = 1,
|
||||
.width = 2,
|
||||
};
|
||||
static struct clkgen_mux_data stih416_a9_mux_data = {
|
||||
.offset = 0,
|
||||
.shift = 0,
|
||||
.width = 2,
|
||||
};
|
||||
|
||||
static struct of_device_id mux_of_match[] = {
|
||||
{
|
||||
.compatible = "st,stih416-clkgenc-vcc-hd",
|
||||
.data = &clkgen_mux_c_vcc_hd_416,
|
||||
},
|
||||
{
|
||||
.compatible = "st,stih416-clkgenf-vcc-fvdp",
|
||||
.data = &clkgen_mux_f_vcc_fvdp_416,
|
||||
},
|
||||
{
|
||||
.compatible = "st,stih416-clkgenf-vcc-hva",
|
||||
.data = &clkgen_mux_f_vcc_hva_416,
|
||||
},
|
||||
{
|
||||
.compatible = "st,stih416-clkgenf-vcc-hd",
|
||||
.data = &clkgen_mux_f_vcc_hd_416,
|
||||
},
|
||||
{
|
||||
.compatible = "st,stih416-clkgenf-vcc-sd",
|
||||
.data = &clkgen_mux_c_vcc_sd_416,
|
||||
},
|
||||
{
|
||||
.compatible = "st,stih415-clkgen-a9-mux",
|
||||
.data = &stih415_a9_mux_data,
|
||||
},
|
||||
{
|
||||
.compatible = "st,stih416-clkgen-a9-mux",
|
||||
.data = &stih416_a9_mux_data,
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
void __init st_of_clkgen_mux_setup(struct device_node *np)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
struct clk *clk;
|
||||
void __iomem *reg;
|
||||
const char **parents;
|
||||
int num_parents;
|
||||
struct clkgen_mux_data *data;
|
||||
|
||||
match = of_match_node(mux_of_match, np);
|
||||
if (!match) {
|
||||
pr_err("%s: No matching data\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
data = (struct clkgen_mux_data *)match->data;
|
||||
|
||||
reg = of_iomap(np, 0);
|
||||
if (!reg) {
|
||||
pr_err("%s: Failed to get base address\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
parents = clkgen_mux_get_parents(np, &num_parents);
|
||||
if (IS_ERR(parents)) {
|
||||
pr_err("%s: Failed to get parents (%ld)\n",
|
||||
__func__, PTR_ERR(parents));
|
||||
return;
|
||||
}
|
||||
|
||||
clk = clk_register_mux(NULL, np->name, parents, num_parents,
|
||||
data->clk_flags | CLK_SET_RATE_PARENT,
|
||||
reg + data->offset,
|
||||
data->shift, data->width, data->mux_flags,
|
||||
data->lock);
|
||||
if (IS_ERR(clk))
|
||||
goto err;
|
||||
|
||||
pr_debug("%s: parent %s rate %u\n",
|
||||
__clk_get_name(clk),
|
||||
__clk_get_name(clk_get_parent(clk)),
|
||||
(unsigned int)clk_get_rate(clk));
|
||||
|
||||
of_clk_add_provider(np, of_clk_src_simple_get, clk);
|
||||
|
||||
err:
|
||||
kfree(parents);
|
||||
|
||||
return;
|
||||
}
|
||||
CLK_OF_DECLARE(clkgen_mux, "st,clkgen-mux", st_of_clkgen_mux_setup);
|
||||
|
||||
#define VCC_MAX_CHANNELS 16
|
||||
|
||||
#define VCC_GATE_OFFSET 0x0
|
||||
#define VCC_MUX_OFFSET 0x4
|
||||
#define VCC_DIV_OFFSET 0x8
|
||||
|
||||
struct clkgen_vcc_data {
|
||||
spinlock_t *lock;
|
||||
unsigned long clk_flags;
|
||||
};
|
||||
|
||||
static struct clkgen_vcc_data st_clkgenc_vcc_416 = {
|
||||
.clk_flags = CLK_SET_RATE_PARENT,
|
||||
};
|
||||
|
||||
static struct clkgen_vcc_data st_clkgenf_vcc_416 = {
|
||||
.lock = &clkgenf_lock,
|
||||
};
|
||||
|
||||
static struct of_device_id vcc_of_match[] = {
|
||||
{ .compatible = "st,stih416-clkgenc", .data = &st_clkgenc_vcc_416 },
|
||||
{ .compatible = "st,stih416-clkgenf", .data = &st_clkgenf_vcc_416 },
|
||||
{}
|
||||
};
|
||||
|
||||
void __init st_of_clkgen_vcc_setup(struct device_node *np)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
void __iomem *reg;
|
||||
const char **parents;
|
||||
int num_parents, i;
|
||||
struct clk_onecell_data *clk_data;
|
||||
struct clkgen_vcc_data *data;
|
||||
|
||||
match = of_match_node(vcc_of_match, np);
|
||||
if (WARN_ON(!match))
|
||||
return;
|
||||
data = (struct clkgen_vcc_data *)match->data;
|
||||
|
||||
reg = of_iomap(np, 0);
|
||||
if (!reg)
|
||||
return;
|
||||
|
||||
parents = clkgen_mux_get_parents(np, &num_parents);
|
||||
if (IS_ERR(parents))
|
||||
return;
|
||||
|
||||
clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
|
||||
if (!clk_data)
|
||||
goto err;
|
||||
|
||||
clk_data->clk_num = VCC_MAX_CHANNELS;
|
||||
clk_data->clks = kzalloc(clk_data->clk_num * sizeof(struct clk *),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!clk_data->clks)
|
||||
goto err;
|
||||
|
||||
for (i = 0; i < clk_data->clk_num; i++) {
|
||||
struct clk *clk;
|
||||
const char *clk_name;
|
||||
struct clk_gate *gate;
|
||||
struct clk_divider *div;
|
||||
struct clk_mux *mux;
|
||||
|
||||
if (of_property_read_string_index(np, "clock-output-names",
|
||||
i, &clk_name))
|
||||
break;
|
||||
|
||||
/*
|
||||
* If we read an empty clock name then the output is unused
|
||||
*/
|
||||
if (*clk_name == '\0')
|
||||
continue;
|
||||
|
||||
gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
|
||||
if (!gate)
|
||||
break;
|
||||
|
||||
div = kzalloc(sizeof(struct clk_divider), GFP_KERNEL);
|
||||
if (!div) {
|
||||
kfree(gate);
|
||||
break;
|
||||
}
|
||||
|
||||
mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL);
|
||||
if (!mux) {
|
||||
kfree(gate);
|
||||
kfree(div);
|
||||
break;
|
||||
}
|
||||
|
||||
gate->reg = reg + VCC_GATE_OFFSET;
|
||||
gate->bit_idx = i;
|
||||
gate->flags = CLK_GATE_SET_TO_DISABLE;
|
||||
gate->lock = data->lock;
|
||||
|
||||
div->reg = reg + VCC_DIV_OFFSET;
|
||||
div->shift = 2 * i;
|
||||
div->width = 2;
|
||||
div->flags = CLK_DIVIDER_POWER_OF_TWO;
|
||||
|
||||
mux->reg = reg + VCC_MUX_OFFSET;
|
||||
mux->shift = 2 * i;
|
||||
mux->mask = 0x3;
|
||||
|
||||
clk = clk_register_composite(NULL, clk_name, parents,
|
||||
num_parents,
|
||||
&mux->hw, &clk_mux_ops,
|
||||
&div->hw, &clk_divider_ops,
|
||||
&gate->hw, &clk_gate_ops,
|
||||
data->clk_flags);
|
||||
if (IS_ERR(clk)) {
|
||||
kfree(gate);
|
||||
kfree(div);
|
||||
kfree(mux);
|
||||
goto err;
|
||||
}
|
||||
|
||||
pr_debug("%s: parent %s rate %u\n",
|
||||
__clk_get_name(clk),
|
||||
__clk_get_name(clk_get_parent(clk)),
|
||||
(unsigned int)clk_get_rate(clk));
|
||||
|
||||
clk_data->clks[i] = clk;
|
||||
}
|
||||
|
||||
kfree(parents);
|
||||
|
||||
of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
|
||||
return;
|
||||
|
||||
err:
|
||||
for (i = 0; i < clk_data->clk_num; i++) {
|
||||
struct clk_composite *composite;
|
||||
|
||||
if (!clk_data->clks[i])
|
||||
continue;
|
||||
|
||||
composite = container_of(__clk_get_hw(clk_data->clks[i]),
|
||||
struct clk_composite, hw);
|
||||
kfree(container_of(composite->gate_hw, struct clk_gate, hw));
|
||||
kfree(container_of(composite->rate_hw, struct clk_divider, hw));
|
||||
kfree(container_of(composite->mux_hw, struct clk_mux, hw));
|
||||
}
|
||||
|
||||
if (clk_data)
|
||||
kfree(clk_data->clks);
|
||||
|
||||
kfree(clk_data);
|
||||
kfree(parents);
|
||||
}
|
||||
CLK_OF_DECLARE(clkgen_vcc, "st,clkgen-vcc", st_of_clkgen_vcc_setup);
|
698
drivers/clk/st/clkgen-pll.c
Normal file
698
drivers/clk/st/clkgen-pll.c
Normal file
|
@ -0,0 +1,698 @@
|
|||
/*
|
||||
* Copyright (C) 2014 STMicroelectronics (R&D) Limited
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Authors:
|
||||
* Stephen Gallimore <stephen.gallimore@st.com>,
|
||||
* Pankaj Dev <pankaj.dev@st.com>.
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/clk-provider.h>
|
||||
|
||||
#include "clkgen.h"
|
||||
|
||||
static DEFINE_SPINLOCK(clkgena_c32_odf_lock);
|
||||
|
||||
/*
|
||||
* Common PLL configuration register bits for PLL800 and PLL1600 C65
|
||||
*/
|
||||
#define C65_MDIV_PLL800_MASK (0xff)
|
||||
#define C65_MDIV_PLL1600_MASK (0x7)
|
||||
#define C65_NDIV_MASK (0xff)
|
||||
#define C65_PDIV_MASK (0x7)
|
||||
|
||||
/*
|
||||
* PLL configuration register bits for PLL3200 C32
|
||||
*/
|
||||
#define C32_NDIV_MASK (0xff)
|
||||
#define C32_IDF_MASK (0x7)
|
||||
#define C32_ODF_MASK (0x3f)
|
||||
#define C32_LDF_MASK (0x7f)
|
||||
|
||||
#define C32_MAX_ODFS (4)
|
||||
|
||||
struct clkgen_pll_data {
|
||||
struct clkgen_field pdn_status;
|
||||
struct clkgen_field locked_status;
|
||||
struct clkgen_field mdiv;
|
||||
struct clkgen_field ndiv;
|
||||
struct clkgen_field pdiv;
|
||||
struct clkgen_field idf;
|
||||
struct clkgen_field ldf;
|
||||
unsigned int num_odfs;
|
||||
struct clkgen_field odf[C32_MAX_ODFS];
|
||||
struct clkgen_field odf_gate[C32_MAX_ODFS];
|
||||
const struct clk_ops *ops;
|
||||
};
|
||||
|
||||
static const struct clk_ops st_pll1600c65_ops;
|
||||
static const struct clk_ops st_pll800c65_ops;
|
||||
static const struct clk_ops stm_pll3200c32_ops;
|
||||
static const struct clk_ops st_pll1200c32_ops;
|
||||
|
||||
static struct clkgen_pll_data st_pll1600c65_ax = {
|
||||
.pdn_status = CLKGEN_FIELD(0x0, 0x1, 19),
|
||||
.locked_status = CLKGEN_FIELD(0x0, 0x1, 31),
|
||||
.mdiv = CLKGEN_FIELD(0x0, C65_MDIV_PLL1600_MASK, 0),
|
||||
.ndiv = CLKGEN_FIELD(0x0, C65_NDIV_MASK, 8),
|
||||
.ops = &st_pll1600c65_ops
|
||||
};
|
||||
|
||||
static struct clkgen_pll_data st_pll800c65_ax = {
|
||||
.pdn_status = CLKGEN_FIELD(0x0, 0x1, 19),
|
||||
.locked_status = CLKGEN_FIELD(0x0, 0x1, 31),
|
||||
.mdiv = CLKGEN_FIELD(0x0, C65_MDIV_PLL800_MASK, 0),
|
||||
.ndiv = CLKGEN_FIELD(0x0, C65_NDIV_MASK, 8),
|
||||
.pdiv = CLKGEN_FIELD(0x0, C65_PDIV_MASK, 16),
|
||||
.ops = &st_pll800c65_ops
|
||||
};
|
||||
|
||||
static struct clkgen_pll_data st_pll3200c32_a1x_0 = {
|
||||
.pdn_status = CLKGEN_FIELD(0x0, 0x1, 31),
|
||||
.locked_status = CLKGEN_FIELD(0x4, 0x1, 31),
|
||||
.ndiv = CLKGEN_FIELD(0x0, C32_NDIV_MASK, 0x0),
|
||||
.idf = CLKGEN_FIELD(0x4, C32_IDF_MASK, 0x0),
|
||||
.num_odfs = 4,
|
||||
.odf = { CLKGEN_FIELD(0x54, C32_ODF_MASK, 4),
|
||||
CLKGEN_FIELD(0x54, C32_ODF_MASK, 10),
|
||||
CLKGEN_FIELD(0x54, C32_ODF_MASK, 16),
|
||||
CLKGEN_FIELD(0x54, C32_ODF_MASK, 22) },
|
||||
.odf_gate = { CLKGEN_FIELD(0x54, 0x1, 0),
|
||||
CLKGEN_FIELD(0x54, 0x1, 1),
|
||||
CLKGEN_FIELD(0x54, 0x1, 2),
|
||||
CLKGEN_FIELD(0x54, 0x1, 3) },
|
||||
.ops = &stm_pll3200c32_ops,
|
||||
};
|
||||
|
||||
static struct clkgen_pll_data st_pll3200c32_a1x_1 = {
|
||||
.pdn_status = CLKGEN_FIELD(0xC, 0x1, 31),
|
||||
.locked_status = CLKGEN_FIELD(0x10, 0x1, 31),
|
||||
.ndiv = CLKGEN_FIELD(0xC, C32_NDIV_MASK, 0x0),
|
||||
.idf = CLKGEN_FIELD(0x10, C32_IDF_MASK, 0x0),
|
||||
.num_odfs = 4,
|
||||
.odf = { CLKGEN_FIELD(0x58, C32_ODF_MASK, 4),
|
||||
CLKGEN_FIELD(0x58, C32_ODF_MASK, 10),
|
||||
CLKGEN_FIELD(0x58, C32_ODF_MASK, 16),
|
||||
CLKGEN_FIELD(0x58, C32_ODF_MASK, 22) },
|
||||
.odf_gate = { CLKGEN_FIELD(0x58, 0x1, 0),
|
||||
CLKGEN_FIELD(0x58, 0x1, 1),
|
||||
CLKGEN_FIELD(0x58, 0x1, 2),
|
||||
CLKGEN_FIELD(0x58, 0x1, 3) },
|
||||
.ops = &stm_pll3200c32_ops,
|
||||
};
|
||||
|
||||
/* 415 specific */
|
||||
static struct clkgen_pll_data st_pll3200c32_a9_415 = {
|
||||
.pdn_status = CLKGEN_FIELD(0x0, 0x1, 0),
|
||||
.locked_status = CLKGEN_FIELD(0x6C, 0x1, 0),
|
||||
.ndiv = CLKGEN_FIELD(0x0, C32_NDIV_MASK, 9),
|
||||
.idf = CLKGEN_FIELD(0x0, C32_IDF_MASK, 22),
|
||||
.num_odfs = 1,
|
||||
.odf = { CLKGEN_FIELD(0x0, C32_ODF_MASK, 3) },
|
||||
.odf_gate = { CLKGEN_FIELD(0x0, 0x1, 28) },
|
||||
.ops = &stm_pll3200c32_ops,
|
||||
};
|
||||
|
||||
static struct clkgen_pll_data st_pll3200c32_ddr_415 = {
|
||||
.pdn_status = CLKGEN_FIELD(0x0, 0x1, 0),
|
||||
.locked_status = CLKGEN_FIELD(0x100, 0x1, 0),
|
||||
.ndiv = CLKGEN_FIELD(0x8, C32_NDIV_MASK, 0),
|
||||
.idf = CLKGEN_FIELD(0x0, C32_IDF_MASK, 25),
|
||||
.num_odfs = 2,
|
||||
.odf = { CLKGEN_FIELD(0x8, C32_ODF_MASK, 8),
|
||||
CLKGEN_FIELD(0x8, C32_ODF_MASK, 14) },
|
||||
.odf_gate = { CLKGEN_FIELD(0x4, 0x1, 28),
|
||||
CLKGEN_FIELD(0x4, 0x1, 29) },
|
||||
.ops = &stm_pll3200c32_ops,
|
||||
};
|
||||
|
||||
static struct clkgen_pll_data st_pll1200c32_gpu_415 = {
|
||||
.pdn_status = CLKGEN_FIELD(0x144, 0x1, 3),
|
||||
.locked_status = CLKGEN_FIELD(0x168, 0x1, 0),
|
||||
.ldf = CLKGEN_FIELD(0x0, C32_LDF_MASK, 3),
|
||||
.idf = CLKGEN_FIELD(0x0, C32_IDF_MASK, 0),
|
||||
.num_odfs = 0,
|
||||
.odf = { CLKGEN_FIELD(0x0, C32_ODF_MASK, 10) },
|
||||
.ops = &st_pll1200c32_ops,
|
||||
};
|
||||
|
||||
/* 416 specific */
|
||||
static struct clkgen_pll_data st_pll3200c32_a9_416 = {
|
||||
.pdn_status = CLKGEN_FIELD(0x0, 0x1, 0),
|
||||
.locked_status = CLKGEN_FIELD(0x6C, 0x1, 0),
|
||||
.ndiv = CLKGEN_FIELD(0x8, C32_NDIV_MASK, 0),
|
||||
.idf = CLKGEN_FIELD(0x0, C32_IDF_MASK, 25),
|
||||
.num_odfs = 1,
|
||||
.odf = { CLKGEN_FIELD(0x8, C32_ODF_MASK, 8) },
|
||||
.odf_gate = { CLKGEN_FIELD(0x4, 0x1, 28) },
|
||||
.ops = &stm_pll3200c32_ops,
|
||||
};
|
||||
|
||||
static struct clkgen_pll_data st_pll3200c32_ddr_416 = {
|
||||
.pdn_status = CLKGEN_FIELD(0x0, 0x1, 0),
|
||||
.locked_status = CLKGEN_FIELD(0x10C, 0x1, 0),
|
||||
.ndiv = CLKGEN_FIELD(0x8, C32_NDIV_MASK, 0),
|
||||
.idf = CLKGEN_FIELD(0x0, C32_IDF_MASK, 25),
|
||||
.num_odfs = 2,
|
||||
.odf = { CLKGEN_FIELD(0x8, C32_ODF_MASK, 8),
|
||||
CLKGEN_FIELD(0x8, C32_ODF_MASK, 14) },
|
||||
.odf_gate = { CLKGEN_FIELD(0x4, 0x1, 28),
|
||||
CLKGEN_FIELD(0x4, 0x1, 29) },
|
||||
.ops = &stm_pll3200c32_ops,
|
||||
};
|
||||
|
||||
static struct clkgen_pll_data st_pll1200c32_gpu_416 = {
|
||||
.pdn_status = CLKGEN_FIELD(0x8E4, 0x1, 3),
|
||||
.locked_status = CLKGEN_FIELD(0x90C, 0x1, 0),
|
||||
.ldf = CLKGEN_FIELD(0x0, C32_LDF_MASK, 3),
|
||||
.idf = CLKGEN_FIELD(0x0, C32_IDF_MASK, 0),
|
||||
.num_odfs = 0,
|
||||
.odf = { CLKGEN_FIELD(0x0, C32_ODF_MASK, 10) },
|
||||
.ops = &st_pll1200c32_ops,
|
||||
};
|
||||
|
||||
/**
|
||||
* DOC: Clock Generated by PLL, rate set and enabled by bootloader
|
||||
*
|
||||
* Traits of this clock:
|
||||
* prepare - clk_(un)prepare only ensures parent is (un)prepared
|
||||
* enable - clk_enable/disable only ensures parent is enabled
|
||||
* rate - rate is fixed. No clk_set_rate support
|
||||
* parent - fixed parent. No clk_set_parent support
|
||||
*/
|
||||
|
||||
/**
|
||||
* PLL clock that is integrated in the ClockGenA instances on the STiH415
|
||||
* and STiH416.
|
||||
*
|
||||
* @hw: handle between common and hardware-specific interfaces.
|
||||
* @type: PLL instance type.
|
||||
* @regs_base: base of the PLL configuration register(s).
|
||||
*
|
||||
*/
|
||||
struct clkgen_pll {
|
||||
struct clk_hw hw;
|
||||
struct clkgen_pll_data *data;
|
||||
void __iomem *regs_base;
|
||||
};
|
||||
|
||||
#define to_clkgen_pll(_hw) container_of(_hw, struct clkgen_pll, hw)
|
||||
|
||||
static int clkgen_pll_is_locked(struct clk_hw *hw)
|
||||
{
|
||||
struct clkgen_pll *pll = to_clkgen_pll(hw);
|
||||
u32 locked = CLKGEN_READ(pll, locked_status);
|
||||
|
||||
return !!locked;
|
||||
}
|
||||
|
||||
static int clkgen_pll_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct clkgen_pll *pll = to_clkgen_pll(hw);
|
||||
u32 poweroff = CLKGEN_READ(pll, pdn_status);
|
||||
return !poweroff;
|
||||
}
|
||||
|
||||
unsigned long recalc_stm_pll800c65(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clkgen_pll *pll = to_clkgen_pll(hw);
|
||||
unsigned long mdiv, ndiv, pdiv;
|
||||
unsigned long rate;
|
||||
uint64_t res;
|
||||
|
||||
if (!clkgen_pll_is_enabled(hw) || !clkgen_pll_is_locked(hw))
|
||||
return 0;
|
||||
|
||||
pdiv = CLKGEN_READ(pll, pdiv);
|
||||
mdiv = CLKGEN_READ(pll, mdiv);
|
||||
ndiv = CLKGEN_READ(pll, ndiv);
|
||||
|
||||
if (!mdiv)
|
||||
mdiv++; /* mdiv=0 or 1 => MDIV=1 */
|
||||
|
||||
res = (uint64_t)2 * (uint64_t)parent_rate * (uint64_t)ndiv;
|
||||
rate = (unsigned long)div64_u64(res, mdiv * (1 << pdiv));
|
||||
|
||||
pr_debug("%s:%s rate %lu\n", __clk_get_name(hw->clk), __func__, rate);
|
||||
|
||||
return rate;
|
||||
|
||||
}
|
||||
|
||||
unsigned long recalc_stm_pll1600c65(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clkgen_pll *pll = to_clkgen_pll(hw);
|
||||
unsigned long mdiv, ndiv;
|
||||
unsigned long rate;
|
||||
|
||||
if (!clkgen_pll_is_enabled(hw) || !clkgen_pll_is_locked(hw))
|
||||
return 0;
|
||||
|
||||
mdiv = CLKGEN_READ(pll, mdiv);
|
||||
ndiv = CLKGEN_READ(pll, ndiv);
|
||||
|
||||
if (!mdiv)
|
||||
mdiv = 1;
|
||||
|
||||
/* Note: input is divided by 1000 to avoid overflow */
|
||||
rate = ((2 * (parent_rate / 1000) * ndiv) / mdiv) * 1000;
|
||||
|
||||
pr_debug("%s:%s rate %lu\n", __clk_get_name(hw->clk), __func__, rate);
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
unsigned long recalc_stm_pll3200c32(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clkgen_pll *pll = to_clkgen_pll(hw);
|
||||
unsigned long ndiv, idf;
|
||||
unsigned long rate = 0;
|
||||
|
||||
if (!clkgen_pll_is_enabled(hw) || !clkgen_pll_is_locked(hw))
|
||||
return 0;
|
||||
|
||||
ndiv = CLKGEN_READ(pll, ndiv);
|
||||
idf = CLKGEN_READ(pll, idf);
|
||||
|
||||
if (idf)
|
||||
/* Note: input is divided to avoid overflow */
|
||||
rate = ((2 * (parent_rate/1000) * ndiv) / idf) * 1000;
|
||||
|
||||
pr_debug("%s:%s rate %lu\n", __clk_get_name(hw->clk), __func__, rate);
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
unsigned long recalc_stm_pll1200c32(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clkgen_pll *pll = to_clkgen_pll(hw);
|
||||
unsigned long odf, ldf, idf;
|
||||
unsigned long rate;
|
||||
|
||||
if (!clkgen_pll_is_enabled(hw) || !clkgen_pll_is_locked(hw))
|
||||
return 0;
|
||||
|
||||
odf = CLKGEN_READ(pll, odf[0]);
|
||||
ldf = CLKGEN_READ(pll, ldf);
|
||||
idf = CLKGEN_READ(pll, idf);
|
||||
|
||||
if (!idf) /* idf==0 means 1 */
|
||||
idf = 1;
|
||||
if (!odf) /* odf==0 means 1 */
|
||||
odf = 1;
|
||||
|
||||
/* Note: input is divided by 1000 to avoid overflow */
|
||||
rate = (((parent_rate / 1000) * ldf) / (odf * idf)) * 1000;
|
||||
|
||||
pr_debug("%s:%s rate %lu\n", __clk_get_name(hw->clk), __func__, rate);
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
static const struct clk_ops st_pll1600c65_ops = {
|
||||
.is_enabled = clkgen_pll_is_enabled,
|
||||
.recalc_rate = recalc_stm_pll1600c65,
|
||||
};
|
||||
|
||||
static const struct clk_ops st_pll800c65_ops = {
|
||||
.is_enabled = clkgen_pll_is_enabled,
|
||||
.recalc_rate = recalc_stm_pll800c65,
|
||||
};
|
||||
|
||||
static const struct clk_ops stm_pll3200c32_ops = {
|
||||
.is_enabled = clkgen_pll_is_enabled,
|
||||
.recalc_rate = recalc_stm_pll3200c32,
|
||||
};
|
||||
|
||||
static const struct clk_ops st_pll1200c32_ops = {
|
||||
.is_enabled = clkgen_pll_is_enabled,
|
||||
.recalc_rate = recalc_stm_pll1200c32,
|
||||
};
|
||||
|
||||
static struct clk * __init clkgen_pll_register(const char *parent_name,
|
||||
struct clkgen_pll_data *pll_data,
|
||||
void __iomem *reg,
|
||||
const char *clk_name)
|
||||
{
|
||||
struct clkgen_pll *pll;
|
||||
struct clk *clk;
|
||||
struct clk_init_data init;
|
||||
|
||||
pll = kzalloc(sizeof(*pll), GFP_KERNEL);
|
||||
if (!pll)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = clk_name;
|
||||
init.ops = pll_data->ops;
|
||||
|
||||
init.flags = CLK_IS_BASIC;
|
||||
init.parent_names = &parent_name;
|
||||
init.num_parents = 1;
|
||||
|
||||
pll->data = pll_data;
|
||||
pll->regs_base = reg;
|
||||
pll->hw.init = &init;
|
||||
|
||||
clk = clk_register(NULL, &pll->hw);
|
||||
if (IS_ERR(clk)) {
|
||||
kfree(pll);
|
||||
return clk;
|
||||
}
|
||||
|
||||
pr_debug("%s: parent %s rate %lu\n",
|
||||
__clk_get_name(clk),
|
||||
__clk_get_name(clk_get_parent(clk)),
|
||||
clk_get_rate(clk));
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
static struct clk * __init clkgen_c65_lsdiv_register(const char *parent_name,
|
||||
const char *clk_name)
|
||||
{
|
||||
struct clk *clk;
|
||||
|
||||
clk = clk_register_fixed_factor(NULL, clk_name, parent_name, 0, 1, 2);
|
||||
if (IS_ERR(clk))
|
||||
return clk;
|
||||
|
||||
pr_debug("%s: parent %s rate %lu\n",
|
||||
__clk_get_name(clk),
|
||||
__clk_get_name(clk_get_parent(clk)),
|
||||
clk_get_rate(clk));
|
||||
return clk;
|
||||
}
|
||||
|
||||
static void __iomem * __init clkgen_get_register_base(
|
||||
struct device_node *np)
|
||||
{
|
||||
struct device_node *pnode;
|
||||
void __iomem *reg = NULL;
|
||||
|
||||
pnode = of_get_parent(np);
|
||||
if (!pnode)
|
||||
return NULL;
|
||||
|
||||
reg = of_iomap(pnode, 0);
|
||||
|
||||
of_node_put(pnode);
|
||||
return reg;
|
||||
}
|
||||
|
||||
#define CLKGENAx_PLL0_OFFSET 0x0
|
||||
#define CLKGENAx_PLL1_OFFSET 0x4
|
||||
|
||||
static void __init clkgena_c65_pll_setup(struct device_node *np)
|
||||
{
|
||||
const int num_pll_outputs = 3;
|
||||
struct clk_onecell_data *clk_data;
|
||||
const char *parent_name;
|
||||
void __iomem *reg;
|
||||
const char *clk_name;
|
||||
|
||||
parent_name = of_clk_get_parent_name(np, 0);
|
||||
if (!parent_name)
|
||||
return;
|
||||
|
||||
reg = clkgen_get_register_base(np);
|
||||
if (!reg)
|
||||
return;
|
||||
|
||||
clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
|
||||
if (!clk_data)
|
||||
return;
|
||||
|
||||
clk_data->clk_num = num_pll_outputs;
|
||||
clk_data->clks = kzalloc(clk_data->clk_num * sizeof(struct clk *),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!clk_data->clks)
|
||||
goto err;
|
||||
|
||||
if (of_property_read_string_index(np, "clock-output-names",
|
||||
0, &clk_name))
|
||||
goto err;
|
||||
|
||||
/*
|
||||
* PLL0 HS (high speed) output
|
||||
*/
|
||||
clk_data->clks[0] = clkgen_pll_register(parent_name,
|
||||
&st_pll1600c65_ax,
|
||||
reg + CLKGENAx_PLL0_OFFSET,
|
||||
clk_name);
|
||||
|
||||
if (IS_ERR(clk_data->clks[0]))
|
||||
goto err;
|
||||
|
||||
if (of_property_read_string_index(np, "clock-output-names",
|
||||
1, &clk_name))
|
||||
goto err;
|
||||
|
||||
/*
|
||||
* PLL0 LS (low speed) output, which is a fixed divide by 2 of the
|
||||
* high speed output.
|
||||
*/
|
||||
clk_data->clks[1] = clkgen_c65_lsdiv_register(__clk_get_name
|
||||
(clk_data->clks[0]),
|
||||
clk_name);
|
||||
|
||||
if (IS_ERR(clk_data->clks[1]))
|
||||
goto err;
|
||||
|
||||
if (of_property_read_string_index(np, "clock-output-names",
|
||||
2, &clk_name))
|
||||
goto err;
|
||||
|
||||
/*
|
||||
* PLL1 output
|
||||
*/
|
||||
clk_data->clks[2] = clkgen_pll_register(parent_name,
|
||||
&st_pll800c65_ax,
|
||||
reg + CLKGENAx_PLL1_OFFSET,
|
||||
clk_name);
|
||||
|
||||
if (IS_ERR(clk_data->clks[2]))
|
||||
goto err;
|
||||
|
||||
of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
|
||||
return;
|
||||
|
||||
err:
|
||||
kfree(clk_data->clks);
|
||||
kfree(clk_data);
|
||||
}
|
||||
CLK_OF_DECLARE(clkgena_c65_plls,
|
||||
"st,clkgena-plls-c65", clkgena_c65_pll_setup);
|
||||
|
||||
static struct clk * __init clkgen_odf_register(const char *parent_name,
|
||||
void * __iomem reg,
|
||||
struct clkgen_pll_data *pll_data,
|
||||
int odf,
|
||||
spinlock_t *odf_lock,
|
||||
const char *odf_name)
|
||||
{
|
||||
struct clk *clk;
|
||||
unsigned long flags;
|
||||
struct clk_gate *gate;
|
||||
struct clk_divider *div;
|
||||
|
||||
flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_GATE;
|
||||
|
||||
gate = kzalloc(sizeof(*gate), GFP_KERNEL);
|
||||
if (!gate)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
gate->flags = CLK_GATE_SET_TO_DISABLE;
|
||||
gate->reg = reg + pll_data->odf_gate[odf].offset;
|
||||
gate->bit_idx = pll_data->odf_gate[odf].shift;
|
||||
gate->lock = odf_lock;
|
||||
|
||||
div = kzalloc(sizeof(*div), GFP_KERNEL);
|
||||
if (!div)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
div->flags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO;
|
||||
div->reg = reg + pll_data->odf[odf].offset;
|
||||
div->shift = pll_data->odf[odf].shift;
|
||||
div->width = fls(pll_data->odf[odf].mask);
|
||||
div->lock = odf_lock;
|
||||
|
||||
clk = clk_register_composite(NULL, odf_name, &parent_name, 1,
|
||||
NULL, NULL,
|
||||
&div->hw, &clk_divider_ops,
|
||||
&gate->hw, &clk_gate_ops,
|
||||
flags);
|
||||
if (IS_ERR(clk))
|
||||
return clk;
|
||||
|
||||
pr_debug("%s: parent %s rate %lu\n",
|
||||
__clk_get_name(clk),
|
||||
__clk_get_name(clk_get_parent(clk)),
|
||||
clk_get_rate(clk));
|
||||
return clk;
|
||||
}
|
||||
|
||||
static struct of_device_id c32_pll_of_match[] = {
|
||||
{
|
||||
.compatible = "st,plls-c32-a1x-0",
|
||||
.data = &st_pll3200c32_a1x_0,
|
||||
},
|
||||
{
|
||||
.compatible = "st,plls-c32-a1x-1",
|
||||
.data = &st_pll3200c32_a1x_1,
|
||||
},
|
||||
{
|
||||
.compatible = "st,stih415-plls-c32-a9",
|
||||
.data = &st_pll3200c32_a9_415,
|
||||
},
|
||||
{
|
||||
.compatible = "st,stih415-plls-c32-ddr",
|
||||
.data = &st_pll3200c32_ddr_415,
|
||||
},
|
||||
{
|
||||
.compatible = "st,stih416-plls-c32-a9",
|
||||
.data = &st_pll3200c32_a9_416,
|
||||
},
|
||||
{
|
||||
.compatible = "st,stih416-plls-c32-ddr",
|
||||
.data = &st_pll3200c32_ddr_416,
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
static void __init clkgen_c32_pll_setup(struct device_node *np)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
struct clk *clk;
|
||||
const char *parent_name, *pll_name;
|
||||
void __iomem *pll_base;
|
||||
int num_odfs, odf;
|
||||
struct clk_onecell_data *clk_data;
|
||||
struct clkgen_pll_data *data;
|
||||
|
||||
match = of_match_node(c32_pll_of_match, np);
|
||||
if (!match) {
|
||||
pr_err("%s: No matching data\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
data = (struct clkgen_pll_data *) match->data;
|
||||
|
||||
parent_name = of_clk_get_parent_name(np, 0);
|
||||
if (!parent_name)
|
||||
return;
|
||||
|
||||
pll_base = clkgen_get_register_base(np);
|
||||
if (!pll_base)
|
||||
return;
|
||||
|
||||
clk = clkgen_pll_register(parent_name, data, pll_base, np->name);
|
||||
if (IS_ERR(clk))
|
||||
return;
|
||||
|
||||
pll_name = __clk_get_name(clk);
|
||||
|
||||
num_odfs = data->num_odfs;
|
||||
|
||||
clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
|
||||
if (!clk_data)
|
||||
return;
|
||||
|
||||
clk_data->clk_num = num_odfs;
|
||||
clk_data->clks = kzalloc(clk_data->clk_num * sizeof(struct clk *),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!clk_data->clks)
|
||||
goto err;
|
||||
|
||||
for (odf = 0; odf < num_odfs; odf++) {
|
||||
struct clk *clk;
|
||||
const char *clk_name;
|
||||
|
||||
if (of_property_read_string_index(np, "clock-output-names",
|
||||
odf, &clk_name))
|
||||
return;
|
||||
|
||||
clk = clkgen_odf_register(pll_name, pll_base, data,
|
||||
odf, &clkgena_c32_odf_lock, clk_name);
|
||||
if (IS_ERR(clk))
|
||||
goto err;
|
||||
|
||||
clk_data->clks[odf] = clk;
|
||||
}
|
||||
|
||||
of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
|
||||
return;
|
||||
|
||||
err:
|
||||
kfree(pll_name);
|
||||
kfree(clk_data->clks);
|
||||
kfree(clk_data);
|
||||
}
|
||||
CLK_OF_DECLARE(clkgen_c32_pll, "st,clkgen-plls-c32", clkgen_c32_pll_setup);
|
||||
|
||||
static struct of_device_id c32_gpu_pll_of_match[] = {
|
||||
{
|
||||
.compatible = "st,stih415-gpu-pll-c32",
|
||||
.data = &st_pll1200c32_gpu_415,
|
||||
},
|
||||
{
|
||||
.compatible = "st,stih416-gpu-pll-c32",
|
||||
.data = &st_pll1200c32_gpu_416,
|
||||
},
|
||||
};
|
||||
|
||||
static void __init clkgengpu_c32_pll_setup(struct device_node *np)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
struct clk *clk;
|
||||
const char *parent_name;
|
||||
void __iomem *reg;
|
||||
const char *clk_name;
|
||||
struct clkgen_pll_data *data;
|
||||
|
||||
match = of_match_node(c32_gpu_pll_of_match, np);
|
||||
if (!match) {
|
||||
pr_err("%s: No matching data\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
data = (struct clkgen_pll_data *)match->data;
|
||||
|
||||
parent_name = of_clk_get_parent_name(np, 0);
|
||||
if (!parent_name)
|
||||
return;
|
||||
|
||||
reg = clkgen_get_register_base(np);
|
||||
if (!reg)
|
||||
return;
|
||||
|
||||
if (of_property_read_string_index(np, "clock-output-names",
|
||||
0, &clk_name))
|
||||
return;
|
||||
|
||||
/*
|
||||
* PLL 1200MHz output
|
||||
*/
|
||||
clk = clkgen_pll_register(parent_name, data, reg, clk_name);
|
||||
|
||||
if (!IS_ERR(clk))
|
||||
of_clk_add_provider(np, of_clk_src_simple_get, clk);
|
||||
|
||||
return;
|
||||
}
|
||||
CLK_OF_DECLARE(clkgengpu_c32_pll,
|
||||
"st,clkgengpu-pll-c32", clkgengpu_c32_pll_setup);
|
48
drivers/clk/st/clkgen.h
Normal file
48
drivers/clk/st/clkgen.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
/************************************************************************
|
||||
File : Clock H/w specific Information
|
||||
|
||||
Author: Pankaj Dev <pankaj.dev@st.com>
|
||||
|
||||
Copyright (C) 2014 STMicroelectronics
|
||||
************************************************************************/
|
||||
|
||||
#ifndef __CLKGEN_INFO_H
|
||||
#define __CLKGEN_INFO_H
|
||||
|
||||
struct clkgen_field {
|
||||
unsigned int offset;
|
||||
unsigned int mask;
|
||||
unsigned int shift;
|
||||
};
|
||||
|
||||
static inline unsigned long clkgen_read(void __iomem *base,
|
||||
struct clkgen_field *field)
|
||||
{
|
||||
return (readl(base + field->offset) >> field->shift) & field->mask;
|
||||
}
|
||||
|
||||
|
||||
static inline void clkgen_write(void __iomem *base, struct clkgen_field *field,
|
||||
unsigned long val)
|
||||
{
|
||||
writel((readl(base + field->offset) &
|
||||
~(field->mask << field->shift)) | (val << field->shift),
|
||||
base + field->offset);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#define CLKGEN_FIELD(_offset, _mask, _shift) { \
|
||||
.offset = _offset, \
|
||||
.mask = _mask, \
|
||||
.shift = _shift, \
|
||||
}
|
||||
|
||||
#define CLKGEN_READ(pll, field) clkgen_read(pll->regs_base, \
|
||||
&pll->data->field)
|
||||
|
||||
#define CLKGEN_WRITE(pll, field, val) clkgen_write(pll->regs_base, \
|
||||
&pll->data->field, val)
|
||||
|
||||
#endif /*__CLKGEN_INFO_H*/
|
||||
|
|
@ -18,6 +18,7 @@
|
|||
#include <linux/clkdev.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/reset-controller.h>
|
||||
|
||||
#include "clk-factors.h"
|
||||
|
||||
|
@ -51,6 +52,8 @@ static void __init sun4i_osc_clk_setup(struct device_node *node)
|
|||
if (!gate)
|
||||
goto err_free_fixed;
|
||||
|
||||
of_property_read_string(node, "clock-output-names", &clk_name);
|
||||
|
||||
/* set up gate and fixed rate properties */
|
||||
gate->reg = of_iomap(node, 0);
|
||||
gate->bit_idx = SUNXI_OSC24M_GATE;
|
||||
|
@ -77,7 +80,7 @@ static void __init sun4i_osc_clk_setup(struct device_node *node)
|
|||
err_free_fixed:
|
||||
kfree(fixed);
|
||||
}
|
||||
CLK_OF_DECLARE(sun4i_osc, "allwinner,sun4i-osc-clk", sun4i_osc_clk_setup);
|
||||
CLK_OF_DECLARE(sun4i_osc, "allwinner,sun4i-a10-osc-clk", sun4i_osc_clk_setup);
|
||||
|
||||
|
||||
|
||||
|
@ -249,7 +252,38 @@ static void sun4i_get_pll5_factors(u32 *freq, u32 parent_rate,
|
|||
*n = DIV_ROUND_UP(div, (*k+1));
|
||||
}
|
||||
|
||||
/**
|
||||
* sun6i_a31_get_pll6_factors() - calculates n, k factors for A31 PLL6
|
||||
* PLL6 rate is calculated as follows
|
||||
* rate = parent_rate * n * (k + 1) / 2
|
||||
* parent_rate is always 24Mhz
|
||||
*/
|
||||
|
||||
static void sun6i_a31_get_pll6_factors(u32 *freq, u32 parent_rate,
|
||||
u8 *n, u8 *k, u8 *m, u8 *p)
|
||||
{
|
||||
u8 div;
|
||||
|
||||
/*
|
||||
* We always have 24MHz / 2, so we can just say that our
|
||||
* parent clock is 12MHz.
|
||||
*/
|
||||
parent_rate = parent_rate / 2;
|
||||
|
||||
/* Normalize value to a parent_rate multiple (24M / 2) */
|
||||
div = *freq / parent_rate;
|
||||
*freq = parent_rate * div;
|
||||
|
||||
/* we were called to round the frequency, we can now return */
|
||||
if (n == NULL)
|
||||
return;
|
||||
|
||||
*k = div / 32;
|
||||
if (*k > 3)
|
||||
*k = 3;
|
||||
|
||||
*n = DIV_ROUND_UP(div, (*k+1));
|
||||
}
|
||||
|
||||
/**
|
||||
* sun4i_get_apb1_factors() - calculates m, p factors for APB1
|
||||
|
@ -265,7 +299,7 @@ static void sun4i_get_apb1_factors(u32 *freq, u32 parent_rate,
|
|||
if (parent_rate < *freq)
|
||||
*freq = parent_rate;
|
||||
|
||||
parent_rate = (parent_rate + (*freq - 1)) / *freq;
|
||||
parent_rate = DIV_ROUND_UP(parent_rate, *freq);
|
||||
|
||||
/* Invalid rate! */
|
||||
if (parent_rate > 32)
|
||||
|
@ -296,7 +330,7 @@ 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
|
||||
* MOD0 rate is calculated as follows
|
||||
* rate = (parent_rate >> p) / (m + 1);
|
||||
*/
|
||||
|
||||
|
@ -310,7 +344,7 @@ static void sun4i_get_mod0_factors(u32 *freq, u32 parent_rate,
|
|||
if (*freq > parent_rate)
|
||||
*freq = parent_rate;
|
||||
|
||||
div = parent_rate / *freq;
|
||||
div = DIV_ROUND_UP(parent_rate, *freq);
|
||||
|
||||
if (div < 16)
|
||||
calcp = 0;
|
||||
|
@ -351,7 +385,7 @@ static void sun7i_a20_get_out_factors(u32 *freq, u32 parent_rate,
|
|||
if (*freq > parent_rate)
|
||||
*freq = parent_rate;
|
||||
|
||||
div = parent_rate / *freq;
|
||||
div = DIV_ROUND_UP(parent_rate, *freq);
|
||||
|
||||
if (div < 32)
|
||||
calcp = 0;
|
||||
|
@ -376,6 +410,102 @@ static void sun7i_a20_get_out_factors(u32 *freq, u32 parent_rate,
|
|||
|
||||
|
||||
|
||||
/**
|
||||
* sun7i_a20_gmac_clk_setup - Setup function for A20/A31 GMAC clock module
|
||||
*
|
||||
* This clock looks something like this
|
||||
* ________________________
|
||||
* MII TX clock from PHY >-----|___________ _________|----> to GMAC core
|
||||
* GMAC Int. RGMII TX clk >----|___________\__/__gate---|----> to PHY
|
||||
* Ext. 125MHz RGMII TX clk >--|__divider__/ |
|
||||
* |________________________|
|
||||
*
|
||||
* The external 125 MHz reference is optional, i.e. GMAC can use its
|
||||
* internal TX clock just fine. The A31 GMAC clock module does not have
|
||||
* the divider controls for the external reference.
|
||||
*
|
||||
* To keep it simple, let the GMAC use either the MII TX clock for MII mode,
|
||||
* and its internal TX clock for GMII and RGMII modes. The GMAC driver should
|
||||
* select the appropriate source and gate/ungate the output to the PHY.
|
||||
*
|
||||
* Only the GMAC should use this clock. Altering the clock so that it doesn't
|
||||
* match the GMAC's operation parameters will result in the GMAC not being
|
||||
* able to send traffic out. The GMAC driver should set the clock rate and
|
||||
* enable/disable this clock to configure the required state. The clock
|
||||
* driver then responds by auto-reparenting the clock.
|
||||
*/
|
||||
|
||||
#define SUN7I_A20_GMAC_GPIT 2
|
||||
#define SUN7I_A20_GMAC_MASK 0x3
|
||||
#define SUN7I_A20_GMAC_PARENTS 2
|
||||
|
||||
static void __init sun7i_a20_gmac_clk_setup(struct device_node *node)
|
||||
{
|
||||
struct clk *clk;
|
||||
struct clk_mux *mux;
|
||||
struct clk_gate *gate;
|
||||
const char *clk_name = node->name;
|
||||
const char *parents[SUN7I_A20_GMAC_PARENTS];
|
||||
void *reg;
|
||||
|
||||
if (of_property_read_string(node, "clock-output-names", &clk_name))
|
||||
return;
|
||||
|
||||
/* allocate mux and gate clock structs */
|
||||
mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL);
|
||||
if (!mux)
|
||||
return;
|
||||
|
||||
gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
|
||||
if (!gate)
|
||||
goto free_mux;
|
||||
|
||||
/* gmac clock requires exactly 2 parents */
|
||||
parents[0] = of_clk_get_parent_name(node, 0);
|
||||
parents[1] = of_clk_get_parent_name(node, 1);
|
||||
if (!parents[0] || !parents[1])
|
||||
goto free_gate;
|
||||
|
||||
reg = of_iomap(node, 0);
|
||||
if (!reg)
|
||||
goto free_gate;
|
||||
|
||||
/* set up gate and fixed rate properties */
|
||||
gate->reg = reg;
|
||||
gate->bit_idx = SUN7I_A20_GMAC_GPIT;
|
||||
gate->lock = &clk_lock;
|
||||
mux->reg = reg;
|
||||
mux->mask = SUN7I_A20_GMAC_MASK;
|
||||
mux->flags = CLK_MUX_INDEX_BIT;
|
||||
mux->lock = &clk_lock;
|
||||
|
||||
clk = clk_register_composite(NULL, clk_name,
|
||||
parents, SUN7I_A20_GMAC_PARENTS,
|
||||
&mux->hw, &clk_mux_ops,
|
||||
NULL, NULL,
|
||||
&gate->hw, &clk_gate_ops,
|
||||
0);
|
||||
|
||||
if (IS_ERR(clk))
|
||||
goto iounmap_reg;
|
||||
|
||||
of_clk_add_provider(node, of_clk_src_simple_get, clk);
|
||||
clk_register_clkdev(clk, clk_name, NULL);
|
||||
|
||||
return;
|
||||
|
||||
iounmap_reg:
|
||||
iounmap(reg);
|
||||
free_gate:
|
||||
kfree(gate);
|
||||
free_mux:
|
||||
kfree(mux);
|
||||
}
|
||||
CLK_OF_DECLARE(sun7i_a20_gmac, "allwinner,sun7i-a20-gmac-clk",
|
||||
sun7i_a20_gmac_clk_setup);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* sunxi_factors_clk_setup() - Setup function for factor clocks
|
||||
*/
|
||||
|
@ -387,6 +517,7 @@ struct factors_data {
|
|||
int mux;
|
||||
struct clk_factors_config *table;
|
||||
void (*getter) (u32 *rate, u32 parent_rate, u8 *n, u8 *k, u8 *m, u8 *p);
|
||||
const char *name;
|
||||
};
|
||||
|
||||
static struct clk_factors_config sun4i_pll1_config = {
|
||||
|
@ -416,6 +547,13 @@ static struct clk_factors_config sun4i_pll5_config = {
|
|||
.kwidth = 2,
|
||||
};
|
||||
|
||||
static struct clk_factors_config sun6i_a31_pll6_config = {
|
||||
.nshift = 8,
|
||||
.nwidth = 5,
|
||||
.kshift = 4,
|
||||
.kwidth = 2,
|
||||
};
|
||||
|
||||
static struct clk_factors_config sun4i_apb1_config = {
|
||||
.mshift = 0,
|
||||
.mwidth = 5,
|
||||
|
@ -451,10 +589,30 @@ static const struct factors_data sun6i_a31_pll1_data __initconst = {
|
|||
.getter = sun6i_a31_get_pll1_factors,
|
||||
};
|
||||
|
||||
static const struct factors_data sun7i_a20_pll4_data __initconst = {
|
||||
.enable = 31,
|
||||
.table = &sun4i_pll5_config,
|
||||
.getter = sun4i_get_pll5_factors,
|
||||
};
|
||||
|
||||
static const struct factors_data sun4i_pll5_data __initconst = {
|
||||
.enable = 31,
|
||||
.table = &sun4i_pll5_config,
|
||||
.getter = sun4i_get_pll5_factors,
|
||||
.name = "pll5",
|
||||
};
|
||||
|
||||
static const struct factors_data sun4i_pll6_data __initconst = {
|
||||
.enable = 31,
|
||||
.table = &sun4i_pll5_config,
|
||||
.getter = sun4i_get_pll5_factors,
|
||||
.name = "pll6",
|
||||
};
|
||||
|
||||
static const struct factors_data sun6i_a31_pll6_data __initconst = {
|
||||
.enable = 31,
|
||||
.table = &sun6i_a31_pll6_config,
|
||||
.getter = sun6i_a31_get_pll6_factors,
|
||||
};
|
||||
|
||||
static const struct factors_data sun4i_apb1_data __initconst = {
|
||||
|
@ -497,14 +655,14 @@ static struct clk * __init sunxi_factors_clk_setup(struct device_node *node,
|
|||
(parents[i] = of_clk_get_parent_name(node, i)) != NULL)
|
||||
i++;
|
||||
|
||||
/* 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);
|
||||
}
|
||||
/*
|
||||
* some factor clocks, such as pll5 and pll6, may have multiple
|
||||
* outputs, and have their name designated in factors_data
|
||||
*/
|
||||
if (data->name)
|
||||
clk_name = data->name;
|
||||
else
|
||||
of_property_read_string(node, "clock-output-names", &clk_name);
|
||||
|
||||
factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL);
|
||||
if (!factors)
|
||||
|
@ -601,6 +759,8 @@ static void __init sunxi_mux_clk_setup(struct device_node *node,
|
|||
(parents[i] = of_clk_get_parent_name(node, i)) != NULL)
|
||||
i++;
|
||||
|
||||
of_property_read_string(node, "clock-output-names", &clk_name);
|
||||
|
||||
clk = clk_register_mux(NULL, clk_name, parents, i,
|
||||
CLK_SET_RATE_NO_REPARENT, reg,
|
||||
data->shift, SUNXI_MUX_GATE_WIDTH,
|
||||
|
@ -660,6 +820,8 @@ static void __init sunxi_divider_clk_setup(struct device_node *node,
|
|||
|
||||
clk_parent = of_clk_get_parent_name(node, 0);
|
||||
|
||||
of_property_read_string(node, "clock-output-names", &clk_name);
|
||||
|
||||
clk = clk_register_divider(NULL, clk_name, clk_parent, 0,
|
||||
reg, data->shift, data->width,
|
||||
data->pow ? CLK_DIVIDER_POWER_OF_TWO : 0,
|
||||
|
@ -672,6 +834,59 @@ static void __init sunxi_divider_clk_setup(struct device_node *node,
|
|||
|
||||
|
||||
|
||||
/**
|
||||
* sunxi_gates_reset... - reset bits in leaf gate clk registers handling
|
||||
*/
|
||||
|
||||
struct gates_reset_data {
|
||||
void __iomem *reg;
|
||||
spinlock_t *lock;
|
||||
struct reset_controller_dev rcdev;
|
||||
};
|
||||
|
||||
static int sunxi_gates_reset_assert(struct reset_controller_dev *rcdev,
|
||||
unsigned long id)
|
||||
{
|
||||
struct gates_reset_data *data = container_of(rcdev,
|
||||
struct gates_reset_data,
|
||||
rcdev);
|
||||
unsigned long flags;
|
||||
u32 reg;
|
||||
|
||||
spin_lock_irqsave(data->lock, flags);
|
||||
|
||||
reg = readl(data->reg);
|
||||
writel(reg & ~BIT(id), data->reg);
|
||||
|
||||
spin_unlock_irqrestore(data->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sunxi_gates_reset_deassert(struct reset_controller_dev *rcdev,
|
||||
unsigned long id)
|
||||
{
|
||||
struct gates_reset_data *data = container_of(rcdev,
|
||||
struct gates_reset_data,
|
||||
rcdev);
|
||||
unsigned long flags;
|
||||
u32 reg;
|
||||
|
||||
spin_lock_irqsave(data->lock, flags);
|
||||
|
||||
reg = readl(data->reg);
|
||||
writel(reg | BIT(id), data->reg);
|
||||
|
||||
spin_unlock_irqrestore(data->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct reset_control_ops sunxi_gates_reset_ops = {
|
||||
.assert = sunxi_gates_reset_assert,
|
||||
.deassert = sunxi_gates_reset_deassert,
|
||||
};
|
||||
|
||||
/**
|
||||
* sunxi_gates_clk_setup() - Setup function for leaf gates on clocks
|
||||
*/
|
||||
|
@ -680,6 +895,7 @@ static void __init sunxi_divider_clk_setup(struct device_node *node,
|
|||
|
||||
struct gates_data {
|
||||
DECLARE_BITMAP(mask, SUNXI_GATES_MAX_SIZE);
|
||||
u32 reset_mask;
|
||||
};
|
||||
|
||||
static const struct gates_data sun4i_axi_gates_data __initconst = {
|
||||
|
@ -746,10 +962,21 @@ static const struct gates_data sun7i_a20_apb1_gates_data __initconst = {
|
|||
.mask = { 0xff80ff },
|
||||
};
|
||||
|
||||
static const struct gates_data sun4i_a10_usb_gates_data __initconst = {
|
||||
.mask = {0x1C0},
|
||||
.reset_mask = 0x07,
|
||||
};
|
||||
|
||||
static const struct gates_data sun5i_a13_usb_gates_data __initconst = {
|
||||
.mask = {0x140},
|
||||
.reset_mask = 0x03,
|
||||
};
|
||||
|
||||
static void __init sunxi_gates_clk_setup(struct device_node *node,
|
||||
struct gates_data *data)
|
||||
{
|
||||
struct clk_onecell_data *clk_data;
|
||||
struct gates_reset_data *reset_data;
|
||||
const char *clk_parent;
|
||||
const char *clk_name;
|
||||
void *reg;
|
||||
|
@ -793,6 +1020,21 @@ static void __init sunxi_gates_clk_setup(struct device_node *node,
|
|||
clk_data->clk_num = i;
|
||||
|
||||
of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
|
||||
|
||||
/* Register a reset controler for gates with reset bits */
|
||||
if (data->reset_mask == 0)
|
||||
return;
|
||||
|
||||
reset_data = kzalloc(sizeof(*reset_data), GFP_KERNEL);
|
||||
if (!reset_data)
|
||||
return;
|
||||
|
||||
reset_data->reg = reg;
|
||||
reset_data->lock = &clk_lock;
|
||||
reset_data->rcdev.nr_resets = __fls(data->reset_mask) + 1;
|
||||
reset_data->rcdev.ops = &sunxi_gates_reset_ops;
|
||||
reset_data->rcdev.of_node = node;
|
||||
reset_controller_register(&reset_data->rcdev);
|
||||
}
|
||||
|
||||
|
||||
|
@ -832,7 +1074,7 @@ static const struct divs_data pll5_divs_data __initconst = {
|
|||
};
|
||||
|
||||
static const struct divs_data pll6_divs_data __initconst = {
|
||||
.factors = &sun4i_pll5_data,
|
||||
.factors = &sun4i_pll6_data,
|
||||
.div = {
|
||||
{ .shift = 0, .table = pll6_sata_tbl, .gate = 14 }, /* M, SATA */
|
||||
{ .fixed = 2 }, /* P, other */
|
||||
|
@ -854,7 +1096,7 @@ 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 *parent;
|
||||
const char *clk_name;
|
||||
struct clk **clks, *pclk;
|
||||
struct clk_hw *gate_hw, *rate_hw;
|
||||
|
@ -868,6 +1110,7 @@ static void __init sunxi_divs_clk_setup(struct device_node *node,
|
|||
|
||||
/* Set up factor clock that we will be dividing */
|
||||
pclk = sunxi_factors_clk_setup(node, data->factors);
|
||||
parent = __clk_get_name(pclk);
|
||||
|
||||
reg = of_iomap(node, 0);
|
||||
|
||||
|
@ -970,56 +1213,60 @@ static void __init sunxi_divs_clk_setup(struct device_node *node,
|
|||
|
||||
/* 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,sun4i-a10-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-pll4-clk", .data = &sun7i_a20_pll4_data,},
|
||||
{.compatible = "allwinner,sun6i-a31-pll6-clk", .data = &sun6i_a31_pll6_data,},
|
||||
{.compatible = "allwinner,sun4i-a10-apb1-clk", .data = &sun4i_apb1_data,},
|
||||
{.compatible = "allwinner,sun4i-a10-mod0-clk", .data = &sun4i_mod0_data,},
|
||||
{.compatible = "allwinner,sun7i-a20-out-clk", .data = &sun7i_a20_out_data,},
|
||||
{}
|
||||
};
|
||||
|
||||
/* Matches for divider clocks */
|
||||
static const struct of_device_id clk_div_match[] __initconst = {
|
||||
{.compatible = "allwinner,sun4i-axi-clk", .data = &sun4i_axi_data,},
|
||||
{.compatible = "allwinner,sun4i-ahb-clk", .data = &sun4i_ahb_data,},
|
||||
{.compatible = "allwinner,sun4i-apb0-clk", .data = &sun4i_apb0_data,},
|
||||
{.compatible = "allwinner,sun4i-a10-axi-clk", .data = &sun4i_axi_data,},
|
||||
{.compatible = "allwinner,sun4i-a10-ahb-clk", .data = &sun4i_ahb_data,},
|
||||
{.compatible = "allwinner,sun4i-a10-apb0-clk", .data = &sun4i_apb0_data,},
|
||||
{.compatible = "allwinner,sun6i-a31-apb2-div-clk", .data = &sun6i_a31_apb2_div_data,},
|
||||
{}
|
||||
};
|
||||
|
||||
/* 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,},
|
||||
{.compatible = "allwinner,sun4i-a10-pll5-clk", .data = &pll5_divs_data,},
|
||||
{.compatible = "allwinner,sun4i-a10-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,},
|
||||
{.compatible = "allwinner,sun4i-apb1-mux-clk", .data = &sun4i_apb1_mux_data,},
|
||||
{.compatible = "allwinner,sun4i-a10-cpu-clk", .data = &sun4i_cpu_mux_data,},
|
||||
{.compatible = "allwinner,sun4i-a10-apb1-mux-clk", .data = &sun4i_apb1_mux_data,},
|
||||
{.compatible = "allwinner,sun6i-a31-ahb1-mux-clk", .data = &sun6i_a31_ahb1_mux_data,},
|
||||
{}
|
||||
};
|
||||
|
||||
/* Matches for gate clocks */
|
||||
static const struct of_device_id clk_gates_match[] __initconst = {
|
||||
{.compatible = "allwinner,sun4i-axi-gates-clk", .data = &sun4i_axi_gates_data,},
|
||||
{.compatible = "allwinner,sun4i-ahb-gates-clk", .data = &sun4i_ahb_gates_data,},
|
||||
{.compatible = "allwinner,sun4i-a10-axi-gates-clk", .data = &sun4i_axi_gates_data,},
|
||||
{.compatible = "allwinner,sun4i-a10-ahb-gates-clk", .data = &sun4i_ahb_gates_data,},
|
||||
{.compatible = "allwinner,sun5i-a10s-ahb-gates-clk", .data = &sun5i_a10s_ahb_gates_data,},
|
||||
{.compatible = "allwinner,sun5i-a13-ahb-gates-clk", .data = &sun5i_a13_ahb_gates_data,},
|
||||
{.compatible = "allwinner,sun6i-a31-ahb1-gates-clk", .data = &sun6i_a31_ahb1_gates_data,},
|
||||
{.compatible = "allwinner,sun7i-a20-ahb-gates-clk", .data = &sun7i_a20_ahb_gates_data,},
|
||||
{.compatible = "allwinner,sun4i-apb0-gates-clk", .data = &sun4i_apb0_gates_data,},
|
||||
{.compatible = "allwinner,sun4i-a10-apb0-gates-clk", .data = &sun4i_apb0_gates_data,},
|
||||
{.compatible = "allwinner,sun5i-a10s-apb0-gates-clk", .data = &sun5i_a10s_apb0_gates_data,},
|
||||
{.compatible = "allwinner,sun5i-a13-apb0-gates-clk", .data = &sun5i_a13_apb0_gates_data,},
|
||||
{.compatible = "allwinner,sun7i-a20-apb0-gates-clk", .data = &sun7i_a20_apb0_gates_data,},
|
||||
{.compatible = "allwinner,sun4i-apb1-gates-clk", .data = &sun4i_apb1_gates_data,},
|
||||
{.compatible = "allwinner,sun4i-a10-apb1-gates-clk", .data = &sun4i_apb1_gates_data,},
|
||||
{.compatible = "allwinner,sun5i-a10s-apb1-gates-clk", .data = &sun5i_a10s_apb1_gates_data,},
|
||||
{.compatible = "allwinner,sun5i-a13-apb1-gates-clk", .data = &sun5i_a13_apb1_gates_data,},
|
||||
{.compatible = "allwinner,sun6i-a31-apb1-gates-clk", .data = &sun6i_a31_apb1_gates_data,},
|
||||
{.compatible = "allwinner,sun7i-a20-apb1-gates-clk", .data = &sun7i_a20_apb1_gates_data,},
|
||||
{.compatible = "allwinner,sun6i-a31-apb2-gates-clk", .data = &sun6i_a31_apb2_gates_data,},
|
||||
{.compatible = "allwinner,sun4i-a10-usb-clk", .data = &sun4i_a10_usb_gates_data,},
|
||||
{.compatible = "allwinner,sun5i-a13-usb-clk", .data = &sun5i_a13_usb_gates_data,},
|
||||
{}
|
||||
};
|
||||
|
||||
|
|
|
@ -130,7 +130,7 @@ static const struct clk_ops tegra_clk_periph_nodiv_ops = {
|
|||
.disable = clk_periph_disable,
|
||||
};
|
||||
|
||||
const struct clk_ops tegra_clk_periph_no_gate_ops = {
|
||||
static 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,
|
||||
|
|
|
@ -34,7 +34,6 @@ static struct ti_dt_clk am33xx_clks[] = {
|
|||
DT_CLK(NULL, "dpll_core_m5_ck", "dpll_core_m5_ck"),
|
||||
DT_CLK(NULL, "dpll_core_m6_ck", "dpll_core_m6_ck"),
|
||||
DT_CLK(NULL, "dpll_mpu_ck", "dpll_mpu_ck"),
|
||||
DT_CLK("cpu0", NULL, "dpll_mpu_ck"),
|
||||
DT_CLK(NULL, "dpll_mpu_m2_ck", "dpll_mpu_m2_ck"),
|
||||
DT_CLK(NULL, "dpll_ddr_ck", "dpll_ddr_ck"),
|
||||
DT_CLK(NULL, "dpll_ddr_m2_ck", "dpll_ddr_m2_ck"),
|
||||
|
|
|
@ -112,7 +112,7 @@ static unsigned long ti_clk_divider_recalc_rate(struct clk_hw *hw,
|
|||
return parent_rate;
|
||||
}
|
||||
|
||||
return parent_rate / div;
|
||||
return DIV_ROUND_UP(parent_rate, div);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -182,7 +182,7 @@ static int ti_clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
|
|||
}
|
||||
parent_rate = __clk_round_rate(__clk_get_parent(hw->clk),
|
||||
MULT_ROUND_UP(rate, i));
|
||||
now = parent_rate / i;
|
||||
now = DIV_ROUND_UP(parent_rate, i);
|
||||
if (now <= rate && now > best) {
|
||||
bestdiv = i;
|
||||
best = now;
|
||||
|
@ -205,7 +205,7 @@ static long ti_clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
|
|||
int div;
|
||||
div = ti_clk_divider_bestdiv(hw, rate, prate);
|
||||
|
||||
return *prate / div;
|
||||
return DIV_ROUND_UP(*prate, div);
|
||||
}
|
||||
|
||||
static int ti_clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
|
@ -216,7 +216,7 @@ static int ti_clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
|
|||
unsigned long flags = 0;
|
||||
u32 val;
|
||||
|
||||
div = parent_rate / rate;
|
||||
div = DIV_ROUND_UP(parent_rate, rate);
|
||||
value = _get_val(divider, div);
|
||||
|
||||
if (value > div_mask(divider))
|
||||
|
|
|
@ -29,7 +29,8 @@ static struct clk *prcc_kclk[(PRCC_NUM_PERIPH_CLUSTERS + 1) * PRCC_PERIPHS_PER_C
|
|||
#define PRCC_KCLK_STORE(clk, base, bit) \
|
||||
prcc_kclk[(base * PRCC_PERIPHS_PER_CLUSTER) + bit] = clk
|
||||
|
||||
struct clk *ux500_twocell_get(struct of_phandle_args *clkspec, void *data)
|
||||
static struct clk *ux500_twocell_get(struct of_phandle_args *clkspec,
|
||||
void *data)
|
||||
{
|
||||
struct clk **clk_data = data;
|
||||
unsigned int base, bit;
|
||||
|
|
|
@ -149,7 +149,7 @@ static void __init zynq_clk_register_fclk(enum zynq_clk fclk,
|
|||
clks[fclk] = clk_register_gate(NULL, clk_name,
|
||||
div1_name, CLK_SET_RATE_PARENT, fclk_gate_reg,
|
||||
0, CLK_GATE_SET_TO_DISABLE, fclk_gate_lock);
|
||||
enable_reg = readl(fclk_gate_reg) & 1;
|
||||
enable_reg = clk_readl(fclk_gate_reg) & 1;
|
||||
if (enable && !enable_reg) {
|
||||
if (clk_prepare_enable(clks[fclk]))
|
||||
pr_warn("%s: FCLK%u enable failed\n", __func__,
|
||||
|
@ -278,7 +278,7 @@ static void __init zynq_clk_setup(struct device_node *np)
|
|||
SLCR_IOPLL_CTRL, 4, 1, 0, &iopll_lock);
|
||||
|
||||
/* CPU clocks */
|
||||
tmp = readl(SLCR_621_TRUE) & 1;
|
||||
tmp = clk_readl(SLCR_621_TRUE) & 1;
|
||||
clk = clk_register_mux(NULL, "cpu_mux", cpu_parents, 4,
|
||||
CLK_SET_RATE_NO_REPARENT, SLCR_ARM_CLK_CTRL, 4, 2, 0,
|
||||
&armclk_lock);
|
||||
|
|
|
@ -90,7 +90,7 @@ static unsigned long zynq_pll_recalc_rate(struct clk_hw *hw,
|
|||
* makes probably sense to redundantly save fbdiv in the struct
|
||||
* zynq_pll to save the IO access.
|
||||
*/
|
||||
fbdiv = (readl(clk->pll_ctrl) & PLLCTRL_FBDIV_MASK) >>
|
||||
fbdiv = (clk_readl(clk->pll_ctrl) & PLLCTRL_FBDIV_MASK) >>
|
||||
PLLCTRL_FBDIV_SHIFT;
|
||||
|
||||
return parent_rate * fbdiv;
|
||||
|
@ -112,7 +112,7 @@ static int zynq_pll_is_enabled(struct clk_hw *hw)
|
|||
|
||||
spin_lock_irqsave(clk->lock, flags);
|
||||
|
||||
reg = readl(clk->pll_ctrl);
|
||||
reg = clk_readl(clk->pll_ctrl);
|
||||
|
||||
spin_unlock_irqrestore(clk->lock, flags);
|
||||
|
||||
|
@ -138,10 +138,10 @@ static int zynq_pll_enable(struct clk_hw *hw)
|
|||
/* Power up PLL and wait for lock */
|
||||
spin_lock_irqsave(clk->lock, flags);
|
||||
|
||||
reg = readl(clk->pll_ctrl);
|
||||
reg = clk_readl(clk->pll_ctrl);
|
||||
reg &= ~(PLLCTRL_RESET_MASK | PLLCTRL_PWRDWN_MASK);
|
||||
writel(reg, clk->pll_ctrl);
|
||||
while (!(readl(clk->pll_status) & (1 << clk->lockbit)))
|
||||
clk_writel(reg, clk->pll_ctrl);
|
||||
while (!(clk_readl(clk->pll_status) & (1 << clk->lockbit)))
|
||||
;
|
||||
|
||||
spin_unlock_irqrestore(clk->lock, flags);
|
||||
|
@ -168,9 +168,9 @@ static void zynq_pll_disable(struct clk_hw *hw)
|
|||
/* shut down PLL */
|
||||
spin_lock_irqsave(clk->lock, flags);
|
||||
|
||||
reg = readl(clk->pll_ctrl);
|
||||
reg = clk_readl(clk->pll_ctrl);
|
||||
reg |= PLLCTRL_RESET_MASK | PLLCTRL_PWRDWN_MASK;
|
||||
writel(reg, clk->pll_ctrl);
|
||||
clk_writel(reg, clk->pll_ctrl);
|
||||
|
||||
spin_unlock_irqrestore(clk->lock, flags);
|
||||
}
|
||||
|
@ -225,9 +225,9 @@ struct clk *clk_register_zynq_pll(const char *name, const char *parent,
|
|||
|
||||
spin_lock_irqsave(pll->lock, flags);
|
||||
|
||||
reg = readl(pll->pll_ctrl);
|
||||
reg = clk_readl(pll->pll_ctrl);
|
||||
reg &= ~PLLCTRL_BPQUAL_MASK;
|
||||
writel(reg, pll->pll_ctrl);
|
||||
clk_writel(reg, pll->pll_ctrl);
|
||||
|
||||
spin_unlock_irqrestore(pll->lock, flags);
|
||||
|
||||
|
|
|
@ -147,6 +147,11 @@
|
|||
#define HI3620_MMC_CLK3 217
|
||||
#define HI3620_MCU_CLK 218
|
||||
|
||||
#define HI3620_SD_CIUCLK 0
|
||||
#define HI3620_MMC_CIUCLK1 1
|
||||
#define HI3620_MMC_CIUCLK2 2
|
||||
#define HI3620_MMC_CIUCLK3 3
|
||||
|
||||
#define HI3620_NR_CLKS 219
|
||||
|
||||
#endif /* __DTS_HI3620_CLOCK_H */
|
||||
|
|
35
include/dt-bindings/clock/hip04-clock.h
Normal file
35
include/dt-bindings/clock/hip04-clock.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2014 Hisilicon Limited.
|
||||
* Copyright (c) 2013-2014 Linaro Limited.
|
||||
*
|
||||
* Author: Haojian Zhuang <haojian.zhuang@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 __DTS_HIP04_CLOCK_H
|
||||
#define __DTS_HIP04_CLOCK_H
|
||||
|
||||
#define HIP04_NONE_CLOCK 0
|
||||
|
||||
/* fixed rate & fixed factor clocks */
|
||||
#define HIP04_OSC50M 1
|
||||
#define HIP04_CLK_50M 2
|
||||
#define HIP04_CLK_168M 3
|
||||
|
||||
#define HIP04_NR_CLKS 64
|
||||
|
||||
#endif /* __DTS_HIP04_CLOCK_H */
|
|
@ -32,6 +32,7 @@
|
|||
#define CLK_GET_ACCURACY_NOCACHE BIT(8) /* do not use the cached clk accuracy */
|
||||
|
||||
struct clk_hw;
|
||||
struct dentry;
|
||||
|
||||
/**
|
||||
* struct clk_ops - Callback operations for hardware clocks; these are to
|
||||
|
@ -127,6 +128,12 @@ struct clk_hw;
|
|||
* separately via calls to .set_parent and .set_rate.
|
||||
* Returns 0 on success, -EERROR otherwise.
|
||||
*
|
||||
* @debug_init: Set up type-specific debugfs entries for this clock. This
|
||||
* is called once, after the debugfs directory entry for this
|
||||
* clock has been created. The dentry pointer representing that
|
||||
* directory is provided as an argument. Called with
|
||||
* prepare_lock held. Returns 0 on success, -EERROR otherwise.
|
||||
*
|
||||
*
|
||||
* The clk_enable/clk_disable and clk_prepare/clk_unprepare pairs allow
|
||||
* implementations to split any work between atomic (enable) and sleepable
|
||||
|
@ -165,6 +172,7 @@ struct clk_ops {
|
|||
unsigned long (*recalc_accuracy)(struct clk_hw *hw,
|
||||
unsigned long parent_accuracy);
|
||||
void (*init)(struct clk_hw *hw);
|
||||
int (*debug_init)(struct clk_hw *hw, struct dentry *dentry);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -78,8 +78,22 @@ struct clk_notifier_data {
|
|||
unsigned long new_rate;
|
||||
};
|
||||
|
||||
/**
|
||||
* clk_notifier_register: register a clock rate-change notifier callback
|
||||
* @clk: clock whose rate we are interested in
|
||||
* @nb: notifier block with callback function pointer
|
||||
*
|
||||
* ProTip: debugging across notifier chains can be frustrating. Make sure that
|
||||
* your notifier callback function prints a nice big warning in case of
|
||||
* failure.
|
||||
*/
|
||||
int clk_notifier_register(struct clk *clk, struct notifier_block *nb);
|
||||
|
||||
/**
|
||||
* clk_notifier_unregister: unregister a clock rate-change notifier callback
|
||||
* @clk: clock whose rate we are no longer interested in
|
||||
* @nb: notifier block which will be unregistered
|
||||
*/
|
||||
int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb);
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in a new issue