Merge tag 'clk-for-linus-3.15' of git://git.linaro.org/people/mike.turquette/linux
authorLinus Torvalds <torvalds@linux-foundation.org>
Sun, 6 Apr 2014 01:39:18 +0000 (18:39 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sun, 6 Apr 2014 01:39:18 +0000 (18:39 -0700)
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
  ...

74 files changed:
Documentation/clk.txt
Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
Documentation/devicetree/bindings/clock/altr_socfpga.txt
Documentation/devicetree/bindings/clock/axi-clkgen.txt
Documentation/devicetree/bindings/clock/clock-bindings.txt
Documentation/devicetree/bindings/clock/hi3620-clock.txt
Documentation/devicetree/bindings/clock/moxa,moxart-clock.txt [new file with mode: 0644]
Documentation/devicetree/bindings/clock/mvebu-core-clock.txt
Documentation/devicetree/bindings/clock/mvebu-corediv-clock.txt
Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt
Documentation/devicetree/bindings/clock/renesas,rz-cpg-clocks.txt [new file with mode: 0644]
Documentation/devicetree/bindings/clock/st/st,clkgen-divmux.txt [new file with mode: 0644]
Documentation/devicetree/bindings/clock/st/st,clkgen-mux.txt [new file with mode: 0644]
Documentation/devicetree/bindings/clock/st/st,clkgen-pll.txt [new file with mode: 0644]
Documentation/devicetree/bindings/clock/st/st,clkgen-prediv.txt [new file with mode: 0644]
Documentation/devicetree/bindings/clock/st/st,clkgen-vcc.txt [new file with mode: 0644]
Documentation/devicetree/bindings/clock/st/st,clkgen.txt [new file with mode: 0644]
Documentation/devicetree/bindings/clock/st/st,quadfs.txt [new file with mode: 0644]
Documentation/devicetree/bindings/clock/sunxi.txt
MAINTAINERS
arch/arm/boot/dts/socfpga.dtsi
arch/arm/mach-socfpga/socfpga.c
drivers/clk/Kconfig
drivers/clk/Makefile
drivers/clk/at91/clk-programmable.c
drivers/clk/at91/clk-system.c
drivers/clk/clk-axi-clkgen.c
drivers/clk/clk-divider.c
drivers/clk/clk-moxart.c [new file with mode: 0644]
drivers/clk/clk-ppc-corenet.c
drivers/clk/clk-s2mps11.c
drivers/clk/clk.c
drivers/clk/clkdev.c
drivers/clk/hisilicon/Makefile
drivers/clk/hisilicon/clk-hi3620.c
drivers/clk/hisilicon/clk-hip04.c [new file with mode: 0644]
drivers/clk/hisilicon/clk.c
drivers/clk/hisilicon/clk.h
drivers/clk/mmp/clk-frac.c
drivers/clk/mvebu/Kconfig
drivers/clk/mvebu/Makefile
drivers/clk/mvebu/armada-375.c [new file with mode: 0644]
drivers/clk/mvebu/armada-38x.c [new file with mode: 0644]
drivers/clk/mvebu/clk-corediv.c
drivers/clk/shmobile/Makefile
drivers/clk/shmobile/clk-div6.c
drivers/clk/shmobile/clk-mstp.c
drivers/clk/shmobile/clk-rcar-gen2.c
drivers/clk/shmobile/clk-rz.c [new file with mode: 0644]
drivers/clk/sirf/clk-atlas6.c
drivers/clk/sirf/clk-common.c
drivers/clk/sirf/clk-prima2.c
drivers/clk/socfpga/Makefile
drivers/clk/socfpga/clk-gate.c [new file with mode: 0644]
drivers/clk/socfpga/clk-periph.c [new file with mode: 0644]
drivers/clk/socfpga/clk-pll.c [new file with mode: 0644]
drivers/clk/socfpga/clk.c
drivers/clk/socfpga/clk.h [new file with mode: 0644]
drivers/clk/st/Makefile [new file with mode: 0644]
drivers/clk/st/clkgen-fsyn.c [new file with mode: 0644]
drivers/clk/st/clkgen-mux.c [new file with mode: 0644]
drivers/clk/st/clkgen-pll.c [new file with mode: 0644]
drivers/clk/st/clkgen.h [new file with mode: 0644]
drivers/clk/sunxi/clk-sunxi.c
drivers/clk/tegra/clk-periph.c
drivers/clk/ti/clk-33xx.c
drivers/clk/ti/divider.c
drivers/clk/ux500/u8500_of_clk.c
drivers/clk/zynq/clkc.c
drivers/clk/zynq/pll.c
include/dt-bindings/clock/hi3620-clock.h
include/dt-bindings/clock/hip04-clock.h [new file with mode: 0644]
include/linux/clk-provider.h
include/linux/clk.h

index 699ef2a323b102a7c176a8cf30fcbd7ef99997ba..c9c399af7c0871c6cca5718f536d1c9335a9c1dd 100644 (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.
index 8c7a4653508dabeeb77e3251531bfddfbc65a40f..df0a452b8526de02bc935f235123acb7ae87b54c 100644 (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>;
+       };
index 0045433eae1f81ef4b3263ba18fa0e6ec8c018e6..5dfd145d3ccf673492e91eee8f9b43d94a3faee0 100644 (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.
index 028b493e97ff2d95591ccc19fccffdbb53db75dd..20e1704e7df2110c518ce71ca07e94a0b4db574a 100644 (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.
index 7c52c29d99fa316c37221fec207dfee47d3c028d..700e7aac37174dc41a23c442abc9ec74db39a4ca 100644 (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:
index 4b71ab41be5357584427fb49921d6c2345886cbc..dad6269f52c5a6c6a2be5c7eb09aa1f4bcce9a9d 100644 (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.
diff --git a/Documentation/devicetree/bindings/clock/moxa,moxart-clock.txt b/Documentation/devicetree/bindings/clock/moxa,moxart-clock.txt
new file mode 100644 (file)
index 0000000..fedea84
--- /dev/null
@@ -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>;
+       };
index 1e662948661e12a9878815a5fede4c93ac7a9201..307a503c5db882cc600710aae58381ea131977ae 100644 (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)
index c62391fc0e39038adbb9d5faff227b7d8257f748..520562a7dc2abedefd6a04c6e8b2870071136cb2 100644 (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
index fc2910fa7e45fdbbe41a0ccdd947e736ca2e0e75..76477be742b21c016e06d59f2a67c28b9ffdabf0 100644 (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
diff --git a/Documentation/devicetree/bindings/clock/renesas,rz-cpg-clocks.txt b/Documentation/devicetree/bindings/clock/renesas,rz-cpg-clocks.txt
new file mode 100644 (file)
index 0000000..98a2574
--- /dev/null
@@ -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";
+       };
diff --git a/Documentation/devicetree/bindings/clock/st/st,clkgen-divmux.txt b/Documentation/devicetree/bindings/clock/st/st,clkgen-divmux.txt
new file mode 100644 (file)
index 0000000..ae56315
--- /dev/null
@@ -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";
+               };
+       };
+
diff --git a/Documentation/devicetree/bindings/clock/st/st,clkgen-mux.txt b/Documentation/devicetree/bindings/clock/st/st,clkgen-mux.txt
new file mode 100644 (file)
index 0000000..943e080
--- /dev/null
@@ -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>;
+       };
diff --git a/Documentation/devicetree/bindings/clock/st/st,clkgen-pll.txt b/Documentation/devicetree/bindings/clock/st/st,clkgen-pll.txt
new file mode 100644 (file)
index 0000000..81eb385
--- /dev/null
@@ -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";
+               };
+       };
diff --git a/Documentation/devicetree/bindings/clock/st/st,clkgen-prediv.txt b/Documentation/devicetree/bindings/clock/st/st,clkgen-prediv.txt
new file mode 100644 (file)
index 0000000..566c9d7
--- /dev/null
@@ -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";
+               };
+       };
+
diff --git a/Documentation/devicetree/bindings/clock/st/st,clkgen-vcc.txt b/Documentation/devicetree/bindings/clock/st/st,clkgen-vcc.txt
new file mode 100644 (file)
index 0000000..4e3ff28
--- /dev/null
@@ -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";
+       };
+
diff --git a/Documentation/devicetree/bindings/clock/st/st,clkgen.txt b/Documentation/devicetree/bindings/clock/st/st,clkgen.txt
new file mode 100644 (file)
index 0000000..49ec5ae
--- /dev/null
@@ -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 */
+               };
+       };
+
diff --git a/Documentation/devicetree/bindings/clock/st/st,quadfs.txt b/Documentation/devicetree/bindings/clock/st/st,quadfs.txt
new file mode 100644 (file)
index 0000000..ec86d62
--- /dev/null
@@ -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";
+        };
index c2cb7621ad2dd194f8342ac353770694136558aa..a5160d8cbb5f7e1c020e925da23d491ca3b3a0a4 100644 (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";
 };
