Merge tag 'clk-for-linus-3.10' of git://git.linaro.org/people/mturquette/linux
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 29 Apr 2013 23:43:54 +0000 (16:43 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 29 Apr 2013 23:43:54 +0000 (16:43 -0700)
Pull clock framework update from Michael Turquette:
 "The common clock framework changes for 3.10 include many fixes for
  existing platforms, as well as adoption of the framework by new
  platforms and devices.

  Some long-needed fixes to the core framework are here as well as new
  features such as improved initialization of clocks from DT as well as
  framework reentrancy for nested clock operations."

* tag 'clk-for-linus-3.10' of git://git.linaro.org/people/mturquette/linux: (44 commits)
  clk: add clk_ignore_unused option to keep boot clocks on
  clk: ux500: fix mismatched types
  clk: vexpress: Add separate SP810 driver
  clk: si5351: make clk-si5351 depend on CONFIG_OF
  clk: export __clk_get_flags for modular clock providers
  clk: vt8500: Missing breaks in vtwm_pll_round_rate/_set_rate.
  clk: sunxi: Unify oscillator clock
  clk: composite: allow fixed rates & fixed dividers
  clk: composite: rename 'div' references to 'rate'
  clk: add si5351 i2c common clock driver
  clk: add device tree fixed-factor-clock binding support
  clk: Properly handle notifier return values
  clk: ux500: abx500: Define clock tree for ab850x
  clk: ux500: Add support for sysctrl clocks
  clk: mvebu: Fix valid value range checking for cpu_freq_select
  clk: Fixup locking issues for clk_set_parent
  clk: Fixup errorhandling for clk_set_parent
  clk: Restructure code for __clk_reparent
  clk: sunxi: drop an unnecesary kmalloc
  clk: sunxi: drop CLK_IGNORE_UNUSED
  ...

48 files changed:
Documentation/arm/sunxi/clocks.txt [new file with mode: 0644]
Documentation/clk.txt
Documentation/devicetree/bindings/clock/axi-clkgen.txt [new file with mode: 0644]
Documentation/devicetree/bindings/clock/fixed-factor-clock.txt [new file with mode: 0644]
Documentation/devicetree/bindings/clock/silabs,si5351.txt [new file with mode: 0644]
Documentation/devicetree/bindings/clock/sunxi.txt [new file with mode: 0644]
Documentation/devicetree/bindings/vendor-prefixes.txt
Documentation/kernel-parameters.txt
arch/arm/mach-imx/clk-busy.c
arch/arm/mach-vexpress/v2m.c
drivers/clk/Kconfig
drivers/clk/Makefile
drivers/clk/clk-axi-clkgen.c [new file with mode: 0644]
drivers/clk/clk-composite.c [new file with mode: 0644]
drivers/clk/clk-divider.c
drivers/clk/clk-fixed-factor.c
drivers/clk/clk-mux.c
drivers/clk/clk-prima2.c
drivers/clk/clk-si5351.c [new file with mode: 0644]
drivers/clk/clk-si5351.h [new file with mode: 0644]
drivers/clk/clk-vt8500.c
drivers/clk/clk-zynq.c
drivers/clk/clk.c
drivers/clk/mvebu/clk-core.c
drivers/clk/mvebu/clk-cpu.c
drivers/clk/mvebu/clk-cpu.h [deleted file]
drivers/clk/mvebu/clk.c
drivers/clk/mxs/clk.c
drivers/clk/spear/spear1340_clock.c
drivers/clk/sunxi/Makefile [new file with mode: 0644]
drivers/clk/sunxi/clk-factors.c [new file with mode: 0644]
drivers/clk/sunxi/clk-factors.h [new file with mode: 0644]
drivers/clk/sunxi/clk-sunxi.c [new file with mode: 0644]
drivers/clk/tegra/clk.h
drivers/clk/ux500/Makefile
drivers/clk/ux500/abx500-clk.c
drivers/clk/ux500/clk-prcmu.c
drivers/clk/ux500/clk-sysctrl.c [new file with mode: 0644]
drivers/clk/ux500/clk.h
drivers/clk/versatile/Makefile
drivers/clk/versatile/clk-sp810.c [new file with mode: 0644]
drivers/clk/versatile/clk-vexpress.c
drivers/clocksource/sunxi_timer.c
include/linux/clk-private.h
include/linux/clk-provider.h
include/linux/clk.h
include/linux/clk/sunxi.h [new file with mode: 0644]
include/linux/platform_data/si5351.h [new file with mode: 0644]

diff --git a/Documentation/arm/sunxi/clocks.txt b/Documentation/arm/sunxi/clocks.txt
new file mode 100644 (file)
index 0000000..e09a88a
--- /dev/null
@@ -0,0 +1,56 @@
+Frequently asked questions about the sunxi clock system
+=======================================================
+
+This document contains useful bits of information that people tend to ask
+about the sunxi clock system, as well as accompanying ASCII art when adequate.
+
+Q: Why is the main 24MHz oscillator gatable? Wouldn't that break the
+   system?
+
+A: The 24MHz oscillator allows gating to save power. Indeed, if gated
+   carelessly the system would stop functioning, but with the right
+   steps, one can gate it and keep the system running. Consider this
+   simplified suspend example:
+
+   While the system is operational, you would see something like
+
+      24MHz         32kHz
+       |
+      PLL1
+       \
+        \_ CPU Mux
+             |
+           [CPU]
+
+   When you are about to suspend, you switch the CPU Mux to the 32kHz
+   oscillator:
+
+      24Mhz         32kHz
+       |              |
+      PLL1            |
+                     /
+           CPU Mux _/
+             |
+           [CPU]
+
+    Finally you can gate the main oscillator
+
+                    32kHz
+                      |
+                      |
+                     /
+           CPU Mux _/
+             |
+           [CPU]
+
+Q: Were can I learn more about the sunxi clocks?
+
+A: The linux-sunxi wiki contains a page documenting the clock registers,
+   you can find it at
+
+        http://linux-sunxi.org/A10/CCM
+
+   The authoritative source for information at this time is the ccmu driver
+   released by Allwinner, you can find it at
+
+        https://github.com/linux-sunxi/linux-sunxi/tree/sunxi-3.0/arch/arm/mach-sun4i/clock/ccmu
index 1943fae..b9911c2 100644 (file)
@@ -174,9 +174,9 @@ int clk_foo_enable(struct clk_hw *hw)
 };
 
 Below is a matrix detailing which clk_ops are mandatory based upon the
-hardware capbilities of that clock.  A cell marked as "y" means
+hardware capabilities of that clock.  A cell marked as "y" means
 mandatory, a cell marked as "n" implies that either including that
-callback is invalid or otherwise uneccesary.  Empty cells are either
+callback is invalid or otherwise unnecessary.  Empty cells are either
 optional or must be evaluated on a case-by-case basis.
 
                            clock hardware characteristics
@@ -231,3 +231,14 @@ To better enforce this policy, always follow this simple rule: any
 statically initialized clock data MUST be defined in a separate file
 from the logic that implements its ops.  Basically separate the logic
 from the data and all is well.
