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:
Linus Torvalds 2014-04-05 18:39:18 -07:00
commit 19bc2eec3c
74 changed files with 5881 additions and 732 deletions

View File

@ -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.

View File

@ -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>;
};

View File

@ -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.

View File

@ -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.

View File

@ -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:

View File

@ -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.

View File

@ -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>;
};

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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";
};

View File

@ -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";
};
};

View 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>;
};

View 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";
};
};

View File

@ -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";
};
};

View 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";
};

View 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 */
};
};

View 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";
};

View File

@ -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";
};

View File

@ -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/

View File

@ -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 {

View File

@ -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[] = {

View File

@ -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"

View File

@ -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/

View File

@ -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))

View File

@ -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;

View File

@ -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, &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, &reg);
axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLKOUT0_1, &reg);
dout = (reg & 0x3f) + ((reg >> 6) & 0x3f);
axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_DIV, &reg);
axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_DIV, &reg);
d = (reg & 0x3f) + ((reg >> 6) & 0x3f);
axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_FB1, &reg);
axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_FB1, &reg);
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",

View File

@ -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
View 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);

View File

@ -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 @@ err_name:
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;

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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);

View 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);

View File

@ -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;
}
}

View File

@ -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 */

View File

@ -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;
}

View File

@ -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

View File

@ -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

View 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);

View 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);

View File

@ -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 @@ err_free_clks:
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);

View File

@ -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

View File

@ -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)

View File

@ -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;

View File

@ -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")) {

View 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);

View File

@ -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.
*/

View File

@ -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.
*/

View File

@ -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.
*/

View File

@ -1 +1,4 @@
obj-y += clk.o
obj-y += clk-gate.o
obj-y += clk-pll.o
obj-y += clk-periph.o

View 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);
}

View 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", &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);
}

View 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", &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);
}

View File

@ -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", &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
View 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
View File

@ -0,0 +1 @@
obj-y += clkgen-mux.o clkgen-pll.o clkgen-fsyn.o

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
View 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
View 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
View 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*/

View File

@ -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 @@ err_free_gate:
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 @@ free_clkdata:
/* 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,},
{}
};

View File

@ -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,

View File

@ -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"),

View File

@ -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))

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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 */

View 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 */

View File

@ -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);
};
/**

View File

@ -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);
/**