index 2421c8d5ec5cc3785e6742e05d44b308d7b84e39..bfff89690c3adc99305af5fe37923db4748db87e 100644 (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/
index 4568c9869d44c37e100cef8484be6fc701799552..56fc214e6d2c3a6fd67f7ce41f11f0ec9a4c8abc 100644 (file)
                                                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 {
index dd0d49cdbe097c09fb15d0a652f2aa6d10e60db9..d86231e11b3449665b02a030b849e9cd75cddb0d 100644 (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[] = {
index f9f605695e409edffc58f02b313f5bec92eba22d..6f56d3a4f01063d06698c46c88e1fce27a096aab 100644 (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"
index 88af4a399d6c30a91fbd581920354a257c201ecc..5f8a28735c9661acdec4d5631899b12dc59f97c4 100644 (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/
index fd792b203eaf2fcf1ca8037a86b34247ba94e36f..62e2509f9df16d52352da1d4aaa13b06d65b4824 100644 (file)
 #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)
+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;
-       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;
+       pres = (pmc_read(pmc, AT91_PMC_PCKR(prog->id)) >> layout->pres_shift) &
+              PROG_PRES_MASK;
+       return parent_rate >> pres;
 }
 
-static int clk_programmable_is_ready(struct clk_hw *hw)
+static long clk_programmable_determine_rate(struct clk_hw *hw,
+                                           unsigned long rate,
+                                           unsigned long *best_parent_rate,
+                                           struct clk **best_parent_clk)
 {
-       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));
-}
+       struct clk *parent = NULL;
+       long best_rate = -EINVAL;
+       unsigned long parent_rate;
+       unsigned long tmp_rate;
+       int shift;
+       int i;
 
-static unsigned long clk_programmable_recalc_rate(struct clk_hw *hw,
-                                                 unsigned long parent_rate)
-{
-       u32 tmp;
-       struct clk_programmable *prog = to_clk_programmable(hw);
-       struct at91_pmc *pmc = prog->pmc;
-       const struct clk_programmable_layout *layout = prog->layout;
+       for (i = 0; i < __clk_get_num_parents(hw->clk); i++) {
+               parent = clk_get_parent_by_index(hw->clk, i);
+               if (!parent)
+                       continue;
 
-       tmp = pmc_read(pmc, AT91_PMC_PCKR(prog->id));
-       prog->pres = (tmp >> layout->pres_shift) & PROG_PRES_MASK;
+               parent_rate = __clk_get_rate(parent);
+               for (shift = 0; shift < PROG_PRES_MASK; shift++) {
+                       tmp_rate = parent_rate >> shift;
+                       if (tmp_rate <= rate)
+                               break;
+               }
 
-       return parent_rate >> prog->pres;
-}
+               if (tmp_rate > rate)
+                       continue;
 
-static long clk_programmable_round_rate(struct clk_hw *hw, unsigned long rate,
-                                       unsigned long *parent_rate)
-{
-       unsigned long best_rate = *parent_rate;
-       unsigned long best_diff;
-       unsigned long new_diff;
-       unsigned long cur_rate;
-       int shift = shift;
-
-       if (rate > *parent_rate)
-               return *parent_rate;
-       else
-               best_diff = *parent_rate - rate;
-
-       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;
+               if (best_rate < 0 || (rate - tmp_rate) < (rate - best_rate)) {
+                       best_rate = tmp_rate;
+                       *best_parent_rate = parent_rate;
+                       *best_parent_clk = parent;
                }
 
-               if (rate > cur_rate)
+               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 (!best_diff) {
-               prog->pres = shift;
-               return 0;
-       }
+       if (!div)
+               return -EINVAL;
 
-       for (shift = 1; shift < PROG_PRES_MASK; shift++) {
-               cur_rate = parent_rate >> shift;
+       shift = fls(div) - 1;
 
-               if (cur_rate > rate)
-                       new_diff = cur_rate - rate;
-               else
-                       new_diff = rate - cur_rate;
+       if (div != (1<<shift))
+               return -EINVAL;
 
-               if (!new_diff)
-                       break;
+       if (shift >= PROG_PRES_MASK)
+               return -EINVAL;
 
-               if (new_diff < best_diff) {
-                       best_diff = new_diff;
-                       best_rate = cur_rate;
-               }
+       pmc_write(pmc, AT91_PMC_PCKR(prog->id),
+                 tmp | (shift << layout->pres_shift));
 
-               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))
index 8f7c0434a09f6020daf5685a7e07d72190c1a9ca..8c96307d736343e3cbc6924c09580408f32c2da7 100644 (file)
 #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"
 
 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;
 
index 8137327847c3af40ab3fb4e512592f68c92e4b85..1127ee46b8024548151b9920246ef286cc887b99 100644 (file)
 #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_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_CLK_FB1,
+               (high << 6) | low, 0xefff);
+       axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_FB2,
+               (edge << 7) | (nocount << 6), 0x03ff);
+
+       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",
index 5543b7df8e16c736c210407a5d837ffb32ab3beb..ec22112e569f7f3dc8f7c9477d0e3f99951e66c6 100644 (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))
diff --git a/drivers/clk/clk-moxart.c b/drivers/clk/clk-moxart.c
new file mode 100644 (file)
index 0000000..30a3b69
--- /dev/null
@@ -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);
index c4f76ed914b023420aaf012b25369c88c3090a1a..8b284be4efa4ad7ae65d1c0947715c5522785cae 100644 (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;
index 00a3abe103a5ac472dfb352a1bb0cec42cd29ddb..f2f62a1bf61aaae9094e316eeddb4830a3286a55 100644 (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);
index c42e608af6bbe0980717a74e69a143cdc742f1b1..dff0373f53c1fb1df1784366a28c35323cde17a5 100644 (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
- * 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
+ * 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.
  *
- * 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
index 48f67218247cb0cb44cfa92b8ed4903a0f193531..a360b2eca5cbff29c793c2100f97b81b22b74f3b 100644 (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);
index a049108341fc60e0b14d50f6f59b5ae51943c5d6..40b33c6a8257af88fb5f66601bbf4a9a1321980d 100644 (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
index f24ad6a3a7970c346001fd4798dbb69b6c11a096..339945d2503b33798ca4e98b51e140f2b8f658ae 100644 (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);
diff --git a/drivers/clk/hisilicon/clk-hip04.c b/drivers/clk/hisilicon/clk-hip04.c
new file mode 100644 (file)
index 0000000..132b57a
--- /dev/null
@@ -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);
index a3a7152c92d97008babad2df47245273db6a9338..276f672e7b1aff8f45b4a8cb86481fa57f5cf3d3 100644 (file)
 #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;
        }
 }
index 4a6beebefb7a691d4dc3b6bb4a5d0c29d93b0a32..43fa5da88f0209760cf79f53a5745c3da99a3e6e 100644 (file)
 #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 */
index 80c1dd15d15cf267270cacb34549cb9cc304eb27..23a56f561812decc149624fe72b803a7bbb726ab 100644 (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;
        }