+
+       Part 6 - Disabling clock gating of unused clocks
+
+Sometimes during development it can be useful to be able to bypass the
+default disabling of unused clocks. For example, if drivers aren't enabling
+clocks properly but rely on them being on from the bootloader, bypassing
+the disabling means that the driver will remain functional while the issues
+are sorted out.
+
+To bypass this disabling, include "clk_ignore_unused" in the bootargs to the
+kernel.
diff --git a/Documentation/devicetree/bindings/clock/axi-clkgen.txt b/Documentation/devicetree/bindings/clock/axi-clkgen.txt
new file mode 100644 (file)
index 0000000..028b493
--- /dev/null
@@ -0,0 +1,22 @@
+Binding for the axi-clkgen clock generator
+
+This binding uses the common clock binding[1].
+
+[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+Required properties:
+- compatible : shall be "adi,axi-clkgen".
+- #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.
+
+Optional properties:
+- clock-output-names : From common clock binding.
+
+Example:
+       clock@0xff000000 {
+               compatible = "adi,axi-clkgen";
+               #clock-cells = <0>;
+               reg = <0xff000000 0x1000>;
+               clocks = <&osc 1>;
+       };
diff --git a/Documentation/devicetree/bindings/clock/fixed-factor-clock.txt b/Documentation/devicetree/bindings/clock/fixed-factor-clock.txt
new file mode 100644 (file)
index 0000000..5757f9a
--- /dev/null
@@ -0,0 +1,24 @@
+Binding for simple fixed factor rate clock sources.
+
+This binding uses the common clock binding[1].
+
+[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+Required properties:
+- compatible : shall be "fixed-factor-clock".
+- #clock-cells : from common clock binding; shall be set to 0.
+- clock-div: fixed divider.
+- clock-mult: fixed multiplier.
+- clocks: parent clock.
+
+Optional properties:
+- clock-output-names : From common clock binding.
+
+Example:
+       clock {
+               compatible = "fixed-factor-clock";
+               clocks = <&parentclk>;
+               #clock-cells = <0>;
+               div = <2>;
+               mult = <1>;
+       };
diff --git a/Documentation/devicetree/bindings/clock/silabs,si5351.txt b/Documentation/devicetree/bindings/clock/silabs,si5351.txt
new file mode 100644 (file)
index 0000000..cc37465
--- /dev/null
@@ -0,0 +1,114 @@
+Binding for Silicon Labs Si5351a/b/c programmable i2c clock generator.
+
+Reference
+[1] Si5351A/B/C Data Sheet
+    http://www.silabs.com/Support%20Documents/TechnicalDocs/Si5351.pdf
+
+The Si5351a/b/c are programmable i2c clock generators with upto 8 output
+clocks. Si5351a also has a reduced pin-count package (MSOP10) where only
+3 output clocks are accessible. The internal structure of the clock
+generators can be found in [1].
+
+==I2C device node==
+
+Required properties:
+- compatible: shall be one of "silabs,si5351{a,a-msop,b,c}".
+- reg: i2c device address, shall be 0x60 or 0x61.
+- #clock-cells: from common clock binding; shall be set to 1.
+- clocks: from common clock binding; list of parent clock
+  handles, shall be xtal reference clock or xtal and clkin for
+  si5351c only.
+- #address-cells: shall be set to 1.
+- #size-cells: shall be set to 0.
+
+Optional properties:
+- silabs,pll-source: pair of (number, source) for each pll. Allows
+  to overwrite clock source of pll A (number=0) or B (number=1).
+
+==Child nodes==
+
+Each of the clock outputs can be overwritten individually by
+using a child node to the I2C device node. If a child node for a clock
+output is not set, the eeprom configuration is not overwritten.
+
+Required child node properties:
+- reg: number of clock output.
+
+Optional child node properties:
+- silabs,clock-source: source clock of the output divider stage N, shall be
+  0 = multisynth N
+  1 = multisynth 0 for output clocks 0-3, else multisynth4
+  2 = xtal
+  3 = clkin (si5351c only)
+- silabs,drive-strength: output drive strength in mA, shall be one of {2,4,6,8}.
+- silabs,multisynth-source: source pll A(0) or B(1) of corresponding multisynth
+  divider.
+- silabs,pll-master: boolean, multisynth can change pll frequency.
+
+==Example==
+
+/* 25MHz reference crystal */
+ref25: ref25M {
+       compatible = "fixed-clock";
+       #clock-cells = <0>;
+       clock-frequency = <25000000>;
+};
+
+i2c-master-node {
+
+       /* Si5351a msop10 i2c clock generator */
+       si5351a: clock-generator@60 {
+               compatible = "silabs,si5351a-msop";
+               reg = <0x60>;
+               #address-cells = <1>;
+               #size-cells = <0>;
+               #clock-cells = <1>;
+
+               /* connect xtal input to 25MHz reference */
+               clocks = <&ref25>;
+
+               /* connect xtal input as source of pll0 and pll1 */
+               silabs,pll-source = <0 0>, <1 0>;
+
+               /*
+                * overwrite clkout0 configuration with:
+                * - 8mA output drive strength
+                * - pll0 as clock source of multisynth0
+                * - multisynth0 as clock source of output divider
+                * - multisynth0 can change pll0
+                * - set initial clock frequency of 74.25MHz
+                */
+               clkout0 {
+                       reg = <0>;
+                       silabs,drive-strength = <8>;
+                       silabs,multisynth-source = <0>;
+                       silabs,clock-source = <0>;
+                       silabs,pll-master;
+                       clock-frequency = <74250000>;
+               };
+
+               /*
+                * overwrite clkout1 configuration with:
+                * - 4mA output drive strength
+                * - pll1 as clock source of multisynth1
+                * - multisynth1 as clock source of output divider
+                * - multisynth1 can change pll1
+                */
+               clkout1 {
+                       reg = <1>;
+                       silabs,drive-strength = <4>;
+                       silabs,multisynth-source = <1>;
+                       silabs,clock-source = <0>;
+                       pll-master;
+               };
+
+               /*
+                * overwrite clkout2 configuration with:
+                * - xtal as clock source of output divider
+                */
+               clkout2 {
+                       reg = <2>;
+                       silabs,clock-source = <2>;
+               };
+       };
+};
diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt
new file mode 100644 (file)
index 0000000..729f524
--- /dev/null
@@ -0,0 +1,151 @@
+Device Tree Clock bindings for arch-sunxi
+
+This binding uses the common clock binding[1].
+
+[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+Required properties:
+- compatible : shall be one of the following:
+       "allwinner,sun4i-osc-clk" - for a gatable oscillator
+       "allwinner,sun4i-pll1-clk" - for the main PLL clock
+       "allwinner,sun4i-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
+       "allwinner,sun4i-apb0-clk" - for the APB0 clock
+       "allwinner,sun4i-apb0-gates-clk" - for the APB0 gates
+       "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
+
+Required properties for all clocks:
+- reg : shall be the control register address for the clock.
+- clocks : shall be the input parent clock(s) phandle for the clock
+- #clock-cells : from common clock binding; shall be set to 0 except for
+       "allwinner,sun4i-*-gates-clk" where it shall be set to 1
+
+Additionally, "allwinner,sun4i-*-gates-clk" clocks require:
+- clock-output-names : the corresponding gate names that the clock controls
+
+For example:
+
+osc24M: osc24M@01c20050 {
+       #clock-cells = <0>;
+       compatible = "allwinner,sun4i-osc-clk";
+       reg = <0x01c20050 0x4>;
+       clocks = <&osc24M_fixed>;
+};
+
+pll1: pll1@01c20000 {
+       #clock-cells = <0>;
+       compatible = "allwinner,sun4i-pll1-clk";
+       reg = <0x01c20000 0x4>;
+       clocks = <&osc24M>;
+};
+
+cpu: cpu@01c20054 {
+       #clock-cells = <0>;
+       compatible = "allwinner,sun4i-cpu-clk";
+       reg = <0x01c20054 0x4>;
+       clocks = <&osc32k>, <&osc24M>, <&pll1>;
+};
+
+
+
+Gate clock outputs
+
+The "allwinner,sun4i-*-gates-clk" clocks provide several gatable outputs;
+their corresponding offsets as present on sun4i are listed below. Note that
+some of these gates are not present on sun5i.
+
+  * AXI gates ("allwinner,sun4i-axi-gates-clk")
+
+    DRAM                                                                0
+
+  * AHB gates ("allwinner,sun4i-ahb-gates-clk")
+
+    USB0                                                                0
+    EHCI0                                                               1
+    OHCI0                                                               2*
+    EHCI1                                                               3
+    OHCI1                                                               4*
+    SS                                                                  5
+    DMA                                                                 6
+    BIST                                                                7
+    MMC0                                                                8
+    MMC1                                                                9
+    MMC2                                                                10
+    MMC3                                                                11
+    MS                                                                  12**
+    NAND                                                                13
+    SDRAM                                                               14
+
+    ACE                                                                 16
+    EMAC                                                                17
+    TS                                                                  18
+
+    SPI0                                                                20
+    SPI1                                                                21
+    SPI2                                                                22
+    SPI3                                                                23
+    PATA                                                                24
+    SATA                                                                25**
+    GPS                                                                 26*
+
+    VE                                                                  32
+    TVD                                                                 33
+    TVE0                                                                34
+    TVE1                                                                35
+    LCD0                                                                36
+    LCD1                                                                37
+
+    CSI0                                                                40
+    CSI1                                                                41
+
+    HDMI                                                                43
+    DE_BE0                                                              44
+    DE_BE1                                                              45
+    DE_FE0                                                              46
+    DE_FE1                                                              47
+
+    MP                                                                  50
+
+    MALI400                                                             52
+
+  * APB0 gates ("allwinner,sun4i-apb0-gates-clk")
+
+    CODEC                                                               0
+    SPDIF                                                               1*
+    AC97                                                                2
+    IIS                                                                 3
+
+    PIO                                                                 5
+    IR0                                                                 6
+    IR1                                                                 7
+
+    KEYPAD                                                              10
+
+  * APB1 gates ("allwinner,sun4i-apb1-gates-clk")
+
+    I2C0                                                                0
+    I2C1                                                                1
+    I2C2                                                                2
+
+    CAN                                                                 4
+    SCR                                                                 5
+    PS20                                                                6
+    PS21                                                                7
+
+    UART0                                                               16
+    UART1                                                               17
+    UART2                                                               18
+    UART3                                                               19
+    UART4                                                               20
+    UART5                                                               21
+    UART6                                                               22
+    UART7                                                               23
+
+Notation:
+ [*]:  The datasheet didn't mention these, but they are present on AW code
+ [**]: The datasheet had this marked as "NC" but they are used on AW code
index 3b3b019..4d1919b 100644 (file)
@@ -49,6 +49,7 @@ samsung       Samsung Semiconductor
 sbs    Smart Battery System
 schindler      Schindler
 sil    Silicon Image
+silabs Silicon Laboratories
 simtek
 sirf   SiRF Technology, Inc.
 snps   Synopsys, Inc.
index 5abc09a..365f7bd 100644 (file)
@@ -44,6 +44,7 @@ parameter is applicable:
        AVR32   AVR32 architecture is enabled.
        AX25    Appropriate AX.25 support is enabled.
        BLACKFIN Blackfin architecture is enabled.
+       CLK     Common clock infrastructure is enabled.
        DRM     Direct Rendering Management support is enabled.
        DYNAMIC_DEBUG Build in debug messages and enable them at runtime
        EDD     BIOS Enhanced Disk Drive Services (EDD) is enabled
@@ -472,6 +473,13 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
 
        cio_ignore=     [S390]
                        See Documentation/s390/CommonIO for details.
+       clk_ignore_unused
+                       [CLK]
+                       Keep all clocks already enabled by bootloader on,
+                       even if no driver has claimed them. This is useful
+                       for debug and development, but should not be
+                       needed on a platform with proper driver support.
+                       For more information, see Documentation/clk.txt.
 
        clock=          [BUGS=X86-32, HW] gettimeofday clocksource override.
                        [Deprecated]
index 1ab91b5..85b728c 100644 (file)
@@ -169,7 +169,7 @@ struct clk *imx_clk_busy_mux(const char *name, void __iomem *reg, u8 shift,
 
        busy->mux.reg = reg;
        busy->mux.shift = shift;
-       busy->mux.width = width;
+       busy->mux.mask = BIT(width) - 1;
        busy->mux.lock = &imx_ccm_lock;
        busy->mux_ops = &clk_mux_ops;
 
index 915683c..c5e20b5 100644 (file)
@@ -21,6 +21,8 @@
 #include <linux/regulator/fixed.h>
 #include <linux/regulator/machine.h>
 #include <linux/vexpress.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
 
 #include <asm/arch_timer.h>
 #include <asm/mach-types.h>
@@ -433,7 +435,7 @@ static void __init v2m_dt_timer_init(void)
 {
        struct device_node *node = NULL;
 
-       vexpress_clk_of_init();
+       of_clk_init(NULL);
 
        do {
                node = of_find_compatible_node(node, NULL, "arm,sp804");
@@ -441,6 +443,10 @@ static void __init v2m_dt_timer_init(void)
        if (node) {
                pr_info("Using SP804 '%s' as a clock & events source\n",
                                node->full_name);
+               WARN_ON(clk_register_clkdev(of_clk_get_by_name(node,
+                               "timclken1"), "v2m-timer0", "sp804"));
+               WARN_ON(clk_register_clkdev(of_clk_get_by_name(node,
+                               "timclken2"), "v2m-timer1", "sp804"));
                v2m_sp804_init(of_iomap(node, 0),
                                irq_of_parse_and_map(node, 0));
        }
index a47e6ee..0357ac4 100644 (file)
@@ -55,6 +55,16 @@ config COMMON_CLK_MAX77686
        ---help---
          This driver supports Maxim 77686 crystal oscillator clock. 
 
+config COMMON_CLK_SI5351
+       tristate "Clock driver for SiLabs 5351A/B/C"
+       depends on I2C
+       depends on OF
+       select REGMAP_I2C
+       select RATIONAL
+       ---help---
+         This driver supports Silicon Labs 5351A/B/C programmable clock
+         generators.
+
 config CLK_TWL6040
        tristate "External McPDM functional clock from twl6040"
        depends on TWL6040_CORE
@@ -63,6 +73,14 @@ config CLK_TWL6040
          McPDM. McPDM module is using the external bit clock on the McPDM bus
          as functional clock.
 
+config COMMON_CLK_AXI_CLKGEN
+       tristate "AXI clkgen driver"
+       depends on ARCH_ZYNQ || MICROBLAZE
+       help
+       ---help---
+         Support for the Analog Devices axi-clkgen pcore clock generator for Xilinx
+         FPGAs. It is commonly used in Analog Devices' reference designs.
+
 endmenu
 
 source "drivers/clk/mvebu/Kconfig"
index 300d477..e7f7fe9 100644 (file)
@@ -7,6 +7,7 @@ obj-$(CONFIG_COMMON_CLK)        += clk-fixed-factor.o
 obj-$(CONFIG_COMMON_CLK)       += clk-fixed-rate.o
 obj-$(CONFIG_COMMON_CLK)       += clk-gate.o
 obj-$(CONFIG_COMMON_CLK)       += clk-mux.o
+obj-$(CONFIG_COMMON_CLK)       += clk-composite.o
 
 # SoCs specific
 obj-$(CONFIG_ARCH_BCM2835)     += clk-bcm2835.o
@@ -23,6 +24,7 @@ ifeq ($(CONFIG_COMMON_CLK), y)
 obj-$(CONFIG_ARCH_MMP)         += mmp/
 endif
 obj-$(CONFIG_MACH_LOONGSON1)   += clk-ls1x.o
+obj-$(CONFIG_ARCH_SUNXI)       += sunxi/
 obj-$(CONFIG_ARCH_U8500)       += ux500/
 obj-$(CONFIG_ARCH_VT8500)      += clk-vt8500.o
 obj-$(CONFIG_ARCH_ZYNQ)                += clk-zynq.o
@@ -31,6 +33,8 @@ obj-$(CONFIG_ARCH_TEGRA)      += tegra/
 obj-$(CONFIG_X86)              += x86/
 
 # Chip specific
+obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN) += clk-axi-clkgen.o
 obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
 obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o
+obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o
 obj-$(CONFIG_CLK_TWL6040)      += clk-twl6040.o
diff --git a/drivers/clk/clk-axi-clkgen.c b/drivers/clk/clk-axi-clkgen.c
new file mode 100644 (file)
index 0000000..8137327
--- /dev/null
@@ -0,0 +1,331 @@
+/*
+ * AXI clkgen driver
+ *
+ * Copyright 2012-2013 Analog Devices Inc.
+ *  Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2.
+ *
+ */
+
+#include <linux/platform_device.h>
+#include <linux/clk-provider.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#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
+
+struct axi_clkgen {
+       void __iomem *base;
+       struct clk_hw clk_hw;
+};
+
+static uint32_t axi_clkgen_lookup_filter(unsigned int m)
+{
+       switch (m) {
+       case 0:
+               return 0x01001990;
+       case 1:
+               return 0x01001190;
+       case 2:
+               return 0x01009890;
+       case 3:
+               return 0x01001890;
+       case 4:
+               return 0x01008890;
+       case 5 ... 8:
+               return 0x01009090;
+       case 9 ... 11:
+               return 0x01000890;
+       case 12:
+               return 0x08009090;
+       case 13 ... 22:
+               return 0x01001090;
+       case 23 ... 36:
+               return 0x01008090;
+       case 37 ... 46:
+               return 0x08001090;
+       default:
+               return 0x08008090;
+       }
+}
+
+static const uint32_t axi_clkgen_lock_table[] = {
+       0x060603e8, 0x060603e8, 0x080803e8, 0x0b0b03e8,
+       0x0e0e03e8, 0x111103e8, 0x131303e8, 0x161603e8,
+       0x191903e8, 0x1c1c03e8, 0x1f1f0384, 0x1f1f0339,
+       0x1f1f02ee, 0x1f1f02bc, 0x1f1f028a, 0x1f1f0271,
+       0x1f1f023f, 0x1f1f0226, 0x1f1f020d, 0x1f1f01f4,
+       0x1f1f01db, 0x1f1f01c2, 0x1f1f01a9, 0x1f1f0190,
+       0x1f1f0190, 0x1f1f0177, 0x1f1f015e, 0x1f1f015e,
+       0x1f1f0145, 0x1f1f0145, 0x1f1f012c, 0x1f1f012c,
+       0x1f1f012c, 0x1f1f0113, 0x1f1f0113, 0x1f1f0113,
+};
+
+static uint32_t axi_clkgen_lookup_lock(unsigned int m)
+{
+       if (m < ARRAY_SIZE(axi_clkgen_lock_table))
+               return axi_clkgen_lock_table[m];
+       return 0x1f1f00fa;
+}
+
+static const unsigned int fpfd_min = 10000;
+static const unsigned int fpfd_max = 300000;
+static const unsigned int fvco_min = 600000;
+static const unsigned int fvco_max = 1200000;
+
+static void axi_clkgen_calc_params(unsigned long fin, unsigned long fout,
+       unsigned int *best_d, unsigned int *best_m, unsigned int *best_dout)
+{
+       unsigned long d, d_min, d_max, _d_min, _d_max;
+       unsigned long m, m_min, m_max;
+       unsigned long f, dout, best_f, fvco;
+
+       fin /= 1000;
+       fout /= 1000;
+
+       best_f = ULONG_MAX;
+       *best_d = 0;
+       *best_m = 0;
+       *best_dout = 0;
+
+       d_min = max_t(unsigned long, DIV_ROUND_UP(fin, fpfd_max), 1);
+       d_max = min_t(unsigned long, fin / fpfd_min, 80);
+
+       m_min = max_t(unsigned long, DIV_ROUND_UP(fvco_min, fin) * d_min, 1);
+       m_max = min_t(unsigned long, fvco_max * d_max / fin, 64);
+
+       for (m = m_min; m <= m_max; m++) {
+               _d_min = max(d_min, DIV_ROUND_UP(fin * m, fvco_max));
+               _d_max = min(d_max, fin * m / fvco_min);
+
+               for (d = _d_min; d <= _d_max; d++) {
+                       fvco = fin * m / d;
+
+                       dout = DIV_ROUND_CLOSEST(fvco, fout);
+                       dout = clamp_t(unsigned long, dout, 1, 128);
+                       f = fvco / dout;
+                       if (abs(f - fout) < abs(best_f - fout)) {
+                               best_f = f;
+                               *best_d = d;
+                               *best_m = m;
+                               *best_dout = dout;
+                               if (best_f == fout)
+                                       return;
+                       }
+               }
+       }
+}
+
+static void axi_clkgen_calc_clk_params(unsigned int divider, unsigned int *low,
+       unsigned int *high, unsigned int *edge, unsigned int *nocount)
+{
+       if (divider == 1)
+               *nocount = 1;
+       else
+               *nocount = 0;
+
+       *high = divider / 2;
+       *edge = divider % 2;
+       *low = divider - *high;
+}
+
+static void axi_clkgen_write(struct axi_clkgen *axi_clkgen,
+       unsigned int reg, unsigned int val)
+{
+       writel(val, axi_clkgen->base + reg);
+}
+
+static void axi_clkgen_read(struct axi_clkgen *axi_clkgen,
+       unsigned int reg, unsigned int *val)
+{
+       *val = readl(axi_clkgen->base + reg);
+}
+
+static struct axi_clkgen *clk_hw_to_axi_clkgen(struct clk_hw *clk_hw)
+{
+       return container_of(clk_hw, struct axi_clkgen, clk_hw);
+}
+
+static int axi_clkgen_set_rate(struct clk_hw *clk_hw,
+       unsigned long rate, unsigned long parent_rate)
+{
+       struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
+       unsigned int d, m, dout;
+       unsigned int nocount;
+       unsigned int high;
+       unsigned int edge;
+       unsigned int low;
+       uint32_t filter;
+       uint32_t lock;
+
+       if (parent_rate == 0 || rate == 0)
+               return -EINVAL;
+
+       axi_clkgen_calc_params(parent_rate, rate, &d, &m, &dout);
+
+       if (d == 0 || dout == 0 || m == 0)
+               return -EINVAL;
+
+       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_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_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);
+
+       return 0;
+}
+
+static long axi_clkgen_round_rate(struct clk_hw *hw, unsigned long rate,
+       unsigned long *parent_rate)
+{
+       unsigned int d, m, dout;
+
+       axi_clkgen_calc_params(*parent_rate, rate, &d, &m, &dout);
+
+       if (d == 0 || dout == 0 || m == 0)
+               return -EINVAL;
+
+       return *parent_rate / d * m / dout;
+}
+
+static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw,
+       unsigned long parent_rate)
+{
+       struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
+       unsigned int d, m, dout;
+       unsigned int reg;
+       unsigned long long tmp;
+
+       axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT1, &reg);
+       dout = (reg & 0x3f) + ((reg >> 6) & 0x3f);
+       axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_DIV, &reg);
+       d = (reg & 0x3f) + ((reg >> 6) & 0x3f);
+       axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_FB1, &reg);
+       m = (reg & 0x3f) + ((reg >> 6) & 0x3f);
+
+       if (d == 0 || dout == 0)
+               return 0;
+
+       tmp = (unsigned long long)(parent_rate / d) * m;
+       do_div(tmp, dout);
+
+       if (tmp > ULONG_MAX)
+               return ULONG_MAX;
+
+       return tmp;
+}
+
+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,
+};
+
+static int axi_clkgen_probe(struct platform_device *pdev)
+{
+       struct axi_clkgen *axi_clkgen;
+       struct clk_init_data init;
+       const char *parent_name;
+       const char *clk_name;
+       struct resource *mem;
+       struct clk *clk;
+
+       axi_clkgen = devm_kzalloc(&pdev->dev, sizeof(*axi_clkgen), GFP_KERNEL);
+       if (!axi_clkgen)
+               return -ENOMEM;
+
+       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       axi_clkgen->base = devm_ioremap_resource(&pdev->dev, mem);
+       if (IS_ERR(axi_clkgen->base))
+               return PTR_ERR(axi_clkgen->base);
+
+       parent_name = of_clk_get_parent_name(pdev->dev.of_node, 0);
+       if (!parent_name)
+               return -EINVAL;
+
+       clk_name = pdev->dev.of_node->name;
+       of_property_read_string(pdev->dev.of_node, "clock-output-names",
+               &clk_name);
+
+       init.name = clk_name;
+       init.ops = &axi_clkgen_ops;
+       init.flags = 0;
+       init.parent_names = &parent_name;
+       init.num_parents = 1;
+
+       axi_clkgen->clk_hw.init = &init;
+       clk = devm_clk_register(&pdev->dev, &axi_clkgen->clk_hw);
+       if (IS_ERR(clk))
+               return PTR_ERR(clk);
+
+       return of_clk_add_provider(pdev->dev.of_node, of_clk_src_simple_get,
+                                   clk);
+}
+
+static int axi_clkgen_remove(struct platform_device *pdev)
+{
+       of_clk_del_provider(pdev->dev.of_node);
+
+       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",
+               .owner = THIS_MODULE,
+               .of_match_table = axi_clkgen_ids,
+       },
+       .probe = axi_clkgen_probe,
+       .remove = axi_clkgen_remove,
+};
+module_platform_driver(axi_clkgen_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Driver for the Analog Devices' AXI clkgen pcore clock generator");
diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c
new file mode 100644 (file)
index 0000000..a33f46f
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2013 NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+
+#define to_clk_composite(_hw) container_of(_hw, struct clk_composite, hw)
+
+static u8 clk_composite_get_parent(struct clk_hw *hw)
+{
+       struct clk_composite *composite = to_clk_composite(hw);
+       const struct clk_ops *mux_ops = composite->mux_ops;
+       struct clk_hw *mux_hw = composite->mux_hw;
+
+       mux_hw->clk = hw->clk;
+
+       return mux_ops->get_parent(mux_hw);
+}
+
+static int clk_composite_set_parent(struct clk_hw *hw, u8 index)
+{
+       struct clk_composite *composite = to_clk_composite(hw);
+       const struct clk_ops *mux_ops = composite->mux_ops;
+       struct clk_hw *mux_hw = composite->mux_hw;
+
+       mux_hw->clk = hw->clk;
+
+       return mux_ops->set_parent(mux_hw, index);
+}
+
+static unsigned long clk_composite_recalc_rate(struct clk_hw *hw,
+                                           unsigned long parent_rate)
+{
+       struct clk_composite *composite = to_clk_composite(hw);
+       const struct clk_ops *rate_ops = composite->rate_ops;
+       struct clk_hw *rate_hw = composite->rate_hw;
+
+       rate_hw->clk = hw->clk;
+
+       return rate_ops->recalc_rate(rate_hw, parent_rate);
+}
+
+static long clk_composite_round_rate(struct clk_hw *hw, unsigned long rate,
+                                 unsigned long *prate)
+{
+       struct clk_composite *composite = to_clk_composite(hw);
+       const struct clk_ops *rate_ops = composite->rate_ops;
+       struct clk_hw *rate_hw = composite->rate_hw;
+
+       rate_hw->clk = hw->clk;
+
+       return rate_ops->round_rate(rate_hw, rate, prate);
+}
+
+static int clk_composite_set_rate(struct clk_hw *hw, unsigned long rate,
+                              unsigned long parent_rate)
+{
+       struct clk_composite *composite = to_clk_composite(hw);
+       const struct clk_ops *rate_ops = composite->rate_ops;
+       struct clk_hw *rate_hw = composite->rate_hw;
+
+       rate_hw->clk = hw->clk;
+
+       return rate_ops->set_rate(rate_hw, rate, parent_rate);
+}
+
+static int clk_composite_is_enabled(struct clk_hw *hw)
+{
+       struct clk_composite *composite = to_clk_composite(hw);
+       const struct clk_ops *gate_ops = composite->gate_ops;
+       struct clk_hw *gate_hw = composite->gate_hw;
+
+       gate_hw->clk = hw->clk;
+
+       return gate_ops->is_enabled(gate_hw);
+}
+
+static int clk_composite_enable(struct clk_hw *hw)
+{
+       struct clk_composite *composite = to_clk_composite(hw);
+       const struct clk_ops *gate_ops = composite->gate_ops;
+       struct clk_hw *gate_hw = composite->gate_hw;
+
+       gate_hw->clk = hw->clk;
+
+       return gate_ops->enable(gate_hw);
+}
+
+static void clk_composite_disable(struct clk_hw *hw)
+{
+       struct clk_composite *composite = to_clk_composite(hw);
+       const struct clk_ops *gate_ops = composite->gate_ops;
+       struct clk_hw *gate_hw = composite->gate_hw;
+
+       gate_hw->clk = hw->clk;
+
+       gate_ops->disable(gate_hw);
+}
+
+struct clk *clk_register_composite(struct device *dev, const char *name,
+                       const char **parent_names, int num_parents,
+                       struct clk_hw *mux_hw, const struct clk_ops *mux_ops,
+                       struct clk_hw *rate_hw, const struct clk_ops *rate_ops,
+                       struct clk_hw *gate_hw, const struct clk_ops *gate_ops,
+                       unsigned long flags)
+{
+       struct clk *clk;
+       struct clk_init_data init;
+       struct clk_composite *composite;
+       struct clk_ops *clk_composite_ops;
+
+       composite = kzalloc(sizeof(*composite), GFP_KERNEL);
+       if (!composite) {
+               pr_err("%s: could not allocate composite clk\n", __func__);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       init.name = name;
+       init.flags = flags | CLK_IS_BASIC;
+       init.parent_names = parent_names;
+       init.num_parents = num_parents;
+
+       clk_composite_ops = &composite->ops;
+
+       if (mux_hw && mux_ops) {
+               if (!mux_ops->get_parent || !mux_ops->set_parent) {
+                       clk = ERR_PTR(-EINVAL);
+                       goto err;
+               }
+
+               composite->mux_hw = mux_hw;
+               composite->mux_ops = mux_ops;
+               clk_composite_ops->get_parent = clk_composite_get_parent;
+               clk_composite_ops->set_parent = clk_composite_set_parent;
+       }
+
+       if (rate_hw && rate_ops) {
+               if (!rate_ops->recalc_rate) {
+                       clk = ERR_PTR(-EINVAL);
+                       goto err;
+               }
+
+               /* .round_rate is a prerequisite for .set_rate */
+               if (rate_ops->round_rate) {
+                       clk_composite_ops->round_rate = clk_composite_round_rate;
+                       if (rate_ops->set_rate) {
+                               clk_composite_ops->set_rate = clk_composite_set_rate;
+                       }
+               } else {
+                       WARN(rate_ops->set_rate,
+                               "%s: missing round_rate op is required\n",
+                               __func__);
+               }
+
+               composite->rate_hw = rate_hw;
+               composite->rate_ops = rate_ops;
+               clk_composite_ops->recalc_rate = clk_composite_recalc_rate;
+       }
+
+       if (gate_hw && gate_ops) {
+               if (!gate_ops->is_enabled || !gate_ops->enable ||
+                   !gate_ops->disable) {
+                       clk = ERR_PTR(-EINVAL);
+                       goto err;
+               }
+
+               composite->gate_hw = gate_hw;
+               composite->gate_ops = gate_ops;
+               clk_composite_ops->is_enabled = clk_composite_is_enabled;
+               clk_composite_ops->enable = clk_composite_enable;
+               clk_composite_ops->disable = clk_composite_disable;
+       }
+
+       init.ops = clk_composite_ops;
+       composite->hw.init = &init;
+
+       clk = clk_register(dev, &composite->hw);
+       if (IS_ERR(clk))
+               goto err;
+
+       if (composite->mux_hw)
+               composite->mux_hw->clk = clk;
+
+       if (composite->rate_hw)
+               composite->rate_hw->clk = clk;
+
+       if (composite->gate_hw)
+               composite->gate_hw->clk = clk;
+
+       return clk;
+
+err:
+       kfree(composite);
+       return clk;
+}
index 68b4021..6d96741 100644 (file)
@@ -109,8 +109,9 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
 
        div = _get_div(divider, val);
        if (!div) {
-               WARN(1, "%s: Invalid divisor for clock %s\n", __func__,
-                                               __clk_get_name(hw->clk));
+               WARN(!(divider->flags & CLK_DIVIDER_ALLOW_ZERO),
+                       "%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n",
+                       __clk_get_name(hw->clk));
                return parent_rate;
        }
 
index 1ef271e..9ff7d51 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/clk-provider.h>
 #include <linux/slab.h>
 #include <linux/err.h>
+#include <linux/of.h>
 
 /*
  * DOC: basic fixed multiplier and divider clock that cannot gate
@@ -96,3 +97,38 @@ struct clk *clk_register_fixed_factor(struct device *dev, const char *name,
 
        return clk;
 }
+#ifdef CONFIG_OF
+/**
+ * of_fixed_factor_clk_setup() - Setup function for simple fixed factor clock
+ */
+void __init of_fixed_factor_clk_setup(struct device_node *node)
+{
+       struct clk *clk;
+       const char *clk_name = node->name;
+       const char *parent_name;
+       u32 div, mult;
+
+       if (of_property_read_u32(node, "clock-div", &div)) {
+               pr_err("%s Fixed factor clock <%s> must have a clock-div property\n",
+                       __func__, node->name);
+               return;
+       }
+
+       if (of_property_read_u32(node, "clock-mult", &mult)) {
+               pr_err("%s Fixed factor clock <%s> must have a clokc-mult property\n",
+                       __func__, node->name);
+               return;
+       }
+
+       of_property_read_string(node, "clock-output-names", &clk_name);
+       parent_name = of_clk_get_parent_name(node, 0);
+
+       clk = clk_register_fixed_factor(NULL, clk_name, parent_name, 0,
+                                       mult, div);
+       if (!IS_ERR(clk))
+               of_clk_add_provider(node, of_clk_src_simple_get, clk);
+}
+EXPORT_SYMBOL_GPL(of_fixed_factor_clk_setup);
+CLK_OF_DECLARE(fixed_factor_clk, "fixed-factor-clock",
+               of_fixed_factor_clk_setup);
+#endif
index 508c032..25b1734 100644 (file)
@@ -32,6 +32,7 @@
 static u8 clk_mux_get_parent(struct clk_hw *hw)
 {
        struct clk_mux *mux = to_clk_mux(hw);
+       int num_parents = __clk_get_num_parents(hw->clk);
        u32 val;
 
        /*
@@ -42,7 +43,16 @@ static u8 clk_mux_get_parent(struct clk_hw *hw)
         * val = 0x4 really means "bit 2, index starts at bit 0"
         */
        val = readl(mux->reg) >> mux->shift;
-       val &= (1 << mux->width) - 1;
+       val &= mux->mask;
+
+       if (mux->table) {
+               int i;
+
+               for (i = 0; i < num_parents; i++)
+                       if (mux->table[i] == val)
+                               return i;
+               return -EINVAL;
+       }
 
        if (val && (mux->flags & CLK_MUX_INDEX_BIT))
                val = ffs(val) - 1;
@@ -50,7 +60,7 @@ static u8 clk_mux_get_parent(struct clk_hw *hw)
        if (val && (mux->flags & CLK_MUX_INDEX_ONE))
                val--;
 
-       if (val >= __clk_get_num_parents(hw->clk))
+       if (val >= num_parents)
                return -EINVAL;
 
        return val;
@@ -62,17 +72,22 @@ static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
        u32 val;
        unsigned long flags = 0;
 
-       if (mux->flags & CLK_MUX_INDEX_BIT)
-               index = (1 << ffs(index));
+       if (mux->table)
+               index = mux->table[index];
 
-       if (mux->flags & CLK_MUX_INDEX_ONE)
-               index++;
+       else {
+               if (mux->flags & CLK_MUX_INDEX_BIT)
+                       index = (1 << ffs(index));
+
+               if (mux->flags & CLK_MUX_INDEX_ONE)
+                       index++;
+       }
 
        if (mux->lock)
                spin_lock_irqsave(mux->lock, flags);
 
        val = readl(mux->reg);
-       val &= ~(((1 << mux->width) - 1) << mux->shift);
+       val &= ~(mux->mask << mux->shift);
        val |= index << mux->shift;
        writel(val, mux->reg);
 
@@ -88,10 +103,10 @@ const struct clk_ops clk_mux_ops = {
 };
 EXPORT_SYMBOL_GPL(clk_mux_ops);
 
-struct clk *clk_register_mux(struct device *dev, const char *name,
+struct clk *clk_register_mux_table(struct device *dev, const char *name,
                const char **parent_names, u8 num_parents, unsigned long flags,
-               void __iomem *reg, u8 shift, u8 width,
-               u8 clk_mux_flags, spinlock_t *lock)
+               void __iomem *reg, u8 shift, u32 mask,
+               u8 clk_mux_flags, u32 *table, spinlock_t *lock)
 {
        struct clk_mux *mux;
        struct clk *clk;
@@ -113,9 +128,10 @@ struct clk *clk_register_mux(struct device *dev, const char *name,
        /* struct clk_mux assignments */
        mux->reg = reg;
        mux->shift = shift;
-       mux->width = width;
+       mux->mask = mask;
        mux->flags = clk_mux_flags;
        mux->lock = lock;
+       mux->table = table;
        mux->hw.init = &init;
 
        clk = clk_register(dev, &mux->hw);
@@ -125,3 +141,15 @@ struct clk *clk_register_mux(struct device *dev, const char *name,
 
        return clk;
 }
+
+struct clk *clk_register_mux(struct device *dev, const char *name,
+               const char **parent_names, u8 num_parents, unsigned long flags,
+               void __iomem *reg, u8 shift, u8 width,
+               u8 clk_mux_flags, spinlock_t *lock)
+{
+       u32 mask = BIT(width) - 1;
+
+       return clk_register_mux_table(dev, name, parent_names, num_parents,
+                                     flags, reg, shift, mask, clk_mux_flags,
+                                     NULL, lock);
+}
index f8e9d0c..643ca65 100644 (file)
@@ -1113,7 +1113,7 @@ void __init sirfsoc_of_clk_init(void)
 
        for (i = pll1; i < maxclk; i++) {
                prima2_clks[i] = clk_register(NULL, prima2_clk_hw_array[i]);
-               BUG_ON(!prima2_clks[i]);
+               BUG_ON(IS_ERR(prima2_clks[i]));
        }
        clk_register_clkdev(prima2_clks[cpu], NULL, "cpu");
        clk_register_clkdev(prima2_clks[io],  NULL, "io");
diff --git a/drivers/clk/clk-si5351.c b/drivers/clk/clk-si5351.c
new file mode 100644 (file)
index 0000000..8927284
--- /dev/null
@@ -0,0 +1,1510 @@
+/*
+ * clk-si5351.c: Silicon Laboratories Si5351A/B/C I2C Clock Generator
+ *
+ * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
+ * Rabeeh Khoury <rabeeh@solid-run.com>
+ *
+ * References:
+ * [1] "Si5351A/B/C Data Sheet"
+ *     http://www.silabs.com/Support%20Documents/TechnicalDocs/Si5351.pdf
+ * [2] "Manually Generating an Si5351 Register Map"
+ *     http://www.silabs.com/Support%20Documents/TechnicalDocs/AN619.pdf
+ *
+ * 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/module.h>
+#include <linux/kernel.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/rational.h>
+#include <linux/i2c.h>
+#include <linux/of_platform.h>
+#include <linux/platform_data/si5351.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <asm/div64.h>
+
+#include "clk-si5351.h"
+
+struct si5351_driver_data;
+
+struct si5351_parameters {
+       unsigned long   p1;
+       unsigned long   p2;
+       unsigned long   p3;
+       int             valid;
+};
+
+struct si5351_hw_data {
+       struct clk_hw                   hw;
+       struct si5351_driver_data       *drvdata;
+       struct si5351_parameters        params;
+       unsigned char                   num;
+};
+
+struct si5351_driver_data {
+       enum si5351_variant     variant;
+       struct i2c_client       *client;
+       struct regmap           *regmap;
+       struct clk_onecell_data onecell;
+
+       struct clk              *pxtal;
+       const char              *pxtal_name;
+       struct clk_hw           xtal;
+       struct clk              *pclkin;
+       const char              *pclkin_name;
+       struct clk_hw           clkin;
+
+       struct si5351_hw_data   pll[2];
+       struct si5351_hw_data   *msynth;
+       struct si5351_hw_data   *clkout;
+};
+
+static const char const *si5351_input_names[] = {
+       "xtal", "clkin"
+};
+static const char const *si5351_pll_names[] = {
+       "plla", "pllb", "vxco"
+};
+static const char const *si5351_msynth_names[] = {
+       "ms0", "ms1", "ms2", "ms3", "ms4", "ms5", "ms6", "ms7"
+};
+static const char const *si5351_clkout_names[] = {
+       "clk0", "clk1", "clk2", "clk3", "clk4", "clk5", "clk6", "clk7"
+};
+
+/*
+ * Si5351 i2c regmap
+ */
+static inline u8 si5351_reg_read(struct si5351_driver_data *drvdata, u8 reg)
+{
+       u32 val;
+       int ret;
+
+       ret = regmap_read(drvdata->regmap, reg, &val);
+       if (ret) {
+               dev_err(&drvdata->client->dev,
+                       "unable to read from reg%02x\n", reg);
+               return 0;
+       }
+
+       return (u8)val;
+}
+
+static inline int si5351_bulk_read(struct si5351_driver_data *drvdata,
+                                  u8 reg, u8 count, u8 *buf)
+{
+       return regmap_bulk_read(drvdata->regmap, reg, buf, count);
+}
+
+static inline int si5351_reg_write(struct si5351_driver_data *drvdata,
+                                  u8 reg, u8 val)
+{
+       return regmap_write(drvdata->regmap, reg, val);
+}
+
+static inline int si5351_bulk_write(struct si5351_driver_data *drvdata,
+                                   u8 reg, u8 count, const u8 *buf)
+{
+       return regmap_raw_write(drvdata->regmap, reg, buf, count);
+}
+
+static inline int si5351_set_bits(struct si5351_driver_data *drvdata,
+                                 u8 reg, u8 mask, u8 val)
+{
+       return regmap_update_bits(drvdata->regmap, reg, mask, val);
+}
+
+static inline u8 si5351_msynth_params_address(int num)
+{
+       if (num > 5)
+               return SI5351_CLK6_PARAMETERS + (num - 6);
+       return SI5351_CLK0_PARAMETERS + (SI5351_PARAMETERS_LENGTH * num);
+}
+
+static void si5351_read_parameters(struct si5351_driver_data *drvdata,
+                                  u8 reg, struct si5351_parameters *params)
+{
+       u8 buf[SI5351_PARAMETERS_LENGTH];
+
+       switch (reg) {
+       case SI5351_CLK6_PARAMETERS:
+       case SI5351_CLK7_PARAMETERS:
+               buf[0] = si5351_reg_read(drvdata, reg);
+               params->p1 = buf[0];
+               params->p2 = 0;
+               params->p3 = 1;
+               break;
+       default:
+               si5351_bulk_read(drvdata, reg, SI5351_PARAMETERS_LENGTH, buf);
+               params->p1 = ((buf[2] & 0x03) << 16) | (buf[3] << 8) | buf[4];
+               params->p2 = ((buf[5] & 0x0f) << 16) | (buf[6] << 8) | buf[7];
+               params->p3 = ((buf[5] & 0xf0) << 12) | (buf[0] << 8) | buf[1];
+       }
+       params->valid = 1;
+}
+
+static void si5351_write_parameters(struct si5351_driver_data *drvdata,
+                                   u8 reg, struct si5351_parameters *params)
+{
+       u8 buf[SI5351_PARAMETERS_LENGTH];
+
+       switch (reg) {
+       case SI5351_CLK6_PARAMETERS:
+       case SI5351_CLK7_PARAMETERS:
+               buf[0] = params->p1 & 0xff;
+               si5351_reg_write(drvdata, reg, buf[0]);
+               break;
+       default:
+               buf[0] = ((params->p3 & 0x0ff00) >> 8) & 0xff;
+               buf[1] = params->p3 & 0xff;
+               /* save rdiv and divby4 */
+               buf[2] = si5351_reg_read(drvdata, reg + 2) & ~0x03;
+               buf[2] |= ((params->p1 & 0x30000) >> 16) & 0x03;
+               buf[3] = ((params->p1 & 0x0ff00) >> 8) & 0xff;
+               buf[4] = params->p1 & 0xff;
+               buf[5] = ((params->p3 & 0xf0000) >> 12) |
+                       ((params->p2 & 0xf0000) >> 16);
+               buf[6] = ((params->p2 & 0x0ff00) >> 8) & 0xff;
+               buf[7] = params->p2 & 0xff;
+               si5351_bulk_write(drvdata, reg, SI5351_PARAMETERS_LENGTH, buf);
+       }
+}
+
+static bool si5351_regmap_is_volatile(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case SI5351_DEVICE_STATUS:
+       case SI5351_INTERRUPT_STATUS:
+       case SI5351_PLL_RESET:
+               return true;
+       }
+       return false;
+}
+
+static bool si5351_regmap_is_writeable(struct device *dev, unsigned int reg)
+{
+       /* reserved registers */
+       if (reg >= 4 && reg <= 8)
+               return false;
+       if (reg >= 10 && reg <= 14)
+               return false;
+       if (reg >= 173 && reg <= 176)
+               return false;
+       if (reg >= 178 && reg <= 182)
+               return false;
+       /* read-only */
+       if (reg == SI5351_DEVICE_STATUS)
+               return false;
+       return true;
+}
+
+static struct regmap_config si5351_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+       .cache_type = REGCACHE_RBTREE,
+       .max_register = 187,
+       .writeable_reg = si5351_regmap_is_writeable,
+       .volatile_reg = si5351_regmap_is_volatile,
+};
+
+/*
+ * Si5351 xtal clock input
+ */
+static int si5351_xtal_prepare(struct clk_hw *hw)
+{
+       struct si5351_driver_data *drvdata =
+               container_of(hw, struct si5351_driver_data, xtal);
+       si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE,
+                       SI5351_XTAL_ENABLE, SI5351_XTAL_ENABLE);
+       return 0;
+}
+
+static void si5351_xtal_unprepare(struct clk_hw *hw)
+{
+       struct si5351_driver_data *drvdata =
+               container_of(hw, struct si5351_driver_data, xtal);
+       si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE,
+                       SI5351_XTAL_ENABLE, 0);
+}
+
+static const struct clk_ops si5351_xtal_ops = {
+       .prepare = si5351_xtal_prepare,
+       .unprepare = si5351_xtal_unprepare,
+};
+
+/*
+ * Si5351 clkin clock input (Si5351C only)
+ */
+static int si5351_clkin_prepare(struct clk_hw *hw)
+{
+       struct si5351_driver_data *drvdata =
+               container_of(hw, struct si5351_driver_data, clkin);
+       si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE,
+                       SI5351_CLKIN_ENABLE, SI5351_CLKIN_ENABLE);
+       return 0;
+}
+
+static void si5351_clkin_unprepare(struct clk_hw *hw)
+{
+       struct si5351_driver_data *drvdata =
+               container_of(hw, struct si5351_driver_data, clkin);
+       si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE,
+                       SI5351_CLKIN_ENABLE, 0);
+}
+
+/*
+ * CMOS clock source constraints:
+ * The input frequency range of the PLL is 10Mhz to 40MHz.
+ * If CLKIN is >40MHz, the input divider must be used.
+ */
+static unsigned long si5351_clkin_recalc_rate(struct clk_hw *hw,
+                                             unsigned long parent_rate)
+{
+       struct si5351_driver_data *drvdata =
+               container_of(hw, struct si5351_driver_data, clkin);
+       unsigned long rate;
+       unsigned char idiv;
+
+       rate = parent_rate;
+       if (parent_rate > 160000000) {
+               idiv = SI5351_CLKIN_DIV_8;
+               rate /= 8;
+       } else if (parent_rate > 80000000) {
+               idiv = SI5351_CLKIN_DIV_4;
+               rate /= 4;
+       } else if (parent_rate > 40000000) {
+               idiv = SI5351_CLKIN_DIV_2;
+               rate /= 2;
+       } else {
+               idiv = SI5351_CLKIN_DIV_1;
+       }
+
+       si5351_set_bits(drvdata, SI5351_PLL_INPUT_SOURCE,
+                       SI5351_CLKIN_DIV_MASK, idiv);
+
+       dev_dbg(&drvdata->client->dev, "%s - clkin div = %d, rate = %lu\n",
+               __func__, (1 << (idiv >> 6)), rate);
+
+       return rate;
+}
+
+static const struct clk_ops si5351_clkin_ops = {
+       .prepare = si5351_clkin_prepare,
+       .unprepare = si5351_clkin_unprepare,
+       .recalc_rate = si5351_clkin_recalc_rate,
+};
+
+/*
+ * Si5351 vxco clock input (Si5351B only)
+ */
+
+static int si5351_vxco_prepare(struct clk_hw *hw)
+{
+       struct si5351_hw_data *hwdata =
+               container_of(hw, struct si5351_hw_data, hw);
+
+       dev_warn(&hwdata->drvdata->client->dev, "VXCO currently unsupported\n");
+
+       return 0;
+}
+
+static void si5351_vxco_unprepare(struct clk_hw *hw)
+{
+}
+
+static unsigned long si5351_vxco_recalc_rate(struct clk_hw *hw,
+                                            unsigned long parent_rate)
+{
+       return 0;
+}
+
+static int si5351_vxco_set_rate(struct clk_hw *hw, unsigned long rate,
+                               unsigned long parent)
+{
+       return 0;
+}
+
+static const struct clk_ops si5351_vxco_ops = {
+       .prepare = si5351_vxco_prepare,
+       .unprepare = si5351_vxco_unprepare,
+       .recalc_rate = si5351_vxco_recalc_rate,
+       .set_rate = si5351_vxco_set_rate,
+};
+
+/*
+ * Si5351 pll a/b
+ *
+ * Feedback Multisynth Divider Equations [2]
+ *
+ * fVCO = fIN * (a + b/c)
+ *
+ * with 15 + 0/1048575 <= (a + b/c) <= 90 + 0/1048575 and
+ * fIN = fXTAL or fIN = fCLKIN/CLKIN_DIV
+ *
+ * Feedback Multisynth Register Equations
+ *
+ * (1) MSNx_P1[17:0] = 128 * a + floor(128 * b/c) - 512
+ * (2) MSNx_P2[19:0] = 128 * b - c * floor(128 * b/c) = (128*b) mod c
+ * (3) MSNx_P3[19:0] = c
+ *
+ * Transposing (2) yields: (4) floor(128 * b/c) = (128 * b / MSNx_P2)/c
+ *
+ * Using (4) on (1) yields:
+ * MSNx_P1 = 128 * a + (128 * b/MSNx_P2)/c - 512
+ * MSNx_P1 + 512 + MSNx_P2/c = 128 * a + 128 * b/c
+ *
+ * a + b/c = (MSNx_P1 + MSNx_P2/MSNx_P3 + 512)/128
+ *         = (MSNx_P1*MSNx_P3 + MSNx_P2 + 512*MSNx_P3)/(128*MSNx_P3)
+ *
+ */
+static int _si5351_pll_reparent(struct si5351_driver_data *drvdata,
+                               int num, enum si5351_pll_src parent)
+{
+       u8 mask = (num == 0) ? SI5351_PLLA_SOURCE : SI5351_PLLB_SOURCE;
+
+       if (parent == SI5351_PLL_SRC_DEFAULT)
+               return 0;
+
+       if (num > 2)
+               return -EINVAL;
+
+       if (drvdata->variant != SI5351_VARIANT_C &&
+           parent != SI5351_PLL_SRC_XTAL)
+               return -EINVAL;
+
+       si5351_set_bits(drvdata, SI5351_PLL_INPUT_SOURCE, mask,
+                       (parent == SI5351_PLL_SRC_XTAL) ? 0 : mask);
+       return 0;
+}
+
+static unsigned char si5351_pll_get_parent(struct clk_hw *hw)
+{
+       struct si5351_hw_data *hwdata =
+               container_of(hw, struct si5351_hw_data, hw);
+       u8 mask = (hwdata->num == 0) ? SI5351_PLLA_SOURCE : SI5351_PLLB_SOURCE;
+       u8 val;
+
+       val = si5351_reg_read(hwdata->drvdata, SI5351_PLL_INPUT_SOURCE);
+
+       return (val & mask) ? 1 : 0;
+}
+
+static int si5351_pll_set_parent(struct clk_hw *hw, u8 index)
+{
+       struct si5351_hw_data *hwdata =
+               container_of(hw, struct si5351_hw_data, hw);
+
+       if (hwdata->drvdata->variant != SI5351_VARIANT_C &&
+           index > 0)
+               return -EPERM;
+
+       if (index > 1)
+               return -EINVAL;
+
+       return _si5351_pll_reparent(hwdata->drvdata, hwdata->num,
+                            (index == 0) ? SI5351_PLL_SRC_XTAL :
+                            SI5351_PLL_SRC_CLKIN);
+}
+
+static unsigned long si5351_pll_recalc_rate(struct clk_hw *hw,
+                                           unsigned long parent_rate)
+{
+       struct si5351_hw_data *hwdata =
+               container_of(hw, struct si5351_hw_data, hw);
+       u8 reg = (hwdata->num == 0) ? SI5351_PLLA_PARAMETERS :
+               SI5351_PLLB_PARAMETERS;
+       unsigned long long rate;
+
+       if (!hwdata->params.valid)
+               si5351_read_parameters(hwdata->drvdata, reg, &hwdata->params);
+
+       if (hwdata->params.p3 == 0)
+               return parent_rate;
+
+       /* fVCO = fIN * (P1*P3 + 512*P3 + P2)/(128*P3) */
+       rate  = hwdata->params.p1 * hwdata->params.p3;
+       rate += 512 * hwdata->params.p3;
+       rate += hwdata->params.p2;
+       rate *= parent_rate;
+       do_div(rate, 128 * hwdata->params.p3);
+
+       dev_dbg(&hwdata->drvdata->client->dev,
+               "%s - %s: p1 = %lu, p2 = %lu, p3 = %lu, parent_rate = %lu, rate = %lu\n",
+               __func__, __clk_get_name(hwdata->hw.clk),
+               hwdata->params.p1, hwdata->params.p2, hwdata->params.p3,
+               parent_rate, (unsigned long)rate);
+
+       return (unsigned long)rate;
+}
+
+static long si5351_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+                                 unsigned long *parent_rate)
+{
+       struct si5351_hw_data *hwdata =
+               container_of(hw, struct si5351_hw_data, hw);
+       unsigned long rfrac, denom, a, b, c;
+       unsigned long long lltmp;
+
+       if (rate < SI5351_PLL_VCO_MIN)
+               rate = SI5351_PLL_VCO_MIN;
+       if (rate > SI5351_PLL_VCO_MAX)
+               rate = SI5351_PLL_VCO_MAX;
+
+       /* determine integer part of feedback equation */
+       a = rate / *parent_rate;
+
+       if (a < SI5351_PLL_A_MIN)
+               rate = *parent_rate * SI5351_PLL_A_MIN;
+       if (a > SI5351_PLL_A_MAX)
+               rate = *parent_rate * SI5351_PLL_A_MAX;
+
+       /* find best approximation for b/c = fVCO mod fIN */
+       denom = 1000 * 1000;
+       lltmp = rate % (*parent_rate);
+       lltmp *= denom;
+       do_div(lltmp, *parent_rate);
+       rfrac = (unsigned long)lltmp;
+
+       b = 0;
+       c = 1;
+       if (rfrac)
+               rational_best_approximation(rfrac, denom,
+                                   SI5351_PLL_B_MAX, SI5351_PLL_C_MAX, &b, &c);
+
+       /* calculate parameters */
+       hwdata->params.p3  = c;
+       hwdata->params.p2  = (128 * b) % c;
+       hwdata->params.p1  = 128 * a;
+       hwdata->params.p1 += (128 * b / c);
+       hwdata->params.p1 -= 512;
+
+       /* recalculate rate by fIN * (a + b/c) */
+       lltmp  = *parent_rate;
+       lltmp *= b;
+       do_div(lltmp, c);
+
+       rate  = (unsigned long)lltmp;
+       rate += *parent_rate * a;
+
+       dev_dbg(&hwdata->drvdata->client->dev,
+               "%s - %s: a = %lu, b = %lu, c = %lu, parent_rate = %lu, rate = %lu\n",
+               __func__, __clk_get_name(hwdata->hw.clk), a, b, c,
+               *parent_rate, rate);
+
+       return rate;
+}
+
+static int si5351_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+                              unsigned long parent_rate)
+{
+       struct si5351_hw_data *hwdata =
+               container_of(hw, struct si5351_hw_data, hw);
+       u8 reg = (hwdata->num == 0) ? SI5351_PLLA_PARAMETERS :
+               SI5351_PLLB_PARAMETERS;
+
+       /* write multisynth parameters */
+       si5351_write_parameters(hwdata->drvdata, reg, &hwdata->params);
+
+       /* plla/pllb ctrl is in clk6/clk7 ctrl registers */
+       si5351_set_bits(hwdata->drvdata, SI5351_CLK6_CTRL + hwdata->num,
+               SI5351_CLK_INTEGER_MODE,
+               (hwdata->params.p2 == 0) ? SI5351_CLK_INTEGER_MODE : 0);
+
+       dev_dbg(&hwdata->drvdata->client->dev,
+               "%s - %s: p1 = %lu, p2 = %lu, p3 = %lu, parent_rate = %lu, rate = %lu\n",
+               __func__, __clk_get_name(hwdata->hw.clk),
+               hwdata->params.p1, hwdata->params.p2, hwdata->params.p3,
+               parent_rate, rate);
+
+       return 0;
+}
+
+static const struct clk_ops si5351_pll_ops = {
+       .set_parent = si5351_pll_set_parent,
+       .get_parent = si5351_pll_get_parent,
+       .recalc_rate = si5351_pll_recalc_rate,
+       .round_rate = si5351_pll_round_rate,
+       .set_rate = si5351_pll_set_rate,
+};
+
+/*
+ * Si5351 multisync divider
+ *
+ * for fOUT <= 150 MHz:
+ *
+ * fOUT = (fIN * (a + b/c)) / CLKOUTDIV
+ *
+ * with 6 + 0/1048575 <= (a + b/c) <= 1800 + 0/1048575 and
+ * fIN = fVCO0, fVCO1
+ *
+ * Output Clock Multisynth Register Equations
+ *
+ * MSx_P1[17:0] = 128 * a + floor(128 * b/c) - 512
+ * MSx_P2[19:0] = 128 * b - c * floor(128 * b/c) = (128*b) mod c
+ * MSx_P3[19:0] = c
+ *
+ * MS[6,7] are integer (P1) divide only, P2 = 0, P3 = 0
+ *
+ * for 150MHz < fOUT <= 160MHz:
+ *
+ * MSx_P1 = 0, MSx_P2 = 0, MSx_P3 = 1, MSx_INT = 1, MSx_DIVBY4 = 11b
+ */
+static int _si5351_msynth_reparent(struct si5351_driver_data *drvdata,
+                                  int num, enum si5351_multisynth_src parent)
+{
+       if (parent == SI5351_MULTISYNTH_SRC_DEFAULT)
+               return 0;
+
+       if (num > 8)
+               return -EINVAL;
+
+       si5351_set_bits(drvdata, SI5351_CLK0_CTRL + num, SI5351_CLK_PLL_SELECT,
+                       (parent == SI5351_MULTISYNTH_SRC_VCO0) ? 0 :
+                       SI5351_CLK_PLL_SELECT);
+       return 0;
+}
+
+static unsigned char si5351_msynth_get_parent(struct clk_hw *hw)
+{
+       struct si5351_hw_data *hwdata =
+               container_of(hw, struct si5351_hw_data, hw);
+       u8 val;
+
+       val = si5351_reg_read(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num);
+
+       return (val & SI5351_CLK_PLL_SELECT) ? 1 : 0;
+}
+
+static int si5351_msynth_set_parent(struct clk_hw *hw, u8 index)
+{
+       struct si5351_hw_data *hwdata =
+               container_of(hw, struct si5351_hw_data, hw);
+
+       return _si5351_msynth_reparent(hwdata->drvdata, hwdata->num,
+                              (index == 0) ? SI5351_MULTISYNTH_SRC_VCO0 :
+                              SI5351_MULTISYNTH_SRC_VCO1);
+}
+
+static unsigned long si5351_msynth_recalc_rate(struct clk_hw *hw,
+                                              unsigned long parent_rate)
+{
+       struct si5351_hw_data *hwdata =
+               container_of(hw, struct si5351_hw_data, hw);
+       u8 reg = si5351_msynth_params_address(hwdata->num);
+       unsigned long long rate;
+       unsigned long m;
+
+       if (!hwdata->params.valid)
+               si5351_read_parameters(hwdata->drvdata, reg, &hwdata->params);
+
+       if (hwdata->params.p3 == 0)
+               return parent_rate;
+
+       /*
+        * multisync0-5: fOUT = (128 * P3 * fIN) / (P1*P3 + P2 + 512*P3)
+        * multisync6-7: fOUT = fIN / P1
+        */
+       rate = parent_rate;
+       if (hwdata->num > 5) {
+               m = hwdata->params.p1;
+       } else if ((si5351_reg_read(hwdata->drvdata, reg + 2) &
+                   SI5351_OUTPUT_CLK_DIVBY4) == SI5351_OUTPUT_CLK_DIVBY4) {
+               m = 4;
+       } else {
+               rate *= 128 * hwdata->params.p3;
+               m = hwdata->params.p1 * hwdata->params.p3;
+               m += hwdata->params.p2;
+               m += 512 * hwdata->params.p3;
+       }
+
+       if (m == 0)
+               return 0;
+       do_div(rate, m);
+
+       dev_dbg(&hwdata->drvdata->client->dev,
+               "%s - %s: p1 = %lu, p2 = %lu, p3 = %lu, m = %lu, parent_rate = %lu, rate = %lu\n",
+               __func__, __clk_get_name(hwdata->hw.clk),
+               hwdata->params.p1, hwdata->params.p2, hwdata->params.p3,
+               m, parent_rate, (unsigned long)rate);
+
+       return (unsigned long)rate;
+}
+
+static long si5351_msynth_round_rate(struct clk_hw *hw, unsigned long rate,
+                                    unsigned long *parent_rate)
+{
+       struct si5351_hw_data *hwdata =
+               container_of(hw, struct si5351_hw_data, hw);
+       unsigned long long lltmp;
+       unsigned long a, b, c;
+       int divby4;
+
+       /* multisync6-7 can only handle freqencies < 150MHz */
+       if (hwdata->num >= 6 && rate > SI5351_MULTISYNTH67_MAX_FREQ)
+               rate = SI5351_MULTISYNTH67_MAX_FREQ;
+
+       /* multisync frequency is 1MHz .. 160MHz */
+       if (rate > SI5351_MULTISYNTH_MAX_FREQ)
+               rate = SI5351_MULTISYNTH_MAX_FREQ;
+       if (rate < SI5351_MULTISYNTH_MIN_FREQ)
+               rate = SI5351_MULTISYNTH_MIN_FREQ;
+
+       divby4 = 0;
+       if (rate > SI5351_MULTISYNTH_DIVBY4_FREQ)
+               divby4 = 1;
+
+       /* multisync can set pll */
+       if (__clk_get_flags(hwdata->hw.clk) & CLK_SET_RATE_PARENT) {
+               /*
+                * find largest integer divider for max
+                * vco frequency and given target rate
+                */
+               if (divby4 == 0) {
+                       lltmp = SI5351_PLL_VCO_MAX;
+                       do_div(lltmp, rate);
+                       a = (unsigned long)lltmp;
+               } else
+                       a = 4;
+
+               b = 0;
+               c = 1;
+
+               *parent_rate = a * rate;
+       } else {
+               unsigned long rfrac, denom;
+
+               /* disable divby4 */
+               if (divby4) {
+                       rate = SI5351_MULTISYNTH_DIVBY4_FREQ;
+                       divby4 = 0;
+               }
+
+               /* determine integer part of divider equation */
+               a = *parent_rate / rate;
+               if (a < SI5351_MULTISYNTH_A_MIN)
+                       a = SI5351_MULTISYNTH_A_MIN;
+               if (hwdata->num >= 6 && a > SI5351_MULTISYNTH67_A_MAX)
+                       a = SI5351_MULTISYNTH67_A_MAX;
+               else if (a > SI5351_MULTISYNTH_A_MAX)
+                       a = SI5351_MULTISYNTH_A_MAX;
+
+               /* find best approximation for b/c = fVCO mod fOUT */
+               denom = 1000 * 1000;
+               lltmp = (*parent_rate) % rate;
+               lltmp *= denom;
+               do_div(lltmp, rate);
+               rfrac = (unsigned long)lltmp;
+
+               b = 0;
+               c = 1;
+               if (rfrac)
+                       rational_best_approximation(rfrac, denom,
+                           SI5351_MULTISYNTH_B_MAX, SI5351_MULTISYNTH_C_MAX,
+                           &b, &c);
+       }
+
+       /* recalculate rate by fOUT = fIN / (a + b/c) */
+       lltmp  = *parent_rate;
+       lltmp *= c;
+       do_div(lltmp, a * c + b);
+       rate  = (unsigned long)lltmp;
+
+       /* calculate parameters */
+       if (divby4) {
+               hwdata->params.p3 = 1;
+               hwdata->params.p2 = 0;
+               hwdata->params.p1 = 0;
+       } else {
+               hwdata->params.p3  = c;
+               hwdata->params.p2  = (128 * b) % c;
+               hwdata->params.p1  = 128 * a;
+               hwdata->params.p1 += (128 * b / c);
+               hwdata->params.p1 -= 512;
+       }
+
+       dev_dbg(&hwdata->drvdata->client->dev,
+               "%s - %s: a = %lu, b = %lu, c = %lu, divby4 = %d, parent_rate = %lu, rate = %lu\n",
+               __func__, __clk_get_name(hwdata->hw.clk), a, b, c, divby4,
+               *parent_rate, rate);
+
+       return rate;
+}
+
+static int si5351_msynth_set_rate(struct clk_hw *hw, unsigned long rate,
+                                 unsigned long parent_rate)
+{
+       struct si5351_hw_data *hwdata =
+               container_of(hw, struct si5351_hw_data, hw);
+       u8 reg = si5351_msynth_params_address(hwdata->num);
+       int divby4 = 0;
+
+       /* write multisynth parameters */
+       si5351_write_parameters(hwdata->drvdata, reg, &hwdata->params);
+
+       if (rate > SI5351_MULTISYNTH_DIVBY4_FREQ)
+               divby4 = 1;
+
+       /* enable/disable integer mode and divby4 on multisynth0-5 */
+       if (hwdata->num < 6) {
+               si5351_set_bits(hwdata->drvdata, reg + 2,
+                               SI5351_OUTPUT_CLK_DIVBY4,
+                               (divby4) ? SI5351_OUTPUT_CLK_DIVBY4 : 0);
+               si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num,
+                       SI5351_CLK_INTEGER_MODE,
+                       (hwdata->params.p2 == 0) ? SI5351_CLK_INTEGER_MODE : 0);
+       }
+
+       dev_dbg(&hwdata->drvdata->client->dev,
+               "%s - %s: p1 = %lu, p2 = %lu, p3 = %lu, divby4 = %d, parent_rate = %lu, rate = %lu\n",
+               __func__, __clk_get_name(hwdata->hw.clk),
+               hwdata->params.p1, hwdata->params.p2, hwdata->params.p3,
+               divby4, parent_rate, rate);
+
+       return 0;
+}
+
+static const struct clk_ops si5351_msynth_ops = {
+       .set_parent = si5351_msynth_set_parent,
+       .get_parent = si5351_msynth_get_parent,
+       .recalc_rate = si5351_msynth_recalc_rate,
+       .round_rate = si5351_msynth_round_rate,
+       .set_rate = si5351_msynth_set_rate,
+};
+
+/*
+ * Si5351 clkout divider
+ */
+static int _si5351_clkout_reparent(struct si5351_driver_data *drvdata,
+                                  int num, enum si5351_clkout_src parent)
+{
+       u8 val;
+
+       if (num > 8)
+               return -EINVAL;
+
+       switch (parent) {
+       case SI5351_CLKOUT_SRC_MSYNTH_N:
+               val = SI5351_CLK_INPUT_MULTISYNTH_N;
+               break;
+       case SI5351_CLKOUT_SRC_MSYNTH_0_4:
+               /* clk0/clk4 can only connect to its own multisync */
+               if (num == 0 || num == 4)
+                       val = SI5351_CLK_INPUT_MULTISYNTH_N;
+               else
+                       val = SI5351_CLK_INPUT_MULTISYNTH_0_4;
+               break;
+       case SI5351_CLKOUT_SRC_XTAL:
+               val = SI5351_CLK_INPUT_XTAL;
+               break;
+       case SI5351_CLKOUT_SRC_CLKIN:
+               if (drvdata->variant != SI5351_VARIANT_C)
+                       return -EINVAL;
+
+               val = SI5351_CLK_INPUT_CLKIN;
+               break;
+       default:
+               return 0;
+       }
+
+       si5351_set_bits(drvdata, SI5351_CLK0_CTRL + num,
+                       SI5351_CLK_INPUT_MASK, val);
+       return 0;
+}
+
+static int _si5351_clkout_set_drive_strength(
+       struct si5351_driver_data *drvdata, int num,
+       enum si5351_drive_strength drive)
+{
+       u8 mask;
+
+       if (num > 8)
+               return -EINVAL;
+
+       switch (drive) {
+       case SI5351_DRIVE_2MA:
+               mask = SI5351_CLK_DRIVE_STRENGTH_2MA;
+               break;
+       case SI5351_DRIVE_4MA:
+               mask = SI5351_CLK_DRIVE_STRENGTH_4MA;
+               break;
+       case SI5351_DRIVE_6MA:
+               mask = SI5351_CLK_DRIVE_STRENGTH_6MA;
+               break;
+       case SI5351_DRIVE_8MA:
+               mask = SI5351_CLK_DRIVE_STRENGTH_8MA;
+               break;
+       default:
+               return 0;
+       }
+
+       si5351_set_bits(drvdata, SI5351_CLK0_CTRL + num,
+                       SI5351_CLK_DRIVE_STRENGTH_MASK, mask);
+       return 0;
+}
+
+static int si5351_clkout_prepare(struct clk_hw *hw)
+{
+       struct si5351_hw_data *hwdata =
+               container_of(hw, struct si5351_hw_data, hw);
+
+       si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num,
+                       SI5351_CLK_POWERDOWN, 0);
+       si5351_set_bits(hwdata->drvdata, SI5351_OUTPUT_ENABLE_CTRL,
+                       (1 << hwdata->num), 0);
+       return 0;
+}
+
+static void si5351_clkout_unprepare(struct clk_hw *hw)
+{
+       struct si5351_hw_data *hwdata =
+               container_of(hw, struct si5351_hw_data, hw);
+
+       si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num,
+                       SI5351_CLK_POWERDOWN, SI5351_CLK_POWERDOWN);
+       si5351_set_bits(hwdata->drvdata, SI5351_OUTPUT_ENABLE_CTRL,
+                       (1 << hwdata->num), (1 << hwdata->num));
+}
+
+static u8 si5351_clkout_get_parent(struct clk_hw *hw)
+{
+       struct si5351_hw_data *hwdata =
+               container_of(hw, struct si5351_hw_data, hw);
+       int index = 0;
+       unsigned char val;
+
+       val = si5351_reg_read(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num);
+       switch (val & SI5351_CLK_INPUT_MASK) {
+       case SI5351_CLK_INPUT_MULTISYNTH_N:
+               index = 0;
+               break;
+       case SI5351_CLK_INPUT_MULTISYNTH_0_4:
+               index = 1;
+               break;
+       case SI5351_CLK_INPUT_XTAL:
+               index = 2;
+               break;
+       case SI5351_CLK_INPUT_CLKIN:
+               index = 3;
+               break;
+       }
+
+       return index;
+}
+
+static int si5351_clkout_set_parent(struct clk_hw *hw, u8 index)
+{
+       struct si5351_hw_data *hwdata =
+               container_of(hw, struct si5351_hw_data, hw);
+       enum si5351_clkout_src parent = SI5351_CLKOUT_SRC_DEFAULT;
+
+       switch (index) {
+       case 0:
+               parent = SI5351_CLKOUT_SRC_MSYNTH_N;
+               break;
+       case 1:
+               parent = SI5351_CLKOUT_SRC_MSYNTH_0_4;
+               break;
+       case 2:
+               parent = SI5351_CLKOUT_SRC_XTAL;
+               break;
+       case 3:
+               parent = SI5351_CLKOUT_SRC_CLKIN;
+               break;
+       }
+
+       return _si5351_clkout_reparent(hwdata->drvdata, hwdata->num, parent);
+}
+
+static unsigned long si5351_clkout_recalc_rate(struct clk_hw *hw,
+                                              unsigned long parent_rate)
+{
+       struct si5351_hw_data *hwdata =
+               container_of(hw, struct si5351_hw_data, hw);
+       unsigned char reg;
+       unsigned char rdiv;
+
+       if (hwdata->num > 5)
+               reg = si5351_msynth_params_address(hwdata->num) + 2;
+       else
+               reg = SI5351_CLK6_7_OUTPUT_DIVIDER;
+
+       rdiv = si5351_reg_read(hwdata->drvdata, reg);
+       if (hwdata->num == 6) {
+               rdiv &= SI5351_OUTPUT_CLK6_DIV_MASK;
+       } else {
+               rdiv &= SI5351_OUTPUT_CLK_DIV_MASK;
+               rdiv >>= SI5351_OUTPUT_CLK_DIV_SHIFT;
+       }
+
+       return parent_rate >> rdiv;
+}
+
+static long si5351_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
+                                    unsigned long *parent_rate)
+{
+       struct si5351_hw_data *hwdata =
+               container_of(hw, struct si5351_hw_data, hw);
+       unsigned char rdiv;
+
+       /* clkout6/7 can only handle output freqencies < 150MHz */
+       if (hwdata->num >= 6 && rate > SI5351_CLKOUT67_MAX_FREQ)
+               rate = SI5351_CLKOUT67_MAX_FREQ;
+
+       /* clkout freqency is 8kHz - 160MHz */
+       if (rate > SI5351_CLKOUT_MAX_FREQ)
+               rate = SI5351_CLKOUT_MAX_FREQ;
+       if (rate < SI5351_CLKOUT_MIN_FREQ)
+               rate = SI5351_CLKOUT_MIN_FREQ;
+
+       /* request frequency if multisync master */
+       if (__clk_get_flags(hwdata->hw.clk) & CLK_SET_RATE_PARENT) {
+               /* use r divider for frequencies below 1MHz */
+               rdiv = SI5351_OUTPUT_CLK_DIV_1;
+               while (rate < SI5351_MULTISYNTH_MIN_FREQ &&
+                      rdiv < SI5351_OUTPUT_CLK_DIV_128) {
+                       rdiv += 1;
+                       rate *= 2;
+               }
+               *parent_rate = rate;
+       } else {
+               unsigned long new_rate, new_err, err;
+
+               /* round to closed rdiv */
+               rdiv = SI5351_OUTPUT_CLK_DIV_1;
+               new_rate = *parent_rate;
+               err = abs(new_rate - rate);
+               do {
+                       new_rate >>= 1;
+                       new_err = abs(new_rate - rate);
+                       if (new_err > err || rdiv == SI5351_OUTPUT_CLK_DIV_128)
+                               break;
+                       rdiv++;
+                       err = new_err;
+               } while (1);
+       }
+       rate = *parent_rate >> rdiv;
+
+       dev_dbg(&hwdata->drvdata->client->dev,
+               "%s - %s: rdiv = %u, parent_rate = %lu, rate = %lu\n",
+               __func__, __clk_get_name(hwdata->hw.clk), (1 << rdiv),
+               *parent_rate, rate);
+
+       return rate;
+}
+
+static int si5351_clkout_set_rate(struct clk_hw *hw, unsigned long rate,
+                                 unsigned long parent_rate)
+{
+       struct si5351_hw_data *hwdata =
+               container_of(hw, struct si5351_hw_data, hw);
+       unsigned long new_rate, new_err, err;
+       unsigned char rdiv;
+
+       /* round to closed rdiv */
+       rdiv = SI5351_OUTPUT_CLK_DIV_1;
+       new_rate = parent_rate;
+       err = abs(new_rate - rate);
+       do {
+               new_rate >>= 1;
+               new_err = abs(new_rate - rate);
+               if (new_err > err || rdiv == SI5351_OUTPUT_CLK_DIV_128)
+                       break;
+               rdiv++;
+               err = new_err;
+       } while (1);
+
+       /* write output divider */
+       switch (hwdata->num) {
+       case 6:
+               si5351_set_bits(hwdata->drvdata, SI5351_CLK6_7_OUTPUT_DIVIDER,
+                               SI5351_OUTPUT_CLK6_DIV_MASK, rdiv);
+               break;
+       case 7:
+               si5351_set_bits(hwdata->drvdata, SI5351_CLK6_7_OUTPUT_DIVIDER,
+                               SI5351_OUTPUT_CLK_DIV_MASK,
+                               rdiv << SI5351_OUTPUT_CLK_DIV_SHIFT);
+               break;
+       default:
+               si5351_set_bits(hwdata->drvdata,
+                               si5351_msynth_params_address(hwdata->num) + 2,
+                               SI5351_OUTPUT_CLK_DIV_MASK,
+                               rdiv << SI5351_OUTPUT_CLK_DIV_SHIFT);
+       }
+
+       /* powerup clkout */
+       si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num,
+                       SI5351_CLK_POWERDOWN, 0);
+
+       dev_dbg(&hwdata->drvdata->client->dev,
+               "%s - %s: rdiv = %u, parent_rate = %lu, rate = %lu\n",
+               __func__, __clk_get_name(hwdata->hw.clk), (1 << rdiv),
+               parent_rate, rate);
+
+       return 0;
+}
+
+static const struct clk_ops si5351_clkout_ops = {
+       .prepare = si5351_clkout_prepare,
+       .unprepare = si5351_clkout_unprepare,
+       .set_parent = si5351_clkout_set_parent,
+       .get_parent = si5351_clkout_get_parent,
+       .recalc_rate = si5351_clkout_recalc_rate,
+       .round_rate = si5351_clkout_round_rate,
+       .set_rate = si5351_clkout_set_rate,
+};
+
+/*
+ * Si5351 i2c probe and DT
+ */
+#ifdef CONFIG_OF
+static const struct of_device_id si5351_dt_ids[] = {
+       { .compatible = "silabs,si5351a", .data = (void *)SI5351_VARIANT_A, },
+       { .compatible = "silabs,si5351a-msop",
+                                        .data = (void *)SI5351_VARIANT_A3, },
+       { .compatible = "silabs,si5351b", .data = (void *)SI5351_VARIANT_B, },
+       { .compatible = "silabs,si5351c", .data = (void *)SI5351_VARIANT_C, },
+       { }
+};
+MODULE_DEVICE_TABLE(of, si5351_dt_ids);
+
+static int si5351_dt_parse(struct i2c_client *client)
+{
+       struct device_node *child, *np = client->dev.of_node;
+       struct si5351_platform_data *pdata;
+       const struct of_device_id *match;
+       struct property *prop;
+       const __be32 *p;
+       int num = 0;
+       u32 val;
+
+       if (np == NULL)
+               return 0;
+
+       match = of_match_node(si5351_dt_ids, np);
+       if (match == NULL)
+               return -EINVAL;
+
+       pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata)
+               return -ENOMEM;
+
+       pdata->variant = (enum si5351_variant)match->data;
+       pdata->clk_xtal = of_clk_get(np, 0);
+       if (!IS_ERR(pdata->clk_xtal))
+               clk_put(pdata->clk_xtal);
+       pdata->clk_clkin = of_clk_get(np, 1);
+       if (!IS_ERR(pdata->clk_clkin))
+               clk_put(pdata->clk_clkin);
+
+       /*
+        * property silabs,pll-source : <num src>, [<..>]
+        * allow to selectively set pll source
+        */
+       of_property_for_each_u32(np, "silabs,pll-source", prop, p, num) {
+               if (num >= 2) {
+                       dev_err(&client->dev,
+                               "invalid pll %d on pll-source prop\n", num);
+                       return -EINVAL;
+               }
+
+               p = of_prop_next_u32(prop, p, &val);
+               if (!p) {
+                       dev_err(&client->dev,
+                               "missing pll-source for pll %d\n", num);
+                       return -EINVAL;
+               }
+
+               switch (val) {
+               case 0:
+                       pdata->pll_src[num] = SI5351_PLL_SRC_XTAL;
+                       break;
+               case 1:
+                       if (pdata->variant != SI5351_VARIANT_C) {
+                               dev_err(&client->dev,
+                                       "invalid parent %d for pll %d\n",
+                                       val, num);
+                               return -EINVAL;
+                       }
+                       pdata->pll_src[num] = SI5351_PLL_SRC_CLKIN;
+                       break;
+               default:
+                       dev_err(&client->dev,
+                                "invalid parent %d for pll %d\n", val, num);
+                       return -EINVAL;
+               }
+       }
+
+       /* per clkout properties */
+       for_each_child_of_node(np, child) {
+               if (of_property_read_u32(child, "reg", &num)) {
+                       dev_err(&client->dev, "missing reg property of %s\n",
+                               child->name);
+                       return -EINVAL;
+               }
+
+               if (num >= 8 ||
+                   (pdata->variant == SI5351_VARIANT_A3 && num >= 3)) {
+                       dev_err(&client->dev, "invalid clkout %d\n", num);
+                       return -EINVAL;
+               }
+
+               if (!of_property_read_u32(child, "silabs,multisynth-source",
+                                         &val)) {
+                       switch (val) {
+                       case 0:
+                               pdata->clkout[num].multisynth_src =
+                                       SI5351_MULTISYNTH_SRC_VCO0;
+                               break;
+                       case 1:
+                               pdata->clkout[num].multisynth_src =
+                                       SI5351_MULTISYNTH_SRC_VCO1;
+                               break;
+                       default:
+                               dev_err(&client->dev,
+                                       "invalid parent %d for multisynth %d\n",
+                                       val, num);
+                               return -EINVAL;
+                       }
+               }
+
+               if (!of_property_read_u32(child, "silabs,clock-source", &val)) {
+                       switch (val) {
+                       case 0:
+                               pdata->clkout[num].clkout_src =
+                                       SI5351_CLKOUT_SRC_MSYNTH_N;
+                               break;
+                       case 1:
+                               pdata->clkout[num].clkout_src =
+                                       SI5351_CLKOUT_SRC_MSYNTH_0_4;
+                               break;
+                       case 2:
+                               pdata->clkout[num].clkout_src =
+                                       SI5351_CLKOUT_SRC_XTAL;
+                               break;
+                       case 3:
+                               if (pdata->variant != SI5351_VARIANT_C) {
+                                       dev_err(&client->dev,
+                                               "invalid parent %d for clkout %d\n",
+                                               val, num);
+                                       return -EINVAL;
+                               }
+                               pdata->clkout[num].clkout_src =
+                                       SI5351_CLKOUT_SRC_CLKIN;
+                               break;
+                       default:
+                               dev_err(&client->dev,
+                                       "invalid parent %d for clkout %d\n",
+                                       val, num);
+                               return -EINVAL;
+                       }
+               }
+
+               if (!of_property_read_u32(child, "silabs,drive-strength",
+                                         &val)) {
+                       switch (val) {
+                       case SI5351_DRIVE_2MA:
+                       case SI5351_DRIVE_4MA:
+                       case SI5351_DRIVE_6MA:
+                       case SI5351_DRIVE_8MA:
+                               pdata->clkout[num].drive = val;
+                               break;
+                       default:
+                               dev_err(&client->dev,
+                                       "invalid drive strength %d for clkout %d\n",
+                                       val, num);
+                               return -EINVAL;
+                       }
+               }
+
+               if (!of_property_read_u32(child, "clock-frequency", &val))
+                       pdata->clkout[num].rate = val;
+
+               pdata->clkout[num].pll_master =
+                       of_property_read_bool(child, "silabs,pll-master");
+       }
+       client->dev.platform_data = pdata;
+
+       return 0;
+}
+#else
+static int si5351_dt_parse(struct i2c_client *client)
+{
+       return 0;
+}
+#endif /* CONFIG_OF */
+
+static int si5351_i2c_probe(struct i2c_client *client,
+                           const struct i2c_device_id *id)
+{
+       struct si5351_platform_data *pdata;
+       struct si5351_driver_data *drvdata;
+       struct clk_init_data init;
+       struct clk *clk;
+       const char *parent_names[4];
+       u8 num_parents, num_clocks;
+       int ret, n;
+
+       ret = si5351_dt_parse(client);
+       if (ret)
+               return ret;
+
+       pdata = client->dev.platform_data;
+       if (!pdata)
+               return -EINVAL;
+
+       drvdata = devm_kzalloc(&client->dev, sizeof(*drvdata), GFP_KERNEL);
+       if (drvdata == NULL) {
+               dev_err(&client->dev, "unable to allocate driver data\n");
+               return -ENOMEM;
+       }
+
+       i2c_set_clientdata(client, drvdata);
+       drvdata->client = client;
+       drvdata->variant = pdata->variant;
+       drvdata->pxtal = pdata->clk_xtal;
+       drvdata->pclkin = pdata->clk_clkin;
+
+       drvdata->regmap = devm_regmap_init_i2c(client, &si5351_regmap_config);
+       if (IS_ERR(drvdata->regmap)) {
+               dev_err(&client->dev, "failed to allocate register map\n");
+               return PTR_ERR(drvdata->regmap);
+       }
+
+       /* Disable interrupts */
+       si5351_reg_write(drvdata, SI5351_INTERRUPT_MASK, 0xf0);
+       /* Set disabled output drivers to drive low */
+       si5351_reg_write(drvdata, SI5351_CLK3_0_DISABLE_STATE, 0x00);
+       si5351_reg_write(drvdata, SI5351_CLK7_4_DISABLE_STATE, 0x00);
+       /* Ensure pll select is on XTAL for Si5351A/B */
+       if (drvdata->variant != SI5351_VARIANT_C)
+               si5351_set_bits(drvdata, SI5351_PLL_INPUT_SOURCE,
+                               SI5351_PLLA_SOURCE | SI5351_PLLB_SOURCE, 0);
+
+       /* setup clock configuration */
+       for (n = 0; n < 2; n++) {
+               ret = _si5351_pll_reparent(drvdata, n, pdata->pll_src[n]);
+               if (ret) {
+                       dev_err(&client->dev,
+                               "failed to reparent pll %d to %d\n",
+                               n, pdata->pll_src[n]);
+                       return ret;
+               }
+       }
+
+       for (n = 0; n < 8; n++) {
+               ret = _si5351_msynth_reparent(drvdata, n,
+                                             pdata->clkout[n].multisynth_src);
+               if (ret) {
+                       dev_err(&client->dev,
+                               "failed to reparent multisynth %d to %d\n",
+                               n, pdata->clkout[n].multisynth_src);
+                       return ret;
+               }
+
+               ret = _si5351_clkout_reparent(drvdata, n,
+                                             pdata->clkout[n].clkout_src);
+               if (ret) {
+                       dev_err(&client->dev,
+                               "failed to reparent clkout %d to %d\n",
+                               n, pdata->clkout[n].clkout_src);
+                       return ret;
+               }
+
+               ret = _si5351_clkout_set_drive_strength(drvdata, n,
+                                                       pdata->clkout[n].drive);
+               if (ret) {
+                       dev_err(&client->dev,
+                               "failed set drive strength of clkout%d to %d\n",
+                               n, pdata->clkout[n].drive);
+                       return ret;
+               }
+       }
+
+       /* register xtal input clock gate */
+       memset(&init, 0, sizeof(init));
+       init.name = si5351_input_names[0];
+       init.ops = &si5351_xtal_ops;
+       init.flags = 0;
+       if (!IS_ERR(drvdata->pxtal)) {
+               drvdata->pxtal_name = __clk_get_name(drvdata->pxtal);
+               init.parent_names = &drvdata->pxtal_name;
+               init.num_parents = 1;
+       }
+       drvdata->xtal.init = &init;
+       clk = devm_clk_register(&client->dev, &drvdata->xtal);
+       if (IS_ERR(clk)) {
+               dev_err(&client->dev, "unable to register %s\n", init.name);
+               return PTR_ERR(clk);
+       }
+
+       /* register clkin input clock gate */
+       if (drvdata->variant == SI5351_VARIANT_C) {
+               memset(&init, 0, sizeof(init));
+               init.name = si5351_input_names[1];
+               init.ops = &si5351_clkin_ops;
+               if (!IS_ERR(drvdata->pclkin)) {
+                       drvdata->pclkin_name = __clk_get_name(drvdata->pclkin);
+                       init.parent_names = &drvdata->pclkin_name;
+                       init.num_parents = 1;
+               }
+               drvdata->clkin.init = &init;
+               clk = devm_clk_register(&client->dev, &drvdata->clkin);
+               if (IS_ERR(clk)) {
+                       dev_err(&client->dev, "unable to register %s\n",
+                               init.name);
+                       return PTR_ERR(clk);
+               }
+       }
+
+       /* Si5351C allows to mux either xtal or clkin to PLL input */
+       num_parents = (drvdata->variant == SI5351_VARIANT_C) ? 2 : 1;
+       parent_names[0] = si5351_input_names[0];
+       parent_names[1] = si5351_input_names[1];
+
+       /* register PLLA */
+       drvdata->pll[0].num = 0;
+       drvdata->pll[0].drvdata = drvdata;
+       drvdata->pll[0].hw.init = &init;
+       memset(&init, 0, sizeof(init));
+       init.name = si5351_pll_names[0];
+       init.ops = &si5351_pll_ops;
+       init.flags = 0;
+       init.parent_names = parent_names;
+       init.num_parents = num_parents;
+       clk = devm_clk_register(&client->dev, &drvdata->pll[0].hw);
+       if (IS_ERR(clk)) {
+               dev_err(&client->dev, "unable to register %s\n", init.name);
+               return -EINVAL;
+       }
+
+       /* register PLLB or VXCO (Si5351B) */
+       drvdata->pll[1].num = 1;
+       drvdata->pll[1].drvdata = drvdata;
+       drvdata->pll[1].hw.init = &init;
+       memset(&init, 0, sizeof(init));
+       if (drvdata->variant == SI5351_VARIANT_B) {
+               init.name = si5351_pll_names[2];
+               init.ops = &si5351_vxco_ops;
+               init.flags = CLK_IS_ROOT;
+               init.parent_names = NULL;
+               init.num_parents = 0;
+       } else {
+               init.name = si5351_pll_names[1];
+               init.ops = &si5351_pll_ops;
+               init.flags = 0;
+               init.parent_names = parent_names;
+               init.num_parents = num_parents;
+       }
+       clk = devm_clk_register(&client->dev, &drvdata->pll[1].hw);
+       if (IS_ERR(clk)) {
+               dev_err(&client->dev, "unable to register %s\n", init.name);
+               return -EINVAL;
+       }
+
+       /* register clk multisync and clk out divider */
+       num_clocks = (drvdata->variant == SI5351_VARIANT_A3) ? 3 : 8;
+       parent_names[0] = si5351_pll_names[0];
+       if (drvdata->variant == SI5351_VARIANT_B)
+               parent_names[1] = si5351_pll_names[2];
+       else
+               parent_names[1] = si5351_pll_names[1];
+
+       drvdata->msynth = devm_kzalloc(&client->dev, num_clocks *
+                                      sizeof(*drvdata->msynth), GFP_KERNEL);
+       drvdata->clkout = devm_kzalloc(&client->dev, num_clocks *
+                                      sizeof(*drvdata->clkout), GFP_KERNEL);
+
+       drvdata->onecell.clk_num = num_clocks;
+       drvdata->onecell.clks = devm_kzalloc(&client->dev,
+               num_clocks * sizeof(*drvdata->onecell.clks), GFP_KERNEL);
+
+       if (WARN_ON(!drvdata->msynth || !drvdata->clkout ||
+                   !drvdata->onecell.clks))
+               return -ENOMEM;
+
+       for (n = 0; n < num_clocks; n++) {
+               drvdata->msynth[n].num = n;
+               drvdata->msynth[n].drvdata = drvdata;
+               drvdata->msynth[n].hw.init = &init;
+               memset(&init, 0, sizeof(init));
+               init.name = si5351_msynth_names[n];
+               init.ops = &si5351_msynth_ops;
+               init.flags = 0;
+               if (pdata->clkout[n].pll_master)
+                       init.flags |= CLK_SET_RATE_PARENT;
+               init.parent_names = parent_names;
+               init.num_parents = 2;
+               clk = devm_clk_register(&client->dev, &drvdata->msynth[n].hw);
+               if (IS_ERR(clk)) {
+                       dev_err(&client->dev, "unable to register %s\n",
+                               init.name);
+                       return -EINVAL;
+               }
+       }
+
+       num_parents = (drvdata->variant == SI5351_VARIANT_C) ? 4 : 3;
+       parent_names[2] = si5351_input_names[0];
+       parent_names[3] = si5351_input_names[1];
+       for (n = 0; n < num_clocks; n++) {
+               parent_names[0] = si5351_msynth_names[n];
+               parent_names[1] = (n < 4) ? si5351_msynth_names[0] :
+                       si5351_msynth_names[4];
+
+               drvdata->clkout[n].num = n;
+               drvdata->clkout[n].drvdata = drvdata;
+               drvdata->clkout[n].hw.init = &init;
+               memset(&init, 0, sizeof(init));
+               init.name = si5351_clkout_names[n];
+               init.ops = &si5351_clkout_ops;
+               init.flags = 0;
+               if (pdata->clkout[n].clkout_src == SI5351_CLKOUT_SRC_MSYNTH_N)
+                       init.flags |= CLK_SET_RATE_PARENT;
+               init.parent_names = parent_names;
+               init.num_parents = num_parents;
+               clk = devm_clk_register(&client->dev, &drvdata->clkout[n].hw);
+               if (IS_ERR(clk)) {
+                       dev_err(&client->dev, "unable to register %s\n",
+                               init.name);
+                       return -EINVAL;
+               }
+               drvdata->onecell.clks[n] = clk;
+       }
+
+       ret = of_clk_add_provider(client->dev.of_node, of_clk_src_onecell_get,
+                                 &drvdata->onecell);
+       if (ret) {
+               dev_err(&client->dev, "unable to add clk provider\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static const struct i2c_device_id si5351_i2c_ids[] = {
+       { "silabs,si5351", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, si5351_i2c_ids);
+
+static struct i2c_driver si5351_driver = {
+       .driver = {
+               .name = "si5351",
+               .of_match_table = of_match_ptr(si5351_dt_ids),
+       },
+       .probe = si5351_i2c_probe,
+       .id_table = si5351_i2c_ids,
+};
+module_i2c_driver(si5351_driver);
+
+MODULE_AUTHOR("Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com");
+MODULE_DESCRIPTION("Silicon Labs Si5351A/B/C clock generator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/clk-si5351.h b/drivers/clk/clk-si5351.h
new file mode 100644 (file)
index 0000000..af41b50
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * clk-si5351.h: Silicon Laboratories Si5351A/B/C I2C Clock Generator
+ *
+ * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
+ * Rabeeh Khoury <rabeeh@solid-run.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.
+ */
+
+#ifndef _CLK_SI5351_H_
+#define _CLK_SI5351_H_
+
+#define SI5351_BUS_BASE_ADDR                   0x60
+
+#define SI5351_PLL_VCO_MIN                     600000000
+#define SI5351_PLL_VCO_MAX                     900000000
+#define SI5351_MULTISYNTH_MIN_FREQ             1000000
+#define SI5351_MULTISYNTH_DIVBY4_FREQ          150000000
+#define SI5351_MULTISYNTH_MAX_FREQ             160000000
+#define SI5351_MULTISYNTH67_MAX_FREQ           SI5351_MULTISYNTH_DIVBY4_FREQ
+#define SI5351_CLKOUT_MIN_FREQ                 8000
+#define SI5351_CLKOUT_MAX_FREQ                 SI5351_MULTISYNTH_MAX_FREQ
+#define SI5351_CLKOUT67_MAX_FREQ               SI5351_MULTISYNTH67_MAX_FREQ
+
+#define SI5351_PLL_A_MIN                       15
+#define SI5351_PLL_A_MAX                       90
+#define SI5351_PLL_B_MAX                       (SI5351_PLL_C_MAX-1)
+#define SI5351_PLL_C_MAX                       1048575
+#define SI5351_MULTISYNTH_A_MIN                        6
+#define SI5351_MULTISYNTH_A_MAX                        1800
+#define SI5351_MULTISYNTH67_A_MAX              254
+#define SI5351_MULTISYNTH_B_MAX                        (SI5351_MULTISYNTH_C_MAX-1)
+#define SI5351_MULTISYNTH_C_MAX                        1048575
+#define SI5351_MULTISYNTH_P1_MAX               ((1<<18)-1)
+#define SI5351_MULTISYNTH_P2_MAX               ((1<<20)-1)
+#define SI5351_MULTISYNTH_P3_MAX               ((1<<20)-1)
+
+#define SI5351_DEVICE_STATUS                   0
+#define SI5351_INTERRUPT_STATUS                        1
+#define SI5351_INTERRUPT_MASK                  2
+#define  SI5351_STATUS_SYS_INIT                        (1<<7)
+#define  SI5351_STATUS_LOL_B                   (1<<6)
+#define  SI5351_STATUS_LOL_A                   (1<<5)
+#define  SI5351_STATUS_LOS                     (1<<4)
+#define SI5351_OUTPUT_ENABLE_CTRL              3
+#define SI5351_OEB_PIN_ENABLE_CTRL             9
+#define SI5351_PLL_INPUT_SOURCE                        15
+#define  SI5351_CLKIN_DIV_MASK                 (3<<6)
+#define  SI5351_CLKIN_DIV_1                    (0<<6)
+#define  SI5351_CLKIN_DIV_2                    (1<<6)
+#define  SI5351_CLKIN_DIV_4                    (2<<6)
+#define  SI5351_CLKIN_DIV_8                    (3<<6)
+#define  SI5351_PLLB_SOURCE                    (1<<3)
+#define  SI5351_PLLA_SOURCE                    (1<<2)
+
+#define SI5351_CLK0_CTRL                       16
+#define SI5351_CLK1_CTRL                       17
+#define SI5351_CLK2_CTRL                       18
+#define SI5351_CLK3_CTRL                       19
+#define SI5351_CLK4_CTRL                       20
+#define SI5351_CLK5_CTRL                       21
+#define SI5351_CLK6_CTRL                       22
+#define SI5351_CLK7_CTRL                       23
+#define  SI5351_CLK_POWERDOWN                  (1<<7)
+#define  SI5351_CLK_INTEGER_MODE               (1<<6)
+#define  SI5351_CLK_PLL_SELECT                 (1<<5)
+#define  SI5351_CLK_INVERT                     (1<<4)
+#define  SI5351_CLK_INPUT_MASK                 (3<<2)
+#define  SI5351_CLK_INPUT_XTAL                 (0<<2)
+#define  SI5351_CLK_INPUT_CLKIN                        (1<<2)
+#define  SI5351_CLK_INPUT_MULTISYNTH_0_4       (2<<2)
+#define  SI5351_CLK_INPUT_MULTISYNTH_N         (3<<2)
+#define  SI5351_CLK_DRIVE_STRENGTH_MASK                (3<<0)
+#define  SI5351_CLK_DRIVE_STRENGTH_2MA         (0<<0)
+#define  SI5351_CLK_DRIVE_STRENGTH_4MA         (1<<0)
+#define  SI5351_CLK_DRIVE_STRENGTH_6MA         (2<<0)
+#define  SI5351_CLK_DRIVE_STRENGTH_8MA         (3<<0)
+
+#define SI5351_CLK3_0_DISABLE_STATE            24
+#define SI5351_CLK7_4_DISABLE_STATE            25
+#define  SI5351_CLK_DISABLE_STATE_LOW          0
+#define  SI5351_CLK_DISABLE_STATE_HIGH         1
+#define  SI5351_CLK_DISABLE_STATE_FLOAT                2
+#define  SI5351_CLK_DISABLE_STATE_NEVER                3
+
+#define SI5351_PARAMETERS_LENGTH               8
+#define SI5351_PLLA_PARAMETERS                 26
+#define SI5351_PLLB_PARAMETERS                 34
+#define SI5351_CLK0_PARAMETERS                 42
+#define SI5351_CLK1_PARAMETERS                 50
+#define SI5351_CLK2_PARAMETERS                 58
+#define SI5351_CLK3_PARAMETERS                 66
+#define SI5351_CLK4_PARAMETERS                 74
+#define SI5351_CLK5_PARAMETERS                 82
+#define SI5351_CLK6_PARAMETERS                 90
+#define SI5351_CLK7_PARAMETERS                 91
+#define SI5351_CLK6_7_OUTPUT_DIVIDER           92
+#define  SI5351_OUTPUT_CLK_DIV_MASK            (7 << 4)
+#define  SI5351_OUTPUT_CLK6_DIV_MASK           (7 << 0)
+#define  SI5351_OUTPUT_CLK_DIV_SHIFT           4
+#define  SI5351_OUTPUT_CLK_DIV6_SHIFT          0
+#define  SI5351_OUTPUT_CLK_DIV_1               0
+#define  SI5351_OUTPUT_CLK_DIV_2               1
+#define  SI5351_OUTPUT_CLK_DIV_4               2
+#define  SI5351_OUTPUT_CLK_DIV_8               3
+#define  SI5351_OUTPUT_CLK_DIV_16              4
+#define  SI5351_OUTPUT_CLK_DIV_32              5
+#define  SI5351_OUTPUT_CLK_DIV_64              6
+#define  SI5351_OUTPUT_CLK_DIV_128             7
+#define  SI5351_OUTPUT_CLK_DIVBY4              (3<<2)
+
+#define SI5351_SSC_PARAM0                      149
+#define SI5351_SSC_PARAM1                      150
+#define SI5351_SSC_PARAM2                      151
+#define SI5351_SSC_PARAM3                      152
+#define SI5351_SSC_PARAM4                      153
+#define SI5351_SSC_PARAM5                      154
+#define SI5351_SSC_PARAM6                      155
+#define SI5351_SSC_PARAM7                      156
+#define SI5351_SSC_PARAM8                      157
+#define SI5351_SSC_PARAM9                      158
+#define SI5351_SSC_PARAM10                     159
+#define SI5351_SSC_PARAM11                     160
+#define SI5351_SSC_PARAM12                     161
+
+#define SI5351_VXCO_PARAMETERS_LOW             162
+#define SI5351_VXCO_PARAMETERS_MID             163
+#define SI5351_VXCO_PARAMETERS_HIGH            164
+
+#define SI5351_CLK0_PHASE_OFFSET               165
+#define SI5351_CLK1_PHASE_OFFSET               166
+#define SI5351_CLK2_PHASE_OFFSET               167
+#define SI5351_CLK3_PHASE_OFFSET               168
+#define SI5351_CLK4_PHASE_OFFSET               169
+#define SI5351_CLK5_PHASE_OFFSET               170
+
+#define SI5351_PLL_RESET                       177
+#define  SI5351_PLL_RESET_B                    (1<<7)
+#define  SI5351_PLL_RESET_A                    (1<<5)
+
+#define SI5351_CRYSTAL_LOAD                    183
+#define  SI5351_CRYSTAL_LOAD_MASK              (3<<6)
+#define  SI5351_CRYSTAL_LOAD_6PF               (1<<6)
+#define  SI5351_CRYSTAL_LOAD_8PF               (2<<6)
+#define  SI5351_CRYSTAL_LOAD_10PF              (3<<6)
+
+#define SI5351_FANOUT_ENABLE                   187
+#define  SI5351_CLKIN_ENABLE                   (1<<7)
+#define  SI5351_XTAL_ENABLE                    (1<<6)
+#define  SI5351_MULTISYNTH_ENABLE              (1<<4)
+
+#endif
index 09c6331..debf688 100644 (file)
@@ -488,6 +488,7 @@ static int vtwm_pll_set_rate(struct clk_hw *hw, unsigned long rate,
        case PLL_TYPE_WM8750:
                wm8750_find_pll_bits(rate, parent_rate, &filter, &mul, &div1, &div2);
                pll_val = WM8750_BITS_TO_VAL(filter, mul, div1, div2);
+               break;
        default:
                pr_err("%s: invalid pll type\n", __func__);
                return 0;
@@ -523,6 +524,7 @@ static long vtwm_pll_round_rate(struct clk_hw *hw, unsigned long rate,
        case PLL_TYPE_WM8750:
                wm8750_find_pll_bits(rate, *prate, &filter, &mul, &div1, &div2);
                round_rate = WM8750_BITS_TO_FREQ(*prate, mul, div1, div2);
+               break;
        default:
                round_rate = 0;
        }
index b14a25f..3206297 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/slab.h>
 #include <linux/kernel.h>
 #include <linux/clk-provider.h>
+#include <linux/clk/zynq.h>
 
 static void __iomem *slcr_base;
 
index ed87b24..934cfd1 100644 (file)
 #include <linux/of.h>
 #include <linux/device.h>
 #include <linux/init.h>
+#include <linux/sched.h>
 
 static DEFINE_SPINLOCK(enable_lock);
 static DEFINE_MUTEX(prepare_lock);
 
+static struct task_struct *prepare_owner;
+static struct task_struct *enable_owner;
+
+static int prepare_refcnt;
+static int enable_refcnt;
+
 static HLIST_HEAD(clk_root_list);
 static HLIST_HEAD(clk_orphan_list);
 static LIST_HEAD(clk_notifier_list);
 
+/***           locking             ***/
+static void clk_prepare_lock(void)
+{
+       if (!mutex_trylock(&prepare_lock)) {
+               if (prepare_owner == current) {
+                       prepare_refcnt++;
+                       return;
+               }
+               mutex_lock(&prepare_lock);
+       }
+       WARN_ON_ONCE(prepare_owner != NULL);
+       WARN_ON_ONCE(prepare_refcnt != 0);
+       prepare_owner = current;
+       prepare_refcnt = 1;
+}
+
+static void clk_prepare_unlock(void)
+{
+       WARN_ON_ONCE(prepare_owner != current);
+       WARN_ON_ONCE(prepare_refcnt == 0);
+
+       if (--prepare_refcnt)
+               return;
+       prepare_owner = NULL;
+       mutex_unlock(&prepare_lock);
+}
+
+static unsigned long clk_enable_lock(void)
+{
+       unsigned long flags;
+
+       if (!spin_trylock_irqsave(&enable_lock, flags)) {
+               if (enable_owner == current) {
+                       enable_refcnt++;
+                       return flags;
+               }
+               spin_lock_irqsave(&enable_lock, flags);
+       }
+       WARN_ON_ONCE(enable_owner != NULL);
+       WARN_ON_ONCE(enable_refcnt != 0);
+       enable_owner = current;
+       enable_refcnt = 1;
+       return flags;
+}
+
+static void clk_enable_unlock(unsigned long flags)
+{
+       WARN_ON_ONCE(enable_owner != current);
+       WARN_ON_ONCE(enable_refcnt == 0);
+
+       if (--enable_refcnt)
+               return;
+       enable_owner = NULL;
+       spin_unlock_irqrestore(&enable_lock, flags);
+}
+
 /***        debugfs support        ***/
 
 #ifdef CONFIG_COMMON_CLK_DEBUG
@@ -69,7 +132,7 @@ static int clk_summary_show(struct seq_file *s, void *data)
        seq_printf(s, "   clock                        enable_cnt  prepare_cnt  rate\n");
        seq_printf(s, "---------------------------------------------------------------------\n");
 
-       mutex_lock(&prepare_lock);
+       clk_prepare_lock();
 
        hlist_for_each_entry(c, &clk_root_list, child_node)
                clk_summary_show_subtree(s, c, 0);
@@ -77,7 +140,7 @@ static int clk_summary_show(struct seq_file *s, void *data)
        hlist_for_each_entry(c, &clk_orphan_list, child_node)
                clk_summary_show_subtree(s, c, 0);
 
-       mutex_unlock(&prepare_lock);
+       clk_prepare_unlock();
 
        return 0;
 }
@@ -130,7 +193,7 @@ static int clk_dump(struct seq_file *s, void *data)
 
        seq_printf(s, "{");
 
-       mutex_lock(&prepare_lock);
+       clk_prepare_lock();
 
        hlist_for_each_entry(c, &clk_root_list, child_node) {
                if (!first_node)
@@ -144,7 +207,7 @@ static int clk_dump(struct seq_file *s, void *data)
                clk_dump_subtree(s, c, 0);
        }
 
-       mutex_unlock(&prepare_lock);
+       clk_prepare_unlock();
 
        seq_printf(s, "}");
        return 0;
@@ -280,6 +343,39 @@ out:
 }
 
 /**
+ * clk_debug_reparent - reparent clk node in the debugfs clk tree
+ * @clk: the clk being reparented
+ * @new_parent: the new clk parent, may be NULL
+ *
+ * Rename clk entry in the debugfs clk tree if debugfs has been
+ * initialized.  Otherwise it bails out early since the debugfs clk tree
+ * will be created lazily by clk_debug_init as part of a late_initcall.
+ *
+ * Caller must hold prepare_lock.
+ */
+static void clk_debug_reparent(struct clk *clk, struct clk *new_parent)
+{
+       struct dentry *d;
+       struct dentry *new_parent_d;
+
+       if (!inited)
+               return;
+
+       if (new_parent)
+               new_parent_d = new_parent->dentry;
+       else
+               new_parent_d = orphandir;
+
+       d = debugfs_rename(clk->dentry->d_parent, clk->dentry,
+                       new_parent_d, clk->name);
+       if (d)
+               clk->dentry = d;
+       else
+               pr_debug("%s: failed to rename debugfs entry for %s\n",
+                               __func__, clk->name);
+}
+
+/**
  * clk_debug_init - lazily create the debugfs clk tree visualization
  *
  * clks are often initialized very early during boot before memory can
@@ -316,7 +412,7 @@ static int __init clk_debug_init(void)
        if (!orphandir)
                return -ENOMEM;
 
-       mutex_lock(&prepare_lock);
+       clk_prepare_lock();
 
        hlist_for_each_entry(clk, &clk_root_list, child_node)
                clk_debug_create_subtree(clk, rootdir);
@@ -326,16 +422,45 @@ static int __init clk_debug_init(void)
 
        inited = 1;
 
-       mutex_unlock(&prepare_lock);
+       clk_prepare_unlock();
 
        return 0;
 }
 late_initcall(clk_debug_init);
 #else
 static inline int clk_debug_register(struct clk *clk) { return 0; }
+static inline void clk_debug_reparent(struct clk *clk, struct clk *new_parent)
+{
+}
 #endif
 
 /* caller must hold prepare_lock */
+static void clk_unprepare_unused_subtree(struct clk *clk)
+{
+       struct clk *child;
+
+       if (!clk)
+               return;
+
+       hlist_for_each_entry(child, &clk->children, child_node)
+               clk_unprepare_unused_subtree(child);
+
+       if (clk->prepare_count)
+               return;
+
+       if (clk->flags & CLK_IGNORE_UNUSED)
+               return;
+
+       if (__clk_is_prepared(clk)) {
+               if (clk->ops->unprepare_unused)
+                       clk->ops->unprepare_unused(clk->hw);
+               else if (clk->ops->unprepare)
+                       clk->ops->unprepare(clk->hw);
+       }
+}
+EXPORT_SYMBOL_GPL(__clk_get_flags);
+
+/* caller must hold prepare_lock */
 static void clk_disable_unused_subtree(struct clk *clk)
 {
        struct clk *child;
@@ -347,7 +472,7 @@ static void clk_disable_unused_subtree(struct clk *clk)
        hlist_for_each_entry(child, &clk->children, child_node)
                clk_disable_unused_subtree(child);
 
-       spin_lock_irqsave(&enable_lock, flags);
+       flags = clk_enable_lock();
 
        if (clk->enable_count)
                goto unlock_out;
@@ -368,17 +493,30 @@ static void clk_disable_unused_subtree(struct clk *clk)
        }
 
 unlock_out:
-       spin_unlock_irqrestore(&enable_lock, flags);
+       clk_enable_unlock(flags);
 
 out:
        return;
 }
 
+static bool clk_ignore_unused;
+static int __init clk_ignore_unused_setup(char *__unused)
+{
+       clk_ignore_unused = true;
+       return 1;
+}
+__setup("clk_ignore_unused", clk_ignore_unused_setup);
+
 static int clk_disable_unused(void)
 {
        struct clk *clk;
 
-       mutex_lock(&prepare_lock);
+       if (clk_ignore_unused) {
+               pr_warn("clk: Not disabling unused clocks\n");
+               return 0;
+       }
+
+       clk_prepare_lock();
 
        hlist_for_each_entry(clk, &clk_root_list, child_node)
                clk_disable_unused_subtree(clk);
@@ -386,7 +524,13 @@ static int clk_disable_unused(void)
        hlist_for_each_entry(clk, &clk_orphan_list, child_node)
                clk_disable_unused_subtree(clk);
 
-       mutex_unlock(&prepare_lock);
+       hlist_for_each_entry(clk, &clk_root_list, child_node)
+               clk_unprepare_unused_subtree(clk);
+
+       hlist_for_each_entry(clk, &clk_orphan_list, child_node)
+               clk_unprepare_unused_subtree(clk);
+
+       clk_prepare_unlock();
 
        return 0;
 }
@@ -451,6 +595,27 @@ unsigned long __clk_get_flags(struct clk *clk)
        return !clk ? 0 : clk->flags;
 }
 
+bool __clk_is_prepared(struct clk *clk)
+{
+       int ret;
+
+       if (!clk)
+               return false;
+
+       /*
+        * .is_prepared is optional for clocks that can prepare
+        * fall back to software usage counter if it is missing
+        */
+       if (!clk->ops->is_prepared) {
+               ret = clk->prepare_count ? 1 : 0;
+               goto out;
+       }
+
+       ret = clk->ops->is_prepared(clk->hw);
+out:
+       return !!ret;
+}
+
 bool __clk_is_enabled(struct clk *clk)
 {
        int ret;
@@ -548,9 +713,9 @@ void __clk_unprepare(struct clk *clk)
  */
 void clk_unprepare(struct clk *clk)
 {
-       mutex_lock(&prepare_lock);
+       clk_prepare_lock();
        __clk_unprepare(clk);
-       mutex_unlock(&prepare_lock);
+       clk_prepare_unlock();
 }
 EXPORT_SYMBOL_GPL(clk_unprepare);
 
@@ -596,9 +761,9 @@ int clk_prepare(struct clk *clk)
 {
        int ret;
 
-       mutex_lock(&prepare_lock);
+       clk_prepare_lock();
        ret = __clk_prepare(clk);
-       mutex_unlock(&prepare_lock);
+       clk_prepare_unlock();
 
        return ret;
 }
@@ -640,9 +805,9 @@ void clk_disable(struct clk *clk)
 {
        unsigned long flags;
 
-       spin_lock_irqsave(&enable_lock, flags);
+       flags = clk_enable_lock();
        __clk_disable(clk);
-       spin_unlock_irqrestore(&enable_lock, flags);
+       clk_enable_unlock(flags);
 }
 EXPORT_SYMBOL_GPL(clk_disable);
 
@@ -693,9 +858,9 @@ int clk_enable(struct clk *clk)
        unsigned long flags;
        int ret;
 
-       spin_lock_irqsave(&enable_lock, flags);
+       flags = clk_enable_lock();
        ret = __clk_enable(clk);
-       spin_unlock_irqrestore(&enable_lock, flags);
+       clk_enable_unlock(flags);
 
        return ret;
 }
@@ -740,9 +905,9 @@ long clk_round_rate(struct clk *clk, unsigned long rate)
 {
        unsigned long ret;
 
-       mutex_lock(&prepare_lock);
+       clk_prepare_lock();
        ret = __clk_round_rate(clk, rate);
-       mutex_unlock(&prepare_lock);
+       clk_prepare_unlock();
 
        return ret;
 }
@@ -837,13 +1002,13 @@ unsigned long clk_get_rate(struct clk *clk)
 {
        unsigned long rate;
 
-       mutex_lock(&prepare_lock);
+       clk_prepare_lock();
 
        if (clk && (clk->flags & CLK_GET_RATE_NOCACHE))
                __clk_recalc_rates(clk, 0);
 
        rate = __clk_get_rate(clk);
-       mutex_unlock(&prepare_lock);
+       clk_prepare_unlock();
 
        return rate;
 }
@@ -876,16 +1041,16 @@ static int __clk_speculate_rates(struct clk *clk, unsigned long parent_rate)
        else
                new_rate = parent_rate;
 
-       /* abort the rate change if a driver returns NOTIFY_BAD */
+       /* abort rate change if a driver returns NOTIFY_BAD or NOTIFY_STOP */
        if (clk->notifier_count)
                ret = __clk_notify(clk, PRE_RATE_CHANGE, clk->rate, new_rate);
 
-       if (ret == NOTIFY_BAD)
+       if (ret & NOTIFY_STOP_MASK)
                goto out;
 
        hlist_for_each_entry(child, &clk->children, child_node) {
                ret = __clk_speculate_rates(child, new_rate);
-               if (ret == NOTIFY_BAD)
+               if (ret & NOTIFY_STOP_MASK)
                        break;
        }
 
@@ -974,11 +1139,11 @@ static struct clk *clk_propagate_rate_change(struct clk *clk, unsigned long even
        int ret = NOTIFY_DONE;
 
        if (clk->rate == clk->new_rate)
-               return 0;
+               return NULL;
 
        if (clk->notifier_count) {
                ret = __clk_notify(clk, event, clk->rate, clk->new_rate);
-               if (ret == NOTIFY_BAD)
+               if (ret & NOTIFY_STOP_MASK)
                        fail_clk = clk;
        }
 
@@ -1048,7 +1213,7 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
        int ret = 0;
 
        /* prevent racing with updates to the clock topology */
-       mutex_lock(&prepare_lock);
+       clk_prepare_lock();
 
        /* bail early if nothing to do */
        if (rate == clk->rate)
@@ -1080,7 +1245,7 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
        clk_change_rate(top);
 
 out:
-       mutex_unlock(&prepare_lock);
+       clk_prepare_unlock();
 
        return ret;
 }
@@ -1096,9 +1261,9 @@ struct clk *clk_get_parent(struct clk *clk)
 {
        struct clk *parent;
 
-       mutex_lock(&prepare_lock);
+       clk_prepare_lock();
        parent = __clk_get_parent(clk);
-       mutex_unlock(&prepare_lock);
+       clk_prepare_unlock();
 
        return parent;
 }
@@ -1162,16 +1327,8 @@ out:
        return ret;
 }
 
-void __clk_reparent(struct clk *clk, struct clk *new_parent)
+static void clk_reparent(struct clk *clk, struct clk *new_parent)
 {
-#ifdef CONFIG_COMMON_CLK_DEBUG
-       struct dentry *d;
-       struct dentry *new_parent_d;
-#endif
-
-       if (!clk || !new_parent)
-               return;
-
        hlist_del(&clk->child_node);
 
        if (new_parent)
@@ -1179,39 +1336,20 @@ void __clk_reparent(struct clk *clk, struct clk *new_parent)
        else
                hlist_add_head(&clk->child_node, &clk_orphan_list);
 
-#ifdef CONFIG_COMMON_CLK_DEBUG
-       if (!inited)
-               goto out;
-
-       if (new_parent)
-               new_parent_d = new_parent->dentry;
-       else
-               new_parent_d = orphandir;
-
-       d = debugfs_rename(clk->dentry->d_parent, clk->dentry,
-                       new_parent_d, clk->name);
-       if (d)
-               clk->dentry = d;
-       else
-               pr_debug("%s: failed to rename debugfs entry for %s\n",
-                               __func__, clk->name);
-out:
-#endif
-
        clk->parent = new_parent;
+}
 
+void __clk_reparent(struct clk *clk, struct clk *new_parent)
+{
+       clk_reparent(clk, new_parent);
+       clk_debug_reparent(clk, new_parent);
        __clk_recalc_rates(clk, POST_RATE_CHANGE);
 }
 