index c339b829d3e33fe60af81cbfb0317cecdb12add5..693f7be129f12a0d159727c33cd126e68cf957e0 100644 (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
index 21bbfb4a9f42e9edcda25a0705ff1f9f701ac8bd..4c66162fb0b440ca326f3f5b1510f71dbf3b755f 100644 (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
diff --git a/drivers/clk/mvebu/armada-375.c b/drivers/clk/mvebu/armada-375.c
new file mode 100644 (file)
index 0000000..c991a4d
--- /dev/null
@@ -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);
diff --git a/drivers/clk/mvebu/armada-38x.c b/drivers/clk/mvebu/armada-38x.c
new file mode 100644 (file)
index 0000000..8bccf4e
--- /dev/null
@@ -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);
index 7162615bcdcdda802e782fb631051c75b0921261..d1e5863d337525ba5138c1505bca994e1d2b3268 100644 (file)
 #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 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 void __init mvebu_corediv_clk_init(struct device_node *node)
+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);
index 9ecef140dba7a663b0a86c3338ea61d5ba21a39f..5404cb931ebf5b9e07b0de1198e3b88b7ed8831d 100644 (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
index aac4756ec52e68e641c740667c8f31cb32cac0eb..f065f694cb65699705def0036c3f47e903e813ae 100644 (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)
index 42d5912b1d25aa908314f62c4138052e9fd51a15..2e5810c88d1150874ece970fb64136a450ef64a6 100644 (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;
 
index 99c27b1c625b8e3eaf514895a0a13ebf8b94fdef..dff7f79a19b9514cf4fff4091fd4d35b5ae11a30 100644 (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")) {
diff --git a/drivers/clk/shmobile/clk-rz.c b/drivers/clk/shmobile/clk-rz.c
new file mode 100644 (file)
index 0000000..7e68e86
--- /dev/null
@@ -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);
index f9f4a15a64ab30306b49e6c2574dde157fbe6b25..d63b76ca60c311edd1dcb66ef35d364e0cb27247 100644 (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.
  */
index 7dde6a82f514834ac76ed9a457f6ccc7a5176a03..37af51c5f213bb496c2dce74ba975cbc4227a22f 100644 (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.
  */
index 7adc5c70c7ff82e51e6c4d42cb72fef02f603492..6968e2ebcd8a4d09959d02b2edbf5ffe19e7b9da 100644 (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.
  */
index 0303c0b99cd0926c6185244ebf74db4911d41538..7e2d15a0c7b8aec7c28e9074727c76406abc6918 100644 (file)
@@ -1 +1,4 @@
 obj-y += clk.o
+obj-y += clk-gate.o
+obj-y += clk-pll.o
+obj-y += clk-periph.o
diff --git a/drivers/clk/socfpga/clk-gate.c b/drivers/clk/socfpga/clk-gate.c
new file mode 100644 (file)
index 0000000..501d513
--- /dev/null
@@ -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);
+}
diff --git a/drivers/clk/socfpga/clk-periph.c b/drivers/clk/socfpga/clk-periph.c
new file mode 100644 (file)
index 0000000..81623a3
--- /dev/null
@@ -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);
+}
diff --git a/drivers/clk/socfpga/clk-pll.c b/drivers/clk/socfpga/clk-pll.c
new file mode 100644 (file)
index 0000000..88dafb5
--- /dev/null
@@ -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);
+}
index 5983a26a8c5fabb04e36425e6eef74939dffbe56..35a960a993f95c72b6247e52032c4184cbdd00b1 100644 (file)
 #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 unsigned long clk_periclk_recalc_rate(struct clk_hw *hwclk,
-                                            unsigned long parent_rate)
-{
-       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;
-}
-
-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 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 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)
+static void __init socfpga_clkmgr_init(struct device_node *node)
 {
-       socfpga_clk_init(node, &clk_pll_ops);
+       clk_mgr_base_addr = of_iomap(node, 0);
+       of_clk_init(socfpga_child_clocks);
 }