-static int __clk_set_parent(struct clk *clk, struct clk *parent)
+static u8 clk_fetch_parent_index(struct clk *clk, struct clk *parent)
 {
-       struct clk *old_parent;
-       unsigned long flags;
-       int ret = -EINVAL;
        u8 i;
 
-       old_parent = clk->parent;
-
        if (!clk->parents)
                clk->parents = kzalloc((sizeof(struct clk*) * clk->num_parents),
                                                                GFP_KERNEL);
@@ -1231,36 +1369,79 @@ static int __clk_set_parent(struct clk *clk, struct clk *parent)
                }
        }
 
-       if (i == clk->num_parents) {
-               pr_debug("%s: clock %s is not a possible parent of clock %s\n",
-                               __func__, parent->name, clk->name);
-               goto out;
-       }
+       return i;
+}
 
-       /* migrate prepare and enable */
+static int __clk_set_parent(struct clk *clk, struct clk *parent, u8 p_index)
+{
+       unsigned long flags;
+       int ret = 0;
+       struct clk *old_parent = clk->parent;
+       bool migrated_enable = false;
+
+       /* migrate prepare */
        if (clk->prepare_count)
                __clk_prepare(parent);
 
-       /* FIXME replace with clk_is_enabled(clk) someday */
-       spin_lock_irqsave(&enable_lock, flags);
-       if (clk->enable_count)
+       flags = clk_enable_lock();
+
+       /* migrate enable */
+       if (clk->enable_count) {
                __clk_enable(parent);
-       spin_unlock_irqrestore(&enable_lock, flags);
+               migrated_enable = true;
+       }
+
+       /* update the clk tree topology */
+       clk_reparent(clk, parent);
+
+       clk_enable_unlock(flags);
 
        /* change clock input source */
-       ret = clk->ops->set_parent(clk->hw, i);
+       if (parent && clk->ops->set_parent)
+               ret = clk->ops->set_parent(clk->hw, p_index);
 
-       /* clean up old prepare and enable */
-       spin_lock_irqsave(&enable_lock, flags);
-       if (clk->enable_count)
+       if (ret) {
+               /*
+                * The error handling is tricky due to that we need to release
+                * the spinlock while issuing the .set_parent callback. This
+                * means the new parent might have been enabled/disabled in
+                * between, which must be considered when doing rollback.
+                */
+               flags = clk_enable_lock();
+
+               clk_reparent(clk, old_parent);
+
+               if (migrated_enable && clk->enable_count) {
+                       __clk_disable(parent);
+               } else if (migrated_enable && (clk->enable_count == 0)) {
+                       __clk_disable(old_parent);
+               } else if (!migrated_enable && clk->enable_count) {
+                       __clk_disable(parent);
+                       __clk_enable(old_parent);
+               }
+
+               clk_enable_unlock(flags);
+
+               if (clk->prepare_count)
+                       __clk_unprepare(parent);
+
+               return ret;
+       }
+
+       /* clean up enable for old parent if migration was done */
+       if (migrated_enable) {
+               flags = clk_enable_lock();
                __clk_disable(old_parent);
-       spin_unlock_irqrestore(&enable_lock, flags);
+               clk_enable_unlock(flags);
+       }
 
+       /* clean up prepare for old parent if migration was done */
        if (clk->prepare_count)
                __clk_unprepare(old_parent);
 
-out:
-       return ret;
+       /* update debugfs with new clk tree topology */
+       clk_debug_reparent(clk, parent);
+       return 0;
 }
 
 /**
@@ -1278,44 +1459,59 @@ out:
 int clk_set_parent(struct clk *clk, struct clk *parent)
 {
        int ret = 0;
+       u8 p_index = 0;
+       unsigned long p_rate = 0;
 
        if (!clk || !clk->ops)
                return -EINVAL;
 
-       if (!clk->ops->set_parent)
+       /* verify ops for for multi-parent clks */
+       if ((clk->num_parents > 1) && (!clk->ops->set_parent))
                return -ENOSYS;
 
        /* prevent racing with updates to the clock topology */
-       mutex_lock(&prepare_lock);
+       clk_prepare_lock();
 
        if (clk->parent == parent)
                goto out;
 
+       /* check that we are allowed to re-parent if the clock is in use */
+       if ((clk->flags & CLK_SET_PARENT_GATE) && clk->prepare_count) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       /* try finding the new parent index */
+       if (parent) {
+               p_index = clk_fetch_parent_index(clk, parent);
+               p_rate = parent->rate;
+               if (p_index == clk->num_parents) {
+                       pr_debug("%s: clk %s can not be parent of clk %s\n",
+                                       __func__, parent->name, clk->name);
+                       ret = -EINVAL;
+                       goto out;
+               }
+       }
+
        /* propagate PRE_RATE_CHANGE notifications */
        if (clk->notifier_count)
-               ret = __clk_speculate_rates(clk, parent->rate);
+               ret = __clk_speculate_rates(clk, p_rate);
 
        /* abort if a driver objects */
-       if (ret == NOTIFY_STOP)
+       if (ret & NOTIFY_STOP_MASK)
                goto out;
 
-       /* only re-parent if the clock is not in use */
-       if ((clk->flags & CLK_SET_PARENT_GATE) && clk->prepare_count)
-               ret = -EBUSY;
-       else
-               ret = __clk_set_parent(clk, parent);
+       /* do the re-parent */
+       ret = __clk_set_parent(clk, parent, p_index);
 