-CLK_OF_DECLARE(socfpga_pll, "altr,socfpga-pll-clock", socfpga_pll_init);
+CLK_OF_DECLARE(socfpga_mgr, "altr,clk-mgr", socfpga_clkmgr_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");
-}
diff --git a/drivers/clk/socfpga/clk.h b/drivers/clk/socfpga/clk.h
new file mode 100644 (file)
index 0000000..d2e5401
--- /dev/null
@@ -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 */
diff --git a/drivers/clk/st/Makefile b/drivers/clk/st/Makefile
new file mode 100644 (file)
index 0000000..c7455ff
--- /dev/null
@@ -0,0 +1 @@
+obj-y += clkgen-mux.o clkgen-pll.o clkgen-fsyn.o
diff --git a/drivers/clk/st/clkgen-fsyn.c b/drivers/clk/st/clkgen-fsyn.c
new file mode 100644 (file)
index 0000000..4f53ee0
--- /dev/null
@@ -0,0 +1,1039 @@
+/*
+ * Copyright (C) 2014 STMicroelectronics R&D Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+/*
+ * 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"
+
+/*
+ * Maximum input clock to the PLL before we divide it down by 2
+ * although in reality in actual systems this has never been seen to
+ * be used.
+ */
+#define QUADFS_NDIV_THRESHOLD 30000000
+
+#define PLL_BW_GOODREF   (0L)
+#define PLL_BW_VBADREF   (1L)
+#define PLL_BW_BADREF    (2L)
+#define PLL_BW_VGOODREF  (3L)
+
+#define QUADFS_MAX_CHAN 4
+
+struct stm_fs {
+       unsigned long ndiv;
+       unsigned long mdiv;
+       unsigned long pe;
+       unsigned long sdiv;
+       unsigned long nsdiv;
+};
+
+static struct stm_fs fs216c65_rtbl[] = {
+       { .mdiv = 0x1f, .pe = 0x0,      .sdiv = 0x7,    .nsdiv = 0 },   /* 312.5 Khz */
+       { .mdiv = 0x17, .pe = 0x25ed,   .sdiv = 0x1,    .nsdiv = 0 },   /* 27    MHz */
+       { .mdiv = 0x1a, .pe = 0x7b36,   .sdiv = 0x2,    .nsdiv = 1 },   /* 36.87 MHz */
+       { .mdiv = 0x13, .pe = 0x0,      .sdiv = 0x2,    .nsdiv = 1 },   /* 48    MHz */
+       { .mdiv = 0x11, .pe = 0x1c72,   .sdiv = 0x1,    .nsdiv = 1 },   /* 108   MHz */
+};
+
+static struct stm_fs fs432c65_rtbl[] = {
+       { .mdiv = 0x1f, .pe = 0x0,      .sdiv = 0x7,    .nsdiv = 0 },   /* 625   Khz */
+       { .mdiv = 0x11, .pe = 0x1c72,   .sdiv = 0x2,    .nsdiv = 1 },   /* 108   MHz */
+       { .mdiv = 0x19, .pe = 0x121a,   .sdiv = 0x0,    .nsdiv = 1 },   /* 297   MHz */
+};
+
+static struct stm_fs fs660c32_rtbl[] = {
+       { .mdiv = 0x01, .pe = 0x2aaa,   .sdiv = 0x8,    .nsdiv = 0 },   /* 600   KHz */
+       { .mdiv = 0x02, .pe = 0x3d33,   .sdiv = 0x0,    .nsdiv = 0 },   /* 148.5 Mhz */
+       { .mdiv = 0x13, .pe = 0x5bcc,   .sdiv = 0x0,    .nsdiv = 1 },   /* 297   Mhz */
+       { .mdiv = 0x0e, .pe = 0x1025,   .sdiv = 0x0,    .nsdiv = 1 },   /* 333   Mhz */
+       { .mdiv = 0x0b, .pe = 0x715f,   .sdiv = 0x0,    .nsdiv = 1 },   /* 350   Mhz */
+};
+
+struct clkgen_quadfs_data {
+       bool reset_present;
+       bool bwfilter_present;
+       bool lockstatus_present;
+       bool nsdiv_present;
+       struct clkgen_field ndiv;
+       struct clkgen_field ref_bw;
+       struct clkgen_field nreset;
+       struct clkgen_field npda;
+       struct clkgen_field lock_status;
+
+       struct clkgen_field nsb[QUADFS_MAX_CHAN];
+       struct clkgen_field en[QUADFS_MAX_CHAN];
+       struct clkgen_field mdiv[QUADFS_MAX_CHAN];
+       struct clkgen_field pe[QUADFS_MAX_CHAN];
+       struct clkgen_field sdiv[QUADFS_MAX_CHAN];
+       struct clkgen_field nsdiv[QUADFS_MAX_CHAN];
+
+       const struct clk_ops *pll_ops;
+       struct stm_fs *rtbl;
+       u8 rtbl_cnt;
+       int  (*get_rate)(unsigned long , struct stm_fs *,
+                       unsigned long *);
+};
+
+static const struct clk_ops st_quadfs_pll_c65_ops;
+static const struct clk_ops st_quadfs_pll_c32_ops;
+static const struct clk_ops st_quadfs_fs216c65_ops;
+static const struct clk_ops st_quadfs_fs432c65_ops;
+static const struct clk_ops st_quadfs_fs660c32_ops;
+
+static int clk_fs216c65_get_rate(unsigned long, struct stm_fs *,
+               unsigned long *);
+static int clk_fs432c65_get_rate(unsigned long, struct stm_fs *,
+               unsigned long *);
+static int clk_fs660c32_dig_get_rate(unsigned long, struct stm_fs *,
+               unsigned long *);
+/*
+ * Values for all of the standalone instances of this clock
+ * generator found in STiH415 and STiH416 SYSCFG register banks. Note
+ * that the individual channel standby control bits (nsb) are in the
+ * first register along with the PLL control bits.
+ */
+static struct clkgen_quadfs_data st_fs216c65_416 = {
+       /* 416 specific */
+       .npda   = CLKGEN_FIELD(0x0, 0x1, 14),
+       .nsb    = { CLKGEN_FIELD(0x0, 0x1, 10),
+                   CLKGEN_FIELD(0x0, 0x1, 11),
+                   CLKGEN_FIELD(0x0, 0x1, 12),
+                   CLKGEN_FIELD(0x0, 0x1, 13) },
+       .nsdiv_present = true,
+       .nsdiv  = { CLKGEN_FIELD(0x0, 0x1, 18),
+                   CLKGEN_FIELD(0x0, 0x1, 19),
+                   CLKGEN_FIELD(0x0, 0x1, 20),
+                   CLKGEN_FIELD(0x0, 0x1, 21) },
+       .mdiv   = { CLKGEN_FIELD(0x4, 0x1f, 0),
+                   CLKGEN_FIELD(0x14, 0x1f, 0),
+                   CLKGEN_FIELD(0x24, 0x1f, 0),
+                   CLKGEN_FIELD(0x34, 0x1f, 0) },
+       .en     = { CLKGEN_FIELD(0x10, 0x1, 0),
+                   CLKGEN_FIELD(0x20, 0x1, 0),
+                   CLKGEN_FIELD(0x30, 0x1, 0),
+                   CLKGEN_FIELD(0x40, 0x1, 0) },
+       .ndiv   = CLKGEN_FIELD(0x0, 0x1, 15),
+       .bwfilter_present = true,
+       .ref_bw = CLKGEN_FIELD(0x0, 0x3, 16),
+       .pe     = { CLKGEN_FIELD(0x8, 0xffff, 0),
+                   CLKGEN_FIELD(0x18, 0xffff, 0),
+                   CLKGEN_FIELD(0x28, 0xffff, 0),
+                   CLKGEN_FIELD(0x38, 0xffff, 0) },
+       .sdiv   = { CLKGEN_FIELD(0xC, 0x7, 0),
+                   CLKGEN_FIELD(0x1C, 0x7, 0),
+                   CLKGEN_FIELD(0x2C, 0x7, 0),
+                   CLKGEN_FIELD(0x3C, 0x7, 0) },
+       .pll_ops        = &st_quadfs_pll_c65_ops,
+       .rtbl           = fs216c65_rtbl,
+       .rtbl_cnt       = ARRAY_SIZE(fs216c65_rtbl),
+       .get_rate       = clk_fs216c65_get_rate,
+};
+
+static struct clkgen_quadfs_data st_fs432c65_416 = {
+       .npda   = CLKGEN_FIELD(0x0, 0x1, 14),
+       .nsb    = { CLKGEN_FIELD(0x0, 0x1, 10),
+                   CLKGEN_FIELD(0x0, 0x1, 11),
+                   CLKGEN_FIELD(0x0, 0x1, 12),
+                   CLKGEN_FIELD(0x0, 0x1, 13) },
+       .nsdiv_present = true,
+       .nsdiv  = { CLKGEN_FIELD(0x0, 0x1, 18),
+                  CLKGEN_FIELD(0x0, 0x1, 19),
+                  CLKGEN_FIELD(0x0, 0x1, 20),
+                  CLKGEN_FIELD(0x0, 0x1, 21) },
+       .mdiv   = { CLKGEN_FIELD(0x4, 0x1f, 0),
+                   CLKGEN_FIELD(0x14, 0x1f, 0),
+                   CLKGEN_FIELD(0x24, 0x1f, 0),
+                   CLKGEN_FIELD(0x34, 0x1f, 0) },
+       .en     = { CLKGEN_FIELD(0x10, 0x1, 0),
+                   CLKGEN_FIELD(0x20, 0x1, 0),
+                   CLKGEN_FIELD(0x30, 0x1, 0),
+                   CLKGEN_FIELD(0x40, 0x1, 0) },
+       .ndiv   = CLKGEN_FIELD(0x0, 0x1, 15),
+       .bwfilter_present = true,
+       .ref_bw = CLKGEN_FIELD(0x0, 0x3, 16),
+       .pe     = { CLKGEN_FIELD(0x8, 0xffff, 0),
+                   CLKGEN_FIELD(0x18, 0xffff, 0),
+                   CLKGEN_FIELD(0x28, 0xffff, 0),
+                   CLKGEN_FIELD(0x38, 0xffff, 0) },
+       .sdiv   = { CLKGEN_FIELD(0xC, 0x7, 0),
+                   CLKGEN_FIELD(0x1C, 0x7, 0),
+                   CLKGEN_FIELD(0x2C, 0x7, 0),
+                   CLKGEN_FIELD(0x3C, 0x7, 0) },
+       .pll_ops        = &st_quadfs_pll_c65_ops,
+       .rtbl           = fs432c65_rtbl,
+       .rtbl_cnt       = ARRAY_SIZE(fs432c65_rtbl),
+       .get_rate       = clk_fs432c65_get_rate,
+};
+
+static struct clkgen_quadfs_data st_fs660c32_E_416 = {
+       .npda   = CLKGEN_FIELD(0x0, 0x1, 14),
+       .nsb    = { CLKGEN_FIELD(0x0, 0x1, 10),
+                   CLKGEN_FIELD(0x0, 0x1, 11),
+                   CLKGEN_FIELD(0x0, 0x1, 12),
+                   CLKGEN_FIELD(0x0, 0x1, 13) },
+       .nsdiv_present = true,
+       .nsdiv  = { CLKGEN_FIELD(0x0, 0x1, 18),
+                   CLKGEN_FIELD(0x0, 0x1, 19),
+                   CLKGEN_FIELD(0x0, 0x1, 20),
+                   CLKGEN_FIELD(0x0, 0x1, 21) },
+       .mdiv   = { CLKGEN_FIELD(0x4, 0x1f, 0),
+                   CLKGEN_FIELD(0x14, 0x1f, 0),
+                   CLKGEN_FIELD(0x24, 0x1f, 0),
+                   CLKGEN_FIELD(0x34, 0x1f, 0) },
+       .en     = { CLKGEN_FIELD(0x10, 0x1, 0),
+                   CLKGEN_FIELD(0x20, 0x1, 0),
+                   CLKGEN_FIELD(0x30, 0x1, 0),
+                   CLKGEN_FIELD(0x40, 0x1, 0) },
+       .ndiv   = CLKGEN_FIELD(0x0, 0x7, 15),
+       .pe     = { CLKGEN_FIELD(0x8, 0x7fff, 0),
+                   CLKGEN_FIELD(0x18, 0x7fff, 0),
+                   CLKGEN_FIELD(0x28, 0x7fff, 0),
+                   CLKGEN_FIELD(0x38, 0x7fff, 0) },
+       .sdiv   = { CLKGEN_FIELD(0xC, 0xf, 0),
+                   CLKGEN_FIELD(0x1C, 0xf, 0),
+                   CLKGEN_FIELD(0x2C, 0xf, 0),
+                   CLKGEN_FIELD(0x3C, 0xf, 0) },
+       .lockstatus_present = true,
+       .lock_status = CLKGEN_FIELD(0xAC, 0x1, 0),
+       .pll_ops        = &st_quadfs_pll_c32_ops,
+       .rtbl           = fs660c32_rtbl,
+       .rtbl_cnt       = ARRAY_SIZE(fs660c32_rtbl),
+       .get_rate       = clk_fs660c32_dig_get_rate,
+};
+
+static struct clkgen_quadfs_data st_fs660c32_F_416 = {
+       .npda   = CLKGEN_FIELD(0x0, 0x1, 14),
+       .nsb    = { CLKGEN_FIELD(0x0, 0x1, 10),
+                   CLKGEN_FIELD(0x0, 0x1, 11),
+                   CLKGEN_FIELD(0x0, 0x1, 12),
+                   CLKGEN_FIELD(0x0, 0x1, 13) },
+       .nsdiv_present = true,
+       .nsdiv  = { CLKGEN_FIELD(0x0, 0x1, 18),
+                   CLKGEN_FIELD(0x0, 0x1, 19),
+                   CLKGEN_FIELD(0x0, 0x1, 20),
+                   CLKGEN_FIELD(0x0, 0x1, 21) },
+       .mdiv   = { CLKGEN_FIELD(0x4, 0x1f, 0),
+                   CLKGEN_FIELD(0x14, 0x1f, 0),
+                   CLKGEN_FIELD(0x24, 0x1f, 0),
+                   CLKGEN_FIELD(0x34, 0x1f, 0) },
+       .en     = { CLKGEN_FIELD(0x10, 0x1, 0),
+                   CLKGEN_FIELD(0x20, 0x1, 0),
+                   CLKGEN_FIELD(0x30, 0x1, 0),
+                   CLKGEN_FIELD(0x40, 0x1, 0) },
+       .ndiv   = CLKGEN_FIELD(0x0, 0x7, 15),
+       .pe     = { CLKGEN_FIELD(0x8, 0x7fff, 0),
+                   CLKGEN_FIELD(0x18, 0x7fff, 0),
+                   CLKGEN_FIELD(0x28, 0x7fff, 0),
+                   CLKGEN_FIELD(0x38, 0x7fff, 0) },
+       .sdiv   = { CLKGEN_FIELD(0xC, 0xf, 0),
+                   CLKGEN_FIELD(0x1C, 0xf, 0),
+                   CLKGEN_FIELD(0x2C, 0xf, 0),
+                   CLKGEN_FIELD(0x3C, 0xf, 0) },
+       .lockstatus_present = true,
+       .lock_status = CLKGEN_FIELD(0xEC, 0x1, 0),
+       .pll_ops        = &st_quadfs_pll_c32_ops,
+       .rtbl           = fs660c32_rtbl,
+       .rtbl_cnt       = ARRAY_SIZE(fs660c32_rtbl),
+       .get_rate       = clk_fs660c32_dig_get_rate,
+};
+
+/**
+ * DOC: A Frequency Synthesizer that multiples its input clock by a fixed factor
+ *
+ * Traits of this clock:
+ * prepare - clk_(un)prepare only ensures parent is (un)prepared
+ * enable - clk_enable and clk_disable are functional & control the Fsyn
+ * rate - inherits rate from parent. set_rate/round_rate/recalc_rate
+ * parent - fixed parent.  No clk_set_parent support
+ */
+
+/**
+ * struct st_clk_quadfs_pll - A pll which outputs a fixed multiplier of
+ *                                  its parent clock, found inside a type of
+ *                                  ST quad channel frequency synthesizer block
+ *
+ * @hw: handle between common and hardware-specific interfaces.
+ * @ndiv: regmap field for the ndiv control.
+ * @regs_base: base address of the configuration registers.
+ * @lock: spinlock.
+ *
+ */
+struct st_clk_quadfs_pll {
+       struct clk_hw   hw;
+       void __iomem    *regs_base;
+       spinlock_t      *lock;
+       struct clkgen_quadfs_data *data;
+       u32 ndiv;
+};
+
+#define to_quadfs_pll(_hw) container_of(_hw, struct st_clk_quadfs_pll, hw)
+
+static int quadfs_pll_enable(struct clk_hw *hw)
+{
+       struct st_clk_quadfs_pll *pll = to_quadfs_pll(hw);
+       unsigned long flags = 0, timeout = jiffies + msecs_to_jiffies(10);
+
+       if (pll->lock)
+               spin_lock_irqsave(pll->lock, flags);
+
+       /*
+        * Bring block out of reset if we have reset control.
+        */
+       if (pll->data->reset_present)
+               CLKGEN_WRITE(pll, nreset, 1);
+
+       /*
+        * Use a fixed input clock noise bandwidth filter for the moment
+        */
+       if (pll->data->bwfilter_present)
+               CLKGEN_WRITE(pll, ref_bw, PLL_BW_GOODREF);
+
+
+       CLKGEN_WRITE(pll, ndiv, pll->ndiv);
+
+       /*
+        * Power up the PLL
+        */
+       CLKGEN_WRITE(pll, npda, 1);
+
+       if (pll->lock)
+               spin_unlock_irqrestore(pll->lock, flags);
+
+       if (pll->data->lockstatus_present)
+               while (!CLKGEN_READ(pll, lock_status)) {
+                       if (time_after(jiffies, timeout))
+                               return -ETIMEDOUT;
+                       cpu_relax();
+               }
+
+       return 0;
+}
+
+static void quadfs_pll_disable(struct clk_hw *hw)
+{
+       struct st_clk_quadfs_pll *pll = to_quadfs_pll(hw);
+       unsigned long flags = 0;
+
+       if (pll->lock)
+               spin_lock_irqsave(pll->lock, flags);
+
+       /*
+        * Powerdown the PLL and then put block into soft reset if we have
+        * reset control.
+        */
+       CLKGEN_WRITE(pll, npda, 0);
+
+       if (pll->data->reset_present)
+               CLKGEN_WRITE(pll, nreset, 0);
+
+       if (pll->lock)
+               spin_unlock_irqrestore(pll->lock, flags);
+}
+
+static int quadfs_pll_is_enabled(struct clk_hw *hw)
+{
+       struct st_clk_quadfs_pll *pll = to_quadfs_pll(hw);
+       u32 npda = CLKGEN_READ(pll, npda);
+
+       return !!npda;
+}
+
+int clk_fs660c32_vco_get_rate(unsigned long input, struct stm_fs *fs,
+                          unsigned long *rate)
+{
+       unsigned long nd = fs->ndiv + 16; /* ndiv value */
+
+       *rate = input * nd;
+
+       return 0;
+}
+
+static unsigned long quadfs_pll_fs660c32_recalc_rate(struct clk_hw *hw,
+                                       unsigned long parent_rate)
+{
+       struct st_clk_quadfs_pll *pll = to_quadfs_pll(hw);
+       unsigned long rate = 0;
+       struct stm_fs params;
+
+       params.ndiv = CLKGEN_READ(pll, ndiv);
+       if (clk_fs660c32_vco_get_rate(parent_rate, &params, &rate))
+               pr_err("%s:%s error calculating rate\n",
+                      __clk_get_name(hw->clk), __func__);
+
+       pll->ndiv = params.ndiv;
+
+       return rate;
+}
+
+int clk_fs660c32_vco_get_params(unsigned long input,
+                               unsigned long output, struct stm_fs *fs)
+{
+/* Formula
+   VCO frequency = (fin x ndiv) / pdiv
+   ndiv = VCOfreq * pdiv / fin
+   */
+       unsigned long pdiv = 1, n;
+
+       /* Output clock range: 384Mhz to 660Mhz */
+       if (output < 384000000 || output > 660000000)
+               return -EINVAL;
+
+       if (input > 40000000)
+               /* This means that PDIV would be 2 instead of 1.
+                  Not supported today. */
+               return -EINVAL;
+
+       input /= 1000;
+       output /= 1000;
+
+       n = output * pdiv / input;
+       if (n < 16)
+               n = 16;
+       fs->ndiv = n - 16; /* Converting formula value to reg value */
+
+       return 0;
+}
+
+static long quadfs_pll_fs660c32_round_rate(struct clk_hw *hw, unsigned long rate
+               , unsigned long *prate)
+{
+       struct stm_fs params;
+
+       if (!clk_fs660c32_vco_get_params(*prate, rate, &params))
+               clk_fs660c32_vco_get_rate(*prate, &params, &rate);
+
+       pr_debug("%s: %s new rate %ld [sdiv=0x%x,md=0x%x,pe=0x%x,nsdiv3=%u]\n",
+                __func__, __clk_get_name(hw->clk),
+                rate, (unsigned int)params.sdiv,
+                (unsigned int)params.mdiv,
+                (unsigned int)params.pe, (unsigned int)params.nsdiv);
+
+       return rate;
+}
+
+static int quadfs_pll_fs660c32_set_rate(struct clk_hw *hw, unsigned long rate,
+                               unsigned long parent_rate)
+{
+       struct st_clk_quadfs_pll *pll = to_quadfs_pll(hw);
+       struct stm_fs params;
+       long hwrate = 0;
+       unsigned long flags = 0;
+
+       if (!rate || !parent_rate)
+               return -EINVAL;
+
+       if (!clk_fs660c32_vco_get_params(parent_rate, rate, &params))
+               clk_fs660c32_vco_get_rate(parent_rate, &params, &hwrate);
+
+       pr_debug("%s: %s new rate %ld [ndiv=0x%x]\n",
+                __func__, __clk_get_name(hw->clk),
+                hwrate, (unsigned int)params.ndiv);
+
+       if (!hwrate)
+               return -EINVAL;
+
+       pll->ndiv = params.ndiv;
+
+       if (pll->lock)
+               spin_lock_irqsave(pll->lock, flags);
+
+       CLKGEN_WRITE(pll, ndiv, pll->ndiv);
+
+       if (pll->lock)
+               spin_unlock_irqrestore(pll->lock, flags);
+
+       return 0;
+}
+
+static const struct clk_ops st_quadfs_pll_c65_ops = {
+       .enable         = quadfs_pll_enable,
+       .disable        = quadfs_pll_disable,
+       .is_enabled     = quadfs_pll_is_enabled,
+};
+
+static const struct clk_ops st_quadfs_pll_c32_ops = {
+       .enable         = quadfs_pll_enable,
+       .disable        = quadfs_pll_disable,
+       .is_enabled     = quadfs_pll_is_enabled,
+       .recalc_rate    = quadfs_pll_fs660c32_recalc_rate,
+       .round_rate     = quadfs_pll_fs660c32_round_rate,
+       .set_rate       = quadfs_pll_fs660c32_set_rate,
+};
+
+static struct clk * __init st_clk_register_quadfs_pll(
+               const char *name, const char *parent_name,
+               struct clkgen_quadfs_data *quadfs, void __iomem *reg,
+               spinlock_t *lock)
+{
+       struct st_clk_quadfs_pll *pll;
+       struct clk *clk;
+       struct clk_init_data init;
+
+       /*
+        * Sanity check required pointers.
+        */
+       if (WARN_ON(!name || !parent_name))
+               return ERR_PTR(-EINVAL);
+
+       pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+       if (!pll)
+               return ERR_PTR(-ENOMEM);
+
+       init.name = name;
+       init.ops = quadfs->pll_ops;
+       init.flags = CLK_IS_BASIC;
+       init.parent_names = &parent_name;
+       init.num_parents = 1;
+
+       pll->data = quadfs;
+       pll->regs_base = reg;
+       pll->lock = lock;
+       pll->hw.init = &init;
+
+       clk = clk_register(NULL, &pll->hw);
+
+       if (IS_ERR(clk))
+               kfree(pll);
+
+       return clk;
+}
+
+/**
+ * DOC: A digital frequency synthesizer
+ *
+ * Traits of this clock:
+ * prepare - clk_(un)prepare only ensures parent is (un)prepared
+ * enable - clk_enable and clk_disable are functional
+ * rate - set rate is functional
+ * parent - fixed parent.  No clk_set_parent support
+ */
+
+/**
+ * struct st_clk_quadfs_fsynth - One clock output from a four channel digital
+ *                                  frequency synthesizer (fsynth) block.
+ *
+ * @hw: handle between common and hardware-specific interfaces
+ *
+ * @nsb: regmap field in the output control register for the digital
+ *       standby of this fsynth channel. This control is active low so
+ *       the channel is in standby when the control bit is cleared.
+ *
+ * @nsdiv: regmap field in the output control register for
+ *          for the optional divide by 3 of this fsynth channel. This control
+ *          is active low so the divide by 3 is active when the control bit is
+ *          cleared and the divide is bypassed when the bit is set.
+ */
+struct st_clk_quadfs_fsynth {
+       struct clk_hw   hw;
+       void __iomem    *regs_base;
+       spinlock_t      *lock;
+       struct clkgen_quadfs_data *data;
+
+       u32 chan;
+       /*
+        * Cached hardware values from set_rate so we can program the
+        * hardware in enable. There are two reasons for this:
+        *
+        *  1. The registers may not be writable until the parent has been
+        *     enabled.
+        *
+        *  2. It restores the clock rate when a driver does an enable
+        *     on PM restore, after a suspend to RAM has lost the hardware
+        *     setup.
+        */
+       u32 md;
+       u32 pe;
+       u32 sdiv;
+       u32 nsdiv;
+};
+
+#define to_quadfs_fsynth(_hw) \
+       container_of(_hw, struct st_clk_quadfs_fsynth, hw)
+
+static void quadfs_fsynth_program_enable(struct st_clk_quadfs_fsynth *fs)
+{
+       /*
+        * Pulse the program enable register lsb to make the hardware take
+        * notice of the new md/pe values with a glitchless transition.
+        */
+       CLKGEN_WRITE(fs, en[fs->chan], 1);
+       CLKGEN_WRITE(fs, en[fs->chan], 0);
+}
+
+static void quadfs_fsynth_program_rate(struct st_clk_quadfs_fsynth *fs)
+{
+       unsigned long flags = 0;
+
+       /*
+        * Ensure the md/pe parameters are ignored while we are
+        * reprogramming them so we can get a glitchless change
+        * when fine tuning the speed of a running clock.
+        */
+       CLKGEN_WRITE(fs, en[fs->chan], 0);
+
+       CLKGEN_WRITE(fs, mdiv[fs->chan], fs->md);
+       CLKGEN_WRITE(fs, pe[fs->chan], fs->pe);
+       CLKGEN_WRITE(fs, sdiv[fs->chan], fs->sdiv);
+
+       if (fs->lock)
+               spin_lock_irqsave(fs->lock, flags);
+
+       if (fs->data->nsdiv_present)
+               CLKGEN_WRITE(fs, nsdiv[fs->chan], fs->nsdiv);
+
+       if (fs->lock)
+               spin_unlock_irqrestore(fs->lock, flags);
+}
+
+static int quadfs_fsynth_enable(struct clk_hw *hw)
+{
+       struct st_clk_quadfs_fsynth *fs = to_quadfs_fsynth(hw);
+       unsigned long flags = 0;
+
+       pr_debug("%s: %s\n", __func__, __clk_get_name(hw->clk));
+
+       quadfs_fsynth_program_rate(fs);
+
+       if (fs->lock)
+               spin_lock_irqsave(fs->lock, flags);
+
+       CLKGEN_WRITE(fs, nsb[fs->chan], 1);
+
+       if (fs->lock)
+               spin_unlock_irqrestore(fs->lock, flags);
+
+       quadfs_fsynth_program_enable(fs);
+
+       return 0;
+}
+
+static void quadfs_fsynth_disable(struct clk_hw *hw)
+{
+       struct st_clk_quadfs_fsynth *fs = to_quadfs_fsynth(hw);
+       unsigned long flags = 0;
+
+       pr_debug("%s: %s\n", __func__, __clk_get_name(hw->clk));
+
+       if (fs->lock)
+               spin_lock_irqsave(fs->lock, flags);
+
+       CLKGEN_WRITE(fs, nsb[fs->chan], 0);
+
+       if (fs->lock)
+               spin_unlock_irqrestore(fs->lock, flags);
+}
+
+static int quadfs_fsynth_is_enabled(struct clk_hw *hw)
+{
+       struct st_clk_quadfs_fsynth *fs = to_quadfs_fsynth(hw);
+       u32 nsb = CLKGEN_READ(fs, nsb[fs->chan]);
+
+       pr_debug("%s: %s enable bit = 0x%x\n",
+                __func__, __clk_get_name(hw->clk), nsb);
+
+       return !!nsb;
+}
+
+#define P15                    (uint64_t)(1 << 15)
+
+static int clk_fs216c65_get_rate(unsigned long input, struct stm_fs *fs,
+               unsigned long *rate)
+{
+       uint64_t res;
+       unsigned long ns;
+       unsigned long nd = 8; /* ndiv stuck at 0 => val = 8 */
+       unsigned long s;
+       long m;
+
+       m = fs->mdiv - 32;
+       s = 1 << (fs->sdiv + 1);
+       ns = (fs->nsdiv ? 1 : 3);
+
+       res = (uint64_t)(s * ns * P15 * (uint64_t)(m + 33));
+       res = res - (s * ns * fs->pe);
+       *rate = div64_u64(P15 * nd * input * 32, res);
+
+       return 0;
+}
+
+static int clk_fs432c65_get_rate(unsigned long input, struct stm_fs *fs,
+               unsigned long *rate)
+{
+       uint64_t res;
+       unsigned long nd = 16; /* ndiv value; stuck at 0 (30Mhz input) */
+       long m;
+       unsigned long sd;
+       unsigned long ns;
+
+       m = fs->mdiv - 32;
+       sd = 1 << (fs->sdiv + 1);
+       ns = (fs->nsdiv ? 1 : 3);
+
+       res = (uint64_t)(sd * ns * P15 * (uint64_t)(m + 33));
+       res = res - (sd * ns * fs->pe);
+       *rate = div64_u64(P15 * nd * input * 32, res);
+
+       return 0;
+}
+
+#define P20            (uint64_t)(1 << 20)
+
+static int clk_fs660c32_dig_get_rate(unsigned long input,
+                               struct stm_fs *fs, unsigned long *rate)
+{
+       unsigned long s = (1 << fs->sdiv);
+       unsigned long ns;
+       uint64_t res;
+
+       /*
+        * 'nsdiv' is a register value ('BIN') which is translated
+        * to a decimal value according to following rules.
+        *
+        *     nsdiv      ns.dec
+        *       0        3
+        *       1        1
+        */
+       ns = (fs->nsdiv == 1) ? 1 : 3;
+
+       res = (P20 * (32 + fs->mdiv) + 32 * fs->pe) * s * ns;
+       *rate = (unsigned long)div64_u64(input * P20 * 32, res);
+
+       return 0;
+}
+
+static int quadfs_fsynt_get_hw_value_for_recalc(struct st_clk_quadfs_fsynth *fs,
+               struct stm_fs *params)
+{
+       /*
+        * Get the initial hardware values for recalc_rate
+        */
+       params->mdiv    = CLKGEN_READ(fs, mdiv[fs->chan]);
+       params->pe      = CLKGEN_READ(fs, pe[fs->chan]);
+       params->sdiv    = CLKGEN_READ(fs, sdiv[fs->chan]);
+
+       if (fs->data->nsdiv_present)
+               params->nsdiv = CLKGEN_READ(fs, nsdiv[fs->chan]);
+       else
+               params->nsdiv = 1;
+
+       /*
+        * If All are NULL then assume no clock rate is programmed.
+        */
+       if (!params->mdiv && !params->pe && !params->sdiv)
+               return 1;
+
+       fs->md = params->mdiv;
+       fs->pe = params->pe;
+       fs->sdiv = params->sdiv;
+       fs->nsdiv = params->nsdiv;
+
+       return 0;
+}
+
+static long quadfs_find_best_rate(struct clk_hw *hw, unsigned long drate,
+                               unsigned long prate, struct stm_fs *params)
+{
+       struct st_clk_quadfs_fsynth *fs = to_quadfs_fsynth(hw);
+       int (*clk_fs_get_rate)(unsigned long ,
+                               struct stm_fs *, unsigned long *);
+       struct stm_fs prev_params;
+       unsigned long prev_rate, rate = 0;
+       unsigned long diff_rate, prev_diff_rate = ~0;
+       int index;
+
+       clk_fs_get_rate = fs->data->get_rate;
+
+       for (index = 0; index < fs->data->rtbl_cnt; index++) {
+               prev_rate = rate;
+
+               *params = fs->data->rtbl[index];
+               prev_params = *params;
+
+               clk_fs_get_rate(prate, &fs->data->rtbl[index], &rate);
+
+               diff_rate = abs(drate - rate);
+
+               if (diff_rate > prev_diff_rate) {
+                       rate = prev_rate;
+                       *params = prev_params;
+                       break;
+               }
+
+               prev_diff_rate = diff_rate;
+
+               if (drate == rate)
+                       return rate;
+       }
+
+
+       if (index == fs->data->rtbl_cnt)
+               *params = prev_params;
+
+       return rate;
+}
+
+static unsigned long quadfs_recalc_rate(struct clk_hw *hw,
+               unsigned long parent_rate)
+{
+       struct st_clk_quadfs_fsynth *fs = to_quadfs_fsynth(hw);
+       unsigned long rate = 0;
+       struct stm_fs params;
+       int (*clk_fs_get_rate)(unsigned long ,
+                               struct stm_fs *, unsigned long *);
+
+       clk_fs_get_rate = fs->data->get_rate;
+
+       if (quadfs_fsynt_get_hw_value_for_recalc(fs, &params))
+               return 0;
+
+       if (clk_fs_get_rate(parent_rate, &params, &rate)) {
+               pr_err("%s:%s error calculating rate\n",
+                      __clk_get_name(hw->clk), __func__);
+       }
+
+       pr_debug("%s:%s rate %lu\n", __clk_get_name(hw->clk), __func__, rate);
+
+       return rate;
+}
+
+static long quadfs_round_rate(struct clk_hw *hw, unsigned long rate,
+                                    unsigned long *prate)
+{
+       struct stm_fs params;
+
+       rate = quadfs_find_best_rate(hw, rate, *prate, &params);
+
+       pr_debug("%s: %s new rate %ld [sdiv=0x%x,md=0x%x,pe=0x%x,nsdiv3=%u]\n",
+                __func__, __clk_get_name(hw->clk),
+                rate, (unsigned int)params.sdiv, (unsigned int)params.mdiv,
+                        (unsigned int)params.pe, (unsigned int)params.nsdiv);
+
+       return rate;
+}
+
+
+static void quadfs_program_and_enable(struct st_clk_quadfs_fsynth *fs,
+               struct stm_fs *params)
+{
+       fs->md = params->mdiv;
+       fs->pe = params->pe;
+       fs->sdiv = params->sdiv;
+       fs->nsdiv = params->nsdiv;
+
+       /*
+        * In some integrations you can only change the fsynth programming when
+        * the parent entity containing it is enabled.
+        */
+       quadfs_fsynth_program_rate(fs);
+       quadfs_fsynth_program_enable(fs);
+}
+
+static int quadfs_set_rate(struct clk_hw *hw, unsigned long rate,
+                                 unsigned long parent_rate)
+{
+       struct st_clk_quadfs_fsynth *fs = to_quadfs_fsynth(hw);
+       struct stm_fs params;
+       long hwrate;
+       int uninitialized_var(i);
+
+       if (!rate || !parent_rate)
+               return -EINVAL;
+
+       memset(&params, 0, sizeof(struct stm_fs));
+
+       hwrate = quadfs_find_best_rate(hw, rate, parent_rate, &params);
+       if (!hwrate)
+               return -EINVAL;
+
+       quadfs_program_and_enable(fs, &params);
+
+       return 0;
+}
+
+
+
+static const struct clk_ops st_quadfs_ops = {
+       .enable         = quadfs_fsynth_enable,
+       .disable        = quadfs_fsynth_disable,
+       .is_enabled     = quadfs_fsynth_is_enabled,
+       .round_rate     = quadfs_round_rate,
+       .set_rate       = quadfs_set_rate,
+       .recalc_rate    = quadfs_recalc_rate,
+};
+
+static struct clk * __init st_clk_register_quadfs_fsynth(
+               const char *name, const char *parent_name,
+               struct clkgen_quadfs_data *quadfs, void __iomem *reg, u32 chan,
+               spinlock_t *lock)
+{
+       struct st_clk_quadfs_fsynth *fs;
+       struct clk *clk;
+       struct clk_init_data init;
+
+       /*
+        * Sanity check required pointers, note that nsdiv3 is optional.
+        */
+       if (WARN_ON(!name || !parent_name))
+               return ERR_PTR(-EINVAL);
+
+       fs = kzalloc(sizeof(*fs), GFP_KERNEL);
+       if (!fs)
+               return ERR_PTR(-ENOMEM);
+
+       init.name = name;
+       init.ops = &st_quadfs_ops;
+       init.flags = CLK_GET_RATE_NOCACHE | CLK_IS_BASIC;
+       init.parent_names = &parent_name;
+       init.num_parents = 1;
+
+       fs->data = quadfs;
+       fs->regs_base = reg;
+       fs->chan = chan;
+       fs->lock = lock;
+       fs->hw.init = &init;
+
+       clk = clk_register(NULL, &fs->hw);
+
+       if (IS_ERR(clk))
+               kfree(fs);
+
+       return clk;
+}
+
+static struct of_device_id quadfs_of_match[] = {
+       {
+               .compatible = "st,stih416-quadfs216",
+               .data = (void *)&st_fs216c65_416
+       },
+       {
+               .compatible = "st,stih416-quadfs432",
+               .data = (void *)&st_fs432c65_416
+       },
+       {
+               .compatible = "st,stih416-quadfs660-E",
+               .data = (void *)&st_fs660c32_E_416
+       },
+       {
+               .compatible = "st,stih416-quadfs660-F",
+               .data = (void *)&st_fs660c32_F_416
+       },
+       {}
+};
+
+static void __init st_of_create_quadfs_fsynths(
+               struct device_node *np, const char *pll_name,
+               struct clkgen_quadfs_data *quadfs, void __iomem *reg,
+               spinlock_t *lock)
+{
+       struct clk_onecell_data *clk_data;
+       int fschan;
+
+       clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
+       if (!clk_data)
+               return;
+
+       clk_data->clk_num = QUADFS_MAX_CHAN;
+       clk_data->clks = kzalloc(QUADFS_MAX_CHAN * sizeof(struct clk *),
+                                GFP_KERNEL);
+
+       if (!clk_data->clks) {
+               kfree(clk_data);
+               return;
+       }
+
+       for (fschan = 0; fschan < QUADFS_MAX_CHAN; fschan++) {
+               struct clk *clk;
+               const char *clk_name;
+
+               if (of_property_read_string_index(np, "clock-output-names",
+                                                 fschan, &clk_name)) {
+                       break;
+               }
+
+               /*
+                * If we read an empty clock name then the channel is unused
+                */
+               if (*clk_name == '\0')
+                       continue;
+
+               clk = st_clk_register_quadfs_fsynth(clk_name, pll_name,
+                               quadfs, reg, fschan, lock);
+
+               /*
+                * If there was an error registering this clock output, clean
+                * up and move on to the next one.
+                */
+               if (!IS_ERR(clk)) {
+                       clk_data->clks[fschan] = 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));
+               }
+       }
+
+       of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
+}
+
+static void __init st_of_quadfs_setup(struct device_node *np)
+{
+       const struct of_device_id *match;
+       struct clk *clk;
+       const char *pll_name, *clk_parent_name;
+       void __iomem *reg;
+       spinlock_t *lock;
+
+       match = of_match_node(quadfs_of_match, np);
+       if (WARN_ON(!match))
+               return;
+
+       reg = of_iomap(np, 0);
+       if (!reg)
+               return;
+
+       clk_parent_name = of_clk_get_parent_name(np, 0);
+       if (!clk_parent_name)
+               return;
+
+       pll_name = kasprintf(GFP_KERNEL, "%s.pll", np->name);
+       if (!pll_name)
+               return;
+
+       lock = kzalloc(sizeof(*lock), GFP_KERNEL);
+       if (!lock)
+               goto err_exit;
+
+       spin_lock_init(lock);
+
+       clk = st_clk_register_quadfs_pll(pll_name, clk_parent_name,
+                       (struct clkgen_quadfs_data *) match->data, reg, lock);
+       if (IS_ERR(clk))
+               goto err_exit;
+       else
+               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));
+
+       st_of_create_quadfs_fsynths(np, pll_name,
+                                   (struct clkgen_quadfs_data *)match->data,
+                                   reg, lock);
+
+err_exit:
+       kfree(pll_name); /* No longer need local copy of the PLL name */
+}
+CLK_OF_DECLARE(quadfs, "st,quadfs", st_of_quadfs_setup);
diff --git a/drivers/clk/st/clkgen-mux.c b/drivers/clk/st/clkgen-mux.c
new file mode 100644 (file)
index 0000000..a329906
--- /dev/null
@@ -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);
diff --git a/drivers/clk/st/clkgen-pll.c b/drivers/clk/st/clkgen-pll.c
new file mode 100644 (file)
index 0000000..bca0a0b
--- /dev/null
@@ -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);
diff --git a/drivers/clk/st/clkgen.h b/drivers/clk/st/clkgen.h
new file mode 100644 (file)
index 0000000..35c8632
--- /dev/null
@@ -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*/
+
index abb6c5ac8a10297505a8a7f1cb1cb0d5f8eccf16..bd7dc733c1ca52170c2a5fd6418770794630235b 100644 (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,},
        {}
 };
 