-       /* propagate ABORT_RATE_CHANGE if .set_parent failed */
-       if (ret) {
+       /* propagate rate recalculation accordingly */
+       if (ret)
                __clk_recalc_rates(clk, ABORT_RATE_CHANGE);
-               goto out;
-       }
-
-       /* propagate rate recalculation downstream */
-       __clk_reparent(clk, parent);
+       else
+               __clk_recalc_rates(clk, POST_RATE_CHANGE);
 
 out:
-       mutex_unlock(&prepare_lock);
+       clk_prepare_unlock();
 
        return ret;
 }
@@ -1338,7 +1534,7 @@ int __clk_init(struct device *dev, struct clk *clk)
        if (!clk)
                return -EINVAL;
 
-       mutex_lock(&prepare_lock);
+       clk_prepare_lock();
 
        /* check to see if a clock with this name is already registered */
        if (__clk_lookup(clk->name)) {
@@ -1462,7 +1658,7 @@ int __clk_init(struct device *dev, struct clk *clk)
        clk_debug_register(clk);
 
 out:
-       mutex_unlock(&prepare_lock);
+       clk_prepare_unlock();
 
        return ret;
 }
@@ -1696,7 +1892,7 @@ int clk_notifier_register(struct clk *clk, struct notifier_block *nb)
        if (!clk || !nb)
                return -EINVAL;
 
-       mutex_lock(&prepare_lock);
+       clk_prepare_lock();
 
        /* search the list of notifiers for this clk */
        list_for_each_entry(cn, &clk_notifier_list, node)
@@ -1720,7 +1916,7 @@ int clk_notifier_register(struct clk *clk, struct notifier_block *nb)
        clk->notifier_count++;
 
 out:
-       mutex_unlock(&prepare_lock);
+       clk_prepare_unlock();
 
        return ret;
 }
@@ -1745,7 +1941,7 @@ int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb)
        if (!clk || !nb)
                return -EINVAL;
 
-       mutex_lock(&prepare_lock);
+       clk_prepare_lock();
 
        list_for_each_entry(cn, &clk_notifier_list, node)
                if (cn->clk == clk)
@@ -1766,7 +1962,7 @@ int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb)
                ret = -ENOENT;
        }
 
-       mutex_unlock(&prepare_lock);
+       clk_prepare_unlock();
 
        return ret;
 }
index 69056a7..2628610 100644 (file)
@@ -156,7 +156,7 @@ static u32 __init armada_370_get_cpu_freq(void __iomem *sar)
 
        cpu_freq_select = ((readl(sar) >> SARL_A370_PCLK_FREQ_OPT) &
                           SARL_A370_PCLK_FREQ_OPT_MASK);
-       if (cpu_freq_select > ARRAY_SIZE(armada_370_cpu_frequencies)) {
+       if (cpu_freq_select >= ARRAY_SIZE(armada_370_cpu_frequencies)) {
                pr_err("CPU freq select unsuported %d\n", cpu_freq_select);
                cpu_freq = 0;
        } else
@@ -278,7 +278,7 @@ static u32 __init armada_xp_get_cpu_freq(void __iomem *sar)
        cpu_freq_select |= (((readl(sar+4) >> SARH_AXP_PCLK_FREQ_OPT) &
                             SARH_AXP_PCLK_FREQ_OPT_MASK)
                            << SARH_AXP_PCLK_FREQ_OPT_SHIFT);
-       if (cpu_freq_select > ARRAY_SIZE(armada_xp_cpu_frequencies)) {
+       if (cpu_freq_select >= ARRAY_SIZE(armada_xp_cpu_frequencies)) {
                pr_err("CPU freq select unsuported: %d\n", cpu_freq_select);
                cpu_freq = 0;
        } else
index 9dd2551..b0fbc07 100644 (file)
@@ -16,7 +16,6 @@
 #include <linux/io.h>
 #include <linux/of.h>
 #include <linux/delay.h>
-#include "clk-cpu.h"
 
 #define SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET    0x0
 #define SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET   0xC
@@ -173,17 +172,5 @@ clks_out:
        kfree(cpuclk);
 }
 
-static const __initconst struct of_device_id clk_cpu_match[] = {
-       {
-               .compatible = "marvell,armada-xp-cpu-clock",
-               .data = of_cpu_clk_setup,
-       },
-       {
-               /* sentinel */
-       },
-};
-
-void __init mvebu_cpu_clk_init(void)
-{
-       of_clk_init(clk_cpu_match);
-}
+CLK_OF_DECLARE(armada_xp_cpu_clock, "marvell,armada-xp-cpu-clock",
+                                        of_cpu_clk_setup);
diff --git a/drivers/clk/mvebu/clk-cpu.h b/drivers/clk/mvebu/clk-cpu.h
deleted file mode 100644 (file)
index 08e2aff..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Marvell MVEBU CPU clock handling.
- *
- * Copyright (C) 2012 Marvell
- *
- * Gregory CLEMENT <gregory.clement@free-electrons.com>
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2.  This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
- */
-
-#ifndef __MVEBU_CLK_CPU_H
-#define __MVEBU_CLK_CPU_H
-
-#ifdef CONFIG_MVEBU_CLK_CPU
-void __init mvebu_cpu_clk_init(void);
-#else
-static inline void mvebu_cpu_clk_init(void) {}
-#endif
-
-#endif
index 855681b..29f10fb 100644 (file)
  * warranty of any kind, whether express or implied.
  */
 #include <linux/kernel.h>
-#include <linux/clk.h>
 #include <linux/clk-provider.h>
-#include <linux/of_address.h>
-#include <linux/clk/mvebu.h>
 #include <linux/of.h>
 #include "clk-core.h"
-#include "clk-cpu.h"
 #include "clk-gating-ctrl.h"
 
 void __init mvebu_clocks_init(void)
 {
        mvebu_core_clk_init();
        mvebu_gating_clk_init();
-       mvebu_cpu_clk_init();
+       of_clk_init(NULL);
 }
index b24d560..5301bce 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/io.h>
 #include <linux/jiffies.h>
 #include <linux/spinlock.h>
+#include "clk.h"
 
 DEFINE_SPINLOCK(mxs_lock);
 
index 82abea3..35e7e26 100644 (file)
@@ -960,47 +960,47 @@ void __init spear1340_clk_init(void)
                        SPEAR1340_SPDIF_IN_CLK_ENB, 0, &_lock);
        clk_register_clkdev(clk, NULL, "d0100000.spdif-in");
 
-       clk = clk_register_gate(NULL, "acp_clk", "acp_mclk", 0,
+       clk = clk_register_gate(NULL, "acp_clk", "ahb_clk", 0,
                        SPEAR1340_PERIP2_CLK_ENB, SPEAR1340_ACP_CLK_ENB, 0,
                        &_lock);
        clk_register_clkdev(clk, NULL, "acp_clk");
 
-       clk = clk_register_gate(NULL, "plgpio_clk", "plgpio_mclk", 0,
+       clk = clk_register_gate(NULL, "plgpio_clk", "ahb_clk", 0,
                        SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_PLGPIO_CLK_ENB, 0,
                        &_lock);
        clk_register_clkdev(clk, NULL, "e2800000.gpio");
 
-       clk = clk_register_gate(NULL, "video_dec_clk", "video_dec_mclk", 0,
+       clk = clk_register_gate(NULL, "video_dec_clk", "ahb_clk", 0,
                        SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_VIDEO_DEC_CLK_ENB,
                        0, &_lock);
        clk_register_clkdev(clk, NULL, "video_dec");
 
-       clk = clk_register_gate(NULL, "video_enc_clk", "video_enc_mclk", 0,
+       clk = clk_register_gate(NULL, "video_enc_clk", "ahb_clk", 0,
                        SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_VIDEO_ENC_CLK_ENB,
                        0, &_lock);
        clk_register_clkdev(clk, NULL, "video_enc");
 
-       clk = clk_register_gate(NULL, "video_in_clk", "video_in_mclk", 0,
+       clk = clk_register_gate(NULL, "video_in_clk", "ahb_clk", 0,
                        SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_VIDEO_IN_CLK_ENB, 0,
                        &_lock);
        clk_register_clkdev(clk, NULL, "spear_vip");
 
-       clk = clk_register_gate(NULL, "cam0_clk", "cam0_mclk", 0,
+       clk = clk_register_gate(NULL, "cam0_clk", "ahb_clk", 0,
                        SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM0_CLK_ENB, 0,
                        &_lock);
        clk_register_clkdev(clk, NULL, "d0200000.cam0");
 
-       clk = clk_register_gate(NULL, "cam1_clk", "cam1_mclk", 0,
+       clk = clk_register_gate(NULL, "cam1_clk", "ahb_clk", 0,
                        SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM1_CLK_ENB, 0,
                        &_lock);
        clk_register_clkdev(clk, NULL, "d0300000.cam1");
 
-       clk = clk_register_gate(NULL, "cam2_clk", "cam2_mclk", 0,
+       clk = clk_register_gate(NULL, "cam2_clk", "ahb_clk", 0,
                        SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM2_CLK_ENB, 0,
                        &_lock);
        clk_register_clkdev(clk, NULL, "d0400000.cam2");
 
-       clk = clk_register_gate(NULL, "cam3_clk", "cam3_mclk", 0,
+       clk = clk_register_gate(NULL, "cam3_clk", "ahb_clk", 0,
                        SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM3_CLK_ENB, 0,
                        &_lock);
        clk_register_clkdev(clk, NULL, "d0500000.cam3");
diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
new file mode 100644 (file)
index 0000000..b5bac91
--- /dev/null
@@ -0,0 +1,5 @@
+#
+# Makefile for sunxi specific clk
+#
+
+obj-y += clk-sunxi.o clk-factors.o
diff --git a/drivers/clk/sunxi/clk-factors.c b/drivers/clk/sunxi/clk-factors.c
new file mode 100644 (file)
index 0000000..88523f9
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2013 Emilio López <emilio@elopez.com.ar>
+ *
+ * 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.
+ *
+ * Adjustable factor-based clock implementation
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/string.h>
+
+#include <linux/delay.h>
+
+#include "clk-factors.h"
+
+/*
+ * DOC: basic adjustable factor-based clock that cannot gate
+ *
+ * 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 * N * (K + 1) >> P) / (M + 1)
+ * parent - fixed parent.  No clk_set_parent support
+ */
+
+struct clk_factors {
+       struct clk_hw hw;
+       void __iomem *reg;
+       struct clk_factors_config *config;
+       void (*get_factors) (u32 *rate, u32 parent, u8 *n, u8 *k, u8 *m, u8 *p);
+       spinlock_t *lock;
+};
+
+#define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw)
+
+#define SETMASK(len, pos)              (((-1U) >> (31-len))  << (pos))
+#define CLRMASK(len, pos)              (~(SETMASK(len, pos)))
+#define FACTOR_GET(bit, len, reg)      (((reg) & SETMASK(len, bit)) >> (bit))
+
+#define FACTOR_SET(bit, len, reg, val) \
+       (((reg) & CLRMASK(len, bit)) | (val << (bit)))
+
+static unsigned long clk_factors_recalc_rate(struct clk_hw *hw,
+                                            unsigned long parent_rate)
+{
+       u8 n = 1, k = 0, p = 0, m = 0;
+       u32 reg;
+       unsigned long rate;
+       struct clk_factors *factors = to_clk_factors(hw);
+       struct clk_factors_config *config = factors->config;
+
+       /* Fetch the register value */
+       reg = readl(factors->reg);
+
+       /* Get each individual factor if applicable */
+       if (config->nwidth != SUNXI_FACTORS_NOT_APPLICABLE)
+               n = FACTOR_GET(config->nshift, config->nwidth, reg);
+       if (config->kwidth != SUNXI_FACTORS_NOT_APPLICABLE)
+               k = FACTOR_GET(config->kshift, config->kwidth, reg);
+       if (config->mwidth != SUNXI_FACTORS_NOT_APPLICABLE)
+               m = FACTOR_GET(config->mshift, config->mwidth, reg);
+       if (config->pwidth != SUNXI_FACTORS_NOT_APPLICABLE)
+               p = FACTOR_GET(config->pshift, config->pwidth, reg);
+
+       /* Calculate the rate */
+       rate = (parent_rate * n * (k + 1) >> p) / (m + 1);
+
+       return rate;
+}
+
+static long clk_factors_round_rate(struct clk_hw *hw, unsigned long rate,
+                                  unsigned long *parent_rate)
+{
+       struct clk_factors *factors = to_clk_factors(hw);
+       factors->get_factors((u32 *)&rate, (u32)*parent_rate,
+                            NULL, NULL, NULL, NULL);
+
+       return rate;
+}
+
+static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate,
+                               unsigned long parent_rate)
+{
+       u8 n, k, m, p;
+       u32 reg;
+       struct clk_factors *factors = to_clk_factors(hw);
+       struct clk_factors_config *config = factors->config;
+       unsigned long flags = 0;
+
+       factors->get_factors((u32 *)&rate, (u32)parent_rate, &n, &k, &m, &p);
+
+       if (factors->lock)
+               spin_lock_irqsave(factors->lock, flags);
+
+       /* Fetch the register value */
+       reg = readl(factors->reg);
+
+       /* Set up the new factors - macros do not do anything if width is 0 */
+       reg = FACTOR_SET(config->nshift, config->nwidth, reg, n);
+       reg = FACTOR_SET(config->kshift, config->kwidth, reg, k);
+       reg = FACTOR_SET(config->mshift, config->mwidth, reg, m);
+       reg = FACTOR_SET(config->pshift, config->pwidth, reg, p);
+
+       /* Apply them now */
+       writel(reg, factors->reg);
+
+       /* delay 500us so pll stabilizes */
+       __delay((rate >> 20) * 500 / 2);
+
+       if (factors->lock)
+               spin_unlock_irqrestore(factors->lock, flags);
+
+       return 0;
+}
+
+static const struct clk_ops clk_factors_ops = {
+       .recalc_rate = clk_factors_recalc_rate,
+       .round_rate = clk_factors_round_rate,
+       .set_rate = clk_factors_set_rate,
+};
+
+/**
+ * clk_register_factors - register a factors clock with
+ * the clock framework
+ * @dev: device registering this clock
+ * @name: name of this clock
+ * @parent_name: name of clock's parent
+ * @flags: framework-specific flags
+ * @reg: register address to adjust factors
+ * @config: shift and width of factors n, k, m and p
+ * @get_factors: function to calculate the factors for a given frequency
+ * @lock: shared register lock for this clock
+ */
+struct clk *clk_register_factors(struct device *dev, const char *name,
+                                const char *parent_name,
+                                unsigned long flags, void __iomem *reg,
+                                struct clk_factors_config *config,
+                                void (*get_factors)(u32 *rate, u32 parent,
+                                                    u8 *n, u8 *k, u8 *m, u8 *p),
+                                spinlock_t *lock)
+{
+       struct clk_factors *factors;
+       struct clk *clk;
+       struct clk_init_data init;
+
+       /* allocate the factors */
+       factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL);
+       if (!factors) {
+               pr_err("%s: could not allocate factors clk\n", __func__);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       init.name = name;
+       init.ops = &clk_factors_ops;
+       init.flags = flags;
+       init.parent_names = (parent_name ? &parent_name : NULL);
+       init.num_parents = (parent_name ? 1 : 0);
+
+       /* struct clk_factors assignments */
+       factors->reg = reg;
+       factors->config = config;
+       factors->lock = lock;
+       factors->hw.init = &init;
+       factors->get_factors = get_factors;
+
+       /* register the clock */
+       clk = clk_register(dev, &factors->hw);
+
+       if (IS_ERR(clk))
+               kfree(factors);
+
+       return clk;
+}
diff --git a/drivers/clk/sunxi/clk-factors.h b/drivers/clk/sunxi/clk-factors.h
new file mode 100644 (file)
index 0000000..f49851c
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef __MACH_SUNXI_CLK_FACTORS_H
+#define __MACH_SUNXI_CLK_FACTORS_H
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+
+#define SUNXI_FACTORS_NOT_APPLICABLE   (0)
+
+struct clk_factors_config {
+       u8 nshift;
+       u8 nwidth;
+       u8 kshift;
+       u8 kwidth;
+       u8 mshift;
+       u8 mwidth;
+       u8 pshift;
+       u8 pwidth;
+};
+
+struct clk *clk_register_factors(struct device *dev, const char *name,
+                                const char *parent_name,
+                                unsigned long flags, void __iomem *reg,
+                                struct clk_factors_config *config,
+                                void (*get_factors) (u32 *rate, u32 parent_rate,
+                                                     u8 *n, u8 *k, u8 *m, u8 *p),
+                                spinlock_t *lock);
+#endif
diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c
new file mode 100644 (file)
index 0000000..8492ad1
--- /dev/null
@@ -0,0 +1,469 @@
+/*
+ * Copyright 2013 Emilio López
+ *
+ * Emilio López <emilio@elopez.com.ar>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/clk/sunxi.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+#include "clk-factors.h"
+
+static DEFINE_SPINLOCK(clk_lock);
+
+/**
+ * sunxi_osc_clk_setup() - Setup function for gatable oscillator
+ */
+
+#define SUNXI_OSC24M_GATE      0
+
+static void __init sunxi_osc_clk_setup(struct device_node *node)
+{
+       struct clk *clk;
+       struct clk_fixed_rate *fixed;
+       struct clk_gate *gate;
+       const char *clk_name = node->name;
+       u32 rate;
+
+       /* allocate fixed-rate and gate clock structs */
+       fixed = kzalloc(sizeof(struct clk_fixed_rate), GFP_KERNEL);
+       if (!fixed)
+               return;
+       gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
+       if (!gate) {
+               kfree(fixed);
+               return;
+       }
+
+       if (of_property_read_u32(node, "clock-frequency", &rate))
+               return;
+
+       /* set up gate and fixed rate properties */
+       gate->reg = of_iomap(node, 0);
+       gate->bit_idx = SUNXI_OSC24M_GATE;
+       gate->lock = &clk_lock;
+       fixed->fixed_rate = rate;
+
+       clk = clk_register_composite(NULL, clk_name,
+                       NULL, 0,
+                       NULL, NULL,
+                       &fixed->hw, &clk_fixed_rate_ops,
+                       &gate->hw, &clk_gate_ops,
+                       CLK_IS_ROOT);
+
+       if (clk) {
+               of_clk_add_provider(node, of_clk_src_simple_get, clk);
+               clk_register_clkdev(clk, clk_name, NULL);
+       }
+}
+
+
+
+/**
+ * sunxi_get_pll1_factors() - calculates n, k, m, p factors for PLL1
+ * PLL1 rate is calculated as follows
+ * rate = (parent_rate * n * (k + 1) >> p) / (m + 1);
+ * parent_rate is always 24Mhz
+ */
+
+static void sunxi_get_pll1_factors(u32 *freq, u32 parent_rate,
+                                  u8 *n, u8 *k, u8 *m, u8 *p)
+{
+       u8 div;
+
+       /* Normalize value to a 6M multiple */
+       div = *freq / 6000000;
+       *freq = 6000000 * div;
+
+       /* we were called to round the frequency, we can now return */
+       if (n == NULL)
+               return;
+
+       /* m is always zero for pll1 */
+       *m = 0;
+
+       /* k is 1 only on these cases */
+       if (*freq >= 768000000 || *freq == 42000000 || *freq == 54000000)
+               *k = 1;
+       else
+               *k = 0;
+
+       /* p will be 3 for divs under 10 */
+       if (div < 10)
+               *p = 3;
+
+       /* p will be 2 for divs between 10 - 20 and odd divs under 32 */
+       else if (div < 20 || (div < 32 && (div & 1)))
+               *p = 2;
+
+       /* p will be 1 for even divs under 32, divs under 40 and odd pairs
+        * of divs between 40-62 */
+       else if (div < 40 || (div < 64 && (div & 2)))
+               *p = 1;
+
+       /* any other entries have p = 0 */
+       else
+               *p = 0;
+
+       /* calculate a suitable n based on k and p */
+       div <<= *p;
+       div /= (*k + 1);
+       *n = div / 4;
+}
+
+
+
+/**
+ * sunxi_get_apb1_factors() - calculates m, p factors for APB1
+ * APB1 rate is calculated as follows
+ * rate = (parent_rate >> p) / (m + 1);
+ */
+
+static void sunxi_get_apb1_factors(u32 *freq, u32 parent_rate,
+                                  u8 *n, u8 *k, u8 *m, u8 *p)
+{
+       u8 calcm, calcp;
+
+       if (parent_rate < *freq)
+               *freq = parent_rate;
+
+       parent_rate = (parent_rate + (*freq - 1)) / *freq;
+
+       /* Invalid rate! */
+       if (parent_rate > 32)
+               return;
+
+       if (parent_rate <= 4)
+               calcp = 0;
+       else if (parent_rate <= 8)
+               calcp = 1;
+       else if (parent_rate <= 16)
+               calcp = 2;
+       else
+               calcp = 3;
+
+       calcm = (parent_rate >> calcp) - 1;
+
+       *freq = (parent_rate >> calcp) / (calcm + 1);
+
+       /* we were called to round the frequency, we can now return */
+       if (n == NULL)
+               return;
+
+       *m = calcm;
+       *p = calcp;
+}
+
+
+
+/**
+ * sunxi_factors_clk_setup() - Setup function for factor clocks
+ */
+
+struct factors_data {
+       struct clk_factors_config *table;
+       void (*getter) (u32 *rate, u32 parent_rate, u8 *n, u8 *k, u8 *m, u8 *p);
+};
+
+static struct clk_factors_config pll1_config = {
+       .nshift = 8,
+       .nwidth = 5,
+       .kshift = 4,
+       .kwidth = 2,
+       .mshift = 0,
+       .mwidth = 2,
+       .pshift = 16,
+       .pwidth = 2,
+};
+
+static struct clk_factors_config apb1_config = {
+       .mshift = 0,
+       .mwidth = 5,
+       .pshift = 16,
+       .pwidth = 2,
+};
+
+static const __initconst struct factors_data pll1_data = {
+       .table = &pll1_config,
+       .getter = sunxi_get_pll1_factors,
+};
+
+static const __initconst struct factors_data apb1_data = {
+       .table = &apb1_config,
+       .getter = sunxi_get_apb1_factors,
+};
+
+static void __init sunxi_factors_clk_setup(struct device_node *node,
+                                          struct factors_data *data)
+{
+       struct clk *clk;
+       const char *clk_name = node->name;
+       const char *parent;
+       void *reg;
+
+       reg = of_iomap(node, 0);
+
+       parent = of_clk_get_parent_name(node, 0);
+
+       clk = clk_register_factors(NULL, clk_name, parent, 0, reg,
+                                  data->table, data->getter, &clk_lock);
+
+       if (clk) {
+               of_clk_add_provider(node, of_clk_src_simple_get, clk);
+               clk_register_clkdev(clk, clk_name, NULL);
+       }
+}
+
+
+
+/**
+ * sunxi_mux_clk_setup() - Setup function for muxes
+ */
+
+#define SUNXI_MUX_GATE_WIDTH   2
+
+struct mux_data {
+       u8 shift;
+};
+
+static const __initconst struct mux_data cpu_data = {
+       .shift = 16,
+};
+
+static const __initconst struct mux_data apb1_mux_data = {
+       .shift = 24,
+};
+
+static void __init sunxi_mux_clk_setup(struct device_node *node,
+                                      struct mux_data *data)
+{
+       struct clk *clk;
+       const char *clk_name = node->name;
+       const char *parents[5];
+       void *reg;
+       int i = 0;
+
+       reg = of_iomap(node, 0);
+
+       while (i < 5 && (parents[i] = of_clk_get_parent_name(node, i)) != NULL)
+               i++;
+
+       clk = clk_register_mux(NULL, clk_name, parents, i, 0, reg,
+                              data->shift, SUNXI_MUX_GATE_WIDTH,
+                              0, &clk_lock);
+
+       if (clk) {
+               of_clk_add_provider(node, of_clk_src_simple_get, clk);
+               clk_register_clkdev(clk, clk_name, NULL);
+       }
+}
+
+
+
+/**
+ * sunxi_divider_clk_setup() - Setup function for simple divider clocks
+ */
+
+#define SUNXI_DIVISOR_WIDTH    2
+
+struct div_data {
+       u8 shift;
+       u8 pow;
+};
+
+static const __initconst struct div_data axi_data = {
+       .shift = 0,
+       .pow = 0,
+};
+
+static const __initconst struct div_data ahb_data = {
+       .shift = 4,
+       .pow = 1,
+};
+
+static const __initconst struct div_data apb0_data = {
+       .shift = 8,
+       .pow = 1,
+};
+
+static void __init sunxi_divider_clk_setup(struct device_node *node,
+                                          struct div_data *data)
+{
+       struct clk *clk;
+       const char *clk_name = node->name;
+       const char *clk_parent;
+       void *reg;
+
+       reg = of_iomap(node, 0);
+
+       clk_parent = of_clk_get_parent_name(node, 0);
+
+       clk = clk_register_divider(NULL, clk_name, clk_parent, 0,
+                                  reg, data->shift, SUNXI_DIVISOR_WIDTH,
+                                  data->pow ? CLK_DIVIDER_POWER_OF_TWO : 0,
+                                  &clk_lock);
+       if (clk) {
+               of_clk_add_provider(node, of_clk_src_simple_get, clk);
+               clk_register_clkdev(clk, clk_name, NULL);
+       }
+}
+
+
+
+/**
+ * sunxi_gates_clk_setup() - Setup function for leaf gates on clocks
+ */
+
+#define SUNXI_GATES_MAX_SIZE   64
+
+struct gates_data {
+       DECLARE_BITMAP(mask, SUNXI_GATES_MAX_SIZE);
+};
+
+static const __initconst struct gates_data axi_gates_data = {
+       .mask = {1},
+};
+
+static const __initconst struct gates_data ahb_gates_data = {
+       .mask = {0x7F77FFF, 0x14FB3F},
+};
+
+static const __initconst struct gates_data apb0_gates_data = {
+       .mask = {0x4EF},
+};
+
+static const __initconst struct gates_data apb1_gates_data = {
+       .mask = {0xFF00F7},
+};
+
+static void __init sunxi_gates_clk_setup(struct device_node *node,
+                                        struct gates_data *data)
+{
+       struct clk_onecell_data *clk_data;
+       const char *clk_parent;
+       const char *clk_name;
+       void *reg;
+       int qty;
+       int i = 0;
+       int j = 0;
+       int ignore;
+
+       reg = of_iomap(node, 0);
+
+       clk_parent = of_clk_get_parent_name(node, 0);
+
+       /* Worst-case size approximation and memory allocation */
+       qty = find_last_bit(data->mask, SUNXI_GATES_MAX_SIZE);
+       clk_data = kmalloc(sizeof(struct clk_onecell_data), GFP_KERNEL);
+       if (!clk_data)
+               return;
+       clk_data->clks = kzalloc((qty+1) * sizeof(struct clk *), GFP_KERNEL);
+       if (!clk_data->clks) {
+               kfree(clk_data);
+               return;
+       }
+
+       for_each_set_bit(i, data->mask, SUNXI_GATES_MAX_SIZE) {
+               of_property_read_string_index(node, "clock-output-names",
+                                             j, &clk_name);
+
+               /* No driver claims this clock, but it should remain gated */
+               ignore = !strcmp("ahb_sdram", clk_name) ? CLK_IGNORE_UNUSED : 0;
+
+               clk_data->clks[i] = clk_register_gate(NULL, clk_name,
+                                                     clk_parent, ignore,
+                                                     reg + 4 * (i/32), i % 32,
+                                                     0, &clk_lock);
+               WARN_ON(IS_ERR(clk_data->clks[i]));
+
+               j++;
+       }
+
+       /* Adjust to the real max */
+       clk_data->clk_num = i;
+
+       of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+}
+
+/* Matches for of_clk_init */
+static const __initconst struct of_device_id clk_match[] = {
+       {.compatible = "allwinner,sun4i-osc-clk", .data = sunxi_osc_clk_setup,},
+       {}
+};
+
+/* Matches for factors clocks */
+static const __initconst struct of_device_id clk_factors_match[] = {
+       {.compatible = "allwinner,sun4i-pll1-clk", .data = &pll1_data,},
+       {.compatible = "allwinner,sun4i-apb1-clk", .data = &apb1_data,},
+       {}
+};
+
+/* Matches for divider clocks */
+static const __initconst struct of_device_id clk_div_match[] = {
+       {.compatible = "allwinner,sun4i-axi-clk", .data = &axi_data,},
+       {.compatible = "allwinner,sun4i-ahb-clk", .data = &ahb_data,},
+       {.compatible = "allwinner,sun4i-apb0-clk", .data = &apb0_data,},
+       {}
+};
+
+/* Matches for mux clocks */
+static const __initconst struct of_device_id clk_mux_match[] = {
+       {.compatible = "allwinner,sun4i-cpu-clk", .data = &cpu_data,},
+       {.compatible = "allwinner,sun4i-apb1-mux-clk", .data = &apb1_mux_data,},
+       {}
+};
+
+/* Matches for gate clocks */
+static const __initconst struct of_device_id clk_gates_match[] = {
+       {.compatible = "allwinner,sun4i-axi-gates-clk", .data = &axi_gates_data,},
+       {.compatible = "allwinner,sun4i-ahb-gates-clk", .data = &ahb_gates_data,},
+       {.compatible = "allwinner,sun4i-apb0-gates-clk", .data = &apb0_gates_data,},
+       {.compatible = "allwinner,sun4i-apb1-gates-clk", .data = &apb1_gates_data,},
+       {}
+};
+
+static void __init of_sunxi_table_clock_setup(const struct of_device_id *clk_match,
+                                             void *function)
+{
+       struct device_node *np;
+       const struct div_data *data;
+       const struct of_device_id *match;
+       void (*setup_function)(struct device_node *, const void *) = function;
+
+       for_each_matching_node(np, clk_match) {
+               match = of_match_node(clk_match, np);
+               data = match->data;
+               setup_function(np, data);
+       }
+}
+
+void __init sunxi_init_clocks(void)
+{
+       /* Register all the simple sunxi clocks on DT */
+       of_clk_init(clk_match);
+
+       /* Register factor clocks */
+       of_sunxi_table_clock_setup(clk_factors_match, sunxi_factors_clk_setup);
+
+       /* Register divider clocks */
+       of_sunxi_table_clock_setup(clk_div_match, sunxi_divider_clk_setup);
+
+       /* Register mux clocks */
+       of_sunxi_table_clock_setup(clk_mux_match, sunxi_mux_clk_setup);
+
+       /* Register gate clocks */
+       of_sunxi_table_clock_setup(clk_gates_match, sunxi_gates_clk_setup);
+}
index 0744731..a09d7dc 100644 (file)
@@ -355,15 +355,16 @@ struct clk *tegra_clk_register_periph_nodiv(const char *name,
                struct tegra_clk_periph *periph, void __iomem *clk_base,
                u32 offset);
 
-#define TEGRA_CLK_PERIPH(_mux_shift, _mux_width, _mux_flags,           \
+#define TEGRA_CLK_PERIPH(_mux_shift, _mux_mask, _mux_flags,            \
                         _div_shift, _div_width, _div_frac_width,       \
                         _div_flags, _clk_num, _enb_refcnt, _regs,      \
-                        _gate_flags)                                   \
+                        _gate_flags, _table)                           \
        {                                                               \
                .mux = {                                                \
                        .flags = _mux_flags,                            \
                        .shift = _mux_shift,                            \
-                       .width = _mux_width,                            \
+                       .mask = _mux_mask,                              \
+                       .table = _table,                                \
                },                                                      \
                .divider = {                                            \
                        .flags = _div_flags,                            \
@@ -393,26 +394,36 @@ struct tegra_periph_init_data {
        const char *dev_id;
 };
 
-#define TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parent_names, _offset, \
-                       _mux_shift, _mux_width, _mux_flags, _div_shift, \
+#define TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parent_names, _offset,\
+                       _mux_shift, _mux_mask, _mux_flags, _div_shift,  \
                        _div_width, _div_frac_width, _div_flags, _regs, \
-                       _clk_num, _enb_refcnt, _gate_flags, _clk_id)    \
+                       _clk_num, _enb_refcnt, _gate_flags, _clk_id, _table) \
        {                                                               \
                .name = _name,                                          \
                .clk_id = _clk_id,                                      \
                .parent_names = _parent_names,                          \
                .num_parents = ARRAY_SIZE(_parent_names),               \
-               .periph = TEGRA_CLK_PERIPH(_mux_shift, _mux_width,      \
+               .periph = TEGRA_CLK_PERIPH(_mux_shift, _mux_mask,       \
                                           _mux_flags, _div_shift,      \
                                           _div_width, _div_frac_width, \
                                           _div_flags, _clk_num,        \
                                           _enb_refcnt, _regs,          \
-                                          _gate_flags),                \
+                                          _gate_flags, _table),        \
                .offset = _offset,                                      \
                .con_id = _con_id,                                      \
                .dev_id = _dev_id,                                      \
        }
 
+#define TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parent_names, _offset,\
+                       _mux_shift, _mux_width, _mux_flags, _div_shift, \
+                       _div_width, _div_frac_width, _div_flags, _regs, \
+                       _clk_num, _enb_refcnt, _gate_flags, _clk_id)    \
+       TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parent_names, _offset,\
+                       _mux_shift, BIT(_mux_width) - 1, _mux_flags,    \
+                       _div_shift, _div_width, _div_frac_width, _div_flags, \
+                       _regs, _clk_num, _enb_refcnt, _gate_flags, _clk_id,\
+                       NULL)
+
 /**
  * struct clk_super_mux - super clock
  *
index bcc0c11..c6a806e 100644 (file)
@@ -5,6 +5,7 @@
 # Clock types
 obj-y += clk-prcc.o
 obj-y += clk-prcmu.o
+obj-y += clk-sysctrl.o
 
 # Clock definitions
 obj-y += u8500_clk.o
index 9f7400d..a0fca00 100644 (file)
 #include <linux/device.h>
 #include <linux/platform_device.h>
 #include <linux/mfd/abx500/ab8500.h>
-
-/* TODO: Add clock implementations here */
-
+#include <linux/mfd/abx500/ab8500-sysctrl.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/mfd/dbx500-prcmu.h>
+#include "clk.h"
 
 /* Clock definitions for ab8500 */
 static int ab8500_reg_clks(struct device *dev)
 {
+       int ret;
+       struct clk *clk;
+
+       const char *intclk_parents[] = {"ab8500_sysclk", "ulpclk"};
+       u16 intclk_reg_sel[] = {0 , AB8500_SYSULPCLKCTRL1};
+       u8 intclk_reg_mask[] = {0 , AB8500_SYSULPCLKCTRL1_SYSULPCLKINTSEL_MASK};
+       u8 intclk_reg_bits[] = {
+               0 ,
+               (1 << AB8500_SYSULPCLKCTRL1_SYSULPCLKINTSEL_SHIFT)
+       };
+
+       dev_info(dev, "register clocks for ab850x\n");
+
+       /* Enable SWAT */
+       ret = ab8500_sysctrl_set(AB8500_SWATCTRL, AB8500_SWATCTRL_SWATENABLE);
+       if (ret)
+               return ret;
+
+       /* ab8500_sysclk */
+       clk = clk_reg_prcmu_gate("ab8500_sysclk", NULL, PRCMU_SYSCLK,
+                               CLK_IS_ROOT);
+       clk_register_clkdev(clk, "sysclk", "ab8500-usb.0");
+       clk_register_clkdev(clk, "sysclk", "ab-iddet.0");
+       clk_register_clkdev(clk, "sysclk", "ab85xx-codec.0");
+       clk_register_clkdev(clk, "sysclk", "shrm_bus");
+
+       /* ab8500_sysclk2 */
+       clk = clk_reg_sysctrl_gate(dev , "ab8500_sysclk2", "ab8500_sysclk",
+               AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_SYSCLKBUF2REQ,
+               AB8500_SYSULPCLKCTRL1_SYSCLKBUF2REQ, 0, 0);
+       clk_register_clkdev(clk, "sysclk", "0-0070");
+
+       /* ab8500_sysclk3 */
+       clk = clk_reg_sysctrl_gate(dev , "ab8500_sysclk3", "ab8500_sysclk",
+               AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_SYSCLKBUF3REQ,
+               AB8500_SYSULPCLKCTRL1_SYSCLKBUF3REQ, 0, 0);
+       clk_register_clkdev(clk, "sysclk", "cg1960_core.0");
+
+       /* ab8500_sysclk4 */
+       clk = clk_reg_sysctrl_gate(dev , "ab8500_sysclk4", "ab8500_sysclk",
+               AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_SYSCLKBUF4REQ,
+               AB8500_SYSULPCLKCTRL1_SYSCLKBUF4REQ, 0, 0);
+
+       /* ab_ulpclk */
+       clk = clk_reg_sysctrl_gate_fixed_rate(dev, "ulpclk", NULL,
+               AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_ULPCLKREQ,
+               AB8500_SYSULPCLKCTRL1_ULPCLKREQ,
+               38400000, 9000, CLK_IS_ROOT);
+       clk_register_clkdev(clk, "ulpclk", "ab85xx-codec.0");
+
+       /* ab8500_intclk */
+       clk = clk_reg_sysctrl_set_parent(dev , "intclk", intclk_parents, 2,
+               intclk_reg_sel, intclk_reg_mask, intclk_reg_bits, 0);
+       clk_register_clkdev(clk, "intclk", "ab85xx-codec.0");
+       clk_register_clkdev(clk, NULL, "ab8500-pwm.1");
+
+       /* ab8500_audioclk */
+       clk = clk_reg_sysctrl_gate(dev , "audioclk", "intclk",
+               AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_AUDIOCLKENA,
+               AB8500_SYSULPCLKCTRL1_AUDIOCLKENA, 0, 0);
+       clk_register_clkdev(clk, "audioclk", "ab85xx-codec.0");
+
        return 0;
 }
 
index 74faa7e..293a288 100644 (file)
 struct clk_prcmu {
        struct clk_hw hw;
        u8 cg_sel;
+       int is_prepared;
        int is_enabled;
+       int opp_requested;
 };
 
 /* PRCMU clock operations. */
 
 static int clk_prcmu_prepare(struct clk_hw *hw)
 {
+       int ret;
        struct clk_prcmu *clk = to_clk_prcmu(hw);
-       return prcmu_request_clock(clk->cg_sel, true);
+
+       ret = prcmu_request_clock(clk->cg_sel, true);
+       if (!ret)
+               clk->is_prepared = 1;
+
+       return ret;;
 }
 
 static void clk_prcmu_unprepare(struct clk_hw *hw)
@@ -36,7 +44,15 @@ static void clk_prcmu_unprepare(struct clk_hw *hw)
        struct clk_prcmu *clk = to_clk_prcmu(hw);
        if (prcmu_request_clock(clk->cg_sel, false))
                pr_err("clk_prcmu: %s failed to disable %s.\n", __func__,
-                       hw->init->name);
+                       __clk_get_name(hw->clk));
+       else
+               clk->is_prepared = 0;
+}
+
+static int clk_prcmu_is_prepared(struct clk_hw *hw)
+{
+       struct clk_prcmu *clk = to_clk_prcmu(hw);
+       return clk->is_prepared;
 }
 
 static int clk_prcmu_enable(struct clk_hw *hw)
@@ -79,58 +95,52 @@ static int clk_prcmu_set_rate(struct clk_hw *hw, unsigned long rate,
        return prcmu_set_clock_rate(clk->cg_sel, rate);
 }
 
-static int request_ape_opp100(bool enable)
-{
-       static int reqs;
-       int err = 0;
-
-       if (enable) {
-               if (!reqs)
-                       err = prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP,
-                                                       "clock", 100);
-               if (!err)
-                       reqs++;
-       } else {
-               reqs--;
-               if (!reqs)
-                       prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP,
-                                               "clock");
-       }
-       return err;
-}
-
 static int clk_prcmu_opp_prepare(struct clk_hw *hw)
 {
        int err;
        struct clk_prcmu *clk = to_clk_prcmu(hw);
 
-       err = request_ape_opp100(true);
-       if (err) {
-               pr_err("clk_prcmu: %s failed to request APE OPP100 for %s.\n",
-                       __func__, hw->init->name);
-               return err;
+       if (!clk->opp_requested) {
+               err = prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP,
+                                               (char *)__clk_get_name(hw->clk),
+                                               100);
+               if (err) {
+                       pr_err("clk_prcmu: %s fail req APE OPP for %s.\n",
+                               __func__, __clk_get_name(hw->clk));
+                       return err;
+               }
+               clk->opp_requested = 1;
        }
 
        err = prcmu_request_clock(clk->cg_sel, true);
-       if (err)
-               request_ape_opp100(false);
+       if (err) {
+               prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP,
+                                       (char *)__clk_get_name(hw->clk));
+               clk->opp_requested = 0;
+               return err;
+       }
 
-       return err;
+       clk->is_prepared = 1;
+       return 0;
 }
 
 static void clk_prcmu_opp_unprepare(struct clk_hw *hw)
 {
        struct clk_prcmu *clk = to_clk_prcmu(hw);
 
-       if (prcmu_request_clock(clk->cg_sel, false))
-               goto out_error;
-       if (request_ape_opp100(false))
-               goto out_error;
-       return;
-
-out_error:
-       pr_err("clk_prcmu: %s failed to disable %s.\n", __func__,
-               hw->init->name);
+       if (prcmu_request_clock(clk->cg_sel, false)) {
+               pr_err("clk_prcmu: %s failed to disable %s.\n", __func__,
+                       __clk_get_name(hw->clk));
+               return;
+       }
+
+       if (clk->opp_requested) {
+               prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP,
+                                       (char *)__clk_get_name(hw->clk));
+               clk->opp_requested = 0;
+       }
+
+       clk->is_prepared = 0;
 }
 
 static int clk_prcmu_opp_volt_prepare(struct clk_hw *hw)
@@ -138,38 +148,49 @@ static int clk_prcmu_opp_volt_prepare(struct clk_hw *hw)
        int err;
        struct clk_prcmu *clk = to_clk_prcmu(hw);
 
-       err = prcmu_request_ape_opp_100_voltage(true);
-       if (err) {
-               pr_err("clk_prcmu: %s failed to request APE OPP VOLT for %s.\n",
-                       __func__, hw->init->name);
-               return err;
+       if (!clk->opp_requested) {
+               err = prcmu_request_ape_opp_100_voltage(true);
+               if (err) {
+                       pr_err("clk_prcmu: %s fail req APE OPP VOLT for %s.\n",
+                               __func__, __clk_get_name(hw->clk));
+                       return err;
+               }
+               clk->opp_requested = 1;
        }
 
        err = prcmu_request_clock(clk->cg_sel, true);
-       if (err)
+       if (err) {
                prcmu_request_ape_opp_100_voltage(false);
+               clk->opp_requested = 0;
+               return err;
+       }
 
-       return err;
+       clk->is_prepared = 1;
+       return 0;
 }
 
 static void clk_prcmu_opp_volt_unprepare(struct clk_hw *hw)
 {
        struct clk_prcmu *clk = to_clk_prcmu(hw);
 
-       if (prcmu_request_clock(clk->cg_sel, false))
-               goto out_error;
-       if (prcmu_request_ape_opp_100_voltage(false))
-               goto out_error;
-       return;
-
-out_error:
-       pr_err("clk_prcmu: %s failed to disable %s.\n", __func__,
-               hw->init->name);
+       if (prcmu_request_clock(clk->cg_sel, false)) {
+               pr_err("clk_prcmu: %s failed to disable %s.\n", __func__,
+                       __clk_get_name(hw->clk));
+               return;
+       }
+
+       if (clk->opp_requested) {
+               prcmu_request_ape_opp_100_voltage(false);
+               clk->opp_requested = 0;
+       }
+
+       clk->is_prepared = 0;
 }
 
 static struct clk_ops clk_prcmu_scalable_ops = {
        .prepare = clk_prcmu_prepare,
        .unprepare = clk_prcmu_unprepare,
+       .is_prepared = clk_prcmu_is_prepared,
        .enable = clk_prcmu_enable,
        .disable = clk_prcmu_disable,
        .is_enabled = clk_prcmu_is_enabled,
@@ -181,6 +202,7 @@ static struct clk_ops clk_prcmu_scalable_ops = {
 static struct clk_ops clk_prcmu_gate_ops = {
        .prepare = clk_prcmu_prepare,
        .unprepare = clk_prcmu_unprepare,
+       .is_prepared = clk_prcmu_is_prepared,
        .enable = clk_prcmu_enable,
        .disable = clk_prcmu_disable,
        .is_enabled = clk_prcmu_is_enabled,
@@ -202,6 +224,7 @@ static struct clk_ops clk_prcmu_rate_ops = {
 static struct clk_ops clk_prcmu_opp_gate_ops = {
        .prepare = clk_prcmu_opp_prepare,
        .unprepare = clk_prcmu_opp_unprepare,
+       .is_prepared = clk_prcmu_is_prepared,
        .enable = clk_prcmu_enable,
        .disable = clk_prcmu_disable,
        .is_enabled = clk_prcmu_is_enabled,
@@ -211,6 +234,7 @@ static struct clk_ops clk_prcmu_opp_gate_ops = {
 static struct clk_ops clk_prcmu_opp_volt_scalable_ops = {
        .prepare = clk_prcmu_opp_volt_prepare,
        .unprepare = clk_prcmu_opp_volt_unprepare,
+       .is_prepared = clk_prcmu_is_prepared,
        .enable = clk_prcmu_enable,
        .disable = clk_prcmu_disable,
        .is_enabled = clk_prcmu_is_enabled,
@@ -242,7 +266,9 @@ static struct clk *clk_reg_prcmu(const char *name,
        }
 
        clk->cg_sel = cg_sel;
+       clk->is_prepared = 1;
        clk->is_enabled = 1;
+       clk->opp_requested = 0;
        /* "rate" can be used for changing the initial frequency */
        if (rate)
                prcmu_set_clock_rate(cg_sel, rate);
diff --git a/drivers/clk/ux500/clk-sysctrl.c b/drivers/clk/ux500/clk-sysctrl.c
new file mode 100644 (file)
index 0000000..bc7e9bd
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * Sysctrl clock implementation for ux500 platform.
+ *
+ * Copyright (C) 2013 ST-Ericsson SA
+ * Author: Ulf Hansson <ulf.hansson@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/mfd/abx500/ab8500-sysctrl.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include "clk.h"
+
+#define SYSCTRL_MAX_NUM_PARENTS 4
+
+#define to_clk_sysctrl(_hw) container_of(_hw, struct clk_sysctrl, hw)
+
+struct clk_sysctrl {
+       struct clk_hw hw;
+       struct device *dev;
+       u8 parent_index;
+       u16 reg_sel[SYSCTRL_MAX_NUM_PARENTS];
+       u8 reg_mask[SYSCTRL_MAX_NUM_PARENTS];
+       u8 reg_bits[SYSCTRL_MAX_NUM_PARENTS];
+       unsigned long rate;
+       unsigned long enable_delay_us;
+};
+
+/* Sysctrl clock operations. */
+
+static int clk_sysctrl_prepare(struct clk_hw *hw)
+{
+       int ret;
+       struct clk_sysctrl *clk = to_clk_sysctrl(hw);
+
+       ret = ab8500_sysctrl_write(clk->reg_sel[0], clk->reg_mask[0],
+                               clk->reg_bits[0]);
+
+       if (!ret && clk->enable_delay_us)
+               usleep_range(clk->enable_delay_us, clk->enable_delay_us);
+
+       return ret;
+}
+
+static void clk_sysctrl_unprepare(struct clk_hw *hw)
+{
+       struct clk_sysctrl *clk = to_clk_sysctrl(hw);
+       if (ab8500_sysctrl_clear(clk->reg_sel[0], clk->reg_mask[0]))
+               dev_err(clk->dev, "clk_sysctrl: %s fail to clear %s.\n",
+                       __func__, __clk_get_name(hw->clk));
+}
+
+static unsigned long clk_sysctrl_recalc_rate(struct clk_hw *hw,
+                                       unsigned long parent_rate)
+{
+       struct clk_sysctrl *clk = to_clk_sysctrl(hw);
+       return clk->rate;
+}
+
+static int clk_sysctrl_set_parent(struct clk_hw *hw, u8 index)
+{
+       struct clk_sysctrl *clk = to_clk_sysctrl(hw);
+       u8 old_index = clk->parent_index;
+       int ret = 0;
+
+       if (clk->reg_sel[old_index]) {
+               ret = ab8500_sysctrl_clear(clk->reg_sel[old_index],
+                                       clk->reg_mask[old_index]);
+               if (ret)
+                       return ret;
+       }
+
+       if (clk->reg_sel[index]) {
+               ret = ab8500_sysctrl_write(clk->reg_sel[index],
+                                       clk->reg_mask[index],
+                                       clk->reg_bits[index]);
+               if (ret) {
+                       if (clk->reg_sel[old_index])
+                               ab8500_sysctrl_write(clk->reg_sel[old_index],
+                                               clk->reg_mask[old_index],
+                                               clk->reg_bits[old_index]);
+                       return ret;
+               }
+       }
+       clk->parent_index = index;
+
+       return ret;
+}
+
+static u8 clk_sysctrl_get_parent(struct clk_hw *hw)
+{
+       struct clk_sysctrl *clk = to_clk_sysctrl(hw);
+       return clk->parent_index;
+}
+
+static struct clk_ops clk_sysctrl_gate_ops = {
+       .prepare = clk_sysctrl_prepare,
+       .unprepare = clk_sysctrl_unprepare,
+};
+
+static struct clk_ops clk_sysctrl_gate_fixed_rate_ops = {
+       .prepare = clk_sysctrl_prepare,
+       .unprepare = clk_sysctrl_unprepare,
+       .recalc_rate = clk_sysctrl_recalc_rate,
+};
+
+static struct clk_ops clk_sysctrl_set_parent_ops = {
+       .set_parent = clk_sysctrl_set_parent,
+       .get_parent = clk_sysctrl_get_parent,
+};
+
+static struct clk *clk_reg_sysctrl(struct device *dev,
+                               const char *name,
+                               const char **parent_names,
+                               u8 num_parents,
+                               u16 *reg_sel,
+                               u8 *reg_mask,
+                               u8 *reg_bits,
+                               unsigned long rate,
+                               unsigned long enable_delay_us,
+                               unsigned long flags,
+                               struct clk_ops *clk_sysctrl_ops)
+{
+       struct clk_sysctrl *clk;
+       struct clk_init_data clk_sysctrl_init;
+       struct clk *clk_reg;
+       int i;
+
+       if (!dev)
+               return ERR_PTR(-EINVAL);
+
+       if (!name || (num_parents > SYSCTRL_MAX_NUM_PARENTS)) {
+               dev_err(dev, "clk_sysctrl: invalid arguments passed\n");
+               return ERR_PTR(-EINVAL);
+       }
+
+       clk = devm_kzalloc(dev, sizeof(struct clk_sysctrl), GFP_KERNEL);
+       if (!clk) {
+               dev_err(dev, "clk_sysctrl: could not allocate clk\n");
+               return ERR_PTR(-ENOMEM);
+       }
+
+       for (i = 0; i < num_parents; i++) {
+               clk->reg_sel[i] = reg_sel[i];
+               clk->reg_bits[i] = reg_bits[i];
+               clk->reg_mask[i] = reg_mask[i];
+       }
+
+       clk->parent_index = 0;
+       clk->rate = rate;
+       clk->enable_delay_us = enable_delay_us;
+       clk->dev = dev;
+
+       clk_sysctrl_init.name = name;
+       clk_sysctrl_init.ops = clk_sysctrl_ops;
+       clk_sysctrl_init.flags = flags;
+       clk_sysctrl_init.parent_names = parent_names;
+       clk_sysctrl_init.num_parents = num_parents;
+       clk->hw.init = &clk_sysctrl_init;
+
+       clk_reg = devm_clk_register(clk->dev, &clk->hw);
+       if (IS_ERR(clk_reg))
+               dev_err(dev, "clk_sysctrl: clk_register failed\n");
+
+       return clk_reg;
+}
+
+struct clk *clk_reg_sysctrl_gate(struct device *dev,
+                               const char *name,
+                               const char *parent_name,
+                               u16 reg_sel,
+                               u8 reg_mask,
+                               u8 reg_bits,
+                               unsigned long enable_delay_us,
+                               unsigned long flags)
+{
+       const char **parent_names = (parent_name ? &parent_name : NULL);
+       u8 num_parents = (parent_name ? 1 : 0);
+
+       return clk_reg_sysctrl(dev, name, parent_names, num_parents,
+                       &reg_sel, &reg_mask, &reg_bits, 0, enable_delay_us,
+                       flags, &clk_sysctrl_gate_ops);
+}
+
+struct clk *clk_reg_sysctrl_gate_fixed_rate(struct device *dev,
+                                       const char *name,
+                                       const char *parent_name,
+                                       u16 reg_sel,
+                                       u8 reg_mask,
+                                       u8 reg_bits,
+                                       unsigned long rate,
+                                       unsigned long enable_delay_us,
+                                       unsigned long flags)
+{
+       const char **parent_names = (parent_name ? &parent_name : NULL);
+       u8 num_parents = (parent_name ? 1 : 0);
+
+       return clk_reg_sysctrl(dev, name, parent_names, num_parents,
+                       &reg_sel, &reg_mask, &reg_bits,
+                       rate, enable_delay_us, flags,
+                       &clk_sysctrl_gate_fixed_rate_ops);
+}
+
+struct clk *clk_reg_sysctrl_set_parent(struct device *dev,
+                               const char *name,
+                               const char **parent_names,
+                               u8 num_parents,
+                               u16 *reg_sel,
+                               u8 *reg_mask,
+                               u8 *reg_bits,
+                               unsigned long flags)
+{
+       return clk_reg_sysctrl(dev, name, parent_names, num_parents,
+                       reg_sel, reg_mask, reg_bits, 0, 0, flags,
+                       &clk_sysctrl_set_parent_ops);
+}
index c3e4491..a2bb92d 100644 (file)
 #define __UX500_CLK_H
 
 #include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/types.h>
 
 struct clk *clk_reg_prcc_pclk(const char *name,
                              const char *parent_name,
-                             unsigned int phy_base,
+                             resource_size_t phy_base,
                              u32 cg_sel,
                              unsigned long flags);
 
 struct clk *clk_reg_prcc_kclk(const char *name,
                              const char *parent_name,
-                             unsigned int phy_base,
+                             resource_size_t phy_base,
                              u32 cg_sel,
                              unsigned long flags);
 
@@ -57,4 +59,32 @@ struct clk *clk_reg_prcmu_opp_volt_scalable(const char *name,
                                            unsigned long rate,
                                            unsigned long flags);
 
+struct clk *clk_reg_sysctrl_gate(struct device *dev,
+                                const char *name,
+                                const char *parent_name,
+                                u16 reg_sel,
+                                u8 reg_mask,
+                                u8 reg_bits,
+                                unsigned long enable_delay_us,
+                                unsigned long flags);
+
+struct clk *clk_reg_sysctrl_gate_fixed_rate(struct device *dev,
+                                           const char *name,
+                                           const char *parent_name,
+                                           u16 reg_sel,
+                                           u8 reg_mask,
+                                           u8 reg_bits,
+                                           unsigned long rate,
+                                           unsigned long enable_delay_us,
+                                           unsigned long flags);
+
+struct clk *clk_reg_sysctrl_set_parent(struct device *dev,
+                                      const char *name,
+                                      const char **parent_names,
+                                      u8 num_parents,
+                                      u16 *reg_sel,
+                                      u8 *reg_mask,
+                                      u8 *reg_bits,
+                                      unsigned long flags);
+
 #endif /* __UX500_CLK_H */
index ec3b88f..c16ca78 100644 (file)
@@ -3,5 +3,5 @@ obj-$(CONFIG_ICST)              += clk-icst.o
 obj-$(CONFIG_ARCH_INTEGRATOR)  += clk-integrator.o
 obj-$(CONFIG_INTEGRATOR_IMPD1) += clk-impd1.o
 obj-$(CONFIG_ARCH_REALVIEW)    += clk-realview.o
-obj-$(CONFIG_ARCH_VEXPRESS)    += clk-vexpress.o
+obj-$(CONFIG_ARCH_VEXPRESS)    += clk-vexpress.o clk-sp810.o
 obj-$(CONFIG_VEXPRESS_CONFIG)  += clk-vexpress-osc.o
diff --git a/drivers/clk/versatile/clk-sp810.c b/drivers/clk/versatile/clk-sp810.c
new file mode 100644 (file)
index 0000000..bf9b15a
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * 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.
+ *
+ * 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.
+ *
+ * Copyright (C) 2013 ARM Limited
+ */
+
+#include <linux/amba/sp810.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+#define to_clk_sp810_timerclken(_hw) \
+               container_of(_hw, struct clk_sp810_timerclken, hw)
+
+struct clk_sp810;
+
+struct clk_sp810_timerclken {
+       struct clk_hw hw;
+       struct clk *clk;
+       struct clk_sp810 *sp810;
+       int channel;
+};
+
+struct clk_sp810 {
+       struct device_node *node;
+       int refclk_index, timclk_index;
+       void __iomem *base;
+       spinlock_t lock;
+       struct clk_sp810_timerclken timerclken[4];
+       struct clk *refclk;
+       struct clk *timclk;
+};
+
+static u8 clk_sp810_timerclken_get_parent(struct clk_hw *hw)
+{
+       struct clk_sp810_timerclken *timerclken = to_clk_sp810_timerclken(hw);
+       u32 val = readl(timerclken->sp810->base + SCCTRL);
+
+       return !!(val & (1 << SCCTRL_TIMERENnSEL_SHIFT(timerclken->channel)));
+}
+
+static int clk_sp810_timerclken_set_parent(struct clk_hw *hw, u8 index)
+{
+       struct clk_sp810_timerclken *timerclken = to_clk_sp810_timerclken(hw);
+       struct clk_sp810 *sp810 = timerclken->sp810;
+       u32 val, shift = SCCTRL_TIMERENnSEL_SHIFT(timerclken->channel);
+       unsigned long flags = 0;
+
+       if (WARN_ON(index > 1))
+               return -EINVAL;
+
+       spin_lock_irqsave(&sp810->lock, flags);
+
+       val = readl(sp810->base + SCCTRL);
+       val &= ~(1 << shift);
+       val |= index << shift;
+       writel(val, sp810->base + SCCTRL);
+
+       spin_unlock_irqrestore(&sp810->lock, flags);
+
+       return 0;
+}
+
+/*
+ * FIXME - setting the parent every time .prepare is invoked is inefficient.
+ * This is better handled by a dedicated clock tree configuration mechanism at
+ * init-time.  Revisit this later when such a mechanism exists
+ */
+static int clk_sp810_timerclken_prepare(struct clk_hw *hw)
+{
+       struct clk_sp810_timerclken *timerclken = to_clk_sp810_timerclken(hw);
+       struct clk_sp810 *sp810 = timerclken->sp810;
+       struct clk *old_parent = __clk_get_parent(hw->clk);
+       struct clk *new_parent;
+
+       if (!sp810->refclk)
+               sp810->refclk = of_clk_get(sp810->node, sp810->refclk_index);
+
+       if (!sp810->timclk)
+               sp810->timclk = of_clk_get(sp810->node, sp810->timclk_index);
+
+       if (WARN_ON(IS_ERR(sp810->refclk) || IS_ERR(sp810->timclk)))
+               return -ENOENT;
+
+       /* Select fastest parent */
+       if (clk_get_rate(sp810->refclk) > clk_get_rate(sp810->timclk))
+               new_parent = sp810->refclk;
+       else
+               new_parent = sp810->timclk;
+
+       /* Switch the parent if necessary */
+       if (old_parent != new_parent) {
+               clk_prepare(new_parent);
+               clk_set_parent(hw->clk, new_parent);
+               clk_unprepare(old_parent);
+       }
+
+       return 0;
+}
+
+static void clk_sp810_timerclken_unprepare(struct clk_hw *hw)
+{
+       struct clk_sp810_timerclken *timerclken = to_clk_sp810_timerclken(hw);
+       struct clk_sp810 *sp810 = timerclken->sp810;
+
+       clk_put(sp810->timclk);
+       clk_put(sp810->refclk);
+}
+
+static const struct clk_ops clk_sp810_timerclken_ops = {
+       .prepare = clk_sp810_timerclken_prepare,
+       .unprepare = clk_sp810_timerclken_unprepare,
+       .get_parent = clk_sp810_timerclken_get_parent,
+       .set_parent = clk_sp810_timerclken_set_parent,
+};
+
+struct clk *clk_sp810_timerclken_of_get(struct of_phandle_args *clkspec,
+               void *data)
+{
+       struct clk_sp810 *sp810 = data;
+
+       if (WARN_ON(clkspec->args_count != 1 || clkspec->args[0] >
+                       ARRAY_SIZE(sp810->timerclken)))
+               return NULL;
+
+       return sp810->timerclken[clkspec->args[0]].clk;
+}
+
+void __init clk_sp810_of_setup(struct device_node *node)
+{
+       struct clk_sp810 *sp810 = kzalloc(sizeof(*sp810), GFP_KERNEL);
+       const char *parent_names[2];
+       char name[12];
+       struct clk_init_data init;
+       int i;
+
+       if (!sp810) {
+               pr_err("Failed to allocate memory for SP810!\n");
+               return;
+       }
+
+       sp810->refclk_index = of_property_match_string(node, "clock-names",
+                       "refclk");
+       parent_names[0] = of_clk_get_parent_name(node, sp810->refclk_index);
+
+       sp810->timclk_index = of_property_match_string(node, "clock-names",
+                       "timclk");
+       parent_names[1] = of_clk_get_parent_name(node, sp810->timclk_index);
+
+       if (parent_names[0] <= 0 || parent_names[1] <= 0) {
+               pr_warn("Failed to obtain parent clocks for SP810!\n");
+               return;
+       }
+
+       sp810->node = node;
+       sp810->base = of_iomap(node, 0);
+       spin_lock_init(&sp810->lock);
+
+       init.name = name;
+       init.ops = &clk_sp810_timerclken_ops;
+       init.flags = CLK_IS_BASIC;
+       init.parent_names = parent_names;
+       init.num_parents = ARRAY_SIZE(parent_names);
+
+       for (i = 0; i < ARRAY_SIZE(sp810->timerclken); i++) {
+               snprintf(name, ARRAY_SIZE(name), "timerclken%d", i);
+
+               sp810->timerclken[i].sp810 = sp810;
+               sp810->timerclken[i].channel = i;
+               sp810->timerclken[i].hw.init = &init;
+
+               sp810->timerclken[i].clk = clk_register(NULL,
+                               &sp810->timerclken[i].hw);
+               WARN_ON(IS_ERR(sp810->timerclken[i].clk));
+       }
+
+       of_clk_add_provider(node, clk_sp810_timerclken_of_get, sp810);
+}
+CLK_OF_DECLARE(sp810, "arm,sp810", clk_sp810_of_setup);
index 82b45aa..a4a728d 100644 (file)
@@ -15,8 +15,6 @@
 #include <linux/clkdev.h>
 #include <linux/clk-provider.h>
 #include <linux/err.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
 #include <linux/vexpress.h>
 
 static struct clk *vexpress_sp810_timerclken[4];
@@ -86,50 +84,3 @@ void __init vexpress_clk_init(void __iomem *sp810_base)
        WARN_ON(clk_register_clkdev(vexpress_sp810_timerclken[1],
                                "v2m-timer1", "sp804"));
 }
-
-#if defined(CONFIG_OF)
-
-struct clk *vexpress_sp810_of_get(struct of_phandle_args *clkspec, void *data)
-{
-       if (WARN_ON(clkspec->args_count != 1 || clkspec->args[0] >
-                       ARRAY_SIZE(vexpress_sp810_timerclken)))
-               return NULL;
-
-       return vexpress_sp810_timerclken[clkspec->args[0]];
-}
-
-void __init vexpress_clk_of_init(void)
-{
-       struct device_node *node;
-       struct clk *clk;
-       struct clk *refclk, *timclk;
-
-       of_clk_init(NULL);
-
-       node = of_find_compatible_node(NULL, NULL, "arm,sp810");
-       vexpress_sp810_init(of_iomap(node, 0));
-       of_clk_add_provider(node, vexpress_sp810_of_get, NULL);
-
-       /* Select "better" (faster) parent for SP804 timers */
-       refclk = of_clk_get_by_name(node, "refclk");
-       timclk = of_clk_get_by_name(node, "timclk");
-       if (!WARN_ON(IS_ERR(refclk) || IS_ERR(timclk))) {
-               int i = 0;
-
-               if (clk_get_rate(refclk) > clk_get_rate(timclk))
-                       clk = refclk;
-               else
-                       clk = timclk;
-
-               for (i = 0; i < ARRAY_SIZE(vexpress_sp810_timerclken); i++)
-                       WARN_ON(clk_set_parent(vexpress_sp810_timerclken[i],
-                                       clk));
-       }
-
-       WARN_ON(clk_register_clkdev(vexpress_sp810_timerclken[0],
-                               "v2m-timer0", "sp804"));
-       WARN_ON(clk_register_clkdev(vexpress_sp810_timerclken[1],
-                               "v2m-timer1", "sp804"));
-}
-
-#endif
index 4086b91..0ce85e2 100644 (file)
@@ -23,7 +23,7 @@
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
 #include <linux/sunxi_timer.h>
-#include <linux/clk-provider.h>
+#include <linux/clk/sunxi.h>
 
 #define TIMER_CTL_REG          0x00
 #define TIMER_CTL_ENABLE               (1 << 0)
@@ -123,7 +123,7 @@ void __init sunxi_timer_init(void)
        if (irq <= 0)
                panic("Can't parse IRQ");
 
-       of_clk_init(NULL);
+       sunxi_init_clocks();
 
        clk = of_clk_get(node, 0);
        if (IS_ERR(clk))
index 9c7f580..dd7adff 100644 (file)
@@ -152,7 +152,7 @@ struct clk {
                },                                              \
                .reg = _reg,                                    \
                .shift = _shift,                                \
-               .width = _width,                                \
+               .mask = BIT(_width) - 1,                        \
                .flags = _mux_flags,                            \
                .lock = _lock,                                  \
        };                                                      \
index 7f197d7..1186098 100644 (file)
@@ -45,6 +45,14 @@ struct clk_hw;
  *             undo any work done in the @prepare callback. Called with
  *             prepare_lock held.
  *
+ * @is_prepared: Queries the hardware to determine if the clock is prepared.
+ *             This function is allowed to sleep. Optional, if this op is not
+ *             set then the prepare count will be used.
+ *
+ * @unprepare_unused: Unprepare the clock atomically.  Only called from
+ *             clk_disable_unused for prepare clocks with special needs.
+ *             Called with prepare mutex held. This function may sleep.
+ *
  * @enable:    Enable the clock atomically. This must not return until the
  *             clock is generating a valid clock signal, usable by consumer
  *             devices. Called with enable_lock held. This function must not
@@ -108,6 +116,8 @@ struct clk_hw;
 struct clk_ops {
        int             (*prepare)(struct clk_hw *hw);
        void            (*unprepare)(struct clk_hw *hw);
+       int             (*is_prepared)(struct clk_hw *hw);
+       void            (*unprepare_unused)(struct clk_hw *hw);
        int             (*enable)(struct clk_hw *hw);
        void            (*disable)(struct clk_hw *hw);
        int             (*is_enabled)(struct clk_hw *hw);
@@ -239,9 +249,14 @@ struct clk_div_table {
  * CLK_DIVIDER_ONE_BASED - by default the divisor is the value read from the
  *     register plus one.  If CLK_DIVIDER_ONE_BASED is set then the divider is
  *     the raw value read from the register, with the value of zero considered
- *     invalid
+ *     invalid, unless CLK_DIVIDER_ALLOW_ZERO is set.
  * CLK_DIVIDER_POWER_OF_TWO - clock divisor is 2 raised to the value read from
  *     the hardware register
+ * CLK_DIVIDER_ALLOW_ZERO - Allow zero divisors.  For dividers which have
+ *     CLK_DIVIDER_ONE_BASED set, it is possible to end up with a zero divisor.
+ *     Some hardware implementations gracefully handle this case and allow a
+ *     zero divisor by not modifying their input clock
+ *     (divide by one / bypass).
  */
 struct clk_divider {
        struct clk_hw   hw;
@@ -255,6 +270,7 @@ struct clk_divider {
 
 #define CLK_DIVIDER_ONE_BASED          BIT(0)
 #define CLK_DIVIDER_POWER_OF_TWO       BIT(1)
+#define CLK_DIVIDER_ALLOW_ZERO         BIT(2)
 
 extern const struct clk_ops clk_divider_ops;
 struct clk *clk_register_divider(struct device *dev, const char *name,
@@ -274,7 +290,7 @@ struct clk *clk_register_divider_table(struct device *dev, const char *name,
  * @reg:       register controlling multiplexer
  * @shift:     shift to multiplexer bit field
  * @width:     width of mutliplexer bit field
- * @num_clks:  number of parent clocks
+ * @flags:     hardware-specific flags
  * @lock:      register lock
  *
  * Clock with multiple selectable parents.  Implements .get_parent, .set_parent
@@ -287,8 +303,9 @@ struct clk *clk_register_divider_table(struct device *dev, const char *name,
 struct clk_mux {
        struct clk_hw   hw;
        void __iomem    *reg;
+       u32             *table;
+       u32             mask;
        u8              shift;
-       u8              width;
        u8              flags;
        spinlock_t      *lock;
 };
@@ -297,11 +314,19 @@ struct clk_mux {
 #define CLK_MUX_INDEX_BIT              BIT(1)
 
 extern const struct clk_ops clk_mux_ops;
+
 struct clk *clk_register_mux(struct device *dev, const char *name,
                const char **parent_names, u8 num_parents, unsigned long flags,
                void __iomem *reg, u8 shift, u8 width,
                u8 clk_mux_flags, spinlock_t *lock);
 
+struct clk *clk_register_mux_table(struct device *dev, const char *name,
+               const char **parent_names, u8 num_parents, unsigned long flags,
+               void __iomem *reg, u8 shift, u32 mask,
+               u8 clk_mux_flags, u32 *table, spinlock_t *lock);
+
+void of_fixed_factor_clk_setup(struct device_node *node);
+
 /**
  * struct clk_fixed_factor - fixed multiplier and divider clock
  *
@@ -325,6 +350,37 @@ struct clk *clk_register_fixed_factor(struct device *dev, const char *name,
                const char *parent_name, unsigned long flags,
                unsigned int mult, unsigned int div);
 
+/***
+ * struct clk_composite - aggregate clock of mux, divider and gate clocks
+ *
+ * @hw:                handle between common and hardware-specific interfaces
+ * @mux_hw:    handle between composite and hardware-specific mux clock
+ * @rate_hw:   handle between composite and hardware-specific rate clock
+ * @gate_hw:   handle between composite and hardware-specific gate clock
+ * @mux_ops:   clock ops for mux
+ * @rate_ops:  clock ops for rate
+ * @gate_ops:  clock ops for gate
+ */
+struct clk_composite {
+       struct clk_hw   hw;
+       struct clk_ops  ops;
+
+       struct clk_hw   *mux_hw;
+       struct clk_hw   *rate_hw;
+       struct clk_hw   *gate_hw;
+
+       const struct clk_ops    *mux_ops;
+       const struct clk_ops    *rate_ops;
+       const struct clk_ops    *gate_ops;
+};
+
+struct clk *clk_register_composite(struct device *dev, const char *name,
+               const char **parent_names, int num_parents,
+               struct clk_hw *mux_hw, const struct clk_ops *mux_ops,
+               struct clk_hw *rate_hw, const struct clk_ops *rate_ops,
+               struct clk_hw *gate_hw, const struct clk_ops *gate_ops,
+               unsigned long flags);
+
 /**
  * clk_register - allocate a new clock, register it and return an opaque cookie
  * @dev: device that is registering this clock
@@ -351,6 +407,7 @@ unsigned int __clk_get_enable_count(struct clk *clk);
 unsigned int __clk_get_prepare_count(struct clk *clk);
 unsigned long __clk_get_rate(struct clk *clk);
 unsigned long __clk_get_flags(struct clk *clk);
+bool __clk_is_prepared(struct clk *clk);
 bool __clk_is_enabled(struct clk *clk);
 struct clk *__clk_lookup(const char *name);
 
index b3ac22d..9a6d045 100644 (file)
@@ -28,16 +28,16 @@ struct clk;
  * PRE_RATE_CHANGE - called immediately before the clk rate is changed,
  *     to indicate that the rate change will proceed.  Drivers must
  *     immediately terminate any operations that will be affected by the
- *     rate change.  Callbacks may either return NOTIFY_DONE or
- *     NOTIFY_STOP.
+ *     rate change.  Callbacks may either return NOTIFY_DONE, NOTIFY_OK,
+ *     NOTIFY_STOP or NOTIFY_BAD.
  *
  * ABORT_RATE_CHANGE: called if the rate change failed for some reason
  *     after PRE_RATE_CHANGE.  In this case, all registered notifiers on
  *     the clk will be called with ABORT_RATE_CHANGE. Callbacks must
- *     always return NOTIFY_DONE.
+ *     always return NOTIFY_DONE or NOTIFY_OK.
  *
  * POST_RATE_CHANGE - called after the clk rate change has successfully
- *     completed.  Callbacks must always return NOTIFY_DONE.
+ *     completed.  Callbacks must always return NOTIFY_DONE or NOTIFY_OK.
  *
  */
 #define PRE_RATE_CHANGE                        BIT(0)
diff --git a/include/linux/clk/sunxi.h b/include/linux/clk/sunxi.h
new file mode 100644 (file)
index 0000000..e074fdd
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2012 Maxime Ripard
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.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.
+ */
+
+#ifndef __LINUX_CLK_SUNXI_H_
+#define __LINUX_CLK_SUNXI_H_
+
+void __init sunxi_init_clocks(void);
+
+#endif
diff --git a/include/linux/platform_data/si5351.h b/include/linux/platform_data/si5351.h
new file mode 100644 (file)
index 0000000..92dabca
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * Si5351A/B/C programmable clock generator platform_data.
+ */
+
+#ifndef __LINUX_PLATFORM_DATA_SI5351_H__
+#define __LINUX_PLATFORM_DATA_SI5351_H__
+
+struct clk;
+
+/**
+ * enum si5351_variant - SiLabs Si5351 chip variant
+ * @SI5351_VARIANT_A: Si5351A (8 output clocks, XTAL input)
+ * @SI5351_VARIANT_A3: Si5351A MSOP10 (3 output clocks, XTAL input)
+ * @SI5351_VARIANT_B: Si5351B (8 output clocks, XTAL/VXCO input)
+ * @SI5351_VARIANT_C: Si5351C (8 output clocks, XTAL/CLKIN input)
+ */
+enum si5351_variant {
+       SI5351_VARIANT_A = 1,
+       SI5351_VARIANT_A3 = 2,
+       SI5351_VARIANT_B = 3,
+       SI5351_VARIANT_C = 4,
+};
+
+/**
+ * enum si5351_pll_src - Si5351 pll clock source
+ * @SI5351_PLL_SRC_DEFAULT: default, do not change eeprom config
+ * @SI5351_PLL_SRC_XTAL: pll source clock is XTAL input
+ * @SI5351_PLL_SRC_CLKIN: pll source clock is CLKIN input (Si5351C only)
+ */
+enum si5351_pll_src {
+       SI5351_PLL_SRC_DEFAULT = 0,
+       SI5351_PLL_SRC_XTAL = 1,
+       SI5351_PLL_SRC_CLKIN = 2,
+};
+
+/**
+ * enum si5351_multisynth_src - Si5351 multisynth clock source
+ * @SI5351_MULTISYNTH_SRC_DEFAULT: default, do not change eeprom config
+ * @SI5351_MULTISYNTH_SRC_VCO0: multisynth source clock is VCO0
+ * @SI5351_MULTISYNTH_SRC_VCO1: multisynth source clock is VCO1/VXCO
+ */
+enum si5351_multisynth_src {
+       SI5351_MULTISYNTH_SRC_DEFAULT = 0,
+       SI5351_MULTISYNTH_SRC_VCO0 = 1,
+       SI5351_MULTISYNTH_SRC_VCO1 = 2,
+};
+
+/**
+ * enum si5351_clkout_src - Si5351 clock output clock source
+ * @SI5351_CLKOUT_SRC_DEFAULT: default, do not change eeprom config
+ * @SI5351_CLKOUT_SRC_MSYNTH_N: clkout N source clock is multisynth N
+ * @SI5351_CLKOUT_SRC_MSYNTH_0_4: clkout N source clock is multisynth 0 (N<4)
+ *                                or 4 (N>=4)
+ * @SI5351_CLKOUT_SRC_XTAL: clkout N source clock is XTAL
+ * @SI5351_CLKOUT_SRC_CLKIN: clkout N source clock is CLKIN (Si5351C only)
+ */
+enum si5351_clkout_src {
+       SI5351_CLKOUT_SRC_DEFAULT = 0,
+       SI5351_CLKOUT_SRC_MSYNTH_N = 1,
+       SI5351_CLKOUT_SRC_MSYNTH_0_4 = 2,
+       SI5351_CLKOUT_SRC_XTAL = 3,
+       SI5351_CLKOUT_SRC_CLKIN = 4,
+};
+
+/**
+ * enum si5351_drive_strength - Si5351 clock output drive strength
+ * @SI5351_DRIVE_DEFAULT: default, do not change eeprom config
+ * @SI5351_DRIVE_2MA: 2mA clock output drive strength
+ * @SI5351_DRIVE_4MA: 4mA clock output drive strength
+ * @SI5351_DRIVE_6MA: 6mA clock output drive strength
+ * @SI5351_DRIVE_8MA: 8mA clock output drive strength
+ */
+enum si5351_drive_strength {
+       SI5351_DRIVE_DEFAULT = 0,
+       SI5351_DRIVE_2MA = 2,
+       SI5351_DRIVE_4MA = 4,
+       SI5351_DRIVE_6MA = 6,
+       SI5351_DRIVE_8MA = 8,
+};
+
+/**
+ * struct si5351_clkout_config - Si5351 clock output configuration
+ * @clkout: clkout number
+ * @multisynth_src: multisynth source clock
+ * @clkout_src: clkout source clock
+ * @pll_master: if true, clkout can also change pll rate
+ * @drive: output drive strength
+ * @rate: initial clkout rate, or default if 0
+ */
+struct si5351_clkout_config {
+       enum si5351_multisynth_src multisynth_src;
+       enum si5351_clkout_src clkout_src;
+       enum si5351_drive_strength drive;
+       bool pll_master;
+       unsigned long rate;
+};
+
+/**
+ * struct si5351_platform_data - Platform data for the Si5351 clock driver
+ * @variant: Si5351 chip variant
+ * @clk_xtal: xtal input clock
+ * @clk_clkin: clkin input clock
+ * @pll_src: array of pll source clock setting
+ * @clkout: array of clkout configuration
+ */
+struct si5351_platform_data {
+       enum si5351_variant variant;
+       struct clk *clk_xtal;
+       struct clk *clk_clkin;
+       enum si5351_pll_src pll_src[2];
+       struct si5351_clkout_config clkout[8];
+};
+
+#endif