index 356e9b804421ee7a27ba8a9cba960c3263260a10..9e899c18af8678c8eac70fab0ede9d8631fff3f3 100644 (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,
index 776ee4594bd44aebe2c457ba40ed6aea8ddd81fd..028b33783d383c695a8c3ef6a467e09634094ad5 100644 (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"),
index a15e445570b288afee9a8fb0db109b9f1fefb884..e6aa10db7bba1f78116ff6cd0b02a49b65bea857 100644 (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))
index cdeff299de26d3a78320022b3473c484bbb78faf..7b55ef89baa53c7ef6d59c19dcf8c9287b773199 100644 (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;
index c812b93a52b28774232e420a389c34b21aef5544..52c09afdcfb724d9c7206c23233b46364cf1b28a 100644 (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);
index 3226f54fa5956a357104f3f8675779179067b717..cec97596fe65988ff473e7f6d08302a7f68ba375 100644 (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);
 
index 6eaa6a45e1100ff0a95e6f13b4c56fb8446cedea..21b9d0e2eb0cd0e018d9cb531e768ed6e849ccc3 100644 (file)
 #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 */
diff --git a/include/dt-bindings/clock/hip04-clock.h b/include/dt-bindings/clock/hip04-clock.h
new file mode 100644 (file)
index 0000000..695e61c
--- /dev/null
@@ -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 */
index 939533da93a7355be0a40422627090f1aad751b2..511917416fb003bd27ba5817fb6c8ee1af8cf498 100644 (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);
 };
 
 /**
index 0dd91148165e4e7df78beb5582b89910c1615f26..fb5e097d8f72b07fb34e480bb817593284dccdd2 100644 (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);
 
 /**